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