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