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