xref: /qemu/hw/gpio/pca9552.c (revision db1015e92e04835c9eb50c29625fe566d1202dbd)
15141d415SCédric Le Goater /*
25141d415SCédric Le Goater  * PCA9552 I2C LED blinker
35141d415SCédric Le Goater  *
45141d415SCédric Le Goater  *     https://www.nxp.com/docs/en/application-note/AN264.pdf
55141d415SCédric Le Goater  *
65141d415SCédric Le Goater  * Copyright (c) 2017-2018, IBM Corporation.
7736132e4SPhilippe Mathieu-Daudé  * Copyright (c) 2020 Philippe Mathieu-Daudé
85141d415SCédric Le Goater  *
95141d415SCédric Le Goater  * This work is licensed under the terms of the GNU GPL, version 2 or
105141d415SCédric Le Goater  * later. See the COPYING file in the top-level directory.
115141d415SCédric Le Goater  */
125141d415SCédric Le Goater 
135141d415SCédric Le Goater #include "qemu/osdep.h"
145141d415SCédric Le Goater #include "qemu/log.h"
150b8fa32fSMarkus Armbruster #include "qemu/module.h"
16b989b89fSPhilippe Mathieu-Daudé #include "qemu/bitops.h"
172df252d8SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
185141d415SCédric Le Goater #include "hw/misc/pca9552.h"
195141d415SCédric Le Goater #include "hw/misc/pca9552_regs.h"
20586f495bSPhilippe Mathieu-Daudé #include "hw/irq.h"
21d6454270SMarkus Armbruster #include "migration/vmstate.h"
22a90d8f84SJoel Stanley #include "qapi/error.h"
23a90d8f84SJoel Stanley #include "qapi/visitor.h"
24b989b89fSPhilippe Mathieu-Daudé #include "trace.h"
25*db1015e9SEduardo Habkost #include "qom/object.h"
265141d415SCédric Le Goater 
27*db1015e9SEduardo Habkost struct PCA955xClass {
28736132e4SPhilippe Mathieu-Daudé     /*< private >*/
29736132e4SPhilippe Mathieu-Daudé     I2CSlaveClass parent_class;
30736132e4SPhilippe Mathieu-Daudé     /*< public >*/
31736132e4SPhilippe Mathieu-Daudé 
32736132e4SPhilippe Mathieu-Daudé     uint8_t pin_count;
33736132e4SPhilippe Mathieu-Daudé     uint8_t max_reg;
34*db1015e9SEduardo Habkost };
35*db1015e9SEduardo Habkost typedef struct PCA955xClass PCA955xClass;
36736132e4SPhilippe Mathieu-Daudé 
37736132e4SPhilippe Mathieu-Daudé #define PCA955X_CLASS(klass) \
38736132e4SPhilippe Mathieu-Daudé     OBJECT_CLASS_CHECK(PCA955xClass, (klass), TYPE_PCA955X)
39736132e4SPhilippe Mathieu-Daudé #define PCA955X_GET_CLASS(obj) \
40736132e4SPhilippe Mathieu-Daudé     OBJECT_GET_CLASS(PCA955xClass, (obj), TYPE_PCA955X)
41736132e4SPhilippe Mathieu-Daudé 
425141d415SCédric Le Goater #define PCA9552_LED_ON   0x0
435141d415SCédric Le Goater #define PCA9552_LED_OFF  0x1
445141d415SCédric Le Goater #define PCA9552_LED_PWM0 0x2
455141d415SCédric Le Goater #define PCA9552_LED_PWM1 0x3
465141d415SCédric Le Goater 
47a90d8f84SJoel Stanley static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
48a90d8f84SJoel Stanley 
49ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
505141d415SCédric Le Goater {
515141d415SCédric Le Goater     uint8_t reg   = PCA9552_LS0 + (pin / 4);
525141d415SCédric Le Goater     uint8_t shift = (pin % 4) << 1;
535141d415SCédric Le Goater 
545141d415SCédric Le Goater     return extract32(s->regs[reg], shift, 2);
555141d415SCédric Le Goater }
565141d415SCédric Le Goater 
57b989b89fSPhilippe Mathieu-Daudé /* Return INPUT status (bit #N belongs to GPIO #N) */
58b989b89fSPhilippe Mathieu-Daudé static uint16_t pca955x_pins_get_status(PCA955xState *s)
59b989b89fSPhilippe Mathieu-Daudé {
60b989b89fSPhilippe Mathieu-Daudé     return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
61b989b89fSPhilippe Mathieu-Daudé }
62b989b89fSPhilippe Mathieu-Daudé 
63b989b89fSPhilippe Mathieu-Daudé static void pca955x_display_pins_status(PCA955xState *s,
64b989b89fSPhilippe Mathieu-Daudé                                         uint16_t previous_pins_status)
65b989b89fSPhilippe Mathieu-Daudé {
66b989b89fSPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
67b989b89fSPhilippe Mathieu-Daudé     uint16_t pins_status, pins_changed;
68b989b89fSPhilippe Mathieu-Daudé     int i;
69b989b89fSPhilippe Mathieu-Daudé 
70b989b89fSPhilippe Mathieu-Daudé     pins_status = pca955x_pins_get_status(s);
71b989b89fSPhilippe Mathieu-Daudé     pins_changed = previous_pins_status ^ pins_status;
72b989b89fSPhilippe Mathieu-Daudé     if (!pins_changed) {
73b989b89fSPhilippe Mathieu-Daudé         return;
74b989b89fSPhilippe Mathieu-Daudé     }
75b989b89fSPhilippe Mathieu-Daudé     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
76b989b89fSPhilippe Mathieu-Daudé         char *buf = g_newa(char, k->pin_count + 1);
77b989b89fSPhilippe Mathieu-Daudé 
78b989b89fSPhilippe Mathieu-Daudé         for (i = 0; i < k->pin_count; i++) {
79b989b89fSPhilippe Mathieu-Daudé             if (extract32(pins_status, i, 1)) {
80b989b89fSPhilippe Mathieu-Daudé                 buf[i] = '*';
81b989b89fSPhilippe Mathieu-Daudé             } else {
82b989b89fSPhilippe Mathieu-Daudé                 buf[i] = '.';
83b989b89fSPhilippe Mathieu-Daudé             }
84b989b89fSPhilippe Mathieu-Daudé         }
85b989b89fSPhilippe Mathieu-Daudé         buf[i] = '\0';
86b989b89fSPhilippe Mathieu-Daudé         trace_pca955x_gpio_status(s->description, buf);
87b989b89fSPhilippe Mathieu-Daudé     }
88d82ab293SPhilippe Mathieu-Daudé     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
89d82ab293SPhilippe Mathieu-Daudé         for (i = 0; i < k->pin_count; i++) {
90d82ab293SPhilippe Mathieu-Daudé             if (extract32(pins_changed, i, 1)) {
91d82ab293SPhilippe Mathieu-Daudé                 unsigned new_state = extract32(pins_status, i, 1);
92d82ab293SPhilippe Mathieu-Daudé 
93d82ab293SPhilippe Mathieu-Daudé                 /*
94d82ab293SPhilippe Mathieu-Daudé                  * We display the state using the PCA logic ("active-high").
95d82ab293SPhilippe Mathieu-Daudé                  * This is not the state of the LED, which signal might be
96d82ab293SPhilippe Mathieu-Daudé                  * wired "active-low" on the board.
97d82ab293SPhilippe Mathieu-Daudé                  */
98d82ab293SPhilippe Mathieu-Daudé                 trace_pca955x_gpio_change(s->description, i,
99d82ab293SPhilippe Mathieu-Daudé                                           !new_state, new_state);
100d82ab293SPhilippe Mathieu-Daudé             }
101d82ab293SPhilippe Mathieu-Daudé         }
102d82ab293SPhilippe Mathieu-Daudé     }
103b989b89fSPhilippe Mathieu-Daudé }
104b989b89fSPhilippe Mathieu-Daudé 
105ec17228aSPhilippe Mathieu-Daudé static void pca955x_update_pin_input(PCA955xState *s)
1065141d415SCédric Le Goater {
107736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
1085141d415SCédric Le Goater     int i;
1095141d415SCédric Le Goater 
110736132e4SPhilippe Mathieu-Daudé     for (i = 0; i < k->pin_count; i++) {
1115141d415SCédric Le Goater         uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
1125141d415SCédric Le Goater         uint8_t input_shift = (i % 8);
113ec17228aSPhilippe Mathieu-Daudé         uint8_t config = pca955x_pin_get_config(s, i);
1145141d415SCédric Le Goater 
1155141d415SCédric Le Goater         switch (config) {
1165141d415SCédric Le Goater         case PCA9552_LED_ON:
117586f495bSPhilippe Mathieu-Daudé             qemu_set_irq(s->gpio[i], 1);
1185141d415SCédric Le Goater             s->regs[input_reg] |= 1 << input_shift;
1195141d415SCédric Le Goater             break;
1205141d415SCédric Le Goater         case PCA9552_LED_OFF:
121586f495bSPhilippe Mathieu-Daudé             qemu_set_irq(s->gpio[i], 0);
1225141d415SCédric Le Goater             s->regs[input_reg] &= ~(1 << input_shift);
1235141d415SCédric Le Goater             break;
1245141d415SCédric Le Goater         case PCA9552_LED_PWM0:
1255141d415SCédric Le Goater         case PCA9552_LED_PWM1:
1265141d415SCédric Le Goater             /* TODO */
1275141d415SCédric Le Goater         default:
1285141d415SCédric Le Goater             break;
1295141d415SCédric Le Goater         }
1305141d415SCédric Le Goater     }
1315141d415SCédric Le Goater }
1325141d415SCédric Le Goater 
133ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
1345141d415SCédric Le Goater {
1355141d415SCédric Le Goater     switch (reg) {
1365141d415SCédric Le Goater     case PCA9552_INPUT0:
1375141d415SCédric Le Goater     case PCA9552_INPUT1:
1385141d415SCédric Le Goater     case PCA9552_PSC0:
1395141d415SCédric Le Goater     case PCA9552_PWM0:
1405141d415SCédric Le Goater     case PCA9552_PSC1:
1415141d415SCédric Le Goater     case PCA9552_PWM1:
1425141d415SCédric Le Goater     case PCA9552_LS0:
1435141d415SCédric Le Goater     case PCA9552_LS1:
1445141d415SCédric Le Goater     case PCA9552_LS2:
1455141d415SCédric Le Goater     case PCA9552_LS3:
1465141d415SCédric Le Goater         return s->regs[reg];
1475141d415SCédric Le Goater     default:
1485141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
1495141d415SCédric Le Goater                       __func__, reg);
1505141d415SCédric Le Goater         return 0xFF;
1515141d415SCédric Le Goater     }
1525141d415SCédric Le Goater }
1535141d415SCédric Le Goater 
154ec17228aSPhilippe Mathieu-Daudé static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
1555141d415SCédric Le Goater {
156b989b89fSPhilippe Mathieu-Daudé     uint16_t pins_status;
157b989b89fSPhilippe Mathieu-Daudé 
1585141d415SCédric Le Goater     switch (reg) {
1595141d415SCédric Le Goater     case PCA9552_PSC0:
1605141d415SCédric Le Goater     case PCA9552_PWM0:
1615141d415SCédric Le Goater     case PCA9552_PSC1:
1625141d415SCédric Le Goater     case PCA9552_PWM1:
1635141d415SCédric Le Goater         s->regs[reg] = data;
1645141d415SCédric Le Goater         break;
1655141d415SCédric Le Goater 
1665141d415SCédric Le Goater     case PCA9552_LS0:
1675141d415SCédric Le Goater     case PCA9552_LS1:
1685141d415SCédric Le Goater     case PCA9552_LS2:
1695141d415SCédric Le Goater     case PCA9552_LS3:
170b989b89fSPhilippe Mathieu-Daudé         pins_status = pca955x_pins_get_status(s);
1715141d415SCédric Le Goater         s->regs[reg] = data;
172ec17228aSPhilippe Mathieu-Daudé         pca955x_update_pin_input(s);
173b989b89fSPhilippe Mathieu-Daudé         pca955x_display_pins_status(s, pins_status);
1745141d415SCédric Le Goater         break;
1755141d415SCédric Le Goater 
1765141d415SCédric Le Goater     case PCA9552_INPUT0:
1775141d415SCédric Le Goater     case PCA9552_INPUT1:
1785141d415SCédric Le Goater     default:
1795141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
1805141d415SCédric Le Goater                       __func__, reg);
1815141d415SCédric Le Goater     }
1825141d415SCédric Le Goater }
1835141d415SCédric Le Goater 
1845141d415SCédric Le Goater /*
1855141d415SCédric Le Goater  * When Auto-Increment is on, the register address is incremented
1865141d415SCédric Le Goater  * after each byte is sent to or received by the device. The index
1875141d415SCédric Le Goater  * rollovers to 0 when the maximum register address is reached.
1885141d415SCédric Le Goater  */
189ec17228aSPhilippe Mathieu-Daudé static void pca955x_autoinc(PCA955xState *s)
1905141d415SCédric Le Goater {
191736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
192736132e4SPhilippe Mathieu-Daudé 
1935141d415SCédric Le Goater     if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
1945141d415SCédric Le Goater         uint8_t reg = s->pointer & 0xf;
1955141d415SCédric Le Goater 
196736132e4SPhilippe Mathieu-Daudé         reg = (reg + 1) % (k->max_reg + 1);
1975141d415SCédric Le Goater         s->pointer = reg | PCA9552_AUTOINC;
1985141d415SCédric Le Goater     }
1995141d415SCédric Le Goater }
2005141d415SCédric Le Goater 
201ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_recv(I2CSlave *i2c)
2025141d415SCédric Le Goater {
203ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2045141d415SCédric Le Goater     uint8_t ret;
2055141d415SCédric Le Goater 
206ec17228aSPhilippe Mathieu-Daudé     ret = pca955x_read(s, s->pointer & 0xf);
2075141d415SCédric Le Goater 
2085141d415SCédric Le Goater     /*
2095141d415SCédric Le Goater      * From the Specs:
2105141d415SCédric Le Goater      *
2115141d415SCédric Le Goater      *     Important Note: When a Read sequence is initiated and the
2125141d415SCédric Le Goater      *     AI bit is set to Logic Level 1, the Read Sequence MUST
2135141d415SCédric Le Goater      *     start by a register different from 0.
2145141d415SCédric Le Goater      *
2155141d415SCédric Le Goater      * I don't know what should be done in this case, so throw an
2165141d415SCédric Le Goater      * error.
2175141d415SCédric Le Goater      */
2185141d415SCédric Le Goater     if (s->pointer == PCA9552_AUTOINC) {
2195141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
2205141d415SCédric Le Goater                       "%s: Autoincrement read starting with register 0\n",
2215141d415SCédric Le Goater                       __func__);
2225141d415SCédric Le Goater     }
2235141d415SCédric Le Goater 
224ec17228aSPhilippe Mathieu-Daudé     pca955x_autoinc(s);
2255141d415SCédric Le Goater 
2265141d415SCédric Le Goater     return ret;
2275141d415SCédric Le Goater }
2285141d415SCédric Le Goater 
229ec17228aSPhilippe Mathieu-Daudé static int pca955x_send(I2CSlave *i2c, uint8_t data)
2305141d415SCédric Le Goater {
231ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2325141d415SCédric Le Goater 
2335141d415SCédric Le Goater     /* First byte sent by is the register address */
2345141d415SCédric Le Goater     if (s->len == 0) {
2355141d415SCédric Le Goater         s->pointer = data;
2365141d415SCédric Le Goater         s->len++;
2375141d415SCédric Le Goater     } else {
238ec17228aSPhilippe Mathieu-Daudé         pca955x_write(s, s->pointer & 0xf, data);
2395141d415SCédric Le Goater 
240ec17228aSPhilippe Mathieu-Daudé         pca955x_autoinc(s);
2415141d415SCédric Le Goater     }
2425141d415SCédric Le Goater 
2435141d415SCédric Le Goater     return 0;
2445141d415SCédric Le Goater }
2455141d415SCédric Le Goater 
246ec17228aSPhilippe Mathieu-Daudé static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
2475141d415SCédric Le Goater {
248ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2495141d415SCédric Le Goater 
2505141d415SCédric Le Goater     s->len = 0;
2515141d415SCédric Le Goater     return 0;
2525141d415SCédric Le Goater }
2535141d415SCédric Le Goater 
254ec17228aSPhilippe Mathieu-Daudé static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
255a90d8f84SJoel Stanley                             void *opaque, Error **errp)
256a90d8f84SJoel Stanley {
257736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
258ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(obj);
259a90d8f84SJoel Stanley     int led, rc, reg;
260a90d8f84SJoel Stanley     uint8_t state;
261a90d8f84SJoel Stanley 
262a90d8f84SJoel Stanley     rc = sscanf(name, "led%2d", &led);
263a90d8f84SJoel Stanley     if (rc != 1) {
264a90d8f84SJoel Stanley         error_setg(errp, "%s: error reading %s", __func__, name);
265a90d8f84SJoel Stanley         return;
266a90d8f84SJoel Stanley     }
267736132e4SPhilippe Mathieu-Daudé     if (led < 0 || led > k->pin_count) {
268a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led %s", __func__, name);
269a90d8f84SJoel Stanley         return;
270a90d8f84SJoel Stanley     }
271a90d8f84SJoel Stanley     /*
272a90d8f84SJoel Stanley      * Get the LSx register as the qom interface should expose the device
273a90d8f84SJoel Stanley      * state, not the modeled 'input line' behaviour which would come from
274a90d8f84SJoel Stanley      * reading the INPUTx reg
275a90d8f84SJoel Stanley      */
276a90d8f84SJoel Stanley     reg = PCA9552_LS0 + led / 4;
277ec17228aSPhilippe Mathieu-Daudé     state = (pca955x_read(s, reg) >> (led % 8)) & 0x3;
278a90d8f84SJoel Stanley     visit_type_str(v, name, (char **)&led_state[state], errp);
279a90d8f84SJoel Stanley }
280a90d8f84SJoel Stanley 
281a90d8f84SJoel Stanley /*
282a90d8f84SJoel Stanley  * Return an LED selector register value based on an existing one, with
283a90d8f84SJoel Stanley  * the appropriate 2-bit state value set for the given LED number (0-3).
284a90d8f84SJoel Stanley  */
285a90d8f84SJoel Stanley static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
286a90d8f84SJoel Stanley {
287a90d8f84SJoel Stanley         return (oldval & (~(0x3 << (led_num << 1)))) |
288a90d8f84SJoel Stanley                 ((state & 0x3) << (led_num << 1));
289a90d8f84SJoel Stanley }
290a90d8f84SJoel Stanley 
291ec17228aSPhilippe Mathieu-Daudé static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
292a90d8f84SJoel Stanley                             void *opaque, Error **errp)
293a90d8f84SJoel Stanley {
294736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
295ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(obj);
296a90d8f84SJoel Stanley     int led, rc, reg, val;
297a90d8f84SJoel Stanley     uint8_t state;
298a90d8f84SJoel Stanley     char *state_str;
299a90d8f84SJoel Stanley 
300668f62ecSMarkus Armbruster     if (!visit_type_str(v, name, &state_str, errp)) {
301a90d8f84SJoel Stanley         return;
302a90d8f84SJoel Stanley     }
303a90d8f84SJoel Stanley     rc = sscanf(name, "led%2d", &led);
304a90d8f84SJoel Stanley     if (rc != 1) {
305a90d8f84SJoel Stanley         error_setg(errp, "%s: error reading %s", __func__, name);
306a90d8f84SJoel Stanley         return;
307a90d8f84SJoel Stanley     }
308736132e4SPhilippe Mathieu-Daudé     if (led < 0 || led > k->pin_count) {
309a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led %s", __func__, name);
310a90d8f84SJoel Stanley         return;
311a90d8f84SJoel Stanley     }
312a90d8f84SJoel Stanley 
313a90d8f84SJoel Stanley     for (state = 0; state < ARRAY_SIZE(led_state); state++) {
314a90d8f84SJoel Stanley         if (!strcmp(state_str, led_state[state])) {
315a90d8f84SJoel Stanley             break;
316a90d8f84SJoel Stanley         }
317a90d8f84SJoel Stanley     }
318a90d8f84SJoel Stanley     if (state >= ARRAY_SIZE(led_state)) {
319a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led state %s", __func__, state_str);
320a90d8f84SJoel Stanley         return;
321a90d8f84SJoel Stanley     }
322a90d8f84SJoel Stanley 
323a90d8f84SJoel Stanley     reg = PCA9552_LS0 + led / 4;
324ec17228aSPhilippe Mathieu-Daudé     val = pca955x_read(s, reg);
325a90d8f84SJoel Stanley     val = pca955x_ledsel(val, led % 4, state);
326ec17228aSPhilippe Mathieu-Daudé     pca955x_write(s, reg, val);
327a90d8f84SJoel Stanley }
328a90d8f84SJoel Stanley 
3295141d415SCédric Le Goater static const VMStateDescription pca9552_vmstate = {
3305141d415SCédric Le Goater     .name = "PCA9552",
3315141d415SCédric Le Goater     .version_id = 0,
3325141d415SCédric Le Goater     .minimum_version_id = 0,
3335141d415SCédric Le Goater     .fields = (VMStateField[]) {
334ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8(len, PCA955xState),
335ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8(pointer, PCA955xState),
336ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
337ec17228aSPhilippe Mathieu-Daudé         VMSTATE_I2C_SLAVE(i2c, PCA955xState),
3385141d415SCédric Le Goater         VMSTATE_END_OF_LIST()
3395141d415SCédric Le Goater     }
3405141d415SCédric Le Goater };
3415141d415SCédric Le Goater 
3425141d415SCédric Le Goater static void pca9552_reset(DeviceState *dev)
3435141d415SCédric Le Goater {
344ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(dev);
3455141d415SCédric Le Goater 
3465141d415SCédric Le Goater     s->regs[PCA9552_PSC0] = 0xFF;
3475141d415SCédric Le Goater     s->regs[PCA9552_PWM0] = 0x80;
3485141d415SCédric Le Goater     s->regs[PCA9552_PSC1] = 0xFF;
3495141d415SCédric Le Goater     s->regs[PCA9552_PWM1] = 0x80;
3505141d415SCédric Le Goater     s->regs[PCA9552_LS0] = 0x55; /* all OFF */
3515141d415SCédric Le Goater     s->regs[PCA9552_LS1] = 0x55;
3525141d415SCédric Le Goater     s->regs[PCA9552_LS2] = 0x55;
3535141d415SCédric Le Goater     s->regs[PCA9552_LS3] = 0x55;
3545141d415SCédric Le Goater 
355ec17228aSPhilippe Mathieu-Daudé     pca955x_update_pin_input(s);
3565141d415SCédric Le Goater 
3575141d415SCédric Le Goater     s->pointer = 0xFF;
3585141d415SCédric Le Goater     s->len = 0;
3595141d415SCédric Le Goater }
3605141d415SCédric Le Goater 
361ec17228aSPhilippe Mathieu-Daudé static void pca955x_initfn(Object *obj)
3625141d415SCédric Le Goater {
363736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
364a90d8f84SJoel Stanley     int led;
3655141d415SCédric Le Goater 
366736132e4SPhilippe Mathieu-Daudé     assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
367736132e4SPhilippe Mathieu-Daudé     for (led = 0; led < k->pin_count; led++) {
368a90d8f84SJoel Stanley         char *name;
369a90d8f84SJoel Stanley 
370a90d8f84SJoel Stanley         name = g_strdup_printf("led%d", led);
371ec17228aSPhilippe Mathieu-Daudé         object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
372d2623129SMarkus Armbruster                             NULL, NULL);
373a90d8f84SJoel Stanley         g_free(name);
374a90d8f84SJoel Stanley     }
3755141d415SCédric Le Goater }
3765141d415SCédric Le Goater 
3772df252d8SPhilippe Mathieu-Daudé static void pca955x_realize(DeviceState *dev, Error **errp)
3782df252d8SPhilippe Mathieu-Daudé {
379586f495bSPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(dev);
3802df252d8SPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(dev);
3812df252d8SPhilippe Mathieu-Daudé 
3822df252d8SPhilippe Mathieu-Daudé     if (!s->description) {
3832df252d8SPhilippe Mathieu-Daudé         s->description = g_strdup("pca-unspecified");
3842df252d8SPhilippe Mathieu-Daudé     }
385586f495bSPhilippe Mathieu-Daudé 
386586f495bSPhilippe Mathieu-Daudé     qdev_init_gpio_out(dev, s->gpio, k->pin_count);
3872df252d8SPhilippe Mathieu-Daudé }
3882df252d8SPhilippe Mathieu-Daudé 
3892df252d8SPhilippe Mathieu-Daudé static Property pca955x_properties[] = {
3902df252d8SPhilippe Mathieu-Daudé     DEFINE_PROP_STRING("description", PCA955xState, description),
3912df252d8SPhilippe Mathieu-Daudé     DEFINE_PROP_END_OF_LIST(),
3922df252d8SPhilippe Mathieu-Daudé };
3932df252d8SPhilippe Mathieu-Daudé 
394736132e4SPhilippe Mathieu-Daudé static void pca955x_class_init(ObjectClass *klass, void *data)
3955141d415SCédric Le Goater {
3962df252d8SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(klass);
3975141d415SCédric Le Goater     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
3985141d415SCédric Le Goater 
399ec17228aSPhilippe Mathieu-Daudé     k->event = pca955x_event;
400ec17228aSPhilippe Mathieu-Daudé     k->recv = pca955x_recv;
401ec17228aSPhilippe Mathieu-Daudé     k->send = pca955x_send;
4022df252d8SPhilippe Mathieu-Daudé     dc->realize = pca955x_realize;
4032df252d8SPhilippe Mathieu-Daudé     device_class_set_props(dc, pca955x_properties);
404736132e4SPhilippe Mathieu-Daudé }
405736132e4SPhilippe Mathieu-Daudé 
406736132e4SPhilippe Mathieu-Daudé static const TypeInfo pca955x_info = {
407736132e4SPhilippe Mathieu-Daudé     .name          = TYPE_PCA955X,
408736132e4SPhilippe Mathieu-Daudé     .parent        = TYPE_I2C_SLAVE,
409736132e4SPhilippe Mathieu-Daudé     .instance_init = pca955x_initfn,
410736132e4SPhilippe Mathieu-Daudé     .instance_size = sizeof(PCA955xState),
411736132e4SPhilippe Mathieu-Daudé     .class_init    = pca955x_class_init,
412fc1bff95SPhilippe Mathieu-Daudé     .class_size    = sizeof(PCA955xClass),
413736132e4SPhilippe Mathieu-Daudé     .abstract      = true,
414736132e4SPhilippe Mathieu-Daudé };
415736132e4SPhilippe Mathieu-Daudé 
416736132e4SPhilippe Mathieu-Daudé static void pca9552_class_init(ObjectClass *oc, void *data)
417736132e4SPhilippe Mathieu-Daudé {
418736132e4SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(oc);
419736132e4SPhilippe Mathieu-Daudé     PCA955xClass *pc = PCA955X_CLASS(oc);
420736132e4SPhilippe Mathieu-Daudé 
4215141d415SCédric Le Goater     dc->reset = pca9552_reset;
4225141d415SCédric Le Goater     dc->vmsd = &pca9552_vmstate;
423736132e4SPhilippe Mathieu-Daudé     pc->max_reg = PCA9552_LS3;
424736132e4SPhilippe Mathieu-Daudé     pc->pin_count = 16;
4255141d415SCédric Le Goater }
4265141d415SCédric Le Goater 
4275141d415SCédric Le Goater static const TypeInfo pca9552_info = {
4285141d415SCédric Le Goater     .name          = TYPE_PCA9552,
429736132e4SPhilippe Mathieu-Daudé     .parent        = TYPE_PCA955X,
4305141d415SCédric Le Goater     .class_init    = pca9552_class_init,
4315141d415SCédric Le Goater };
4325141d415SCédric Le Goater 
433ec17228aSPhilippe Mathieu-Daudé static void pca955x_register_types(void)
4345141d415SCédric Le Goater {
435736132e4SPhilippe Mathieu-Daudé     type_register_static(&pca955x_info);
4365141d415SCédric Le Goater     type_register_static(&pca9552_info);
4375141d415SCédric Le Goater }
4385141d415SCédric Le Goater 
439ec17228aSPhilippe Mathieu-Daudé type_init(pca955x_register_types)
440