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 7Background 8 - PCA9555 is one of two modules on TTCI board 9 - This library provides methods to interact with PCA9555 programmatically 10 11Dependency 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 15Notes: 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 22import logging 23from autotest_lib.client.common_lib import i2c_slave 24 25 26# I2C constants 27PCA9555_SLV = 0x27 # I2C slave address of PCA9555 28 29# PCA9555 registers 30PCA_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 43PCA_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 58PCA_OUT0_INIT_VAL = 0xff7f 59PCA_CONF0_INIT_VAL = 0xc007 60 61 62class PcaError(Exception): 63 """Base class for all errors in this module.""" 64 65 66class 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