1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.server.cros.faft.cr50_test import Cr50Test 10from autotest_lib.server.cros.servo import servo 11 12 13class firmware_Cr50OpenWhileAPOff(Cr50Test): 14 """Verify the console can be opened while the AP is off. 15 16 Make sure it runs ok when cr50 saw the AP turn off and when it resets while 17 the AP is off. 18 19 This test would work the same with any cr50 ccd command that uses vendor 20 commands. 'ccd open' is just one. 21 """ 22 version = 1 23 24 SLEEP_DELAY = 20 25 SHORT_DELAY = 2 26 CCD_PASSWORD_RATE_LIMIT = 3 27 28 def initialize(self, host, cmdline_args, full_args): 29 """Initialize the test""" 30 self.changed_dut_state = False 31 super(firmware_Cr50OpenWhileAPOff, self).initialize(host, cmdline_args, 32 full_args) 33 34 if not hasattr(self, 'cr50'): 35 raise error.TestNAError('Test can only be run on devices with ' 36 'access to the Cr50 console') 37 38 # TODO(mruthven): replace with dependency on servo v4 with servo micro 39 # and type c cable. 40 if (self.servo.get_servo_version(active=True) != 41 'servo_v4_with_servo_micro'): 42 raise error.TestNAError('Run using servo v4 with servo micro') 43 44 if not self.cr50.servo_dts_mode_is_valid(): 45 raise error.TestNAError('Plug in servo v4 type c cable into ccd ' 46 'port') 47 48 self.fast_ccd_open(enable_testlab=True) 49 # make sure password is cleared. 50 self.cr50.send_command('ccd reset') 51 # Set GscFullConsole to Always, so we can always use gpioset. 52 self.cr50.set_cap('GscFullConsole', 'Always') 53 # You can only open cr50 from the console if a password is set. Set 54 # a password, so we can use it to open cr50 while the AP is off. 55 self.set_ccd_password(self.CCD_PASSWORD) 56 57 # Asserting warm_reset will hold the AP in reset if the system uses 58 # SYS_RST instead of PLT_RST. If the system uses PLT_RST, we have to 59 # hold the EC in reset to guarantee the device won't turn on during 60 # open. 61 # warm_reset doesn't interfere with rdd, so it's best to use that when 62 # possible. 63 self.reset_ec = self.cr50.uses_board_property('BOARD_USE_PLT_RESET') 64 self.changed_dut_state = True 65 if self.reset_ec and not self.reset_device_get_deep_sleep_count(True): 66 # Some devices can't tell the AP is off when the EC is off. Try 67 # deep sleep with just the AP off. 68 self.reset_ec = False 69 # If deep sleep doesn't work at all, we can't run the test. 70 if not self.reset_device_get_deep_sleep_count(True): 71 raise error.TestNAError('Skipping test on device without deep ' 72 'sleep support') 73 # We can't hold the ec in reset and enter deep sleep. Set the 74 # capability so physical presence isn't required for open. 75 logging.info("deep sleep doesn't work with EC in reset. skipping " 76 "physical presence checks.") 77 # set OpenNoLongPP so open won't require pressing the power button. 78 self.cr50.set_cap('OpenNoLongPP', 'Always') 79 else: 80 logging.info('Physical presence can be used during open') 81 82 83 def cleanup(self): 84 """Make sure the device is on at the end of the test""" 85 # If we got far enough to start changing the DUT power state, attempt to 86 # turn the DUT back on and reenable the cr50 console. 87 try: 88 if self.changed_dut_state: 89 self.restore_dut() 90 finally: 91 super(firmware_Cr50OpenWhileAPOff, self).cleanup() 92 93 94 def restore_dut(self): 95 """Turn on the device and reset cr50 96 97 Do a deep sleep reset to fix the cr50 console. Then turn the device on. 98 99 Raises: 100 TestFail if the cr50 console doesn't work 101 """ 102 logging.info('attempt cr50 console recovery') 103 104 # The console may be hung. Run through reset manually, so we dont need 105 # the console. 106 self.turn_device('off') 107 # Toggle dts mode to enter and exit deep sleep 108 self.toggle_dts_mode() 109 # Turn the device back on 110 self.turn_device('on') 111 112 # Verify the cr50 console responds to commands. 113 try: 114 logging.info(self.cr50.get_ccdstate()) 115 except servo.ResponsiveConsoleError as e: 116 logging.info('Console is responsive. Unable to match output: %s', 117 str(e)) 118 except servo.UnresponsiveConsoleError as e: 119 raise error.TestFail('Could not restore Cr50 console') 120 logging.info('Cr50 console ok.') 121 122 123 def turn_device(self, state): 124 """Turn the device off or on. 125 126 If we are testing ccd open fully, it will also assert device reset so 127 power button presses wont turn on the AP 128 """ 129 # Assert or deassert the device reset signal. The reset signal state 130 # should be the inverse of the device state. 131 reset_signal_state = 'on' if state == 'off' else 'off' 132 if self.reset_ec: 133 self.servo.set('cold_reset', reset_signal_state) 134 else: 135 self.servo.set('warm_reset', reset_signal_state) 136 137 time.sleep(self.SHORT_DELAY) 138 139 # Press the power button to turn on the AP, if it doesn't automatically 140 # turn on after deasserting the reset signal. ap_is_on will print the 141 # ccdstate which is useful for debugging. Do that first, so it always 142 # happens. 143 if not self.cr50.ap_is_on() and state == 'on': 144 self.servo.power_short_press() 145 time.sleep(self.SHORT_DELAY) 146 147 148 def reset_device_get_deep_sleep_count(self, deep_sleep): 149 """Reset the device. Use dts mode to enable deep sleep if requested. 150 151 Args: 152 deep_sleep: True if Cr50 should enter deep sleep 153 154 Returns: 155 The number of times Cr50 entered deep sleep during reset 156 """ 157 self.turn_device('off') 158 # Do a deep sleep reset to restore the cr50 console. 159 ds_count = self.deep_sleep_reset_get_count() if deep_sleep else 0 160 self.turn_device('on') 161 return ds_count 162 163 164 def set_dts(self, state): 165 """Set servo v4 dts mode""" 166 self.servo.set_dts_mode(state) 167 # Some boards can't detect DTS mode when the EC is off. After 0.X.18, 168 # we can set CCD_MODE_L manually using gpioset. If detection is working, 169 # this won't do anything. If it isn't working, it'll force cr50 to 170 # disconnect ccd. 171 if state == 'off': 172 time.sleep(self.SHORT_DELAY) 173 self.cr50.send_command('gpioset CCD_MODE_L 1') 174 175 176 def toggle_dts_mode(self): 177 """Toggle DTS mode to enable and disable deep sleep""" 178 # We cant use cr50 ccd_disable/enable, because those uses the cr50 179 # console. Call servo_v4_dts_mode directly. 180 self.set_dts('off') 181 182 time.sleep(self.SLEEP_DELAY) 183 self.set_dts('on') 184 185 186 def deep_sleep_reset_get_count(self): 187 """Toggle ccd to get to do a deep sleep reset 188 189 Returns: 190 The number of times cr50 entered deep sleep 191 """ 192 start_count = self.cr50.get_deep_sleep_count() 193 # CCD is what's keeping Cr50 awake. Toggle DTS mode to turn off ccd 194 # so cr50 will enter deep sleep 195 self.toggle_dts_mode() 196 # Return the number of times cr50 entered deep sleep. 197 return self.cr50.get_deep_sleep_count() - start_count 198 199 200 def try_ccd_open(self, cr50_reset): 201 """Try 'ccd open' and make sure the console doesn't hang""" 202 self.cr50.set_ccd_level('lock', self.CCD_PASSWORD) 203 try: 204 self.turn_device('off') 205 if cr50_reset: 206 if not self.deep_sleep_reset_get_count(): 207 raise error.TestFail('Did not detect a cr50 reset') 208 # Verify ccd open 209 self.cr50.set_ccd_level('open', self.CCD_PASSWORD) 210 finally: 211 self.restore_dut() 212 213 214 def run_once(self): 215 """Turn off the AP and try ccd open.""" 216 self.try_ccd_open(False) 217 self.try_ccd_open(True) 218