1# Copyright 2016 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 math 7import time 8 9from autotest_lib.client.common_lib import error 10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 11from autotest_lib.server.cros.servo import pd_console 12 13 14class firmware_PDVbusRequest(FirmwareTest): 15 """ 16 Servo based USB PD VBUS level test. This test is written to use both 17 the DUT and PDTester test board. It requires that the DUT support 18 dualrole (SRC or SNK) operation. VBUS change requests occur in two 19 methods. 20 21 The 1st test initiates the VBUS change by using special PDTester 22 feature to send new SRC CAP message. This causes the DUT to request 23 a new VBUS voltage matching what's in the SRC CAP message. 24 25 The 2nd test configures the DUT in SNK mode and uses the pd console 26 command 'pd 0/1 dev V' command where V is the desired voltage 27 5/12/20. This test is more risky and won't be executed if the 1st 28 test is failed. If the DUT max input voltage is not 20V, like 12V, 29 and the FAFT config is set wrong, it may negotiate to a voltage 30 higher than it can support, that may damage the DUT. 31 32 Pass critera is all voltage transitions are successful. 33 34 """ 35 version = 1 36 37 PD_SETTLE_DELAY = 4 38 USBC_SINK_VOLTAGE = 5 39 VBUS_TOLERANCE = 0.12 40 41 VOLTAGE_SEQUENCE = [5, 12, 20, 12, 5, 20, 5, 5, 12, 12, 20] 42 43 def _compare_vbus(self, expected_vbus_voltage, ok_to_fail): 44 """Check VBUS using pdtester 45 46 @param expected_vbus_voltage: nominal VBUS level (in volts) 47 @param ok_to_fail: True to not treat voltage-not-matched as failure. 48 49 @returns: a tuple containing pass/fail indication and logging string 50 """ 51 # Get Vbus voltage and current 52 vbus_voltage = self.pdtester.vbus_voltage 53 # Compute voltage tolerance range 54 tolerance = self.VBUS_TOLERANCE * expected_vbus_voltage 55 voltage_difference = math.fabs(expected_vbus_voltage - vbus_voltage) 56 result_str = 'Target = %02dV:\tAct = %.2f\tDelta = %.2f' % \ 57 (expected_vbus_voltage, vbus_voltage, voltage_difference) 58 # Verify that measured Vbus voltage is within expected range 59 voltage_difference = math.fabs(expected_vbus_voltage - vbus_voltage) 60 if voltage_difference > tolerance: 61 result = 'ALLOWED_FAIL' if ok_to_fail else 'FAIL' 62 else: 63 result = 'PASS' 64 return result, result_str 65 66 def initialize(self, host, cmdline_args, flip_cc=False): 67 super(firmware_PDVbusRequest, self).initialize(host, cmdline_args) 68 self.setup_pdtester(flip_cc) 69 # Only run in normal mode 70 self.switcher.setup_mode('normal') 71 self.usbpd.send_command('chan 0') 72 73 def cleanup(self): 74 # Set back to the max 20V SRC mode at the end. 75 self.pdtester.charge(self.pdtester.USBC_MAX_VOLTAGE) 76 77 self.usbpd.send_command('chan 0xffffffff') 78 super(firmware_PDVbusRequest, self).cleanup() 79 80 def run_once(self): 81 """Exectue VBUS request test. 82 83 """ 84 # TODO(b/35573842): Refactor to use PDPortPartner to probe the port 85 self.pdtester_port = 1 if 'servo_v4' in self.pdtester.servo_type else 0 86 87 # create objects for pd utilities 88 pd_dut_utils = pd_console.PDConsoleUtils(self.usbpd) 89 pd_pdtester_utils = pd_console.PDConsoleUtils(self.pdtester) 90 91 # Make sure PD support exists in the UART console 92 if pd_dut_utils.verify_pd_console() == False: 93 raise error.TestFail("pd command not present on console!") 94 95 # Type C connection (PD contract) should exist at this point 96 dut_state = pd_dut_utils.query_pd_connection() 97 logging.info('DUT PD connection state: %r', dut_state) 98 if dut_state['connect'] == False: 99 raise error.TestFail("pd connection not found") 100 101 dut_voltage_limit = self.faft_config.usbc_input_voltage_limit 102 is_override = self.faft_config.charger_profile_override 103 if is_override: 104 logging.info('*** Custom charger profile takes over, which may ' 105 'cause voltage-not-matched. It is OK to fail. *** ') 106 107 pdtester_failures = [] 108 logging.info('Start PDTester initiated tests') 109 for voltage in self.pdtester.get_charging_voltages(): 110 logging.info('********* %r *********', voltage) 111 # Set charging voltage 112 self.pdtester.charge(voltage) 113 # Wait for new PD contract to be established 114 time.sleep(self.PD_SETTLE_DELAY) 115 # Get current PDTester PD state 116 pdtester_state = pd_pdtester_utils.get_pd_state(self.pdtester_port) 117 # If PDTester is sink, then Vbus_exp = 5v, not skip failure even 118 # using charger profile override. 119 if pdtester_state == pd_pdtester_utils.SNK_CONNECT: 120 expected_vbus_voltage = self.USBC_SINK_VOLTAGE 121 ok_to_fail = False 122 else: 123 expected_vbus_voltage = min(voltage, dut_voltage_limit) 124 ok_to_fail = is_override 125 126 result, result_str = self._compare_vbus(expected_vbus_voltage, 127 ok_to_fail) 128 logging.info('%s, %s', result_str, result) 129 if result == 'FAIL': 130 pdtester_failures.append(result_str) 131 132 # PDTester is set back to 20V SRC mode. 133 self.pdtester.charge(self.pdtester.USBC_MAX_VOLTAGE) 134 time.sleep(self.PD_SETTLE_DELAY) 135 136 if pdtester_failures: 137 logging.error('PDTester voltage source cap failures') 138 for fail in pdtester_failures: 139 logging.error('%s', fail) 140 number = len(pdtester_failures) 141 raise error.TestFail('PDTester failed %d times' % number) 142 143 # The DUT must be in SNK mode for the pd <port> dev <voltage> 144 # command to have an effect. 145 if dut_state['role'] != pd_dut_utils.SNK_CONNECT: 146 # DUT needs to be in SINK Mode, attempt to force change 147 pd_dut_utils.set_pd_dualrole(dut_state['port'], 'snk') 148 time.sleep(self.PD_SETTLE_DELAY) 149 if (pd_dut_utils.get_pd_state(dut_state['port']) != 150 pd_dut_utils.SNK_CONNECT): 151 raise error.TestFail("DUT not able to connect in SINK mode") 152 153 logging.info('Start of DUT initiated tests') 154 dut_failures = [] 155 for v in self.VOLTAGE_SEQUENCE: 156 if v > dut_voltage_limit: 157 logging.info('Target = %02dV: skipped, over the limit %0dV', 158 v, dut_voltage_limit) 159 continue 160 # Build 'pd <port> dev <voltage> command 161 cmd = 'pd %d dev %d' % (dut_state['port'], v) 162 pd_dut_utils.send_pd_command(cmd) 163 time.sleep(self.PD_SETTLE_DELAY) 164 result, result_str = self._compare_vbus(v, ok_to_fail=is_override) 165 logging.info('%s, %s', result_str, result) 166 if result == 'FAIL': 167 dut_failures.append(result_str) 168 169 # Make sure DUT is set back to its max voltage so DUT will accept all 170 # options 171 cmd = 'pd %d dev %d' % (dut_state['port'], dut_voltage_limit) 172 pd_dut_utils.send_pd_command(cmd) 173 time.sleep(self.PD_SETTLE_DELAY) 174 # The next group of tests need DUT to connect in SNK and SRC modes 175 pd_dut_utils.set_pd_dualrole(dut_state['port'], 'on') 176 177 if dut_failures: 178 logging.error('DUT voltage request failures') 179 for fail in dut_failures: 180 logging.error('%s', fail) 181 number = len(dut_failures) 182 raise error.TestFail('DUT failed %d times' % number) 183