blob: 9f82950598579f70b58427bfef69c431a54a2113 [file] [log] [blame]
Tan Gao3768ffe2011-08-30 11:12:20 -07001# 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)