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