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