xref: /openbmc/qemu/hw/misc/pca9552.c (revision 073d9f2c)
1 /*
2  * PCA9552 I2C LED blinker
3  *
4  *     https://www.nxp.com/docs/en/application-note/AN264.pdf
5  *
6  * Copyright (c) 2017-2018, IBM Corporation.
7  *
8  * This work is licensed under the terms of the GNU GPL, version 2 or
9  * later. See the COPYING file in the top-level directory.
10  */
11 
12 #include "qemu/osdep.h"
13 #include "qemu/log.h"
14 #include "hw/hw.h"
15 #include "hw/misc/pca9552.h"
16 #include "hw/misc/pca9552_regs.h"
17 
18 #define PCA9552_LED_ON   0x0
19 #define PCA9552_LED_OFF  0x1
20 #define PCA9552_LED_PWM0 0x2
21 #define PCA9552_LED_PWM1 0x3
22 
23 static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin)
24 {
25     uint8_t reg   = PCA9552_LS0 + (pin / 4);
26     uint8_t shift = (pin % 4) << 1;
27 
28     return extract32(s->regs[reg], shift, 2);
29 }
30 
31 static void pca9552_update_pin_input(PCA9552State *s)
32 {
33     int i;
34 
35     for (i = 0; i < s->nr_leds; i++) {
36         uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
37         uint8_t input_shift = (i % 8);
38         uint8_t config = pca9552_pin_get_config(s, i);
39 
40         switch (config) {
41         case PCA9552_LED_ON:
42             s->regs[input_reg] |= 1 << input_shift;
43             break;
44         case PCA9552_LED_OFF:
45             s->regs[input_reg] &= ~(1 << input_shift);
46             break;
47         case PCA9552_LED_PWM0:
48         case PCA9552_LED_PWM1:
49             /* TODO */
50         default:
51             break;
52         }
53     }
54 }
55 
56 static uint8_t pca9552_read(PCA9552State *s, uint8_t reg)
57 {
58     switch (reg) {
59     case PCA9552_INPUT0:
60     case PCA9552_INPUT1:
61     case PCA9552_PSC0:
62     case PCA9552_PWM0:
63     case PCA9552_PSC1:
64     case PCA9552_PWM1:
65     case PCA9552_LS0:
66     case PCA9552_LS1:
67     case PCA9552_LS2:
68     case PCA9552_LS3:
69         return s->regs[reg];
70     default:
71         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
72                       __func__, reg);
73         return 0xFF;
74     }
75 }
76 
77 static void pca9552_write(PCA9552State *s, uint8_t reg, uint8_t data)
78 {
79     switch (reg) {
80     case PCA9552_PSC0:
81     case PCA9552_PWM0:
82     case PCA9552_PSC1:
83     case PCA9552_PWM1:
84         s->regs[reg] = data;
85         break;
86 
87     case PCA9552_LS0:
88     case PCA9552_LS1:
89     case PCA9552_LS2:
90     case PCA9552_LS3:
91         s->regs[reg] = data;
92         pca9552_update_pin_input(s);
93         break;
94 
95     case PCA9552_INPUT0:
96     case PCA9552_INPUT1:
97     default:
98         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
99                       __func__, reg);
100     }
101 }
102 
103 /*
104  * When Auto-Increment is on, the register address is incremented
105  * after each byte is sent to or received by the device. The index
106  * rollovers to 0 when the maximum register address is reached.
107  */
108 static void pca9552_autoinc(PCA9552State *s)
109 {
110     if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
111         uint8_t reg = s->pointer & 0xf;
112 
113         reg = (reg + 1) % (s->max_reg + 1);
114         s->pointer = reg | PCA9552_AUTOINC;
115     }
116 }
117 
118 static int pca9552_recv(I2CSlave *i2c)
119 {
120     PCA9552State *s = PCA9552(i2c);
121     uint8_t ret;
122 
123     ret = pca9552_read(s, s->pointer & 0xf);
124 
125     /*
126      * From the Specs:
127      *
128      *     Important Note: When a Read sequence is initiated and the
129      *     AI bit is set to Logic Level 1, the Read Sequence MUST
130      *     start by a register different from 0.
131      *
132      * I don't know what should be done in this case, so throw an
133      * error.
134      */
135     if (s->pointer == PCA9552_AUTOINC) {
136         qemu_log_mask(LOG_GUEST_ERROR,
137                       "%s: Autoincrement read starting with register 0\n",
138                       __func__);
139     }
140 
141     pca9552_autoinc(s);
142 
143     return ret;
144 }
145 
146 static int pca9552_send(I2CSlave *i2c, uint8_t data)
147 {
148     PCA9552State *s = PCA9552(i2c);
149 
150     /* First byte sent by is the register address */
151     if (s->len == 0) {
152         s->pointer = data;
153         s->len++;
154     } else {
155         pca9552_write(s, s->pointer & 0xf, data);
156 
157         pca9552_autoinc(s);
158     }
159 
160     return 0;
161 }
162 
163 static int pca9552_event(I2CSlave *i2c, enum i2c_event event)
164 {
165     PCA9552State *s = PCA9552(i2c);
166 
167     s->len = 0;
168     return 0;
169 }
170 
171 static const VMStateDescription pca9552_vmstate = {
172     .name = "PCA9552",
173     .version_id = 0,
174     .minimum_version_id = 0,
175     .fields = (VMStateField[]) {
176         VMSTATE_UINT8(len, PCA9552State),
177         VMSTATE_UINT8(pointer, PCA9552State),
178         VMSTATE_UINT8_ARRAY(regs, PCA9552State, PCA9552_NR_REGS),
179         VMSTATE_I2C_SLAVE(i2c, PCA9552State),
180         VMSTATE_END_OF_LIST()
181     }
182 };
183 
184 static void pca9552_reset(DeviceState *dev)
185 {
186     PCA9552State *s = PCA9552(dev);
187 
188     s->regs[PCA9552_PSC0] = 0xFF;
189     s->regs[PCA9552_PWM0] = 0x80;
190     s->regs[PCA9552_PSC1] = 0xFF;
191     s->regs[PCA9552_PWM1] = 0x80;
192     s->regs[PCA9552_LS0] = 0x55; /* all OFF */
193     s->regs[PCA9552_LS1] = 0x55;
194     s->regs[PCA9552_LS2] = 0x55;
195     s->regs[PCA9552_LS3] = 0x55;
196 
197     pca9552_update_pin_input(s);
198 
199     s->pointer = 0xFF;
200     s->len = 0;
201 }
202 
203 static void pca9552_initfn(Object *obj)
204 {
205     PCA9552State *s = PCA9552(obj);
206 
207     /* If support for the other PCA955X devices are implemented, these
208      * constant values might be part of class structure describing the
209      * PCA955X device
210      */
211     s->max_reg = PCA9552_LS3;
212     s->nr_leds = 16;
213 }
214 
215 static void pca9552_class_init(ObjectClass *klass, void *data)
216 {
217     DeviceClass *dc = DEVICE_CLASS(klass);
218     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
219 
220     k->event = pca9552_event;
221     k->recv = pca9552_recv;
222     k->send = pca9552_send;
223     dc->reset = pca9552_reset;
224     dc->vmsd = &pca9552_vmstate;
225 }
226 
227 static const TypeInfo pca9552_info = {
228     .name          = TYPE_PCA9552,
229     .parent        = TYPE_I2C_SLAVE,
230     .instance_init = pca9552_initfn,
231     .instance_size = sizeof(PCA9552State),
232     .class_init    = pca9552_class_init,
233 };
234 
235 static void pca9552_register_types(void)
236 {
237     type_register_static(&pca9552_info);
238 }
239 
240 type_init(pca9552_register_types)
241