1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport logging 7*9c5db199SXin Liimport re 8*9c5db199SXin Liimport time 9*9c5db199SXin Li 10*9c5db199SXin Lifrom autotest_lib.client.bin import utils 11*9c5db199SXin Lifrom autotest_lib.server.cros.servo import chrome_ec 12*9c5db199SXin Li 13*9c5db199SXin Li 14*9c5db199SXin Liclass PDTesterError(Exception): 15*9c5db199SXin Li """Error object for PDTester""" 16*9c5db199SXin Li pass 17*9c5db199SXin Li 18*9c5db199SXin Li 19*9c5db199SXin Liclass PDTester(chrome_ec.ChromeEC): 20*9c5db199SXin Li """Manages control of a PDTester hardware. 21*9c5db199SXin Li 22*9c5db199SXin Li PDTester is a general term for hardware developed to aid in USB type-C 23*9c5db199SXin Li debug and control of various type C host devices. It can be either a 24*9c5db199SXin Li Plankton board or a Servo v4 board. 25*9c5db199SXin Li 26*9c5db199SXin Li We control the PDTester board via the UART and the Servod interfaces. 27*9c5db199SXin Li PDTester provides many interfaces that access the hardware. It can 28*9c5db199SXin Li also be passed into the PDConsoleUtils as a console which then 29*9c5db199SXin Li provides methods to access the pd console. 30*9c5db199SXin Li 31*9c5db199SXin Li This class is to abstract these interfaces. 32*9c5db199SXin Li """ 33*9c5db199SXin Li # USB charging command delays in seconds. 34*9c5db199SXin Li USBC_COMMAND_DELAY = 0.5 35*9c5db199SXin Li # PDTester USBC commands. 36*9c5db199SXin Li USBC_DRSWAP = 'usbc_drswap' 37*9c5db199SXin Li USBC_PRSWAP = 'usbc_prswap' 38*9c5db199SXin Li USBC_ROLE = 'usbc_role' # TODO(b:140256624): deprecate by USBC_PR 39*9c5db199SXin Li USBC_PR = 'usbc_pr' 40*9c5db199SXin Li USBC_MUX = 'usbc_mux' 41*9c5db199SXin Li RE_USBC_ROLE_VOLTAGE = r'src(\d+)v' 42*9c5db199SXin Li USBC_SRC_CAPS = 'ada_srccaps' 43*9c5db199SXin Li USBC_CHARGING_VOLTAGES = { 44*9c5db199SXin Li 0: 'sink', 45*9c5db199SXin Li 5: 'src5v', 46*9c5db199SXin Li 9: 'src9v', 47*9c5db199SXin Li 10: 'src10v', 48*9c5db199SXin Li 12: 'src12v', 49*9c5db199SXin Li 15: 'src15v', 50*9c5db199SXin Li 20: 'src20v'} 51*9c5db199SXin Li # TODO(b:140256624): deprecate by USBC_CHARGING_VOLTAGES 52*9c5db199SXin Li USBC_CHARGING_VOLTAGES_LEGACY = { 53*9c5db199SXin Li 0: 'sink', 54*9c5db199SXin Li 5: 'src5v', 55*9c5db199SXin Li 12: 'src12v', 56*9c5db199SXin Li 20: 'src20v'} 57*9c5db199SXin Li USBC_MAX_VOLTAGE = 20 58*9c5db199SXin Li VBUS_VOLTAGE_MV = 'vbus_voltage' 59*9c5db199SXin Li VBUS_CURRENT_MA = 'vbus_current' 60*9c5db199SXin Li VBUS_POWER_MW = 'vbus_power' 61*9c5db199SXin Li # USBC PD states. 62*9c5db199SXin Li USBC_PD_STATES = { 63*9c5db199SXin Li 'sink': 'SNK_READY', 64*9c5db199SXin Li 'source': 'SRC_READY'} 65*9c5db199SXin Li POLL_STATE_SECS = 2 66*9c5db199SXin Li FIRST_PD_SETUP_ELEMENT = ['servo_v4', 'servo_v4p1'] 67*9c5db199SXin Li SECOND_PD_SETUP_ELEMENT = ['servo_micro', 'c2d2'] 68*9c5db199SXin Li 69*9c5db199SXin Li def __init__(self, servo, servod_proxy): 70*9c5db199SXin Li """Initialize and keep the servo object. 71*9c5db199SXin Li 72*9c5db199SXin Li @param servo: A Servo object 73*9c5db199SXin Li @param servod_proxy: Servod proxy for pdtester host 74*9c5db199SXin Li """ 75*9c5db199SXin Li self.servo_type = servo.get_servo_version() 76*9c5db199SXin Li pd_tester_device = self.servo_type.split('_with_')[0] 77*9c5db199SXin Li if pd_tester_device in self.FIRST_PD_SETUP_ELEMENT: 78*9c5db199SXin Li uart_prefix = pd_tester_device + "_uart" 79*9c5db199SXin Li else: 80*9c5db199SXin Li uart_prefix = 'ec_uart' 81*9c5db199SXin Li 82*9c5db199SXin Li super(PDTester, self).__init__(servo, uart_prefix) 83*9c5db199SXin Li # save servod proxy for methods that access PDTester servod 84*9c5db199SXin Li self._server = servod_proxy 85*9c5db199SXin Li self.init_hardware() 86*9c5db199SXin Li 87*9c5db199SXin Li 88*9c5db199SXin Li def init_hardware(self): 89*9c5db199SXin Li """Initializes PDTester hardware.""" 90*9c5db199SXin Li if self.servo_type == 'plankton': 91*9c5db199SXin Li if not int(self.get('debug_usb_sel')): 92*9c5db199SXin Li raise PDTesterError('debug_usb_sel (SW3) should be ON!! ' 93*9c5db199SXin Li 'Please use CN15 to connect Plankton.') 94*9c5db199SXin Li self.set('typec_to_hub_sw', '0') 95*9c5db199SXin Li self.set('usb2_mux_sw', '1') 96*9c5db199SXin Li self.set('usb_dn_pwren', 'on') 97*9c5db199SXin Li 98*9c5db199SXin Li 99*9c5db199SXin Li def set(self, control_name, value): 100*9c5db199SXin Li """Sets the value of a control using servod. 101*9c5db199SXin Li 102*9c5db199SXin Li @param control_name: pdtester servo control item 103*9c5db199SXin Li @param value: value to set pdtester servo control item 104*9c5db199SXin Li """ 105*9c5db199SXin Li assert control_name 106*9c5db199SXin Li self._server.set(control_name, value) 107*9c5db199SXin Li 108*9c5db199SXin Li 109*9c5db199SXin Li def get(self, control_name): 110*9c5db199SXin Li """Gets the value of a control from servod. 111*9c5db199SXin Li 112*9c5db199SXin Li @param control_name: pdtester servo control item 113*9c5db199SXin Li """ 114*9c5db199SXin Li assert control_name 115*9c5db199SXin Li return self._server.get(control_name) 116*9c5db199SXin Li 117*9c5db199SXin Li 118*9c5db199SXin Li @property 119*9c5db199SXin Li def vbus_voltage(self): 120*9c5db199SXin Li """Gets PDTester VBUS voltage in volts.""" 121*9c5db199SXin Li return float(self.get(self.VBUS_VOLTAGE_MV)) / 1000.0 122*9c5db199SXin Li 123*9c5db199SXin Li 124*9c5db199SXin Li @property 125*9c5db199SXin Li def vbus_current(self): 126*9c5db199SXin Li """Gets PDTester VBUS current in amps.""" 127*9c5db199SXin Li return float(self.get(self.VBUS_CURRENT_MA)) / 1000.0 128*9c5db199SXin Li 129*9c5db199SXin Li 130*9c5db199SXin Li @property 131*9c5db199SXin Li def vbus_power(self): 132*9c5db199SXin Li """Gets PDTester charging power in watts.""" 133*9c5db199SXin Li return float(self.get(self.VBUS_POWER_MW)) / 1000.0 134*9c5db199SXin Li 135*9c5db199SXin Li def get_adapter_source_caps(self): 136*9c5db199SXin Li """Gets a list of SourceCap Tuples in mV/mA.""" 137*9c5db199SXin Li try: 138*9c5db199SXin Li res = self.get(self.USBC_SRC_CAPS) 139*9c5db199SXin Li except: 140*9c5db199SXin Li raise PDTesterError('Unsupported servov4 command(%s). ' 141*9c5db199SXin Li 'Maybe firmware or servod too old? ' 142*9c5db199SXin Li 'sudo servo_updater -b servo_v4; ' 143*9c5db199SXin Li 'sudo emerge hdctools' % self.USBC_SRC_CAPS) 144*9c5db199SXin Li 145*9c5db199SXin Li srccaps = [] 146*9c5db199SXin Li for pdo_str in res: 147*9c5db199SXin Li m = re.match(r'\d: (\d+)mV/(\d+)mA', pdo_str) 148*9c5db199SXin Li srccaps.append((int(m.group(1)), int(m.group(2)))) 149*9c5db199SXin Li return srccaps 150*9c5db199SXin Li 151*9c5db199SXin Li def get_charging_voltages(self): 152*9c5db199SXin Li """Gets the lists of available charging voltages of the adapter.""" 153*9c5db199SXin Li try: 154*9c5db199SXin Li srccaps = self.get_adapter_source_caps() 155*9c5db199SXin Li except PDTesterError: 156*9c5db199SXin Li # htctools and servov4 is not updated, fallback to the old path. 157*9c5db199SXin Li logging.warning('hdctools or servov4 firmware too old, fallback to ' 158*9c5db199SXin Li 'fixed charging voltages.') 159*9c5db199SXin Li return list(self.USBC_CHARGING_VOLTAGES_LEGACY.keys()) 160*9c5db199SXin Li 161*9c5db199SXin Li # insert 0 voltage for sink 162*9c5db199SXin Li vols = [0] 163*9c5db199SXin Li for pdo in srccaps: 164*9c5db199SXin Li # Only include the voltages that are in USBC_CHARGING_VOLTAGES 165*9c5db199SXin Li if pdo[0] / 1000 in self.USBC_CHARGING_VOLTAGES: 166*9c5db199SXin Li vols.append(pdo[0] / 1000) 167*9c5db199SXin Li else: 168*9c5db199SXin Li logging.debug("Omitting unsupported PDO = %s", pdo) 169*9c5db199SXin Li return vols 170*9c5db199SXin Li 171*9c5db199SXin Li def charge(self, voltage): 172*9c5db199SXin Li """Sets PDTester to provide power at specific voltage. 173*9c5db199SXin Li 174*9c5db199SXin Li @param voltage: Specified charging voltage in volts. 175*9c5db199SXin Li """ 176*9c5db199SXin Li charging_voltages = self.get_charging_voltages() 177*9c5db199SXin Li if voltage not in charging_voltages: 178*9c5db199SXin Li logging.warning( 179*9c5db199SXin Li 'Unsupported voltage(%s) of the adapter. ' 180*9c5db199SXin Li 'Maybe firmware or servod too old? ' 181*9c5db199SXin Li 'sudo servo_updater -b servo_v4; ' 182*9c5db199SXin Li 'sudo emerge hdctools', voltage) 183*9c5db199SXin Li if voltage not in self.USBC_CHARGING_VOLTAGES: 184*9c5db199SXin Li raise PDTesterError( 185*9c5db199SXin Li 'Cannot set voltage to %s, not supported by %s' % 186*9c5db199SXin Li (voltage, self.USBC_PR)) 187*9c5db199SXin Li 188*9c5db199SXin Li try: 189*9c5db199SXin Li self.set(self.USBC_PR, self.USBC_CHARGING_VOLTAGES[voltage]) 190*9c5db199SXin Li except: 191*9c5db199SXin Li if voltage not in self.USBC_CHARGING_VOLTAGES_LEGACY: 192*9c5db199SXin Li raise PDTesterError( 193*9c5db199SXin Li 'Cannot set voltage to %s, not supported by %s' % 194*9c5db199SXin Li (voltage, self.USBC_ROLE)) 195*9c5db199SXin Li self.set(self.USBC_ROLE, 196*9c5db199SXin Li self.USBC_CHARGING_VOLTAGES_LEGACY[voltage]) 197*9c5db199SXin Li time.sleep(self.USBC_COMMAND_DELAY) 198*9c5db199SXin Li 199*9c5db199SXin Li @property 200*9c5db199SXin Li def charging_voltage(self): 201*9c5db199SXin Li """Gets current charging voltage.""" 202*9c5db199SXin Li try: 203*9c5db199SXin Li usbc_pr = self.get(self.USBC_PR) 204*9c5db199SXin Li except: 205*9c5db199SXin Li logging.warning( 206*9c5db199SXin Li 'Unsupported control(%s). ' 207*9c5db199SXin Li 'Maybe firmware or servod too old? ' 208*9c5db199SXin Li 'sudo servo_updater -b servo_v4; ' 209*9c5db199SXin Li 'sudo emerge hdctools', self.USBC_PR) 210*9c5db199SXin Li usbc_pr = self.get(self.USBC_ROLE) 211*9c5db199SXin Li m = re.match(self.RE_USBC_ROLE_VOLTAGE, usbc_pr) 212*9c5db199SXin Li if m: 213*9c5db199SXin Li return int(m.group(1)) 214*9c5db199SXin Li 215*9c5db199SXin Li if usbc_pr == self.USBC_CHARGING_VOLTAGES[0]: 216*9c5db199SXin Li return 0 217*9c5db199SXin Li 218*9c5db199SXin Li raise PDTesterError('Invalid USBC power role: %s' % usbc_pr) 219*9c5db199SXin Li 220*9c5db199SXin Li 221*9c5db199SXin Li def poll_pd_state(self, state): 222*9c5db199SXin Li """Polls until PDTester pd goes to the specific state. 223*9c5db199SXin Li 224*9c5db199SXin Li @param state: Specified pd state name. 225*9c5db199SXin Li """ 226*9c5db199SXin Li if state not in self.USBC_PD_STATES: 227*9c5db199SXin Li raise PDTesterError('Invalid state name: %s' % state) 228*9c5db199SXin Li utils.poll_for_condition( 229*9c5db199SXin Li lambda: self.get('pd_state') == self.USBC_PD_STATES[state], 230*9c5db199SXin Li exception=utils.TimeoutError('PDTester not in %s state ' 231*9c5db199SXin Li 'after %s seconds.' % 232*9c5db199SXin Li (self.USBC_PD_STATES[state], 233*9c5db199SXin Li self.POLL_STATE_SECS)), 234*9c5db199SXin Li timeout=self.POLL_STATE_SECS) 235*9c5db199SXin Li 236*9c5db199SXin Li 237*9c5db199SXin Li def set_usbc_mux(self, mux): 238*9c5db199SXin Li """Sets PDTester usbc_mux. 239*9c5db199SXin Li 240*9c5db199SXin Li @param mux: Specified mux state name. 241*9c5db199SXin Li """ 242*9c5db199SXin Li if mux not in ['dp', 'usb']: 243*9c5db199SXin Li raise PDTesterError('Invalid mux name: %s, ' 244*9c5db199SXin Li 'should be either \'dp\' or \'usb\'.' % mux) 245*9c5db199SXin Li self.set(self.USBC_MUX, mux) 246*9c5db199SXin Li time.sleep(self.USBC_COMMAND_DELAY) 247*9c5db199SXin Li 248*9c5db199SXin Li def allow_pr_swap(self, allow): 249*9c5db199SXin Li """Issue usbc_action prswap PDTester command 250*9c5db199SXin Li 251*9c5db199SXin Li @param allow: a bool for ACK or NACK to PR_SWAP 252*9c5db199SXin Li command requested by DUT 253*9c5db199SXin Li @returns value of prswap in PDTester FW 254*9c5db199SXin Li """ 255*9c5db199SXin Li self.set(self.USBC_PRSWAP, int(allow)) 256*9c5db199SXin Li 257*9c5db199SXin Li def allow_dr_swap(self, allow): 258*9c5db199SXin Li """Issue usbc_action drswap PDTester command 259*9c5db199SXin Li 260*9c5db199SXin Li @param allow: a bool for ACK or NACK to DR_SWAP 261*9c5db199SXin Li command requested by DUT 262*9c5db199SXin Li @returns value of drswap in PDTester FW 263*9c5db199SXin Li """ 264*9c5db199SXin Li self.set(self.USBC_DRSWAP, int(allow)) 265