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 Plankton test board. It requires that the DUT support 18 dualrole (SRC or SNK) operation. VBUS change requests occur in two 19 methods. First, with the DUT in SNK mode, it uses the pd console command 20 'pd 0/1 dev V' command where V is the desired voltage 5/12/20. The 2nd 21 test initiates the VBUS change by using special Plankton feature to 22 send new SRC CAP message. This causes the DUT to request a new VBUS 23 voltage mathcing what's in the SRC CAP message. 24 25 Pass critera is all voltage transitions are successful. 26 27 """ 28 version = 1 29 30 PD_SETTLE_DELAY = 4 31 USBC_SINK_VOLTAGE = 5 32 USBC_MAX_VOLTAGE = 20 33 VBUS_TOLERANCE = 0.12 34 35 VOLTAGE_SEQUENCE = [5, 12, 20, 12, 5, 20, 5, 5, 12, 12, 20] 36 37 def _compare_vbus(self, expected_vbus_voltage): 38 """Check VBUS using plankton 39 40 @param expected_vbus_voltage: nominal VBUS level (in volts) 41 42 @returns: a tuple containing pass/fail indication and logging string 43 """ 44 # Get Vbus voltage and current 45 vbus_voltage = self.plankton.vbus_voltage 46 vbus_current = self.plankton.vbus_current 47 # Compute voltage tolerance range 48 tolerance = self.VBUS_TOLERANCE * expected_vbus_voltage 49 voltage_difference = math.fabs(expected_vbus_voltage - vbus_voltage) 50 result_str = 'Target = %02dV:\tAct = %.2f\tDelta = %.2f' % \ 51 (expected_vbus_voltage, vbus_voltage, voltage_difference) 52 # Verify that measured Vbus voltage is within expected range 53 voltage_difference = math.fabs(expected_vbus_voltage - vbus_voltage) 54 if voltage_difference > tolerance: 55 result = 'FAIL' 56 else: 57 result = 'PASS' 58 return result, result_str 59 60 def initialize(self, host, cmdline_args): 61 super(firmware_PDVbusRequest, self).initialize(host, cmdline_args) 62 # Only run in normal mode 63 self.switcher.setup_mode('normal') 64 self.usbpd.send_command('chan 0') 65 66 def cleanup(self): 67 self.usbpd.send_command('chan 0xffffffff') 68 super(firmware_PDVbusRequest, self).cleanup() 69 70 def run_once(self): 71 """Exectue VBUS request test. 72 73 """ 74 75 # create objects for pd utilities 76 pd_dut_utils = pd_console.PDConsoleUtils(self.usbpd) 77 pd_plankton_utils = pd_console.PDConsoleUtils(self.plankton) 78 79 # Make sure PD support exists in the UART console 80 if pd_dut_utils.verify_pd_console() == False: 81 raise error.TestFail("pd command not present on console!") 82 83 # Type C connection (PD contract) should exist at this point 84 dut_state = pd_dut_utils.query_pd_connection() 85 logging.info('DUT PD connection state: %r', dut_state) 86 if dut_state['connect'] == False: 87 raise error.TestFail("pd connection not found") 88 if dut_state['role'] != pd_dut_utils.SNK_CONNECT: 89 # DUT needs to be in SINK Mode, attempt to force change 90 pd_dut_utils.set_pd_dualrole('snk') 91 time.sleep(self.PD_SETTLE_DELAY) 92 if pd_dut_utils.get_pd_state(dut_state['port']) != pd_dut_utils.SNK_CONNECT: 93 raise error.TestFail("DUT not able to connect in SINK mode") 94 95 # Plankton must be set to 20V SRC mode in order for the DUT 96 # to be able to request all 3 possible voltage levels (5, 12, 20). 97 # The DUT must be in SNK mode for the pd <port> dev <voltage> 98 # command to have an effect. 99 self.plankton.charge(self.USBC_MAX_VOLTAGE) 100 time.sleep(self.PD_SETTLE_DELAY) 101 logging.info('Start of DUT initiated tests') 102 dut_failures = [] 103 for v in self.VOLTAGE_SEQUENCE: 104 # Build 'pd <port> dev <voltage> command 105 cmd = 'pd %d dev %d' % (dut_state['port'], v) 106 pd_dut_utils.send_pd_command(cmd) 107 time.sleep(self.PD_SETTLE_DELAY) 108 result, result_str = self._compare_vbus(v) 109 logging.info('%s, %s', result_str, result) 110 if result == 'FAIL': 111 dut_failures.append(result_str) 112 113 # Make sure Plankton is set back to 20VSRC so DUT will accept all options 114 cmd = 'pd %d dev %d' % (dut_state['port'], self.USBC_MAX_VOLTAGE) 115 time.sleep(self.PD_SETTLE_DELAY) 116 # The next group of tests need DUT to connect in SNK and SRC modes 117 pd_dut_utils.set_pd_dualrole('on') 118 119 plankton_failures = [] 120 logging.info('Start Plankton initiated tests') 121 for voltage in self.plankton.get_charging_voltages(): 122 logging.info('********* %r *********', voltage) 123 # Set charging voltage 124 self.plankton.charge(voltage) 125 # Wait for new PD contract to be established 126 time.sleep(self.PD_SETTLE_DELAY) 127 # Get current Plankton PD state 128 plankton_state = pd_plankton_utils.get_pd_state(0) 129 expected_vbus_voltage = self.plankton.charging_voltage 130 # If Plankton is sink, then Vbus_exp = 5v 131 if plankton_state == pd_plankton_utils.SNK_CONNECT: 132 expected_vbus_voltage = self.USBC_SINK_VOLTAGE 133 result, result_str = self._compare_vbus(expected_vbus_voltage) 134 logging.info('%s, %s', result_str, result) 135 if result == 'FAIL': 136 plankton_failures.append(result_str) 137 138 if dut_failures: 139 logging.error('DUT voltage request failures') 140 for fail in dut_failures: 141 logging.error('%s', fail) 142 143 if plankton_failures: 144 logging.error('Plankton voltage source cap failures') 145 for fail in plankton_failures: 146 logging.error('%s', fail) 147 148 if dut_failures or plankton_failures: 149 if dut_failures and plankton_failures: 150 test = 'DUT and Plankton' 151 number = len(dut_failures) + len(plankton_failures) 152 elif dut_failures: 153 test = 'DUT' 154 number = len(dut_failures) 155 else: 156 test = 'Plankton' 157 number = len(plankton_failures) 158 raise error.TestFail('%s failed %d times' % (test, number)) 159