1# Copyright 2017 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 chrome_ti50 11 12 13class firmware_Cr50CCDServoCap(Cr50Test): 14 """Verify Cr50 CCD output enable/disable when servo is connected. 15 16 Verify Cr50 will enable/disable the CCD servo output capabilities when servo 17 is attached/detached. 18 """ 19 version = 1 20 21 # Time used to wait for Cr50 to detect the servo state. Cr50 updates the ccd 22 # state once a second. Wait 2 seconds to be conservative. 23 SLEEP = 2 24 25 # A list of the actions we should verify 26 TEST_CASES = [ 27 'fake_servo on, cr50_run reboot', 28 'fake_servo on, rdd attach, cr50_run reboot', 29 30 'rdd attach, fake_servo on, cr50_run reboot, fake_servo off', 31 'rdd attach, fake_servo on, rdd detach', 32 'rdd attach, fake_servo off, rdd detach', 33 ] 34 35 ON = 0 36 OFF = 1 37 UNDETECTABLE = 2 38 STATUS_MAP = [ 'on', 'off', 'unknown' ] 39 # Create maps for the different ccd states. Mapping each state to 'on', 40 # 'off', and 'unknown'. These lists map to the acceptable [ on values, off 41 # values, and unknown state values] 42 ON_MAP = [ 'on', 'off', '' ] 43 ENABLED_MAP = [ 'enabled', 'disabled', '' ] 44 CONNECTED_MAP = [ 'connected', 'disconnected', 'undetectable' ] 45 ASSERTED_MAP = ['asserted', 'deasserted', ''] 46 VALID_STATES = { 47 'AP': ON_MAP, 48 'EC': ON_MAP, 49 'AP UART': ON_MAP, 50 'Rdd': CONNECTED_MAP, 51 'Servo': CONNECTED_MAP, 52 'CCD EXT': ENABLED_MAP, 53 'CCD_MODE': ASSERTED_MAP, 54 } 55 # TODO(mruthven): remove CCD_ENABLED_KEYS and mentions of 'CCD EXT' once 56 # prepvt and mp images use CCD_MODE. 57 # Old ccdstate uses CCD EXT. The new ccdstate output uses CCD_MODE. 58 CCD_ENABLED_KEYS = ['CCD EXT', 'CCD_MODE'] 59 # RESULT_ORDER is a list of the CCD state strings. The order corresponds 60 # with the order of the key states in EXPECTED_RESULTS. 61 RESULT_ORDER = ['Rdd', 'CCD_MODE', 'Servo'] 62 # A dictionary containing an order of steps to verify and the expected ccd 63 # states as the value. 64 # 65 # The keys are a list of strings with the order of steps to run. 66 # 67 # The values are the expected state of [rdd, ccd ext, servo]. The ccdstate 68 # strings are in RESULT_ORDER. The order of the EXPECTED_RESULTS key states 69 # must match the order in RESULT_ORDER. 70 # 71 # There are three valid states: UNDETECTABLE, ON, or OFF. Undetectable only 72 # describes the servo state when EC uart is enabled. If the ec uart is 73 # enabled, cr50 cannot detect servo and the state becomes undetectable. All 74 # other ccdstates can only be off or on. Cr50 has a lot of different words 75 # for off off and on. So VALID_STATES can be used to convert off, on, and 76 # undetectable to the actual state strings. 77 EXPECTED_RESULTS = { 78 # The state all tests will start with. Servo and the ccd cable are 79 # disconnected. 80 'reset_ccd state' : [OFF, OFF, OFF], 81 82 # If rdd is attached all ccd functionality will be enabled, and servo 83 # will be undetectable. 84 'rdd attach' : [ON, ON, UNDETECTABLE], 85 86 # Cr50 cannot detect servo if ccd has been enabled first 87 'rdd attach, fake_servo off' : [ON, ON, UNDETECTABLE], 88 'rdd attach, fake_servo off, rdd detach' : [OFF, OFF, OFF], 89 'rdd attach, fake_servo on' : [ON, ON, UNDETECTABLE], 90 'rdd attach, fake_servo on, rdd detach' : [OFF, OFF, ON], 91 # Cr50 can detect servo after a reboot even if rdd was attached before 92 # servo. 93 'rdd attach, fake_servo on, cr50_run reboot' : [ON, ON, ON], 94 # Once servo is detached, Cr50 will immediately reenable the EC uart. 95 'rdd attach, fake_servo on, cr50_run reboot, fake_servo off' : 96 [ON, ON, UNDETECTABLE], 97 98 # Cr50 can detect a servo attach 99 'fake_servo on' : [OFF, OFF, ON], 100 # Cr50 knows servo is attached when ccd is enabled, so it wont enable 101 # uart. 102 'fake_servo on, rdd attach' : [ON, ON, ON], 103 'fake_servo on, rdd attach, cr50_run reboot' : [ON, ON, ON], 104 'fake_servo on, cr50_run reboot' : [OFF, OFF, ON], 105 } 106 107 108 def initialize(self, host, cmdline_args, full_args): 109 super(firmware_Cr50CCDServoCap, self).initialize(host, cmdline_args, 110 full_args) 111 if not hasattr(self, 'cr50'): 112 raise error.TestNAError('Test can only be run on devices with ' 113 'access to the Cr50 console') 114 115 if ('servo_v4' not in self.servo.get_servo_type() 116 or not self.servo.main_device_is_flex()): 117 raise error.TestNAError('Must use servo v4 with flex(c2d2 or ' 118 'servo_micro)') 119 120 if not self.cr50.servo_dts_mode_is_valid(): 121 raise error.TestNAError('Need working servo v4 DTS control') 122 123 if not self.cr50.check_servo_monitor(): 124 raise error.TestNAError('Cannot run on device that does not ' 125 'support servo dectection with ' 126 'ec_uart_en:off/on') 127 # Make sure cr50 is open with testlab enabled. 128 self.fast_ccd_open(enable_testlab=True) 129 if not self.cr50.testlab_is_on(): 130 raise error.TestNAError('Cr50 testlab mode needs to be enabled') 131 logging.info('Cr50 is %s', self.servo.get('gsc_ccd_level')) 132 self.cr50.set_cap('UartGscTxECRx', 'Always') 133 self.ec_efs_support = ( 134 self.cr50.uses_board_property('BOARD_EC_CR50_COMM_SUPPORT')) 135 self._ccd_prefix = ('' if self.servo.main_device_is_ccd() else 136 self.servo.get_ccd_servo_device()) 137 # Check EC uart if servo has ccd controls and the board has an EC. 138 self.check_ec_uart = ( 139 self.servo.has_control('ec_board', prefix=self._ccd_prefix) 140 and self.check_ec_capability(suppress_warning=True)) 141 142 143 def cleanup(self): 144 """Reenable the EC uart""" 145 try: 146 self.fake_servo('on') 147 self.rdd('detach') 148 self.rdd('attach') 149 finally: 150 super(firmware_Cr50CCDServoCap, self).cleanup() 151 152 153 def state_matches(self, state_dict, state_name, expected_value): 154 """Check the current state. Make sure it matches expected value""" 155 if state_name in self.CCD_ENABLED_KEYS: 156 for state_name in self.CCD_ENABLED_KEYS: 157 if state_name in state_dict: 158 logging.info('Using %r for ccd enabled key', state_name) 159 break 160 161 valid_state = self.VALID_STATES[state_name][expected_value] 162 # I2C isn't a reliable flag, because the hardware often doesn't support 163 # it. Remove any I2C flags from the ccdstate output. 164 current_state = state_dict[state_name].replace(' I2C', '') 165 if isinstance(valid_state, list): 166 return current_state in valid_state 167 return current_state == valid_state 168 169 170 def state_is_on(self, ccdstate, state_name): 171 """Returns true if the state is on""" 172 return self.state_matches(ccdstate, state_name, self.ON) 173 174 175 def ccd_ec_uart_works(self): 176 """Returns True if the CCD ec uart works.""" 177 try: 178 self.servo.get('ec_board', prefix=self._ccd_prefix) 179 logging.info('ccd ec console is responsive') 180 return True 181 except: 182 logging.info('ccd ec console is unresponsive') 183 return False 184 185 186 def check_state_flags(self, ccdstate): 187 """Check the state flags against the reset of the device state 188 189 If there is any mismatch between the device state and state flags, 190 return a list of errors. 191 """ 192 flags = ccdstate['State flags'] 193 ap_uart_enabled = 'UARTAP' in flags 194 ec_uart_enabled = 'UARTEC' in flags 195 ap_uart_tx_enabled = 'UARTAP+TX' in flags 196 ec_uart_tx_enabled = 'UARTEC+TX' in flags 197 ec_usb_tx_enabled = 'USBEC+TX' in flags 198 199 ccd_ec_uart_enabled = ec_uart_tx_enabled and ec_usb_tx_enabled 200 ccd_enabled = ap_uart_enabled or ec_usb_tx_enabled 201 output_enabled = ap_uart_tx_enabled 202 if not self.ec_efs_support: 203 output_enabled |= ec_uart_tx_enabled 204 ccd_enabled |= ec_uart_enabled 205 206 ccd_mode_is_asserted = self.state_is_on(ccdstate, 'CCD_MODE') 207 mismatch = [] 208 logging.info('checking state flags') 209 if ccd_enabled and not ccd_mode_is_asserted: 210 mismatch.append('CCD functionality enabled CCD_MODE asserted') 211 if ccd_mode_is_asserted: 212 if output_enabled and self.state_is_on(ccdstate, 'Servo'): 213 mismatch.append('CCD output is enabled with servo attached') 214 if not isinstance(self.cr50, chrome_ti50.ChromeTi50): 215 if ap_uart_enabled != self.state_is_on(ccdstate, 'AP UART'): 216 mismatch.append('AP UART enabled without AP UART on') 217 if ec_uart_enabled != self.state_is_on(ccdstate, 'EC'): 218 mismatch.append('EC UART enabled without EC on') 219 if self.check_ec_uart: 220 ccd_ec_uart_works = self.ccd_ec_uart_works() 221 if (self.servo.get('ec_uart_en') == 'off' 222 and ccd_ec_uart_enabled and not ccd_ec_uart_works): 223 mismatch.append('ccd ec uart does not work with EC+TX ' 224 'enabled.') 225 if not ccd_ec_uart_enabled and ccd_ec_uart_works: 226 mismatch.append('ccd ec uart works with EC+TX disabled.') 227 return mismatch 228 229 230 231 def verify_ccdstate(self, run): 232 """Verify the current state matches the expected result from the run. 233 234 Args: 235 run: the string representing the actions that have been run. 236 237 Raises: 238 TestError if any of the states are not correct 239 """ 240 if run not in self.EXPECTED_RESULTS: 241 raise error.TestError('Add results for %s to EXPECTED_RESULTS' % run) 242 expected_states = self.EXPECTED_RESULTS[run] 243 244 # Wait a short time for the ccd state to settle 245 time.sleep(self.SLEEP) 246 247 ccdstate = self.cr50.get_ccdstate() 248 # Check the state flags. Make sure they're in line with the rest of 249 # ccdstate 250 mismatch = self.check_state_flags(ccdstate) 251 for i, expected_state in enumerate(expected_states): 252 name = self.RESULT_ORDER[i] 253 if expected_state == None: 254 logging.info('No expected %s state skipping check', name) 255 continue 256 # Check that the current state matches the expected state 257 if not self.state_matches(ccdstate, name, expected_state): 258 mismatch.append('%s is %r not %r' % (name, ccdstate[name], 259 self.STATUS_MAP[expected_state])) 260 if mismatch: 261 logging.info(ccdstate) 262 raise error.TestFail('Unexpected states after %s: %s' % (run, 263 mismatch)) 264 265 266 def cr50_run(self, action): 267 """Reboot cr50 268 269 @param action: string 'reboot' 270 """ 271 if action == 'reboot': 272 self.cr50.reboot() 273 self.cr50.send_command('ccd testlab open') 274 time.sleep(self.SLEEP) 275 276 277 def reset_ccd(self, state=None): 278 """detach the ccd cable and disconnect servo. 279 280 State is ignored. It just exists to be consistent with the other action 281 functions. 282 283 @param state: a var that is ignored 284 """ 285 self.rdd('detach') 286 self.fake_servo('off') 287 288 289 def rdd(self, state): 290 """Attach or detach the ccd cable. 291 292 @param state: string 'attach' or 'detach' 293 """ 294 self.servo.set_dts_mode('on' if state == 'attach' else 'off') 295 time.sleep(self.SLEEP) 296 297 298 def fake_servo(self, state): 299 """Mimic servo on/off 300 301 Cr50 monitors the servo EC uart tx signal to detect servo. If the signal 302 is pulled up, then Cr50 will think servo is connnected. Enable the ec 303 uart to enable the pullup. Disable the it to remove the pullup. 304 305 It takes some time for Cr50 to detect the servo state so wait 2 seconds 306 before returning. 307 """ 308 self.servo.set('ec_uart_en', state) 309 310 # Cr50 needs time to detect the servo state 311 time.sleep(self.SLEEP) 312 313 314 def run_steps(self, steps): 315 """Do each step in steps and then verify the uart state. 316 317 The uart state is order dependent, so we need to know all of the 318 previous steps to verify the state. This will do all of the steps in 319 the string and verify the Cr50 CCD uart state after each step. 320 321 @param steps: a comma separated string with the steps to run 322 """ 323 # The order of steps is separated by ', '. Remove the last step and 324 # run all of the steps before it. 325 separated_steps = steps.rsplit(', ', 1) 326 if len(separated_steps) > 1: 327 self.run_steps(separated_steps[0]) 328 329 step = separated_steps[-1] 330 # The func and state are separated by ' ' 331 func, state = step.split(' ') 332 logging.info('running %s', step) 333 getattr(self, func)(state) 334 335 # Verify the ccd state is correct 336 self.verify_ccdstate(steps) 337 338 339 def run_once(self): 340 """Run through TEST_CASES and verify that Cr50 enables/disables uart""" 341 for steps in self.TEST_CASES: 342 self.run_steps('reset_ccd state') 343 logging.info('TESTING: %s', steps) 344 self.run_steps(steps) 345 logging.info('VERIFIED: %s', steps) 346