xref: /openbmc/qemu/hw/gpio/pca9552.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
16328d8ffSCédric Le Goater /*
26328d8ffSCédric Le Goater  * PCA9552 I2C LED blinker
36328d8ffSCédric Le Goater  *
46328d8ffSCédric Le Goater  *     https://www.nxp.com/docs/en/application-note/AN264.pdf
56328d8ffSCédric Le Goater  *
66328d8ffSCédric Le Goater  * Copyright (c) 2017-2018, IBM Corporation.
76328d8ffSCédric Le Goater  * Copyright (c) 2020 Philippe Mathieu-Daudé
86328d8ffSCédric Le Goater  *
96328d8ffSCédric Le Goater  * This work is licensed under the terms of the GNU GPL, version 2 or
106328d8ffSCédric Le Goater  * later. See the COPYING file in the top-level directory.
116328d8ffSCédric Le Goater  */
126328d8ffSCédric Le Goater 
136328d8ffSCédric Le Goater #include "qemu/osdep.h"
146328d8ffSCédric Le Goater #include "qemu/log.h"
156328d8ffSCédric Le Goater #include "qemu/module.h"
166328d8ffSCédric Le Goater #include "qemu/bitops.h"
176328d8ffSCédric Le Goater #include "hw/qdev-properties.h"
186328d8ffSCédric Le Goater #include "hw/gpio/pca9552.h"
196328d8ffSCédric Le Goater #include "hw/gpio/pca9552_regs.h"
206328d8ffSCédric Le Goater #include "hw/irq.h"
216328d8ffSCédric Le Goater #include "migration/vmstate.h"
226328d8ffSCédric Le Goater #include "qapi/error.h"
236328d8ffSCédric Le Goater #include "qapi/visitor.h"
246328d8ffSCédric Le Goater #include "trace.h"
256328d8ffSCédric Le Goater #include "qom/object.h"
266328d8ffSCédric Le Goater 
276328d8ffSCédric Le Goater struct PCA955xClass {
286328d8ffSCédric Le Goater     /*< private >*/
296328d8ffSCédric Le Goater     I2CSlaveClass parent_class;
306328d8ffSCédric Le Goater     /*< public >*/
316328d8ffSCédric Le Goater 
326328d8ffSCédric Le Goater     uint8_t pin_count;
336328d8ffSCédric Le Goater     uint8_t max_reg;
346328d8ffSCédric Le Goater };
356328d8ffSCédric Le Goater typedef struct PCA955xClass PCA955xClass;
366328d8ffSCédric Le Goater 
376328d8ffSCédric Le Goater DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
386328d8ffSCédric Le Goater                        TYPE_PCA955X)
396328d8ffSCédric Le Goater /*
406328d8ffSCédric Le Goater  * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
416328d8ffSCédric Le Goater  *        chips are the reverse of the PCA953X family of chips.
426328d8ffSCédric Le Goater  */
436328d8ffSCédric Le Goater #define PCA9552_LED_ON   0x0
446328d8ffSCédric Le Goater #define PCA9552_LED_OFF  0x1
456328d8ffSCédric Le Goater #define PCA9552_LED_PWM0 0x2
466328d8ffSCédric Le Goater #define PCA9552_LED_PWM1 0x3
476328d8ffSCédric Le Goater #define PCA9552_PIN_LOW  0x0
486328d8ffSCédric Le Goater #define PCA9552_PIN_HIZ  0x1
496328d8ffSCédric Le Goater 
506328d8ffSCédric Le Goater static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
516328d8ffSCédric Le Goater 
pca955x_pin_get_config(PCA955xState * s,int pin)526328d8ffSCédric Le Goater static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
536328d8ffSCédric Le Goater {
546328d8ffSCédric Le Goater     uint8_t reg   = PCA9552_LS0 + (pin / 4);
556328d8ffSCédric Le Goater     uint8_t shift = (pin % 4) << 1;
566328d8ffSCédric Le Goater 
576328d8ffSCédric Le Goater     return extract32(s->regs[reg], shift, 2);
586328d8ffSCédric Le Goater }
596328d8ffSCédric Le Goater 
606328d8ffSCédric Le Goater /* Return INPUT status (bit #N belongs to GPIO #N) */
pca955x_pins_get_status(PCA955xState * s)616328d8ffSCédric Le Goater static uint16_t pca955x_pins_get_status(PCA955xState *s)
626328d8ffSCédric Le Goater {
636328d8ffSCédric Le Goater     return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
646328d8ffSCédric Le Goater }
656328d8ffSCédric Le Goater 
pca955x_display_pins_status(PCA955xState * s,uint16_t previous_pins_status)666328d8ffSCédric Le Goater static void pca955x_display_pins_status(PCA955xState *s,
676328d8ffSCédric Le Goater                                         uint16_t previous_pins_status)
686328d8ffSCédric Le Goater {
696328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
706328d8ffSCédric Le Goater     uint16_t pins_status, pins_changed;
716328d8ffSCédric Le Goater     int i;
726328d8ffSCédric Le Goater 
736328d8ffSCédric Le Goater     pins_status = pca955x_pins_get_status(s);
746328d8ffSCédric Le Goater     pins_changed = previous_pins_status ^ pins_status;
756328d8ffSCédric Le Goater     if (!pins_changed) {
766328d8ffSCédric Le Goater         return;
776328d8ffSCédric Le Goater     }
786328d8ffSCédric Le Goater     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
796328d8ffSCédric Le Goater         char *buf = g_newa(char, k->pin_count + 1);
806328d8ffSCédric Le Goater 
816328d8ffSCédric Le Goater         for (i = 0; i < k->pin_count; i++) {
826328d8ffSCédric Le Goater             if (extract32(pins_status, i, 1)) {
836328d8ffSCédric Le Goater                 buf[i] = '*';
846328d8ffSCédric Le Goater             } else {
856328d8ffSCédric Le Goater                 buf[i] = '.';
866328d8ffSCédric Le Goater             }
876328d8ffSCédric Le Goater         }
886328d8ffSCédric Le Goater         buf[i] = '\0';
896328d8ffSCédric Le Goater         trace_pca955x_gpio_status(s->description, buf);
906328d8ffSCédric Le Goater     }
916328d8ffSCédric Le Goater     if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
926328d8ffSCédric Le Goater         for (i = 0; i < k->pin_count; i++) {
936328d8ffSCédric Le Goater             if (extract32(pins_changed, i, 1)) {
946328d8ffSCédric Le Goater                 unsigned new_state = extract32(pins_status, i, 1);
956328d8ffSCédric Le Goater 
966328d8ffSCédric Le Goater                 /*
976328d8ffSCédric Le Goater                  * We display the state using the PCA logic ("active-high").
986328d8ffSCédric Le Goater                  * This is not the state of the LED, which signal might be
996328d8ffSCédric Le Goater                  * wired "active-low" on the board.
1006328d8ffSCédric Le Goater                  */
1016328d8ffSCédric Le Goater                 trace_pca955x_gpio_change(s->description, i,
1026328d8ffSCédric Le Goater                                           !new_state, new_state);
1036328d8ffSCédric Le Goater             }
1046328d8ffSCédric Le Goater         }
1056328d8ffSCédric Le Goater     }
1066328d8ffSCédric Le Goater }
1076328d8ffSCédric Le Goater 
pca955x_update_pin_input(PCA955xState * s)1086328d8ffSCédric Le Goater static void pca955x_update_pin_input(PCA955xState *s)
1096328d8ffSCédric Le Goater {
1106328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
1116328d8ffSCédric Le Goater     int i;
1126328d8ffSCédric Le Goater 
1136328d8ffSCédric Le Goater     for (i = 0; i < k->pin_count; i++) {
1146328d8ffSCédric Le Goater         uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
1156328d8ffSCédric Le Goater         uint8_t bit_mask = 1 << (i % 8);
1166328d8ffSCédric Le Goater         uint8_t config = pca955x_pin_get_config(s, i);
1176328d8ffSCédric Le Goater         uint8_t old_value = s->regs[input_reg] & bit_mask;
1186328d8ffSCédric Le Goater         uint8_t new_value;
1196328d8ffSCédric Le Goater 
1206328d8ffSCédric Le Goater         switch (config) {
1216328d8ffSCédric Le Goater         case PCA9552_LED_ON:
1226328d8ffSCédric Le Goater             /* Pin is set to 0V to turn on LED */
1236328d8ffSCédric Le Goater             s->regs[input_reg] &= ~bit_mask;
1246328d8ffSCédric Le Goater             break;
1256328d8ffSCédric Le Goater         case PCA9552_LED_OFF:
1266328d8ffSCédric Le Goater             /*
1276328d8ffSCédric Le Goater              * Pin is set to Hi-Z to turn off LED and
1286328d8ffSCédric Le Goater              * pullup sets it to a logical 1 unless
1296328d8ffSCédric Le Goater              * external device drives it low.
1306328d8ffSCédric Le Goater              */
1316328d8ffSCédric Le Goater             if (s->ext_state[i] == PCA9552_PIN_LOW) {
1326328d8ffSCédric Le Goater                 s->regs[input_reg] &= ~bit_mask;
1336328d8ffSCédric Le Goater             } else {
1346328d8ffSCédric Le Goater                 s->regs[input_reg] |=  bit_mask;
1356328d8ffSCédric Le Goater             }
1366328d8ffSCédric Le Goater             break;
1376328d8ffSCédric Le Goater         case PCA9552_LED_PWM0:
1386328d8ffSCédric Le Goater         case PCA9552_LED_PWM1:
1396328d8ffSCédric Le Goater             /* TODO */
1406328d8ffSCédric Le Goater         default:
1416328d8ffSCédric Le Goater             break;
1426328d8ffSCédric Le Goater         }
1436328d8ffSCédric Le Goater 
1446328d8ffSCédric Le Goater         /* update irq state only if pin state changed */
1456328d8ffSCédric Le Goater         new_value = s->regs[input_reg] & bit_mask;
1466328d8ffSCédric Le Goater         if (new_value != old_value) {
1476328d8ffSCédric Le Goater             qemu_set_irq(s->gpio_out[i], !!new_value);
1486328d8ffSCédric Le Goater         }
1496328d8ffSCédric Le Goater     }
1506328d8ffSCédric Le Goater }
1516328d8ffSCédric Le Goater 
pca955x_read(PCA955xState * s,uint8_t reg)1526328d8ffSCédric Le Goater static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
1536328d8ffSCédric Le Goater {
1546328d8ffSCédric Le Goater     switch (reg) {
1556328d8ffSCédric Le Goater     case PCA9552_INPUT0:
1566328d8ffSCédric Le Goater     case PCA9552_INPUT1:
1576328d8ffSCédric Le Goater     case PCA9552_PSC0:
1586328d8ffSCédric Le Goater     case PCA9552_PWM0:
1596328d8ffSCédric Le Goater     case PCA9552_PSC1:
1606328d8ffSCédric Le Goater     case PCA9552_PWM1:
1616328d8ffSCédric Le Goater     case PCA9552_LS0:
1626328d8ffSCédric Le Goater     case PCA9552_LS1:
1636328d8ffSCédric Le Goater     case PCA9552_LS2:
1646328d8ffSCédric Le Goater     case PCA9552_LS3:
1656328d8ffSCédric Le Goater         return s->regs[reg];
1666328d8ffSCédric Le Goater     default:
1676328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
1686328d8ffSCédric Le Goater                       __func__, reg);
1696328d8ffSCédric Le Goater         return 0xFF;
1706328d8ffSCédric Le Goater     }
1716328d8ffSCédric Le Goater }
1726328d8ffSCédric Le Goater 
pca955x_write(PCA955xState * s,uint8_t reg,uint8_t data)1736328d8ffSCédric Le Goater static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
1746328d8ffSCédric Le Goater {
1756328d8ffSCédric Le Goater     uint16_t pins_status;
1766328d8ffSCédric Le Goater 
1776328d8ffSCédric Le Goater     switch (reg) {
1786328d8ffSCédric Le Goater     case PCA9552_PSC0:
1796328d8ffSCédric Le Goater     case PCA9552_PWM0:
1806328d8ffSCédric Le Goater     case PCA9552_PSC1:
1816328d8ffSCédric Le Goater     case PCA9552_PWM1:
1826328d8ffSCédric Le Goater         s->regs[reg] = data;
1836328d8ffSCédric Le Goater         break;
1846328d8ffSCédric Le Goater 
1856328d8ffSCédric Le Goater     case PCA9552_LS0:
1866328d8ffSCédric Le Goater     case PCA9552_LS1:
1876328d8ffSCédric Le Goater     case PCA9552_LS2:
1886328d8ffSCédric Le Goater     case PCA9552_LS3:
1896328d8ffSCédric Le Goater         pins_status = pca955x_pins_get_status(s);
1906328d8ffSCédric Le Goater         s->regs[reg] = data;
1916328d8ffSCédric Le Goater         pca955x_update_pin_input(s);
1926328d8ffSCédric Le Goater         pca955x_display_pins_status(s, pins_status);
1936328d8ffSCédric Le Goater         break;
1946328d8ffSCédric Le Goater 
1956328d8ffSCédric Le Goater     case PCA9552_INPUT0:
1966328d8ffSCédric Le Goater     case PCA9552_INPUT1:
1976328d8ffSCédric Le Goater     default:
1986328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
1996328d8ffSCédric Le Goater                       __func__, reg);
2006328d8ffSCédric Le Goater     }
2016328d8ffSCédric Le Goater }
2026328d8ffSCédric Le Goater 
2036328d8ffSCédric Le Goater /*
2046328d8ffSCédric Le Goater  * When Auto-Increment is on, the register address is incremented
2056328d8ffSCédric Le Goater  * after each byte is sent to or received by the device. The index
2066328d8ffSCédric Le Goater  * rollovers to 0 when the maximum register address is reached.
2076328d8ffSCédric Le Goater  */
pca955x_autoinc(PCA955xState * s)2086328d8ffSCédric Le Goater static void pca955x_autoinc(PCA955xState *s)
2096328d8ffSCédric Le Goater {
2106328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
2116328d8ffSCédric Le Goater 
2126328d8ffSCédric Le Goater     if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
2136328d8ffSCédric Le Goater         uint8_t reg = s->pointer & 0xf;
2146328d8ffSCédric Le Goater 
2156328d8ffSCédric Le Goater         reg = (reg + 1) % (k->max_reg + 1);
2166328d8ffSCédric Le Goater         s->pointer = reg | PCA9552_AUTOINC;
2176328d8ffSCédric Le Goater     }
2186328d8ffSCédric Le Goater }
2196328d8ffSCédric Le Goater 
pca955x_recv(I2CSlave * i2c)2206328d8ffSCédric Le Goater static uint8_t pca955x_recv(I2CSlave *i2c)
2216328d8ffSCédric Le Goater {
2226328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
2236328d8ffSCédric Le Goater     uint8_t ret;
2246328d8ffSCédric Le Goater 
2256328d8ffSCédric Le Goater     ret = pca955x_read(s, s->pointer & 0xf);
2266328d8ffSCédric Le Goater 
2276328d8ffSCédric Le Goater     /*
2286328d8ffSCédric Le Goater      * From the Specs:
2296328d8ffSCédric Le Goater      *
2306328d8ffSCédric Le Goater      *     Important Note: When a Read sequence is initiated and the
2316328d8ffSCédric Le Goater      *     AI bit is set to Logic Level 1, the Read Sequence MUST
2326328d8ffSCédric Le Goater      *     start by a register different from 0.
2336328d8ffSCédric Le Goater      *
2346328d8ffSCédric Le Goater      * I don't know what should be done in this case, so throw an
2356328d8ffSCédric Le Goater      * error.
2366328d8ffSCédric Le Goater      */
2376328d8ffSCédric Le Goater     if (s->pointer == PCA9552_AUTOINC) {
2386328d8ffSCédric Le Goater         qemu_log_mask(LOG_GUEST_ERROR,
2396328d8ffSCédric Le Goater                       "%s: Autoincrement read starting with register 0\n",
2406328d8ffSCédric Le Goater                       __func__);
2416328d8ffSCédric Le Goater     }
2426328d8ffSCédric Le Goater 
2436328d8ffSCédric Le Goater     pca955x_autoinc(s);
2446328d8ffSCédric Le Goater 
2456328d8ffSCédric Le Goater     return ret;
2466328d8ffSCédric Le Goater }
2476328d8ffSCédric Le Goater 
pca955x_send(I2CSlave * i2c,uint8_t data)2486328d8ffSCédric Le Goater static int pca955x_send(I2CSlave *i2c, uint8_t data)
2496328d8ffSCédric Le Goater {
2506328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
2516328d8ffSCédric Le Goater 
2526328d8ffSCédric Le Goater     /* First byte sent by is the register address */
2536328d8ffSCédric Le Goater     if (s->len == 0) {
2546328d8ffSCédric Le Goater         s->pointer = data;
2556328d8ffSCédric Le Goater         s->len++;
2566328d8ffSCédric Le Goater     } else {
2576328d8ffSCédric Le Goater         pca955x_write(s, s->pointer & 0xf, data);
2586328d8ffSCédric Le Goater 
2596328d8ffSCédric Le Goater         pca955x_autoinc(s);
2606328d8ffSCédric Le Goater     }
2616328d8ffSCédric Le Goater 
2626328d8ffSCédric Le Goater     return 0;
2636328d8ffSCédric Le Goater }
2646328d8ffSCédric Le Goater 
pca955x_event(I2CSlave * i2c,enum i2c_event event)2656328d8ffSCédric Le Goater static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
2666328d8ffSCédric Le Goater {
2676328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(i2c);
2686328d8ffSCédric Le Goater 
2696328d8ffSCédric Le Goater     s->len = 0;
2706328d8ffSCédric Le Goater     return 0;
2716328d8ffSCédric Le Goater }
2726328d8ffSCédric Le Goater 
pca955x_get_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)2736328d8ffSCédric Le Goater static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
2746328d8ffSCédric Le Goater                             void *opaque, Error **errp)
2756328d8ffSCédric Le Goater {
2766328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
2776328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(obj);
2786328d8ffSCédric Le Goater     int led, rc, reg;
2796328d8ffSCédric Le Goater     uint8_t state;
2806328d8ffSCédric Le Goater 
2816328d8ffSCédric Le Goater     rc = sscanf(name, "led%2d", &led);
2826328d8ffSCédric Le Goater     if (rc != 1) {
2836328d8ffSCédric Le Goater         error_setg(errp, "%s: error reading %s", __func__, name);
2846328d8ffSCédric Le Goater         return;
2856328d8ffSCédric Le Goater     }
2866328d8ffSCédric Le Goater     if (led < 0 || led > k->pin_count) {
2876328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led %s", __func__, name);
2886328d8ffSCédric Le Goater         return;
2896328d8ffSCédric Le Goater     }
2906328d8ffSCédric Le Goater     /*
2916328d8ffSCédric Le Goater      * Get the LSx register as the qom interface should expose the device
2926328d8ffSCédric Le Goater      * state, not the modeled 'input line' behaviour which would come from
2936328d8ffSCédric Le Goater      * reading the INPUTx reg
2946328d8ffSCédric Le Goater      */
2956328d8ffSCédric Le Goater     reg = PCA9552_LS0 + led / 4;
2966328d8ffSCédric Le Goater     state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
2976328d8ffSCédric Le Goater     visit_type_str(v, name, (char **)&led_state[state], errp);
2986328d8ffSCédric Le Goater }
2996328d8ffSCédric Le Goater 
3006328d8ffSCédric Le Goater /*
3016328d8ffSCédric Le Goater  * Return an LED selector register value based on an existing one, with
3026328d8ffSCédric Le Goater  * the appropriate 2-bit state value set for the given LED number (0-3).
3036328d8ffSCédric Le Goater  */
pca955x_ledsel(uint8_t oldval,int led_num,int state)3046328d8ffSCédric Le Goater static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
3056328d8ffSCédric Le Goater {
3066328d8ffSCédric Le Goater         return (oldval & (~(0x3 << (led_num << 1)))) |
3076328d8ffSCédric Le Goater                 ((state & 0x3) << (led_num << 1));
3086328d8ffSCédric Le Goater }
3096328d8ffSCédric Le Goater 
pca955x_set_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)3106328d8ffSCédric Le Goater static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
3116328d8ffSCédric Le Goater                             void *opaque, Error **errp)
3126328d8ffSCédric Le Goater {
3136328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
3146328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(obj);
3156328d8ffSCédric Le Goater     int led, rc, reg, val;
3166328d8ffSCédric Le Goater     uint8_t state;
3176328d8ffSCédric Le Goater     char *state_str;
3186328d8ffSCédric Le Goater 
3196328d8ffSCédric Le Goater     if (!visit_type_str(v, name, &state_str, errp)) {
3206328d8ffSCédric Le Goater         return;
3216328d8ffSCédric Le Goater     }
3226328d8ffSCédric Le Goater     rc = sscanf(name, "led%2d", &led);
3236328d8ffSCédric Le Goater     if (rc != 1) {
3246328d8ffSCédric Le Goater         error_setg(errp, "%s: error reading %s", __func__, name);
3256328d8ffSCédric Le Goater         return;
3266328d8ffSCédric Le Goater     }
3276328d8ffSCédric Le Goater     if (led < 0 || led > k->pin_count) {
3286328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led %s", __func__, name);
3296328d8ffSCédric Le Goater         return;
3306328d8ffSCédric Le Goater     }
3316328d8ffSCédric Le Goater 
3326328d8ffSCédric Le Goater     for (state = 0; state < ARRAY_SIZE(led_state); state++) {
3336328d8ffSCédric Le Goater         if (!strcmp(state_str, led_state[state])) {
3346328d8ffSCédric Le Goater             break;
3356328d8ffSCédric Le Goater         }
3366328d8ffSCédric Le Goater     }
3376328d8ffSCédric Le Goater     if (state >= ARRAY_SIZE(led_state)) {
3386328d8ffSCédric Le Goater         error_setg(errp, "%s invalid led state %s", __func__, state_str);
3396328d8ffSCédric Le Goater         return;
3406328d8ffSCédric Le Goater     }
3416328d8ffSCédric Le Goater 
3426328d8ffSCédric Le Goater     reg = PCA9552_LS0 + led / 4;
3436328d8ffSCédric Le Goater     val = pca955x_read(s, reg);
3446328d8ffSCédric Le Goater     val = pca955x_ledsel(val, led % 4, state);
3456328d8ffSCédric Le Goater     pca955x_write(s, reg, val);
3466328d8ffSCédric Le Goater }
3476328d8ffSCédric Le Goater 
3486328d8ffSCédric Le Goater static const VMStateDescription pca9552_vmstate = {
3496328d8ffSCédric Le Goater     .name = "PCA9552",
3506328d8ffSCédric Le Goater     .version_id = 0,
3516328d8ffSCédric Le Goater     .minimum_version_id = 0,
3526328d8ffSCédric Le Goater     .fields = (const VMStateField[]) {
3536328d8ffSCédric Le Goater         VMSTATE_UINT8(len, PCA955xState),
3546328d8ffSCédric Le Goater         VMSTATE_UINT8(pointer, PCA955xState),
3556328d8ffSCédric Le Goater         VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
3566328d8ffSCédric Le Goater         VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
3576328d8ffSCédric Le Goater         VMSTATE_I2C_SLAVE(i2c, PCA955xState),
3586328d8ffSCédric Le Goater         VMSTATE_END_OF_LIST()
3596328d8ffSCédric Le Goater     }
3606328d8ffSCédric Le Goater };
3616328d8ffSCédric Le Goater 
pca9552_reset(DeviceState * dev)3626328d8ffSCédric Le Goater static void pca9552_reset(DeviceState *dev)
3636328d8ffSCédric Le Goater {
3646328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(dev);
3656328d8ffSCédric Le Goater 
3666328d8ffSCédric Le Goater     s->regs[PCA9552_PSC0] = 0xFF;
3676328d8ffSCédric Le Goater     s->regs[PCA9552_PWM0] = 0x80;
3686328d8ffSCédric Le Goater     s->regs[PCA9552_PSC1] = 0xFF;
3696328d8ffSCédric Le Goater     s->regs[PCA9552_PWM1] = 0x80;
3706328d8ffSCédric Le Goater     s->regs[PCA9552_LS0] = 0x55; /* all OFF */
3716328d8ffSCédric Le Goater     s->regs[PCA9552_LS1] = 0x55;
3726328d8ffSCédric Le Goater     s->regs[PCA9552_LS2] = 0x55;
3736328d8ffSCédric Le Goater     s->regs[PCA9552_LS3] = 0x55;
3746328d8ffSCédric Le Goater 
3756328d8ffSCédric Le Goater     memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
3766328d8ffSCédric Le Goater     pca955x_update_pin_input(s);
3776328d8ffSCédric Le Goater 
3786328d8ffSCédric Le Goater     s->pointer = 0xFF;
3796328d8ffSCédric Le Goater     s->len = 0;
3806328d8ffSCédric Le Goater }
3816328d8ffSCédric Le Goater 
pca955x_initfn(Object * obj)3826328d8ffSCédric Le Goater static void pca955x_initfn(Object *obj)
3836328d8ffSCédric Le Goater {
3846328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(obj);
3856328d8ffSCédric Le Goater     int led;
3866328d8ffSCédric Le Goater 
3876328d8ffSCédric Le Goater     assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
3886328d8ffSCédric Le Goater     for (led = 0; led < k->pin_count; led++) {
3896328d8ffSCédric Le Goater         char *name;
3906328d8ffSCédric Le Goater 
3916328d8ffSCédric Le Goater         name = g_strdup_printf("led%d", led);
3926328d8ffSCédric Le Goater         object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
3936328d8ffSCédric Le Goater                             NULL, NULL);
3946328d8ffSCédric Le Goater         g_free(name);
3956328d8ffSCédric Le Goater     }
3966328d8ffSCédric Le Goater }
3976328d8ffSCédric Le Goater 
pca955x_set_ext_state(PCA955xState * s,int pin,int level)3986328d8ffSCédric Le Goater static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
3996328d8ffSCédric Le Goater {
4006328d8ffSCédric Le Goater     if (s->ext_state[pin] != level) {
4016328d8ffSCédric Le Goater         uint16_t pins_status = pca955x_pins_get_status(s);
4026328d8ffSCédric Le Goater         s->ext_state[pin] = level;
4036328d8ffSCédric Le Goater         pca955x_update_pin_input(s);
4046328d8ffSCédric Le Goater         pca955x_display_pins_status(s, pins_status);
4056328d8ffSCédric Le Goater     }
4066328d8ffSCédric Le Goater }
4076328d8ffSCédric Le Goater 
pca955x_gpio_in_handler(void * opaque,int pin,int level)4086328d8ffSCédric Le Goater static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
4096328d8ffSCédric Le Goater {
4106328d8ffSCédric Le Goater 
4116328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(opaque);
4126328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(s);
4136328d8ffSCédric Le Goater 
4146328d8ffSCédric Le Goater     assert((pin >= 0) && (pin < k->pin_count));
4156328d8ffSCédric Le Goater     pca955x_set_ext_state(s, pin, level);
4166328d8ffSCédric Le Goater }
4176328d8ffSCédric Le Goater 
pca955x_realize(DeviceState * dev,Error ** errp)4186328d8ffSCédric Le Goater static void pca955x_realize(DeviceState *dev, Error **errp)
4196328d8ffSCédric Le Goater {
4206328d8ffSCédric Le Goater     PCA955xClass *k = PCA955X_GET_CLASS(dev);
4216328d8ffSCédric Le Goater     PCA955xState *s = PCA955X(dev);
4226328d8ffSCédric Le Goater 
4236328d8ffSCédric Le Goater     if (!s->description) {
4246328d8ffSCédric Le Goater         s->description = g_strdup("pca-unspecified");
4256328d8ffSCédric Le Goater     }
4266328d8ffSCédric Le Goater 
4276328d8ffSCédric Le Goater     qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
4286328d8ffSCédric Le Goater     qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
4296328d8ffSCédric Le Goater }
4306328d8ffSCédric Le Goater 
4316328d8ffSCédric Le Goater static Property pca955x_properties[] = {
4326328d8ffSCédric Le Goater     DEFINE_PROP_STRING("description", PCA955xState, description),
4336328d8ffSCédric Le Goater     DEFINE_PROP_END_OF_LIST(),
4346328d8ffSCédric Le Goater };
4356328d8ffSCédric Le Goater 
pca955x_class_init(ObjectClass * klass,void * data)4366328d8ffSCédric Le Goater static void pca955x_class_init(ObjectClass *klass, void *data)
4376328d8ffSCédric Le Goater {
4386328d8ffSCédric Le Goater     DeviceClass *dc = DEVICE_CLASS(klass);
4396328d8ffSCédric Le Goater     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
4406328d8ffSCédric Le Goater 
4416328d8ffSCédric Le Goater     k->event = pca955x_event;
4426328d8ffSCédric Le Goater     k->recv = pca955x_recv;
4436328d8ffSCédric Le Goater     k->send = pca955x_send;
4446328d8ffSCédric Le Goater     dc->realize = pca955x_realize;
4456328d8ffSCédric Le Goater     device_class_set_props(dc, pca955x_properties);
4466328d8ffSCédric Le Goater }
4476328d8ffSCédric Le Goater 
4486328d8ffSCédric Le Goater static const TypeInfo pca955x_info = {
4496328d8ffSCédric Le Goater     .name          = TYPE_PCA955X,
4506328d8ffSCédric Le Goater     .parent        = TYPE_I2C_SLAVE,
4516328d8ffSCédric Le Goater     .instance_init = pca955x_initfn,
4526328d8ffSCédric Le Goater     .instance_size = sizeof(PCA955xState),
4536328d8ffSCédric Le Goater     .class_init    = pca955x_class_init,
4546328d8ffSCédric Le Goater     .class_size    = sizeof(PCA955xClass),
4556328d8ffSCédric Le Goater     .abstract      = true,
4566328d8ffSCédric Le Goater };
4576328d8ffSCédric Le Goater 
pca9552_class_init(ObjectClass * oc,void * data)4586328d8ffSCédric Le Goater static void pca9552_class_init(ObjectClass *oc, void *data)
4596328d8ffSCédric Le Goater {
4606328d8ffSCédric Le Goater     DeviceClass *dc = DEVICE_CLASS(oc);
4616328d8ffSCédric Le Goater     PCA955xClass *pc = PCA955X_CLASS(oc);
4626328d8ffSCédric Le Goater 
463*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, pca9552_reset);
4646328d8ffSCédric Le Goater     dc->vmsd = &pca9552_vmstate;
4656328d8ffSCédric Le Goater     pc->max_reg = PCA9552_LS3;
4666328d8ffSCédric Le Goater     pc->pin_count = 16;
4676328d8ffSCédric Le Goater }
4686328d8ffSCédric Le Goater 
4696328d8ffSCédric Le Goater static const TypeInfo pca9552_info = {
4706328d8ffSCédric Le Goater     .name          = TYPE_PCA9552,
4716328d8ffSCédric Le Goater     .parent        = TYPE_PCA955X,
4726328d8ffSCédric Le Goater     .class_init    = pca9552_class_init,
4736328d8ffSCédric Le Goater };
4746328d8ffSCédric Le Goater 
pca955x_register_types(void)4756328d8ffSCédric Le Goater static void pca955x_register_types(void)
4766328d8ffSCédric Le Goater {
4776328d8ffSCédric Le Goater     type_register_static(&pca955x_info);
4786328d8ffSCédric Le Goater     type_register_static(&pca9552_info);
4796328d8ffSCédric Le Goater }
4806328d8ffSCédric Le Goater 
4816328d8ffSCédric Le Goater type_init(pca955x_register_types)
482