1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright (c) 2011 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 Li"""A Python library to interact with PCA9555 module for TPM testing. 7*9c5db199SXin Li 8*9c5db199SXin LiBackground 9*9c5db199SXin Li - PCA9555 is one of two modules on TTCI board 10*9c5db199SXin Li - This library provides methods to interact with PCA9555 programmatically 11*9c5db199SXin Li 12*9c5db199SXin LiDependency 13*9c5db199SXin Li - This library depends on a new C shared library called "libsmogcheck.so". 14*9c5db199SXin Li - In order to run test cases built using this API, one needs a TTCI board 15*9c5db199SXin Li 16*9c5db199SXin LiNotes: 17*9c5db199SXin Li - An exception is raised if it doesn't make logical sense to continue program 18*9c5db199SXin Li flow (e.g. I/O error prevents test case from executing) 19*9c5db199SXin Li - An exception is caught and then converted to an error code if the caller 20*9c5db199SXin Li expects to check for error code per API definition 21*9c5db199SXin Li""" 22*9c5db199SXin Li 23*9c5db199SXin Liimport logging 24*9c5db199SXin Lifrom autotest_lib.client.common_lib import i2c_node 25*9c5db199SXin Li 26*9c5db199SXin Li 27*9c5db199SXin Li# I2C constants 28*9c5db199SXin LiPCA9555_SLV = 0x27 # I2C node address of PCA9555 29*9c5db199SXin Li 30*9c5db199SXin Li# PCA9555 registers 31*9c5db199SXin LiPCA_REG = { 32*9c5db199SXin Li 'IN0': 0, # Input Port 0 33*9c5db199SXin Li 'IN1': 1, # Input Port 1 34*9c5db199SXin Li 'OUT0': 2, # Output Port 0 35*9c5db199SXin Li 'OUT1': 3, # Output Port 1 36*9c5db199SXin Li 'PI0': 4, # Polarity Inversion 0 37*9c5db199SXin Li 'PI1': 5, # Polarity Inversion 1 38*9c5db199SXin Li 'CONF0': 6, # Configuration 0 39*9c5db199SXin Li 'CONF1': 7, # Configuration 1 40*9c5db199SXin Li } 41*9c5db199SXin Li 42*9c5db199SXin Li# Each '1' represents turning on corresponding LED via writing to PCA9555 43*9c5db199SXin Li# Output Port Registers 44*9c5db199SXin LiPCA_BIT_ONE = { 45*9c5db199SXin Li 'unalloc_0': 0x01, 46*9c5db199SXin Li 'unalloc_1': 0x02, 47*9c5db199SXin Li 'unalloc_2': 0x04, 48*9c5db199SXin Li 'tpm_i2c': 0x08, 49*9c5db199SXin Li 'yellow_led': 0x10, 50*9c5db199SXin Li 'main_power': 0x10, 51*9c5db199SXin Li 'red_led': 0x20, 52*9c5db199SXin Li 'backup_power': 0x20, 53*9c5db199SXin Li 'reset': 0x40, 54*9c5db199SXin Li 'pp': 0x80, 55*9c5db199SXin Li } 56*9c5db199SXin Li 57*9c5db199SXin Li# Constants used to initialize PCA registers 58*9c5db199SXin Li# TODO(tgao): document these bits after stevenh replies 59*9c5db199SXin LiPCA_OUT0_INIT_VAL = 0xff7f 60*9c5db199SXin LiPCA_CONF0_INIT_VAL = 0xc007 61*9c5db199SXin Li 62*9c5db199SXin Li 63*9c5db199SXin Liclass PcaError(Exception): 64*9c5db199SXin Li """Base class for all errors in this module.""" 65*9c5db199SXin Li 66*9c5db199SXin Li 67*9c5db199SXin Liclass PcaController(i2c_node.I2cNode): 68*9c5db199SXin Li """Object to control PCA9555 module on TTCI board.""" 69*9c5db199SXin Li 70*9c5db199SXin Li def __init__(self): 71*9c5db199SXin Li """Initialize PCA9555 module on the TTCI board. 72*9c5db199SXin Li 73*9c5db199SXin Li Raises: 74*9c5db199SXin Li PcaError: if error initializing PCA9555 module. 75*9c5db199SXin Li """ 76*9c5db199SXin Li super(PcaController, self).__init__() 77*9c5db199SXin Li logging.info('Attempt to initialize PCA9555 module') 78*9c5db199SXin Li try: 79*9c5db199SXin Li self.setNodeAddress(PCA9555_SLV) 80*9c5db199SXin Li self.writeWord(PCA_REG['OUT0'], PCA_OUT0_INIT_VAL) 81*9c5db199SXin Li self.writeWord(PCA_REG['PI0'], 0) 82*9c5db199SXin Li self.writeWord(PCA_REG['CONF0'], PCA_CONF0_INIT_VAL) 83*9c5db199SXin Li except PcaError as e: 84*9c5db199SXin Li raise PcaError('Error initializing PCA9555: %s' % e) 85*9c5db199SXin Li 86*9c5db199SXin Li def setPCAcontrol(self, key, turn_on): 87*9c5db199SXin Li """Sets specific bit value in Output Port 0 of PCA9555. 88*9c5db199SXin Li 89*9c5db199SXin Li Args: 90*9c5db199SXin Li key: a string, valid dict keys in PCA_BIT_ONE. 91*9c5db199SXin Li turn_on: a boolean, true = set bit value to 1. 92*9c5db199SXin Li 93*9c5db199SXin Li Returns: 94*9c5db199SXin Li an integer, 0 for success and -1 for error. 95*9c5db199SXin Li """ 96*9c5db199SXin Li logging.info('Attempt to set %r bit to %r', key, turn_on) 97*9c5db199SXin Li try: 98*9c5db199SXin Li byte_read = self.readByte(PCA_REG['OUT0']) 99*9c5db199SXin Li if turn_on: 100*9c5db199SXin Li write_byte = byte_read | PCA_BIT_ONE[key] 101*9c5db199SXin Li else: 102*9c5db199SXin Li write_byte = byte_read & ~PCA_BIT_ONE[key] 103*9c5db199SXin Li self.writeByte(PCA_REG['OUT0'], write_byte) 104*9c5db199SXin Li return 0 105*9c5db199SXin Li except PcaError as e: 106*9c5db199SXin Li logging.error('Error setting PCA9555 Output Port 0: %s', e) 107*9c5db199SXin Li return -1 108*9c5db199SXin Li 109*9c5db199SXin Li def _computeLEDmask(self, bit_value, failure, warning): 110*9c5db199SXin Li """Computes proper bit mask to set LED values. 111*9c5db199SXin Li 112*9c5db199SXin Li Args: 113*9c5db199SXin Li <see docstring for TTCI_Set_LEDs()> 114*9c5db199SXin Li 115*9c5db199SXin Li Returns: 116*9c5db199SXin Li an integer, 8-bit mask. 117*9c5db199SXin Li 118*9c5db199SXin Li Raises: 119*9c5db199SXin Li PcaError: if bit value is out of range. 120*9c5db199SXin Li """ 121*9c5db199SXin Li bit_mask = 0 122*9c5db199SXin Li if bit_value < 0 or bit_value > 15: 123*9c5db199SXin Li raise PcaError('Error: bit_value out of range [0, 15]') 124*9c5db199SXin Li 125*9c5db199SXin Li bit_mask = bit_value 126*9c5db199SXin Li if failure: 127*9c5db199SXin Li bit_mask |= 0x20 128*9c5db199SXin Li if warning: 129*9c5db199SXin Li bit_mask |= 0x10 130*9c5db199SXin Li 131*9c5db199SXin Li return bit_mask 132*9c5db199SXin Li 133*9c5db199SXin Li def getPCAbitStatus(self, key): 134*9c5db199SXin Li """Gets specific bit value from Output Port 0 of PCA9555. 135*9c5db199SXin Li 136*9c5db199SXin Li Args: 137*9c5db199SXin Li key: a string, valid dict keys in PCA_BIT_ONE. 138*9c5db199SXin Li 139*9c5db199SXin Li Returns: 140*9c5db199SXin Li an integer, 0 for success and -1 for error. 141*9c5db199SXin Li status: a boolean, True if bit value is '1' and False if bit value 142*9c5db199SXin Li is '0'. 143*9c5db199SXin Li """ 144*9c5db199SXin Li status = False 145*9c5db199SXin Li try: 146*9c5db199SXin Li if PCA_BIT_ONE[key] & self.readByte(PCA_REG['OUT0']): 147*9c5db199SXin Li status = True 148*9c5db199SXin Li return (0, status) 149*9c5db199SXin Li except PcaError as e: 150*9c5db199SXin Li logging.error('Error reading from PCA9555 Output Port 0: %s', e) 151*9c5db199SXin Li return (-1, status) 152*9c5db199SXin Li 153*9c5db199SXin Li def setLEDs(self, bit_value, failure, warning): 154*9c5db199SXin Li """De/activate PCA9555 LEDs. 155*9c5db199SXin Li 156*9c5db199SXin Li Mapping of LED to bit values in Output Port 1 (register 3) 157*9c5db199SXin Li (default bit value = 1 <--> LED OFF) 158*9c5db199SXin Li LED 0 (GREEN): O1.0 (mask = 0x01) 159*9c5db199SXin Li LED 1 (GREEN): O1.1 (mask = 0x02) 160*9c5db199SXin Li LED 2 (GREEN): O1.2 (mask = 0x04) 161*9c5db199SXin Li LED 3 (GREEN): O1.3 (mask = 0x08) 162*9c5db199SXin Li LED 4 (YELLOW): O1.4 (mask = 0x10) 163*9c5db199SXin Li LED 5 (RED): O1.5 (mask = 0x20) 164*9c5db199SXin Li 165*9c5db199SXin Li To change LED bit values: 166*9c5db199SXin Li 1) read byte value from register 3 167*9c5db199SXin Li 2) set all 6 lower bits to 1 (LED OFF) by logical OR with 0x3f 168*9c5db199SXin Li 3) set appropriate bits to 0 (LED ON) by logical XOR with proper mask 169*9c5db199SXin Li 4) write updated byte value to register 3 170*9c5db199SXin Li 171*9c5db199SXin Li An example: bit_value=9, failure=False, warning=True 172*9c5db199SXin Li 1) read back, say, 0x96, or 1001 0110 (LEDs 0, 3, 5 ON) 173*9c5db199SXin Li 2) 0x96 | 0x3f = 0xbf (all LEDs OFF) 174*9c5db199SXin Li 3) bit_value=9 -> turn on LEDs 1, 2 175*9c5db199SXin Li failure=False -> keep LED 5 off 176*9c5db199SXin Li warning=True -> turn on LED 4 177*9c5db199SXin Li proper mask = 0001 0110, or 0x16 178*9c5db199SXin Li 0xbf ^ 0x16 = 0xa9, or 1010 1001 (LEDs 1, 2, 4 ON) 179*9c5db199SXin Li 4) write 0xa9 to register 3 180*9c5db199SXin Li 181*9c5db199SXin Li Args: 182*9c5db199SXin Li bit_value: an integer between 0 and 15, representing 4-bit binary 183*9c5db199SXin Li value for green LEDs (i.e. 0~3). 184*9c5db199SXin Li failure: a boolean, true = set red LED value to 0. 185*9c5db199SXin Li warning: a boolean, true = set yellow LED value to 0. 186*9c5db199SXin Li 187*9c5db199SXin Li Returns: 188*9c5db199SXin Li an integer, 0 for success and -1 for error. 189*9c5db199SXin Li """ 190*9c5db199SXin Li logging.info('Attempt to set LED values: bit_value=%r, failure=%r, ' 191*9c5db199SXin Li 'warning=%r', bit_value, failure, warning) 192*9c5db199SXin Li try: 193*9c5db199SXin Li byte_read = self.readByte(PCA_REG['OUT1']) 194*9c5db199SXin Li reset_low6 = byte_read | 0x3f 195*9c5db199SXin Li bit_mask = self._computeLEDmask(bit_value, failure, warning) 196*9c5db199SXin Li write_byte = reset_low6 ^ bit_mask 197*9c5db199SXin Li logging.debug('byte_read = 0x%x, reset_low6 = 0x%x, ' 198*9c5db199SXin Li 'bit_mask = 0x%x, write_byte = 0x%x', 199*9c5db199SXin Li byte_read, reset_low6, bit_mask, write_byte) 200*9c5db199SXin Li self.writeByte(PCA_REG['OUT1'], write_byte) 201*9c5db199SXin Li return 0 202*9c5db199SXin Li except PcaError as e: 203*9c5db199SXin Li logging.error('Error setting PCA9555 Output Port 0: %s', e) 204*9c5db199SXin Li return -1 205*9c5db199SXin Li 206*9c5db199SXin Li def getSwitchStatus(self): 207*9c5db199SXin Li """Checks status of DIP Switches (2-bit). 208*9c5db199SXin Li 209*9c5db199SXin Li Returns: 210*9c5db199SXin Li ret: an integer, error code. 0 = no error. 211*9c5db199SXin Li status: an integer, valid value in range [0, 3]. 212*9c5db199SXin Li """ 213*9c5db199SXin Li logging.info('Attempt to read DIP switch status') 214*9c5db199SXin Li ret = -1 215*9c5db199SXin Li status = -1 216*9c5db199SXin Li try: 217*9c5db199SXin Li byte_read = self.readByte(PCA_REG['IN1']) 218*9c5db199SXin Li # Right shift 6-bit to get 2 high-order bits 219*9c5db199SXin Li status = byte_read >> 6 220*9c5db199SXin Li logging.info('DIP switch status = 0x%x', status) 221*9c5db199SXin Li ret = 0 222*9c5db199SXin Li except PcaError as e: 223*9c5db199SXin Li logging.error('No byte read from PCA9555 Input Port 1: %s', e) 224*9c5db199SXin Li 225*9c5db199SXin Li return (ret, status) 226*9c5db199SXin Li 227*9c5db199SXin Li def getLEDstatus(self): 228*9c5db199SXin Li """Checks LED status. 229*9c5db199SXin Li 230*9c5db199SXin Li Returns: 231*9c5db199SXin Li ret: an integer, 0 for success and -1 for error. 232*9c5db199SXin Li bit_value: an integer between 0 and 15, representing 4-bit binary 233*9c5db199SXin Li value for green LEDs (i.e. 0~3). Default: -1. 234*9c5db199SXin Li failure: a boolean, true = red LED has value 0. Default: False. 235*9c5db199SXin Li warning: a boolean, true = yellow LED has value 0. Default: False. 236*9c5db199SXin Li """ 237*9c5db199SXin Li ret = -1 238*9c5db199SXin Li bit_value = -1 239*9c5db199SXin Li failure = False 240*9c5db199SXin Li warning = False 241*9c5db199SXin Li 242*9c5db199SXin Li try: 243*9c5db199SXin Li byte_read = self.readByte(PCA_REG['OUT1']) 244*9c5db199SXin Li if not (byte_read | PCA_BIT_ONE['red_led']): 245*9c5db199SXin Li failure = True 246*9c5db199SXin Li if not (byte_read | PCA_BIT_ONE['yellow_led']): 247*9c5db199SXin Li warning = True 248*9c5db199SXin Li bit_value = byte_read & 0xf # Get lower 4-bit value 249*9c5db199SXin Li logging.info('LED bit_value = %r, failure = %r, warning = %r', 250*9c5db199SXin Li bit_value, failure, warning) 251*9c5db199SXin Li ret = 0 252*9c5db199SXin Li except PcaError as e: 253*9c5db199SXin Li logging.error('No byte read from PCA9555 Output Port 1: %s', e) 254*9c5db199SXin Li 255*9c5db199SXin Li return (ret, bit_value, failure, warning) 256