xref: /qemu/hw/gpio/pca9552.c (revision fc1bff958998910ec8d25db86cd2f53ff125f7ab)
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"
255141d415SCédric Le Goater 
26736132e4SPhilippe Mathieu-Daudé typedef struct PCA955xClass {
27736132e4SPhilippe Mathieu-Daudé     /*< private >*/
28736132e4SPhilippe Mathieu-Daudé     I2CSlaveClass parent_class;
29736132e4SPhilippe Mathieu-Daudé     /*< public >*/
30736132e4SPhilippe Mathieu-Daudé 
31736132e4SPhilippe Mathieu-Daudé     uint8_t pin_count;
32736132e4SPhilippe Mathieu-Daudé     uint8_t max_reg;
33736132e4SPhilippe Mathieu-Daudé } PCA955xClass;
34736132e4SPhilippe Mathieu-Daudé 
35736132e4SPhilippe Mathieu-Daudé #define PCA955X_CLASS(klass) \
36736132e4SPhilippe Mathieu-Daudé     OBJECT_CLASS_CHECK(PCA955xClass, (klass), TYPE_PCA955X)
37736132e4SPhilippe Mathieu-Daudé #define PCA955X_GET_CLASS(obj) \
38736132e4SPhilippe Mathieu-Daudé     OBJECT_GET_CLASS(PCA955xClass, (obj), TYPE_PCA955X)
39736132e4SPhilippe Mathieu-Daudé 
405141d415SCédric Le Goater #define PCA9552_LED_ON   0x0
415141d415SCédric Le Goater #define PCA9552_LED_OFF  0x1
425141d415SCédric Le Goater #define PCA9552_LED_PWM0 0x2
435141d415SCédric Le Goater #define PCA9552_LED_PWM1 0x3
445141d415SCédric Le Goater 
45a90d8f84SJoel Stanley static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
46a90d8f84SJoel Stanley 
47ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
485141d415SCédric Le Goater {
495141d415SCédric Le Goater     uint8_t reg   = PCA9552_LS0 + (pin / 4);
505141d415SCédric Le Goater     uint8_t shift = (pin % 4) << 1;
515141d415SCédric Le Goater 
525141d415SCédric Le Goater     return extract32(s->regs[reg], shift, 2);
535141d415SCédric Le Goater }
545141d415SCédric Le Goater 
55b989b89fSPhilippe Mathieu-Daudé /* Return INPUT status (bit #N belongs to GPIO #N) */
56b989b89fSPhilippe Mathieu-Daudé static uint16_t pca955x_pins_get_status(PCA955xState *s)
57b989b89fSPhilippe Mathieu-Daudé {
58b989b89fSPhilippe Mathieu-Daudé     return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
59b989b89fSPhilippe Mathieu-Daudé }
60b989b89fSPhilippe Mathieu-Daudé 
61b989b89fSPhilippe Mathieu-Daudé static void pca955x_display_pins_status(PCA955xState *s,
62b989b89fSPhilippe Mathieu-Daudé                                         uint16_t previous_pins_status)
63b989b89fSPhilippe Mathieu-Daudé {
64b989b89fSPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
65b989b89fSPhilippe Mathieu-Daudé     uint16_t pins_status, pins_changed;
66b989b89fSPhilippe Mathieu-Daudé     int i;
67b989b89fSPhilippe Mathieu-Daudé 
68b989b89fSPhilippe Mathieu-Daudé     pins_status = pca955x_pins_get_status(s);
69b989b89fSPhilippe Mathieu-Daudé     pins_changed = previous_pins_status ^ pins_status;
70b989b89fSPhilippe Mathieu-Daudé     if (!pins_changed) {
71b989b89fSPhilippe Mathieu-Daudé         return;
72b989b89fSPhilippe Mathieu-Daudé     }
73b989b89fSPhilippe Mathieu-Daudé     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
74b989b89fSPhilippe Mathieu-Daudé         char *buf = g_newa(char, k->pin_count + 1);
75b989b89fSPhilippe Mathieu-Daudé 
76b989b89fSPhilippe Mathieu-Daudé         for (i = 0; i < k->pin_count; i++) {
77b989b89fSPhilippe Mathieu-Daudé             if (extract32(pins_status, i, 1)) {
78b989b89fSPhilippe Mathieu-Daudé                 buf[i] = '*';
79b989b89fSPhilippe Mathieu-Daudé             } else {
80b989b89fSPhilippe Mathieu-Daudé                 buf[i] = '.';
81b989b89fSPhilippe Mathieu-Daudé             }
82b989b89fSPhilippe Mathieu-Daudé         }
83b989b89fSPhilippe Mathieu-Daudé         buf[i] = '\0';
84b989b89fSPhilippe Mathieu-Daudé         trace_pca955x_gpio_status(s->description, buf);
85b989b89fSPhilippe Mathieu-Daudé     }
86d82ab293SPhilippe Mathieu-Daudé     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
87d82ab293SPhilippe Mathieu-Daudé         for (i = 0; i < k->pin_count; i++) {
88d82ab293SPhilippe Mathieu-Daudé             if (extract32(pins_changed, i, 1)) {
89d82ab293SPhilippe Mathieu-Daudé                 unsigned new_state = extract32(pins_status, i, 1);
90d82ab293SPhilippe Mathieu-Daudé 
91d82ab293SPhilippe Mathieu-Daudé                 /*
92d82ab293SPhilippe Mathieu-Daudé                  * We display the state using the PCA logic ("active-high").
93d82ab293SPhilippe Mathieu-Daudé                  * This is not the state of the LED, which signal might be
94d82ab293SPhilippe Mathieu-Daudé                  * wired "active-low" on the board.
95d82ab293SPhilippe Mathieu-Daudé                  */
96d82ab293SPhilippe Mathieu-Daudé                 trace_pca955x_gpio_change(s->description, i,
97d82ab293SPhilippe Mathieu-Daudé                                           !new_state, new_state);
98d82ab293SPhilippe Mathieu-Daudé             }
99d82ab293SPhilippe Mathieu-Daudé         }
100d82ab293SPhilippe Mathieu-Daudé     }
101b989b89fSPhilippe Mathieu-Daudé }
102b989b89fSPhilippe Mathieu-Daudé 
103ec17228aSPhilippe Mathieu-Daudé static void pca955x_update_pin_input(PCA955xState *s)
1045141d415SCédric Le Goater {
105736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
1065141d415SCédric Le Goater     int i;
1075141d415SCédric Le Goater 
108736132e4SPhilippe Mathieu-Daudé     for (i = 0; i < k->pin_count; i++) {
1095141d415SCédric Le Goater         uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
1105141d415SCédric Le Goater         uint8_t input_shift = (i % 8);
111ec17228aSPhilippe Mathieu-Daudé         uint8_t config = pca955x_pin_get_config(s, i);
1125141d415SCédric Le Goater 
1135141d415SCédric Le Goater         switch (config) {
1145141d415SCédric Le Goater         case PCA9552_LED_ON:
115586f495bSPhilippe Mathieu-Daudé             qemu_set_irq(s->gpio[i], 1);
1165141d415SCédric Le Goater             s->regs[input_reg] |= 1 << input_shift;
1175141d415SCédric Le Goater             break;
1185141d415SCédric Le Goater         case PCA9552_LED_OFF:
119586f495bSPhilippe Mathieu-Daudé             qemu_set_irq(s->gpio[i], 0);
1205141d415SCédric Le Goater             s->regs[input_reg] &= ~(1 << input_shift);
1215141d415SCédric Le Goater             break;
1225141d415SCédric Le Goater         case PCA9552_LED_PWM0:
1235141d415SCédric Le Goater         case PCA9552_LED_PWM1:
1245141d415SCédric Le Goater             /* TODO */
1255141d415SCédric Le Goater         default:
1265141d415SCédric Le Goater             break;
1275141d415SCédric Le Goater         }
1285141d415SCédric Le Goater     }
1295141d415SCédric Le Goater }
1305141d415SCédric Le Goater 
131ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
1325141d415SCédric Le Goater {
1335141d415SCédric Le Goater     switch (reg) {
1345141d415SCédric Le Goater     case PCA9552_INPUT0:
1355141d415SCédric Le Goater     case PCA9552_INPUT1:
1365141d415SCédric Le Goater     case PCA9552_PSC0:
1375141d415SCédric Le Goater     case PCA9552_PWM0:
1385141d415SCédric Le Goater     case PCA9552_PSC1:
1395141d415SCédric Le Goater     case PCA9552_PWM1:
1405141d415SCédric Le Goater     case PCA9552_LS0:
1415141d415SCédric Le Goater     case PCA9552_LS1:
1425141d415SCédric Le Goater     case PCA9552_LS2:
1435141d415SCédric Le Goater     case PCA9552_LS3:
1445141d415SCédric Le Goater         return s->regs[reg];
1455141d415SCédric Le Goater     default:
1465141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
1475141d415SCédric Le Goater                       __func__, reg);
1485141d415SCédric Le Goater         return 0xFF;
1495141d415SCédric Le Goater     }
1505141d415SCédric Le Goater }
1515141d415SCédric Le Goater 
152ec17228aSPhilippe Mathieu-Daudé static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
1535141d415SCédric Le Goater {
154b989b89fSPhilippe Mathieu-Daudé     uint16_t pins_status;
155b989b89fSPhilippe Mathieu-Daudé 
1565141d415SCédric Le Goater     switch (reg) {
1575141d415SCédric Le Goater     case PCA9552_PSC0:
1585141d415SCédric Le Goater     case PCA9552_PWM0:
1595141d415SCédric Le Goater     case PCA9552_PSC1:
1605141d415SCédric Le Goater     case PCA9552_PWM1:
1615141d415SCédric Le Goater         s->regs[reg] = data;
1625141d415SCédric Le Goater         break;
1635141d415SCédric Le Goater 
1645141d415SCédric Le Goater     case PCA9552_LS0:
1655141d415SCédric Le Goater     case PCA9552_LS1:
1665141d415SCédric Le Goater     case PCA9552_LS2:
1675141d415SCédric Le Goater     case PCA9552_LS3:
168b989b89fSPhilippe Mathieu-Daudé         pins_status = pca955x_pins_get_status(s);
1695141d415SCédric Le Goater         s->regs[reg] = data;
170ec17228aSPhilippe Mathieu-Daudé         pca955x_update_pin_input(s);
171b989b89fSPhilippe Mathieu-Daudé         pca955x_display_pins_status(s, pins_status);
1725141d415SCédric Le Goater         break;
1735141d415SCédric Le Goater 
1745141d415SCédric Le Goater     case PCA9552_INPUT0:
1755141d415SCédric Le Goater     case PCA9552_INPUT1:
1765141d415SCédric Le Goater     default:
1775141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
1785141d415SCédric Le Goater                       __func__, reg);
1795141d415SCédric Le Goater     }
1805141d415SCédric Le Goater }
1815141d415SCédric Le Goater 
1825141d415SCédric Le Goater /*
1835141d415SCédric Le Goater  * When Auto-Increment is on, the register address is incremented
1845141d415SCédric Le Goater  * after each byte is sent to or received by the device. The index
1855141d415SCédric Le Goater  * rollovers to 0 when the maximum register address is reached.
1865141d415SCédric Le Goater  */
187ec17228aSPhilippe Mathieu-Daudé static void pca955x_autoinc(PCA955xState *s)
1885141d415SCédric Le Goater {
189736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(s);
190736132e4SPhilippe Mathieu-Daudé 
1915141d415SCédric Le Goater     if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
1925141d415SCédric Le Goater         uint8_t reg = s->pointer & 0xf;
1935141d415SCédric Le Goater 
194736132e4SPhilippe Mathieu-Daudé         reg = (reg + 1) % (k->max_reg + 1);
1955141d415SCédric Le Goater         s->pointer = reg | PCA9552_AUTOINC;
1965141d415SCédric Le Goater     }
1975141d415SCédric Le Goater }
1985141d415SCédric Le Goater 
199ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_recv(I2CSlave *i2c)
2005141d415SCédric Le Goater {
201ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2025141d415SCédric Le Goater     uint8_t ret;
2035141d415SCédric Le Goater 
204ec17228aSPhilippe Mathieu-Daudé     ret = pca955x_read(s, s->pointer & 0xf);
2055141d415SCédric Le Goater 
2065141d415SCédric Le Goater     /*
2075141d415SCédric Le Goater      * From the Specs:
2085141d415SCédric Le Goater      *
2095141d415SCédric Le Goater      *     Important Note: When a Read sequence is initiated and the
2105141d415SCédric Le Goater      *     AI bit is set to Logic Level 1, the Read Sequence MUST
2115141d415SCédric Le Goater      *     start by a register different from 0.
2125141d415SCédric Le Goater      *
2135141d415SCédric Le Goater      * I don't know what should be done in this case, so throw an
2145141d415SCédric Le Goater      * error.
2155141d415SCédric Le Goater      */
2165141d415SCédric Le Goater     if (s->pointer == PCA9552_AUTOINC) {
2175141d415SCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
2185141d415SCédric Le Goater                       "%s: Autoincrement read starting with register 0\n",
2195141d415SCédric Le Goater                       __func__);
2205141d415SCédric Le Goater     }
2215141d415SCédric Le Goater 
222ec17228aSPhilippe Mathieu-Daudé     pca955x_autoinc(s);
2235141d415SCédric Le Goater 
2245141d415SCédric Le Goater     return ret;
2255141d415SCédric Le Goater }
2265141d415SCédric Le Goater 
227ec17228aSPhilippe Mathieu-Daudé static int pca955x_send(I2CSlave *i2c, uint8_t data)
2285141d415SCédric Le Goater {
229ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2305141d415SCédric Le Goater 
2315141d415SCédric Le Goater     /* First byte sent by is the register address */
2325141d415SCédric Le Goater     if (s->len == 0) {
2335141d415SCédric Le Goater         s->pointer = data;
2345141d415SCédric Le Goater         s->len++;
2355141d415SCédric Le Goater     } else {
236ec17228aSPhilippe Mathieu-Daudé         pca955x_write(s, s->pointer & 0xf, data);
2375141d415SCédric Le Goater 
238ec17228aSPhilippe Mathieu-Daudé         pca955x_autoinc(s);
2395141d415SCédric Le Goater     }
2405141d415SCédric Le Goater 
2415141d415SCédric Le Goater     return 0;
2425141d415SCédric Le Goater }
2435141d415SCédric Le Goater 
244ec17228aSPhilippe Mathieu-Daudé static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
2455141d415SCédric Le Goater {
246ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(i2c);
2475141d415SCédric Le Goater 
2485141d415SCédric Le Goater     s->len = 0;
2495141d415SCédric Le Goater     return 0;
2505141d415SCédric Le Goater }
2515141d415SCédric Le Goater 
252ec17228aSPhilippe Mathieu-Daudé static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
253a90d8f84SJoel Stanley                             void *opaque, Error **errp)
254a90d8f84SJoel Stanley {
255736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
256ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(obj);
257a90d8f84SJoel Stanley     int led, rc, reg;
258a90d8f84SJoel Stanley     uint8_t state;
259a90d8f84SJoel Stanley 
260a90d8f84SJoel Stanley     rc = sscanf(name, "led%2d", &led);
261a90d8f84SJoel Stanley     if (rc != 1) {
262a90d8f84SJoel Stanley         error_setg(errp, "%s: error reading %s", __func__, name);
263a90d8f84SJoel Stanley         return;
264a90d8f84SJoel Stanley     }
265736132e4SPhilippe Mathieu-Daudé     if (led < 0 || led > k->pin_count) {
266a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led %s", __func__, name);
267a90d8f84SJoel Stanley         return;
268a90d8f84SJoel Stanley     }
269a90d8f84SJoel Stanley     /*
270a90d8f84SJoel Stanley      * Get the LSx register as the qom interface should expose the device
271a90d8f84SJoel Stanley      * state, not the modeled 'input line' behaviour which would come from
272a90d8f84SJoel Stanley      * reading the INPUTx reg
273a90d8f84SJoel Stanley      */
274a90d8f84SJoel Stanley     reg = PCA9552_LS0 + led / 4;
275ec17228aSPhilippe Mathieu-Daudé     state = (pca955x_read(s, reg) >> (led % 8)) & 0x3;
276a90d8f84SJoel Stanley     visit_type_str(v, name, (char **)&led_state[state], errp);
277a90d8f84SJoel Stanley }
278a90d8f84SJoel Stanley 
279a90d8f84SJoel Stanley /*
280a90d8f84SJoel Stanley  * Return an LED selector register value based on an existing one, with
281a90d8f84SJoel Stanley  * the appropriate 2-bit state value set for the given LED number (0-3).
282a90d8f84SJoel Stanley  */
283a90d8f84SJoel Stanley static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
284a90d8f84SJoel Stanley {
285a90d8f84SJoel Stanley         return (oldval & (~(0x3 << (led_num << 1)))) |
286a90d8f84SJoel Stanley                 ((state & 0x3) << (led_num << 1));
287a90d8f84SJoel Stanley }
288a90d8f84SJoel Stanley 
289ec17228aSPhilippe Mathieu-Daudé static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
290a90d8f84SJoel Stanley                             void *opaque, Error **errp)
291a90d8f84SJoel Stanley {
292736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
293ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(obj);
294a90d8f84SJoel Stanley     Error *local_err = NULL;
295a90d8f84SJoel Stanley     int led, rc, reg, val;
296a90d8f84SJoel Stanley     uint8_t state;
297a90d8f84SJoel Stanley     char *state_str;
298a90d8f84SJoel Stanley 
299a90d8f84SJoel Stanley     visit_type_str(v, name, &state_str, &local_err);
300a90d8f84SJoel Stanley     if (local_err) {
301a90d8f84SJoel Stanley         error_propagate(errp, local_err);
302a90d8f84SJoel Stanley         return;
303a90d8f84SJoel Stanley     }
304a90d8f84SJoel Stanley     rc = sscanf(name, "led%2d", &led);
305a90d8f84SJoel Stanley     if (rc != 1) {
306a90d8f84SJoel Stanley         error_setg(errp, "%s: error reading %s", __func__, name);
307a90d8f84SJoel Stanley         return;
308a90d8f84SJoel Stanley     }
309736132e4SPhilippe Mathieu-Daudé     if (led < 0 || led > k->pin_count) {
310a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led %s", __func__, name);
311a90d8f84SJoel Stanley         return;
312a90d8f84SJoel Stanley     }
313a90d8f84SJoel Stanley 
314a90d8f84SJoel Stanley     for (state = 0; state < ARRAY_SIZE(led_state); state++) {
315a90d8f84SJoel Stanley         if (!strcmp(state_str, led_state[state])) {
316a90d8f84SJoel Stanley             break;
317a90d8f84SJoel Stanley         }
318a90d8f84SJoel Stanley     }
319a90d8f84SJoel Stanley     if (state >= ARRAY_SIZE(led_state)) {
320a90d8f84SJoel Stanley         error_setg(errp, "%s invalid led state %s", __func__, state_str);
321a90d8f84SJoel Stanley         return;
322a90d8f84SJoel Stanley     }
323a90d8f84SJoel Stanley 
324a90d8f84SJoel Stanley     reg = PCA9552_LS0 + led / 4;
325ec17228aSPhilippe Mathieu-Daudé     val = pca955x_read(s, reg);
326a90d8f84SJoel Stanley     val = pca955x_ledsel(val, led % 4, state);
327ec17228aSPhilippe Mathieu-Daudé     pca955x_write(s, reg, val);
328a90d8f84SJoel Stanley }
329a90d8f84SJoel Stanley 
3305141d415SCédric Le Goater static const VMStateDescription pca9552_vmstate = {
3315141d415SCédric Le Goater     .name = "PCA9552",
3325141d415SCédric Le Goater     .version_id = 0,
3335141d415SCédric Le Goater     .minimum_version_id = 0,
3345141d415SCédric Le Goater     .fields = (VMStateField[]) {
335ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8(len, PCA955xState),
336ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8(pointer, PCA955xState),
337ec17228aSPhilippe Mathieu-Daudé         VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
338ec17228aSPhilippe Mathieu-Daudé         VMSTATE_I2C_SLAVE(i2c, PCA955xState),
3395141d415SCédric Le Goater         VMSTATE_END_OF_LIST()
3405141d415SCédric Le Goater     }
3415141d415SCédric Le Goater };
3425141d415SCédric Le Goater 
3435141d415SCédric Le Goater static void pca9552_reset(DeviceState *dev)
3445141d415SCédric Le Goater {
345ec17228aSPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(dev);
3465141d415SCédric Le Goater 
3475141d415SCédric Le Goater     s->regs[PCA9552_PSC0] = 0xFF;
3485141d415SCédric Le Goater     s->regs[PCA9552_PWM0] = 0x80;
3495141d415SCédric Le Goater     s->regs[PCA9552_PSC1] = 0xFF;
3505141d415SCédric Le Goater     s->regs[PCA9552_PWM1] = 0x80;
3515141d415SCédric Le Goater     s->regs[PCA9552_LS0] = 0x55; /* all OFF */
3525141d415SCédric Le Goater     s->regs[PCA9552_LS1] = 0x55;
3535141d415SCédric Le Goater     s->regs[PCA9552_LS2] = 0x55;
3545141d415SCédric Le Goater     s->regs[PCA9552_LS3] = 0x55;
3555141d415SCédric Le Goater 
356ec17228aSPhilippe Mathieu-Daudé     pca955x_update_pin_input(s);
3575141d415SCédric Le Goater 
3585141d415SCédric Le Goater     s->pointer = 0xFF;
3595141d415SCédric Le Goater     s->len = 0;
3605141d415SCédric Le Goater }
3615141d415SCédric Le Goater 
362ec17228aSPhilippe Mathieu-Daudé static void pca955x_initfn(Object *obj)
3635141d415SCédric Le Goater {
364736132e4SPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(obj);
365a90d8f84SJoel Stanley     int led;
3665141d415SCédric Le Goater 
367736132e4SPhilippe Mathieu-Daudé     assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
368736132e4SPhilippe Mathieu-Daudé     for (led = 0; led < k->pin_count; led++) {
369a90d8f84SJoel Stanley         char *name;
370a90d8f84SJoel Stanley 
371a90d8f84SJoel Stanley         name = g_strdup_printf("led%d", led);
372ec17228aSPhilippe Mathieu-Daudé         object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
373d2623129SMarkus Armbruster                             NULL, NULL);
374a90d8f84SJoel Stanley         g_free(name);
375a90d8f84SJoel Stanley     }
3765141d415SCédric Le Goater }
3775141d415SCédric Le Goater 
3782df252d8SPhilippe Mathieu-Daudé static void pca955x_realize(DeviceState *dev, Error **errp)
3792df252d8SPhilippe Mathieu-Daudé {
380586f495bSPhilippe Mathieu-Daudé     PCA955xClass *k = PCA955X_GET_CLASS(dev);
3812df252d8SPhilippe Mathieu-Daudé     PCA955xState *s = PCA955X(dev);
3822df252d8SPhilippe Mathieu-Daudé 
3832df252d8SPhilippe Mathieu-Daudé     if (!s->description) {
3842df252d8SPhilippe Mathieu-Daudé         s->description = g_strdup("pca-unspecified");
3852df252d8SPhilippe Mathieu-Daudé     }
386586f495bSPhilippe Mathieu-Daudé 
387586f495bSPhilippe Mathieu-Daudé     qdev_init_gpio_out(dev, s->gpio, k->pin_count);
3882df252d8SPhilippe Mathieu-Daudé }
3892df252d8SPhilippe Mathieu-Daudé 
3902df252d8SPhilippe Mathieu-Daudé static Property pca955x_properties[] = {
3912df252d8SPhilippe Mathieu-Daudé     DEFINE_PROP_STRING("description", PCA955xState, description),
3922df252d8SPhilippe Mathieu-Daudé     DEFINE_PROP_END_OF_LIST(),
3932df252d8SPhilippe Mathieu-Daudé };
3942df252d8SPhilippe Mathieu-Daudé 
395736132e4SPhilippe Mathieu-Daudé static void pca955x_class_init(ObjectClass *klass, void *data)
3965141d415SCédric Le Goater {
3972df252d8SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(klass);
3985141d415SCédric Le Goater     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
3995141d415SCédric Le Goater 
400ec17228aSPhilippe Mathieu-Daudé     k->event = pca955x_event;
401ec17228aSPhilippe Mathieu-Daudé     k->recv = pca955x_recv;
402ec17228aSPhilippe Mathieu-Daudé     k->send = pca955x_send;
4032df252d8SPhilippe Mathieu-Daudé     dc->realize = pca955x_realize;
4042df252d8SPhilippe Mathieu-Daudé     device_class_set_props(dc, pca955x_properties);
405736132e4SPhilippe Mathieu-Daudé }
406736132e4SPhilippe Mathieu-Daudé 
407736132e4SPhilippe Mathieu-Daudé static const TypeInfo pca955x_info = {
408736132e4SPhilippe Mathieu-Daudé     .name          = TYPE_PCA955X,
409736132e4SPhilippe Mathieu-Daudé     .parent        = TYPE_I2C_SLAVE,
410736132e4SPhilippe Mathieu-Daudé     .instance_init = pca955x_initfn,
411736132e4SPhilippe Mathieu-Daudé     .instance_size = sizeof(PCA955xState),
412736132e4SPhilippe Mathieu-Daudé     .class_init    = pca955x_class_init,
413*fc1bff95SPhilippe Mathieu-Daudé     .class_size    = sizeof(PCA955xClass),
414736132e4SPhilippe Mathieu-Daudé     .abstract      = true,
415736132e4SPhilippe Mathieu-Daudé };
416736132e4SPhilippe Mathieu-Daudé 
417736132e4SPhilippe Mathieu-Daudé static void pca9552_class_init(ObjectClass *oc, void *data)
418736132e4SPhilippe Mathieu-Daudé {
419736132e4SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(oc);
420736132e4SPhilippe Mathieu-Daudé     PCA955xClass *pc = PCA955X_CLASS(oc);
421736132e4SPhilippe Mathieu-Daudé 
4225141d415SCédric Le Goater     dc->reset = pca9552_reset;
4235141d415SCédric Le Goater     dc->vmsd = &pca9552_vmstate;
424736132e4SPhilippe Mathieu-Daudé     pc->max_reg = PCA9552_LS3;
425736132e4SPhilippe Mathieu-Daudé     pc->pin_count = 16;
4265141d415SCédric Le Goater }
4275141d415SCédric Le Goater 
4285141d415SCédric Le Goater static const TypeInfo pca9552_info = {
4295141d415SCédric Le Goater     .name          = TYPE_PCA9552,
430736132e4SPhilippe Mathieu-Daudé     .parent        = TYPE_PCA955X,
4315141d415SCédric Le Goater     .class_init    = pca9552_class_init,
4325141d415SCédric Le Goater };
4335141d415SCédric Le Goater 
434ec17228aSPhilippe Mathieu-Daudé static void pca955x_register_types(void)
4355141d415SCédric Le Goater {
436736132e4SPhilippe Mathieu-Daudé     type_register_static(&pca955x_info);
4375141d415SCédric Le Goater     type_register_static(&pca9552_info);
4385141d415SCédric Le Goater }
4395141d415SCédric Le Goater 
440ec17228aSPhilippe Mathieu-Daudé type_init(pca955x_register_types)
441