xref: /openbmc/qemu/hw/gpio/pca9552.c (revision 6328d8ff)
1*6328d8ffSCédric Le Goater /*
2*6328d8ffSCédric Le Goater  * PCA9552 I2C LED blinker
3*6328d8ffSCédric Le Goater  *
4*6328d8ffSCédric Le Goater  *     https://www.nxp.com/docs/en/application-note/AN264.pdf
5*6328d8ffSCédric Le Goater  *
6*6328d8ffSCédric Le Goater  * Copyright (c) 2017-2018, IBM Corporation.
7*6328d8ffSCédric Le Goater  * Copyright (c) 2020 Philippe Mathieu-Daudé
8*6328d8ffSCédric Le Goater  *
9*6328d8ffSCédric Le Goater  * This work is licensed under the terms of the GNU GPL, version 2 or
10*6328d8ffSCédric Le Goater  * later. See the COPYING file in the top-level directory.
11*6328d8ffSCédric Le Goater  */
12*6328d8ffSCédric Le Goater 
13*6328d8ffSCédric Le Goater #include "qemu/osdep.h"
14*6328d8ffSCédric Le Goater #include "qemu/log.h"
15*6328d8ffSCédric Le Goater #include "qemu/module.h"
16*6328d8ffSCédric Le Goater #include "qemu/bitops.h"
17*6328d8ffSCédric Le Goater #include "hw/qdev-properties.h"
18*6328d8ffSCédric Le Goater #include "hw/gpio/pca9552.h"
19*6328d8ffSCédric Le Goater #include "hw/gpio/pca9552_regs.h"
20*6328d8ffSCédric Le Goater #include "hw/irq.h"
21*6328d8ffSCédric Le Goater #include "migration/vmstate.h"
22*6328d8ffSCédric Le Goater #include "qapi/error.h"
23*6328d8ffSCédric Le Goater #include "qapi/visitor.h"
24*6328d8ffSCédric Le Goater #include "trace.h"
25*6328d8ffSCédric Le Goater #include "qom/object.h"
26*6328d8ffSCédric Le Goater 
27*6328d8ffSCédric Le Goater struct PCA955xClass {
28*6328d8ffSCédric Le Goater     /*< private >*/
29*6328d8ffSCédric Le Goater     I2CSlaveClass parent_class;
30*6328d8ffSCédric Le Goater     /*< public >*/
31*6328d8ffSCédric Le Goater 
32*6328d8ffSCédric Le Goater     uint8_t pin_count;
33*6328d8ffSCédric Le Goater     uint8_t max_reg;
34*6328d8ffSCédric Le Goater };
35*6328d8ffSCédric Le Goater typedef struct PCA955xClass PCA955xClass;
36*6328d8ffSCédric Le Goater 
37*6328d8ffSCédric Le Goater DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
38*6328d8ffSCédric Le Goater                        TYPE_PCA955X)
39*6328d8ffSCédric Le Goater /*
40*6328d8ffSCédric Le Goater  * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
41*6328d8ffSCédric Le Goater  *        chips are the reverse of the PCA953X family of chips.
42*6328d8ffSCédric Le Goater  */
43*6328d8ffSCédric Le Goater #define PCA9552_LED_ON   0x0
44*6328d8ffSCédric Le Goater #define PCA9552_LED_OFF  0x1
45*6328d8ffSCédric Le Goater #define PCA9552_LED_PWM0 0x2
46*6328d8ffSCédric Le Goater #define PCA9552_LED_PWM1 0x3
47*6328d8ffSCédric Le Goater #define PCA9552_PIN_LOW  0x0
48*6328d8ffSCédric Le Goater #define PCA9552_PIN_HIZ  0x1
49*6328d8ffSCédric Le Goater 
50*6328d8ffSCédric Le Goater static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
51*6328d8ffSCédric Le Goater 
pca955x_pin_get_config(PCA955xState * s,int pin)52*6328d8ffSCédric Le Goater static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
53*6328d8ffSCédric Le Goater {
54*6328d8ffSCédric Le Goater     uint8_t reg   = PCA9552_LS0 + (pin / 4);
55*6328d8ffSCédric Le Goater     uint8_t shift = (pin % 4) << 1;
56*6328d8ffSCédric Le Goater 
57*6328d8ffSCédric Le Goater     return extract32(s->regs[reg], shift, 2);
58*6328d8ffSCédric Le Goater }
59*6328d8ffSCédric Le Goater 
60*6328d8ffSCédric Le Goater /* Return INPUT status (bit #N belongs to GPIO #N) */
pca955x_pins_get_status(PCA955xState * s)61*6328d8ffSCédric Le Goater static uint16_t pca955x_pins_get_status(PCA955xState *s)
62*6328d8ffSCédric Le Goater {
63*6328d8ffSCédric Le Goater     return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
64*6328d8ffSCédric Le Goater }
65*6328d8ffSCédric Le Goater 
pca955x_display_pins_status(PCA955xState * s,uint16_t previous_pins_status)66*6328d8ffSCédric Le Goater static void pca955x_display_pins_status(PCA955xState *s,
67*6328d8ffSCédric Le Goater                                         uint16_t previous_pins_status)
68*6328d8ffSCédric Le Goater {
69*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
70*6328d8ffSCédric Le Goater     uint16_t pins_status, pins_changed;
71*6328d8ffSCédric Le Goater     int i;
72*6328d8ffSCédric Le Goater 
73*6328d8ffSCédric Le Goater     pins_status = pca955x_pins_get_status(s);
74*6328d8ffSCédric Le Goater     pins_changed = previous_pins_status ^ pins_status;
75*6328d8ffSCédric Le Goater     if (!pins_changed) {
76*6328d8ffSCédric Le Goater         return;
77*6328d8ffSCédric Le Goater     }
78*6328d8ffSCédric Le Goater     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
79*6328d8ffSCédric Le Goater         char *buf = g_newa(char, k->pin_count + 1);
80*6328d8ffSCédric Le Goater 
81*6328d8ffSCédric Le Goater         for (i = 0; i < k->pin_count; i++) {
82*6328d8ffSCédric Le Goater             if (extract32(pins_status, i, 1)) {
83*6328d8ffSCédric Le Goater                 buf[i] = '*';
84*6328d8ffSCédric Le Goater             } else {
85*6328d8ffSCédric Le Goater                 buf[i] = '.';
86*6328d8ffSCédric Le Goater             }
87*6328d8ffSCédric Le Goater         }
88*6328d8ffSCédric Le Goater         buf[i] = '\0';
89*6328d8ffSCédric Le Goater         trace_pca955x_gpio_status(s->description, buf);
90*6328d8ffSCédric Le Goater     }
91*6328d8ffSCédric Le Goater     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
92*6328d8ffSCédric Le Goater         for (i = 0; i < k->pin_count; i++) {
93*6328d8ffSCédric Le Goater             if (extract32(pins_changed, i, 1)) {
94*6328d8ffSCédric Le Goater                 unsigned new_state = extract32(pins_status, i, 1);
95*6328d8ffSCédric Le Goater 
96*6328d8ffSCédric Le Goater                 /*
97*6328d8ffSCédric Le Goater                  * We display the state using the PCA logic ("active-high").
98*6328d8ffSCédric Le Goater                  * This is not the state of the LED, which signal might be
99*6328d8ffSCédric Le Goater                  * wired "active-low" on the board.
100*6328d8ffSCédric Le Goater                  */
101*6328d8ffSCédric Le Goater                 trace_pca955x_gpio_change(s->description, i,
102*6328d8ffSCédric Le Goater                                           !new_state, new_state);
103*6328d8ffSCédric Le Goater             }
104*6328d8ffSCédric Le Goater         }
105*6328d8ffSCédric Le Goater     }
106*6328d8ffSCédric Le Goater }
107*6328d8ffSCédric Le Goater 
pca955x_update_pin_input(PCA955xState * s)108*6328d8ffSCédric Le Goater static void pca955x_update_pin_input(PCA955xState *s)
109*6328d8ffSCédric Le Goater {
110*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
111*6328d8ffSCédric Le Goater     int i;
112*6328d8ffSCédric Le Goater 
113*6328d8ffSCédric Le Goater     for (i = 0; i < k->pin_count; i++) {
114*6328d8ffSCédric Le Goater         uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
115*6328d8ffSCédric Le Goater         uint8_t bit_mask = 1 << (i % 8);
116*6328d8ffSCédric Le Goater         uint8_t config = pca955x_pin_get_config(s, i);
117*6328d8ffSCédric Le Goater         uint8_t old_value = s->regs[input_reg] & bit_mask;
118*6328d8ffSCédric Le Goater         uint8_t new_value;
119*6328d8ffSCédric Le Goater 
120*6328d8ffSCédric Le Goater         switch (config) {
121*6328d8ffSCédric Le Goater         case PCA9552_LED_ON:
122*6328d8ffSCédric Le Goater             /* Pin is set to 0V to turn on LED */
123*6328d8ffSCédric Le Goater             s->regs[input_reg] &= ~bit_mask;
124*6328d8ffSCédric Le Goater             break;
125*6328d8ffSCédric Le Goater         case PCA9552_LED_OFF:
126*6328d8ffSCédric Le Goater             /*
127*6328d8ffSCédric Le Goater              * Pin is set to Hi-Z to turn off LED and
128*6328d8ffSCédric Le Goater              * pullup sets it to a logical 1 unless
129*6328d8ffSCédric Le Goater              * external device drives it low.
130*6328d8ffSCédric Le Goater              */
131*6328d8ffSCédric Le Goater             if (s->ext_state[i] == PCA9552_PIN_LOW) {
132*6328d8ffSCédric Le Goater                 s->regs[input_reg] &= ~bit_mask;
133*6328d8ffSCédric Le Goater             } else {
134*6328d8ffSCédric Le Goater                 s->regs[input_reg] |=  bit_mask;
135*6328d8ffSCédric Le Goater             }
136*6328d8ffSCédric Le Goater             break;
137*6328d8ffSCédric Le Goater         case PCA9552_LED_PWM0:
138*6328d8ffSCédric Le Goater         case PCA9552_LED_PWM1:
139*6328d8ffSCédric Le Goater             /* TODO */
140*6328d8ffSCédric Le Goater         default:
141*6328d8ffSCédric Le Goater             break;
142*6328d8ffSCédric Le Goater         }
143*6328d8ffSCédric Le Goater 
144*6328d8ffSCédric Le Goater         /* update irq state only if pin state changed */
145*6328d8ffSCédric Le Goater         new_value = s->regs[input_reg] & bit_mask;
146*6328d8ffSCédric Le Goater         if (new_value != old_value) {
147*6328d8ffSCédric Le Goater             qemu_set_irq(s->gpio_out[i], !!new_value);
148*6328d8ffSCédric Le Goater         }
149*6328d8ffSCédric Le Goater     }
150*6328d8ffSCédric Le Goater }
151*6328d8ffSCédric Le Goater 
pca955x_read(PCA955xState * s,uint8_t reg)152*6328d8ffSCédric Le Goater static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
153*6328d8ffSCédric Le Goater {
154*6328d8ffSCédric Le Goater     switch (reg) {
155*6328d8ffSCédric Le Goater     case PCA9552_INPUT0:
156*6328d8ffSCédric Le Goater     case PCA9552_INPUT1:
157*6328d8ffSCédric Le Goater     case PCA9552_PSC0:
158*6328d8ffSCédric Le Goater     case PCA9552_PWM0:
159*6328d8ffSCédric Le Goater     case PCA9552_PSC1:
160*6328d8ffSCédric Le Goater     case PCA9552_PWM1:
161*6328d8ffSCédric Le Goater     case PCA9552_LS0:
162*6328d8ffSCédric Le Goater     case PCA9552_LS1:
163*6328d8ffSCédric Le Goater     case PCA9552_LS2:
164*6328d8ffSCédric Le Goater     case PCA9552_LS3:
165*6328d8ffSCédric Le Goater         return s->regs[reg];
166*6328d8ffSCédric Le Goater     default:
167*6328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
168*6328d8ffSCédric Le Goater                       __func__, reg);
169*6328d8ffSCédric Le Goater         return 0xFF;
170*6328d8ffSCédric Le Goater     }
171*6328d8ffSCédric Le Goater }
172*6328d8ffSCédric Le Goater 
pca955x_write(PCA955xState * s,uint8_t reg,uint8_t data)173*6328d8ffSCédric Le Goater static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
174*6328d8ffSCédric Le Goater {
175*6328d8ffSCédric Le Goater     uint16_t pins_status;
176*6328d8ffSCédric Le Goater 
177*6328d8ffSCédric Le Goater     switch (reg) {
178*6328d8ffSCédric Le Goater     case PCA9552_PSC0:
179*6328d8ffSCédric Le Goater     case PCA9552_PWM0:
180*6328d8ffSCédric Le Goater     case PCA9552_PSC1:
181*6328d8ffSCédric Le Goater     case PCA9552_PWM1:
182*6328d8ffSCédric Le Goater         s->regs[reg] = data;
183*6328d8ffSCédric Le Goater         break;
184*6328d8ffSCédric Le Goater 
185*6328d8ffSCédric Le Goater     case PCA9552_LS0:
186*6328d8ffSCédric Le Goater     case PCA9552_LS1:
187*6328d8ffSCédric Le Goater     case PCA9552_LS2:
188*6328d8ffSCédric Le Goater     case PCA9552_LS3:
189*6328d8ffSCédric Le Goater         pins_status = pca955x_pins_get_status(s);
190*6328d8ffSCédric Le Goater         s->regs[reg] = data;
191*6328d8ffSCédric Le Goater         pca955x_update_pin_input(s);
192*6328d8ffSCédric Le Goater         pca955x_display_pins_status(s, pins_status);
193*6328d8ffSCédric Le Goater         break;
194*6328d8ffSCédric Le Goater 
195*6328d8ffSCédric Le Goater     case PCA9552_INPUT0:
196*6328d8ffSCédric Le Goater     case PCA9552_INPUT1:
197*6328d8ffSCédric Le Goater     default:
198*6328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
199*6328d8ffSCédric Le Goater                       __func__, reg);
200*6328d8ffSCédric Le Goater     }
201*6328d8ffSCédric Le Goater }
202*6328d8ffSCédric Le Goater 
203*6328d8ffSCédric Le Goater /*
204*6328d8ffSCédric Le Goater  * When Auto-Increment is on, the register address is incremented
205*6328d8ffSCédric Le Goater  * after each byte is sent to or received by the device. The index
206*6328d8ffSCédric Le Goater  * rollovers to 0 when the maximum register address is reached.
207*6328d8ffSCédric Le Goater  */
pca955x_autoinc(PCA955xState * s)208*6328d8ffSCédric Le Goater static void pca955x_autoinc(PCA955xState *s)
209*6328d8ffSCédric Le Goater {
210*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
211*6328d8ffSCédric Le Goater 
212*6328d8ffSCédric Le Goater     if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
213*6328d8ffSCédric Le Goater         uint8_t reg = s->pointer & 0xf;
214*6328d8ffSCédric Le Goater 
215*6328d8ffSCédric Le Goater         reg = (reg + 1) % (k->max_reg + 1);
216*6328d8ffSCédric Le Goater         s->pointer = reg | PCA9552_AUTOINC;
217*6328d8ffSCédric Le Goater     }
218*6328d8ffSCédric Le Goater }
219*6328d8ffSCédric Le Goater 
pca955x_recv(I2CSlave * i2c)220*6328d8ffSCédric Le Goater static uint8_t pca955x_recv(I2CSlave *i2c)
221*6328d8ffSCédric Le Goater {
222*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
223*6328d8ffSCédric Le Goater     uint8_t ret;
224*6328d8ffSCédric Le Goater 
225*6328d8ffSCédric Le Goater     ret = pca955x_read(s, s->pointer & 0xf);
226*6328d8ffSCédric Le Goater 
227*6328d8ffSCédric Le Goater     /*
228*6328d8ffSCédric Le Goater      * From the Specs:
229*6328d8ffSCédric Le Goater      *
230*6328d8ffSCédric Le Goater      *     Important Note: When a Read sequence is initiated and the
231*6328d8ffSCédric Le Goater      *     AI bit is set to Logic Level 1, the Read Sequence MUST
232*6328d8ffSCédric Le Goater      *     start by a register different from 0.
233*6328d8ffSCédric Le Goater      *
234*6328d8ffSCédric Le Goater      * I don't know what should be done in this case, so throw an
235*6328d8ffSCédric Le Goater      * error.
236*6328d8ffSCédric Le Goater      */
237*6328d8ffSCédric Le Goater     if (s->pointer == PCA9552_AUTOINC) {
238*6328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
239*6328d8ffSCédric Le Goater                       "%s: Autoincrement read starting with register 0\n",
240*6328d8ffSCédric Le Goater                       __func__);
241*6328d8ffSCédric Le Goater     }
242*6328d8ffSCédric Le Goater 
243*6328d8ffSCédric Le Goater     pca955x_autoinc(s);
244*6328d8ffSCédric Le Goater 
245*6328d8ffSCédric Le Goater     return ret;
246*6328d8ffSCédric Le Goater }
247*6328d8ffSCédric Le Goater 
pca955x_send(I2CSlave * i2c,uint8_t data)248*6328d8ffSCédric Le Goater static int pca955x_send(I2CSlave *i2c, uint8_t data)
249*6328d8ffSCédric Le Goater {
250*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
251*6328d8ffSCédric Le Goater 
252*6328d8ffSCédric Le Goater     /* First byte sent by is the register address */
253*6328d8ffSCédric Le Goater     if (s->len == 0) {
254*6328d8ffSCédric Le Goater         s->pointer = data;
255*6328d8ffSCédric Le Goater         s->len++;
256*6328d8ffSCédric Le Goater     } else {
257*6328d8ffSCédric Le Goater         pca955x_write(s, s->pointer & 0xf, data);
258*6328d8ffSCédric Le Goater 
259*6328d8ffSCédric Le Goater         pca955x_autoinc(s);
260*6328d8ffSCédric Le Goater     }
261*6328d8ffSCédric Le Goater 
262*6328d8ffSCédric Le Goater     return 0;
263*6328d8ffSCédric Le Goater }
264*6328d8ffSCédric Le Goater 
pca955x_event(I2CSlave * i2c,enum i2c_event event)265*6328d8ffSCédric Le Goater static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
266*6328d8ffSCédric Le Goater {
267*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
268*6328d8ffSCédric Le Goater 
269*6328d8ffSCédric Le Goater     s->len = 0;
270*6328d8ffSCédric Le Goater     return 0;
271*6328d8ffSCédric Le Goater }
272*6328d8ffSCédric Le Goater 
pca955x_get_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)273*6328d8ffSCédric Le Goater static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
274*6328d8ffSCédric Le Goater                             void *opaque, Error **errp)
275*6328d8ffSCédric Le Goater {
276*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
277*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(obj);
278*6328d8ffSCédric Le Goater     int led, rc, reg;
279*6328d8ffSCédric Le Goater     uint8_t state;
280*6328d8ffSCédric Le Goater 
281*6328d8ffSCédric Le Goater     rc = sscanf(name, "led%2d", &led);
282*6328d8ffSCédric Le Goater     if (rc != 1) {
283*6328d8ffSCédric Le Goater         error_setg(errp, "%s: error reading %s", __func__, name);
284*6328d8ffSCédric Le Goater         return;
285*6328d8ffSCédric Le Goater     }
286*6328d8ffSCédric Le Goater     if (led < 0 || led > k->pin_count) {
287*6328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led %s", __func__, name);
288*6328d8ffSCédric Le Goater         return;
289*6328d8ffSCédric Le Goater     }
290*6328d8ffSCédric Le Goater     /*
291*6328d8ffSCédric Le Goater      * Get the LSx register as the qom interface should expose the device
292*6328d8ffSCédric Le Goater      * state, not the modeled 'input line' behaviour which would come from
293*6328d8ffSCédric Le Goater      * reading the INPUTx reg
294*6328d8ffSCédric Le Goater      */
295*6328d8ffSCédric Le Goater     reg = PCA9552_LS0 + led / 4;
296*6328d8ffSCédric Le Goater     state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
297*6328d8ffSCédric Le Goater     visit_type_str(v, name, (char **)&led_state[state], errp);
298*6328d8ffSCédric Le Goater }
299*6328d8ffSCédric Le Goater 
300*6328d8ffSCédric Le Goater /*
301*6328d8ffSCédric Le Goater  * Return an LED selector register value based on an existing one, with
302*6328d8ffSCédric Le Goater  * the appropriate 2-bit state value set for the given LED number (0-3).
303*6328d8ffSCédric Le Goater  */
pca955x_ledsel(uint8_t oldval,int led_num,int state)304*6328d8ffSCédric Le Goater static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
305*6328d8ffSCédric Le Goater {
306*6328d8ffSCédric Le Goater         return (oldval & (~(0x3 << (led_num << 1)))) |
307*6328d8ffSCédric Le Goater                 ((state & 0x3) << (led_num << 1));
308*6328d8ffSCédric Le Goater }
309*6328d8ffSCédric Le Goater 
pca955x_set_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)310*6328d8ffSCédric Le Goater static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
311*6328d8ffSCédric Le Goater                             void *opaque, Error **errp)
312*6328d8ffSCédric Le Goater {
313*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
314*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(obj);
315*6328d8ffSCédric Le Goater     int led, rc, reg, val;
316*6328d8ffSCédric Le Goater     uint8_t state;
317*6328d8ffSCédric Le Goater     char *state_str;
318*6328d8ffSCédric Le Goater 
319*6328d8ffSCédric Le Goater     if (!visit_type_str(v, name, &state_str, errp)) {
320*6328d8ffSCédric Le Goater         return;
321*6328d8ffSCédric Le Goater     }
322*6328d8ffSCédric Le Goater     rc = sscanf(name, "led%2d", &led);
323*6328d8ffSCédric Le Goater     if (rc != 1) {
324*6328d8ffSCédric Le Goater         error_setg(errp, "%s: error reading %s", __func__, name);
325*6328d8ffSCédric Le Goater         return;
326*6328d8ffSCédric Le Goater     }
327*6328d8ffSCédric Le Goater     if (led < 0 || led > k->pin_count) {
328*6328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led %s", __func__, name);
329*6328d8ffSCédric Le Goater         return;
330*6328d8ffSCédric Le Goater     }
331*6328d8ffSCédric Le Goater 
332*6328d8ffSCédric Le Goater     for (state = 0; state < ARRAY_SIZE(led_state); state++) {
333*6328d8ffSCédric Le Goater         if (!strcmp(state_str, led_state[state])) {
334*6328d8ffSCédric Le Goater             break;
335*6328d8ffSCédric Le Goater         }
336*6328d8ffSCédric Le Goater     }
337*6328d8ffSCédric Le Goater     if (state >= ARRAY_SIZE(led_state)) {
338*6328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led state %s", __func__, state_str);
339*6328d8ffSCédric Le Goater         return;
340*6328d8ffSCédric Le Goater     }
341*6328d8ffSCédric Le Goater 
342*6328d8ffSCédric Le Goater     reg = PCA9552_LS0 + led / 4;
343*6328d8ffSCédric Le Goater     val = pca955x_read(s, reg);
344*6328d8ffSCédric Le Goater     val = pca955x_ledsel(val, led % 4, state);
345*6328d8ffSCédric Le Goater     pca955x_write(s, reg, val);
346*6328d8ffSCédric Le Goater }
347*6328d8ffSCédric Le Goater 
348*6328d8ffSCédric Le Goater static const VMStateDescription pca9552_vmstate = {
349*6328d8ffSCédric Le Goater     .name = "PCA9552",
350*6328d8ffSCédric Le Goater     .version_id = 0,
351*6328d8ffSCédric Le Goater     .minimum_version_id = 0,
352*6328d8ffSCédric Le Goater     .fields = (const VMStateField[]) {
353*6328d8ffSCédric Le Goater         VMSTATE_UINT8(len, PCA955xState),
354*6328d8ffSCédric Le Goater         VMSTATE_UINT8(pointer, PCA955xState),
355*6328d8ffSCédric Le Goater         VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
356*6328d8ffSCédric Le Goater         VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
357*6328d8ffSCédric Le Goater         VMSTATE_I2C_SLAVE(i2c, PCA955xState),
358*6328d8ffSCédric Le Goater         VMSTATE_END_OF_LIST()
359*6328d8ffSCédric Le Goater     }
360*6328d8ffSCédric Le Goater };
361*6328d8ffSCédric Le Goater 
pca9552_reset(DeviceState * dev)362*6328d8ffSCédric Le Goater static void pca9552_reset(DeviceState *dev)
363*6328d8ffSCédric Le Goater {
364*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(dev);
365*6328d8ffSCédric Le Goater 
366*6328d8ffSCédric Le Goater     s->regs[PCA9552_PSC0] = 0xFF;
367*6328d8ffSCédric Le Goater     s->regs[PCA9552_PWM0] = 0x80;
368*6328d8ffSCédric Le Goater     s->regs[PCA9552_PSC1] = 0xFF;
369*6328d8ffSCédric Le Goater     s->regs[PCA9552_PWM1] = 0x80;
370*6328d8ffSCédric Le Goater     s->regs[PCA9552_LS0] = 0x55; /* all OFF */
371*6328d8ffSCédric Le Goater     s->regs[PCA9552_LS1] = 0x55;
372*6328d8ffSCédric Le Goater     s->regs[PCA9552_LS2] = 0x55;
373*6328d8ffSCédric Le Goater     s->regs[PCA9552_LS3] = 0x55;
374*6328d8ffSCédric Le Goater 
375*6328d8ffSCédric Le Goater     memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
376*6328d8ffSCédric Le Goater     pca955x_update_pin_input(s);
377*6328d8ffSCédric Le Goater 
378*6328d8ffSCédric Le Goater     s->pointer = 0xFF;
379*6328d8ffSCédric Le Goater     s->len = 0;
380*6328d8ffSCédric Le Goater }
381*6328d8ffSCédric Le Goater 
pca955x_initfn(Object * obj)382*6328d8ffSCédric Le Goater static void pca955x_initfn(Object *obj)
383*6328d8ffSCédric Le Goater {
384*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
385*6328d8ffSCédric Le Goater     int led;
386*6328d8ffSCédric Le Goater 
387*6328d8ffSCédric Le Goater     assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
388*6328d8ffSCédric Le Goater     for (led = 0; led < k->pin_count; led++) {
389*6328d8ffSCédric Le Goater         char *name;
390*6328d8ffSCédric Le Goater 
391*6328d8ffSCédric Le Goater         name = g_strdup_printf("led%d", led);
392*6328d8ffSCédric Le Goater         object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
393*6328d8ffSCédric Le Goater                             NULL, NULL);
394*6328d8ffSCédric Le Goater         g_free(name);
395*6328d8ffSCédric Le Goater     }
396*6328d8ffSCédric Le Goater }
397*6328d8ffSCédric Le Goater 
pca955x_set_ext_state(PCA955xState * s,int pin,int level)398*6328d8ffSCédric Le Goater static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
399*6328d8ffSCédric Le Goater {
400*6328d8ffSCédric Le Goater     if (s->ext_state[pin] != level) {
401*6328d8ffSCédric Le Goater         uint16_t pins_status = pca955x_pins_get_status(s);
402*6328d8ffSCédric Le Goater         s->ext_state[pin] = level;
403*6328d8ffSCédric Le Goater         pca955x_update_pin_input(s);
404*6328d8ffSCédric Le Goater         pca955x_display_pins_status(s, pins_status);
405*6328d8ffSCédric Le Goater     }
406*6328d8ffSCédric Le Goater }
407*6328d8ffSCédric Le Goater 
pca955x_gpio_in_handler(void * opaque,int pin,int level)408*6328d8ffSCédric Le Goater static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
409*6328d8ffSCédric Le Goater {
410*6328d8ffSCédric Le Goater 
411*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(opaque);
412*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
413*6328d8ffSCédric Le Goater 
414*6328d8ffSCédric Le Goater     assert((pin >= 0) && (pin < k->pin_count));
415*6328d8ffSCédric Le Goater     pca955x_set_ext_state(s, pin, level);
416*6328d8ffSCédric Le Goater }
417*6328d8ffSCédric Le Goater 
pca955x_realize(DeviceState * dev,Error ** errp)418*6328d8ffSCédric Le Goater static void pca955x_realize(DeviceState *dev, Error **errp)
419*6328d8ffSCédric Le Goater {
420*6328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(dev);
421*6328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(dev);
422*6328d8ffSCédric Le Goater 
423*6328d8ffSCédric Le Goater     if (!s->description) {
424*6328d8ffSCédric Le Goater         s->description = g_strdup("pca-unspecified");
425*6328d8ffSCédric Le Goater     }
426*6328d8ffSCédric Le Goater 
427*6328d8ffSCédric Le Goater     qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
428*6328d8ffSCédric Le Goater     qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
429*6328d8ffSCédric Le Goater }
430*6328d8ffSCédric Le Goater 
431*6328d8ffSCédric Le Goater static Property pca955x_properties[] = {
432*6328d8ffSCédric Le Goater     DEFINE_PROP_STRING("description", PCA955xState, description),
433*6328d8ffSCédric Le Goater     DEFINE_PROP_END_OF_LIST(),
434*6328d8ffSCédric Le Goater };
435*6328d8ffSCédric Le Goater 
pca955x_class_init(ObjectClass * klass,void * data)436*6328d8ffSCédric Le Goater static void pca955x_class_init(ObjectClass *klass, void *data)
437*6328d8ffSCédric Le Goater {
438*6328d8ffSCédric Le Goater     DeviceClass *dc = DEVICE_CLASS(klass);
439*6328d8ffSCédric Le Goater     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
440*6328d8ffSCédric Le Goater 
441*6328d8ffSCédric Le Goater     k->event = pca955x_event;
442*6328d8ffSCédric Le Goater     k->recv = pca955x_recv;
443*6328d8ffSCédric Le Goater     k->send = pca955x_send;
444*6328d8ffSCédric Le Goater     dc->realize = pca955x_realize;
445*6328d8ffSCédric Le Goater     device_class_set_props(dc, pca955x_properties);
446*6328d8ffSCédric Le Goater }
447*6328d8ffSCédric Le Goater 
448*6328d8ffSCédric Le Goater static const TypeInfo pca955x_info = {
449*6328d8ffSCédric Le Goater     .name          = TYPE_PCA955X,
450*6328d8ffSCédric Le Goater     .parent        = TYPE_I2C_SLAVE,
451*6328d8ffSCédric Le Goater     .instance_init = pca955x_initfn,
452*6328d8ffSCédric Le Goater     .instance_size = sizeof(PCA955xState),
453*6328d8ffSCédric Le Goater     .class_init    = pca955x_class_init,
454*6328d8ffSCédric Le Goater     .class_size    = sizeof(PCA955xClass),
455*6328d8ffSCédric Le Goater     .abstract      = true,
456*6328d8ffSCédric Le Goater };
457*6328d8ffSCédric Le Goater 
pca9552_class_init(ObjectClass * oc,void * data)458*6328d8ffSCédric Le Goater static void pca9552_class_init(ObjectClass *oc, void *data)
459*6328d8ffSCédric Le Goater {
460*6328d8ffSCédric Le Goater     DeviceClass *dc = DEVICE_CLASS(oc);
461*6328d8ffSCédric Le Goater     PCA955xClass *pc = PCA955X_CLASS(oc);
462*6328d8ffSCédric Le Goater 
463*6328d8ffSCédric Le Goater     dc->reset = pca9552_reset;
464*6328d8ffSCédric Le Goater     dc->vmsd = &pca9552_vmstate;
465*6328d8ffSCédric Le Goater     pc->max_reg = PCA9552_LS3;
466*6328d8ffSCédric Le Goater     pc->pin_count = 16;
467*6328d8ffSCédric Le Goater }
468*6328d8ffSCédric Le Goater 
469*6328d8ffSCédric Le Goater static const TypeInfo pca9552_info = {
470*6328d8ffSCédric Le Goater     .name          = TYPE_PCA9552,
471*6328d8ffSCédric Le Goater     .parent        = TYPE_PCA955X,
472*6328d8ffSCédric Le Goater     .class_init    = pca9552_class_init,
473*6328d8ffSCédric Le Goater };
474*6328d8ffSCédric Le Goater 
pca955x_register_types(void)475*6328d8ffSCédric Le Goater static void pca955x_register_types(void)
476*6328d8ffSCédric Le Goater {
477*6328d8ffSCédric Le Goater     type_register_static(&pca955x_info);
478*6328d8ffSCédric Le Goater     type_register_static(&pca9552_info);
479*6328d8ffSCédric Le Goater }
480*6328d8ffSCédric Le Goater 
481*6328d8ffSCédric Le Goater type_init(pca955x_register_types)
482