Tan Gao | 3768ffe | 2011-08-30 11:12:20 -0700 | [diff] [blame^] | 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) |