Home | History | Annotate | Download | only in common_lib
      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