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"
186328d8ffSCédric Le Goater #include "hw/gpio/pca9552.h"
196328d8ffSCédric Le Goater #include "hw/gpio/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"
25db1015e9SEduardo Habkost #include "qom/object.h"
265141d415SCédric Le Goater
27db1015e9SEduardo 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;
34db1015e9SEduardo Habkost };
35db1015e9SEduardo Habkost typedef struct PCA955xClass PCA955xClass;
36736132e4SPhilippe Mathieu-Daudé
378110fa1dSEduardo Habkost DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
388110fa1dSEduardo Habkost TYPE_PCA955X)
397b99fb30SGlenn Miles /*
407b99fb30SGlenn Miles * Note: The LED_ON and LED_OFF configuration values for the PCA955X
417b99fb30SGlenn Miles * chips are the reverse of the PCA953X family of chips.
427b99fb30SGlenn Miles */
435141d415SCédric Le Goater #define PCA9552_LED_ON 0x0
445141d415SCédric Le Goater #define PCA9552_LED_OFF 0x1
455141d415SCédric Le Goater #define PCA9552_LED_PWM0 0x2
465141d415SCédric Le Goater #define PCA9552_LED_PWM1 0x3
47ff557c27SGlenn Miles #define PCA9552_PIN_LOW 0x0
48ff557c27SGlenn Miles #define PCA9552_PIN_HIZ 0x1
495141d415SCédric Le Goater
50a90d8f84SJoel Stanley static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
51a90d8f84SJoel Stanley
pca955x_pin_get_config(PCA955xState * s,int pin)52ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
535141d415SCédric Le Goater {
545141d415SCédric Le Goater uint8_t reg = PCA9552_LS0 + (pin / 4);
555141d415SCédric Le Goater uint8_t shift = (pin % 4) << 1;
565141d415SCédric Le Goater
575141d415SCédric Le Goater return extract32(s->regs[reg], shift, 2);
585141d415SCédric Le Goater }
595141d415SCédric Le Goater
60b989b89fSPhilippe Mathieu-Daudé /* Return INPUT status (bit #N belongs to GPIO #N) */
pca955x_pins_get_status(PCA955xState * s)61b989b89fSPhilippe Mathieu-Daudé static uint16_t pca955x_pins_get_status(PCA955xState *s)
62b989b89fSPhilippe Mathieu-Daudé {
63b989b89fSPhilippe Mathieu-Daudé return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0];
64b989b89fSPhilippe Mathieu-Daudé }
65b989b89fSPhilippe Mathieu-Daudé
pca955x_display_pins_status(PCA955xState * s,uint16_t previous_pins_status)66b989b89fSPhilippe Mathieu-Daudé static void pca955x_display_pins_status(PCA955xState *s,
67b989b89fSPhilippe Mathieu-Daudé uint16_t previous_pins_status)
68b989b89fSPhilippe Mathieu-Daudé {
69b989b89fSPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(s);
70b989b89fSPhilippe Mathieu-Daudé uint16_t pins_status, pins_changed;
71b989b89fSPhilippe Mathieu-Daudé int i;
72b989b89fSPhilippe Mathieu-Daudé
73b989b89fSPhilippe Mathieu-Daudé pins_status = pca955x_pins_get_status(s);
74b989b89fSPhilippe Mathieu-Daudé pins_changed = previous_pins_status ^ pins_status;
75b989b89fSPhilippe Mathieu-Daudé if (!pins_changed) {
76b989b89fSPhilippe Mathieu-Daudé return;
77b989b89fSPhilippe Mathieu-Daudé }
78b989b89fSPhilippe Mathieu-Daudé if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) {
79b989b89fSPhilippe Mathieu-Daudé char *buf = g_newa(char, k->pin_count + 1);
80b989b89fSPhilippe Mathieu-Daudé
81b989b89fSPhilippe Mathieu-Daudé for (i = 0; i < k->pin_count; i++) {
82b989b89fSPhilippe Mathieu-Daudé if (extract32(pins_status, i, 1)) {
83b989b89fSPhilippe Mathieu-Daudé buf[i] = '*';
84b989b89fSPhilippe Mathieu-Daudé } else {
85b989b89fSPhilippe Mathieu-Daudé buf[i] = '.';
86b989b89fSPhilippe Mathieu-Daudé }
87b989b89fSPhilippe Mathieu-Daudé }
88b989b89fSPhilippe Mathieu-Daudé buf[i] = '\0';
89b989b89fSPhilippe Mathieu-Daudé trace_pca955x_gpio_status(s->description, buf);
90b989b89fSPhilippe Mathieu-Daudé }
91d82ab293SPhilippe Mathieu-Daudé if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) {
92d82ab293SPhilippe Mathieu-Daudé for (i = 0; i < k->pin_count; i++) {
93d82ab293SPhilippe Mathieu-Daudé if (extract32(pins_changed, i, 1)) {
94d82ab293SPhilippe Mathieu-Daudé unsigned new_state = extract32(pins_status, i, 1);
95d82ab293SPhilippe Mathieu-Daudé
96d82ab293SPhilippe Mathieu-Daudé /*
97d82ab293SPhilippe Mathieu-Daudé * We display the state using the PCA logic ("active-high").
98d82ab293SPhilippe Mathieu-Daudé * This is not the state of the LED, which signal might be
99d82ab293SPhilippe Mathieu-Daudé * wired "active-low" on the board.
100d82ab293SPhilippe Mathieu-Daudé */
101d82ab293SPhilippe Mathieu-Daudé trace_pca955x_gpio_change(s->description, i,
102d82ab293SPhilippe Mathieu-Daudé !new_state, new_state);
103d82ab293SPhilippe Mathieu-Daudé }
104d82ab293SPhilippe Mathieu-Daudé }
105d82ab293SPhilippe Mathieu-Daudé }
106b989b89fSPhilippe Mathieu-Daudé }
107b989b89fSPhilippe Mathieu-Daudé
pca955x_update_pin_input(PCA955xState * s)108ec17228aSPhilippe Mathieu-Daudé static void pca955x_update_pin_input(PCA955xState *s)
1095141d415SCédric Le Goater {
110736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(s);
1115141d415SCédric Le Goater int i;
1125141d415SCédric Le Goater
113736132e4SPhilippe Mathieu-Daudé for (i = 0; i < k->pin_count; i++) {
1145141d415SCédric Le Goater uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
115ff557c27SGlenn Miles uint8_t bit_mask = 1 << (i % 8);
116ec17228aSPhilippe Mathieu-Daudé uint8_t config = pca955x_pin_get_config(s, i);
117ff557c27SGlenn Miles uint8_t old_value = s->regs[input_reg] & bit_mask;
118ff557c27SGlenn Miles uint8_t new_value;
1195141d415SCédric Le Goater
1205141d415SCédric Le Goater switch (config) {
1215141d415SCédric Le Goater case PCA9552_LED_ON:
1227b99fb30SGlenn Miles /* Pin is set to 0V to turn on LED */
123ff557c27SGlenn Miles s->regs[input_reg] &= ~bit_mask;
1245141d415SCédric Le Goater break;
1257b99fb30SGlenn Miles case PCA9552_LED_OFF:
1267b99fb30SGlenn Miles /*
1277b99fb30SGlenn Miles * Pin is set to Hi-Z to turn off LED and
128ff557c27SGlenn Miles * pullup sets it to a logical 1 unless
129ff557c27SGlenn Miles * external device drives it low.
1307b99fb30SGlenn Miles */
131ff557c27SGlenn Miles if (s->ext_state[i] == PCA9552_PIN_LOW) {
132ff557c27SGlenn Miles s->regs[input_reg] &= ~bit_mask;
133ff557c27SGlenn Miles } else {
134ff557c27SGlenn Miles s->regs[input_reg] |= bit_mask;
135ff557c27SGlenn Miles }
1367b99fb30SGlenn Miles break;
1375141d415SCédric Le Goater case PCA9552_LED_PWM0:
1385141d415SCédric Le Goater case PCA9552_LED_PWM1:
1395141d415SCédric Le Goater /* TODO */
1405141d415SCédric Le Goater default:
1415141d415SCédric Le Goater break;
1425141d415SCédric Le Goater }
143ff557c27SGlenn Miles
144ff557c27SGlenn Miles /* update irq state only if pin state changed */
145ff557c27SGlenn Miles new_value = s->regs[input_reg] & bit_mask;
146ff557c27SGlenn Miles if (new_value != old_value) {
147ff557c27SGlenn Miles qemu_set_irq(s->gpio_out[i], !!new_value);
148ff557c27SGlenn Miles }
1495141d415SCédric Le Goater }
1505141d415SCédric Le Goater }
1515141d415SCédric Le Goater
pca955x_read(PCA955xState * s,uint8_t reg)152ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
1535141d415SCédric Le Goater {
1545141d415SCédric Le Goater switch (reg) {
1555141d415SCédric Le Goater case PCA9552_INPUT0:
1565141d415SCédric Le Goater case PCA9552_INPUT1:
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 case PCA9552_LS0:
1625141d415SCédric Le Goater case PCA9552_LS1:
1635141d415SCédric Le Goater case PCA9552_LS2:
1645141d415SCédric Le Goater case PCA9552_LS3:
1655141d415SCédric Le Goater return s->regs[reg];
1665141d415SCédric Le Goater default:
1675141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
1685141d415SCédric Le Goater __func__, reg);
1695141d415SCédric Le Goater return 0xFF;
1705141d415SCédric Le Goater }
1715141d415SCédric Le Goater }
1725141d415SCédric Le Goater
pca955x_write(PCA955xState * s,uint8_t reg,uint8_t data)173ec17228aSPhilippe Mathieu-Daudé static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
1745141d415SCédric Le Goater {
175b989b89fSPhilippe Mathieu-Daudé uint16_t pins_status;
176b989b89fSPhilippe Mathieu-Daudé
1775141d415SCédric Le Goater switch (reg) {
1785141d415SCédric Le Goater case PCA9552_PSC0:
1795141d415SCédric Le Goater case PCA9552_PWM0:
1805141d415SCédric Le Goater case PCA9552_PSC1:
1815141d415SCédric Le Goater case PCA9552_PWM1:
1825141d415SCédric Le Goater s->regs[reg] = data;
1835141d415SCédric Le Goater break;
1845141d415SCédric Le Goater
1855141d415SCédric Le Goater case PCA9552_LS0:
1865141d415SCédric Le Goater case PCA9552_LS1:
1875141d415SCédric Le Goater case PCA9552_LS2:
1885141d415SCédric Le Goater case PCA9552_LS3:
189b989b89fSPhilippe Mathieu-Daudé pins_status = pca955x_pins_get_status(s);
1905141d415SCédric Le Goater s->regs[reg] = data;
191ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s);
192b989b89fSPhilippe Mathieu-Daudé pca955x_display_pins_status(s, pins_status);
1935141d415SCédric Le Goater break;
1945141d415SCédric Le Goater
1955141d415SCédric Le Goater case PCA9552_INPUT0:
1965141d415SCédric Le Goater case PCA9552_INPUT1:
1975141d415SCédric Le Goater default:
1985141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
1995141d415SCédric Le Goater __func__, reg);
2005141d415SCédric Le Goater }
2015141d415SCédric Le Goater }
2025141d415SCédric Le Goater
2035141d415SCédric Le Goater /*
2045141d415SCédric Le Goater * When Auto-Increment is on, the register address is incremented
2055141d415SCédric Le Goater * after each byte is sent to or received by the device. The index
2065141d415SCédric Le Goater * rollovers to 0 when the maximum register address is reached.
2075141d415SCédric Le Goater */
pca955x_autoinc(PCA955xState * s)208ec17228aSPhilippe Mathieu-Daudé static void pca955x_autoinc(PCA955xState *s)
2095141d415SCédric Le Goater {
210736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(s);
211736132e4SPhilippe Mathieu-Daudé
2125141d415SCédric Le Goater if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
2135141d415SCédric Le Goater uint8_t reg = s->pointer & 0xf;
2145141d415SCédric Le Goater
215736132e4SPhilippe Mathieu-Daudé reg = (reg + 1) % (k->max_reg + 1);
2165141d415SCédric Le Goater s->pointer = reg | PCA9552_AUTOINC;
2175141d415SCédric Le Goater }
2185141d415SCédric Le Goater }
2195141d415SCédric Le Goater
pca955x_recv(I2CSlave * i2c)220ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_recv(I2CSlave *i2c)
2215141d415SCédric Le Goater {
222ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c);
2235141d415SCédric Le Goater uint8_t ret;
2245141d415SCédric Le Goater
225ec17228aSPhilippe Mathieu-Daudé ret = pca955x_read(s, s->pointer & 0xf);
2265141d415SCédric Le Goater
2275141d415SCédric Le Goater /*
2285141d415SCédric Le Goater * From the Specs:
2295141d415SCédric Le Goater *
2305141d415SCédric Le Goater * Important Note: When a Read sequence is initiated and the
2315141d415SCédric Le Goater * AI bit is set to Logic Level 1, the Read Sequence MUST
2325141d415SCédric Le Goater * start by a register different from 0.
2335141d415SCédric Le Goater *
2345141d415SCédric Le Goater * I don't know what should be done in this case, so throw an
2355141d415SCédric Le Goater * error.
2365141d415SCédric Le Goater */
2375141d415SCédric Le Goater if (s->pointer == PCA9552_AUTOINC) {
2385141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR,
2395141d415SCédric Le Goater "%s: Autoincrement read starting with register 0\n",
2405141d415SCédric Le Goater __func__);
2415141d415SCédric Le Goater }
2425141d415SCédric Le Goater
243ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s);
2445141d415SCédric Le Goater
2455141d415SCédric Le Goater return ret;
2465141d415SCédric Le Goater }
2475141d415SCédric Le Goater
pca955x_send(I2CSlave * i2c,uint8_t data)248ec17228aSPhilippe Mathieu-Daudé static int pca955x_send(I2CSlave *i2c, uint8_t data)
2495141d415SCédric Le Goater {
250ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c);
2515141d415SCédric Le Goater
2525141d415SCédric Le Goater /* First byte sent by is the register address */
2535141d415SCédric Le Goater if (s->len == 0) {
2545141d415SCédric Le Goater s->pointer = data;
2555141d415SCédric Le Goater s->len++;
2565141d415SCédric Le Goater } else {
257ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, s->pointer & 0xf, data);
2585141d415SCédric Le Goater
259ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s);
2605141d415SCédric Le Goater }
2615141d415SCédric Le Goater
2625141d415SCédric Le Goater return 0;
2635141d415SCédric Le Goater }
2645141d415SCédric Le Goater
pca955x_event(I2CSlave * i2c,enum i2c_event event)265ec17228aSPhilippe Mathieu-Daudé static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
2665141d415SCédric Le Goater {
267ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c);
2685141d415SCédric Le Goater
2695141d415SCédric Le Goater s->len = 0;
2705141d415SCédric Le Goater return 0;
2715141d415SCédric Le Goater }
2725141d415SCédric Le Goater
pca955x_get_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)273ec17228aSPhilippe Mathieu-Daudé static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
274a90d8f84SJoel Stanley void *opaque, Error **errp)
275a90d8f84SJoel Stanley {
276736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj);
277ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj);
278a90d8f84SJoel Stanley int led, rc, reg;
279a90d8f84SJoel Stanley uint8_t state;
280a90d8f84SJoel Stanley
281a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led);
282a90d8f84SJoel Stanley if (rc != 1) {
283a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name);
284a90d8f84SJoel Stanley return;
285a90d8f84SJoel Stanley }
286736132e4SPhilippe Mathieu-Daudé if (led < 0 || led > k->pin_count) {
287a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name);
288a90d8f84SJoel Stanley return;
289a90d8f84SJoel Stanley }
290a90d8f84SJoel Stanley /*
291a90d8f84SJoel Stanley * Get the LSx register as the qom interface should expose the device
292a90d8f84SJoel Stanley * state, not the modeled 'input line' behaviour which would come from
293a90d8f84SJoel Stanley * reading the INPUTx reg
294a90d8f84SJoel Stanley */
295a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4;
2960c33a48dSAndrew Jeffery state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
297a90d8f84SJoel Stanley visit_type_str(v, name, (char **)&led_state[state], errp);
298a90d8f84SJoel Stanley }
299a90d8f84SJoel Stanley
300a90d8f84SJoel Stanley /*
301a90d8f84SJoel Stanley * Return an LED selector register value based on an existing one, with
302a90d8f84SJoel Stanley * the appropriate 2-bit state value set for the given LED number (0-3).
303a90d8f84SJoel Stanley */
pca955x_ledsel(uint8_t oldval,int led_num,int state)304a90d8f84SJoel Stanley static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
305a90d8f84SJoel Stanley {
306a90d8f84SJoel Stanley return (oldval & (~(0x3 << (led_num << 1)))) |
307a90d8f84SJoel Stanley ((state & 0x3) << (led_num << 1));
308a90d8f84SJoel Stanley }
309a90d8f84SJoel Stanley
pca955x_set_led(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)310ec17228aSPhilippe Mathieu-Daudé static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
311a90d8f84SJoel Stanley void *opaque, Error **errp)
312a90d8f84SJoel Stanley {
313736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj);
314ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj);
315a90d8f84SJoel Stanley int led, rc, reg, val;
316a90d8f84SJoel Stanley uint8_t state;
317a90d8f84SJoel Stanley char *state_str;
318a90d8f84SJoel Stanley
319668f62ecSMarkus Armbruster if (!visit_type_str(v, name, &state_str, errp)) {
320a90d8f84SJoel Stanley return;
321a90d8f84SJoel Stanley }
322a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led);
323a90d8f84SJoel Stanley if (rc != 1) {
324a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name);
325a90d8f84SJoel Stanley return;
326a90d8f84SJoel Stanley }
327736132e4SPhilippe Mathieu-Daudé if (led < 0 || led > k->pin_count) {
328a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name);
329a90d8f84SJoel Stanley return;
330a90d8f84SJoel Stanley }
331a90d8f84SJoel Stanley
332a90d8f84SJoel Stanley for (state = 0; state < ARRAY_SIZE(led_state); state++) {
333a90d8f84SJoel Stanley if (!strcmp(state_str, led_state[state])) {
334a90d8f84SJoel Stanley break;
335a90d8f84SJoel Stanley }
336a90d8f84SJoel Stanley }
337a90d8f84SJoel Stanley if (state >= ARRAY_SIZE(led_state)) {
338a90d8f84SJoel Stanley error_setg(errp, "%s invalid led state %s", __func__, state_str);
339a90d8f84SJoel Stanley return;
340a90d8f84SJoel Stanley }
341a90d8f84SJoel Stanley
342a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4;
343ec17228aSPhilippe Mathieu-Daudé val = pca955x_read(s, reg);
344a90d8f84SJoel Stanley val = pca955x_ledsel(val, led % 4, state);
345ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, reg, val);
346a90d8f84SJoel Stanley }
347a90d8f84SJoel Stanley
3485141d415SCédric Le Goater static const VMStateDescription pca9552_vmstate = {
3495141d415SCédric Le Goater .name = "PCA9552",
3505141d415SCédric Le Goater .version_id = 0,
3515141d415SCédric Le Goater .minimum_version_id = 0,
352e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
353ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(len, PCA955xState),
354ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(pointer, PCA955xState),
355ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
356ff557c27SGlenn Miles VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
357ec17228aSPhilippe Mathieu-Daudé VMSTATE_I2C_SLAVE(i2c, PCA955xState),
3585141d415SCédric Le Goater VMSTATE_END_OF_LIST()
3595141d415SCédric Le Goater }
3605141d415SCédric Le Goater };
3615141d415SCédric Le Goater
pca9552_reset(DeviceState * dev)3625141d415SCédric Le Goater static void pca9552_reset(DeviceState *dev)
3635141d415SCédric Le Goater {
364ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(dev);
3655141d415SCédric Le Goater
3665141d415SCédric Le Goater s->regs[PCA9552_PSC0] = 0xFF;
3675141d415SCédric Le Goater s->regs[PCA9552_PWM0] = 0x80;
3685141d415SCédric Le Goater s->regs[PCA9552_PSC1] = 0xFF;
3695141d415SCédric Le Goater s->regs[PCA9552_PWM1] = 0x80;
3705141d415SCédric Le Goater s->regs[PCA9552_LS0] = 0x55; /* all OFF */
3715141d415SCédric Le Goater s->regs[PCA9552_LS1] = 0x55;
3725141d415SCédric Le Goater s->regs[PCA9552_LS2] = 0x55;
3735141d415SCédric Le Goater s->regs[PCA9552_LS3] = 0x55;
3745141d415SCédric Le Goater
375ff557c27SGlenn Miles memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
376ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s);
3775141d415SCédric Le Goater
3785141d415SCédric Le Goater s->pointer = 0xFF;
3795141d415SCédric Le Goater s->len = 0;
3805141d415SCédric Le Goater }
3815141d415SCédric Le Goater
pca955x_initfn(Object * obj)382ec17228aSPhilippe Mathieu-Daudé static void pca955x_initfn(Object *obj)
3835141d415SCédric Le Goater {
384736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj);
385a90d8f84SJoel Stanley int led;
3865141d415SCédric Le Goater
387736132e4SPhilippe Mathieu-Daudé assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
388736132e4SPhilippe Mathieu-Daudé for (led = 0; led < k->pin_count; led++) {
389a90d8f84SJoel Stanley char *name;
390a90d8f84SJoel Stanley
391a90d8f84SJoel Stanley name = g_strdup_printf("led%d", led);
392ec17228aSPhilippe Mathieu-Daudé object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
393d2623129SMarkus Armbruster NULL, NULL);
394a90d8f84SJoel Stanley g_free(name);
395a90d8f84SJoel Stanley }
3965141d415SCédric Le Goater }
3975141d415SCédric Le Goater
pca955x_set_ext_state(PCA955xState * s,int pin,int level)398ff557c27SGlenn Miles static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
399ff557c27SGlenn Miles {
400ff557c27SGlenn Miles if (s->ext_state[pin] != level) {
401ff557c27SGlenn Miles uint16_t pins_status = pca955x_pins_get_status(s);
402ff557c27SGlenn Miles s->ext_state[pin] = level;
403ff557c27SGlenn Miles pca955x_update_pin_input(s);
404ff557c27SGlenn Miles pca955x_display_pins_status(s, pins_status);
405ff557c27SGlenn Miles }
406ff557c27SGlenn Miles }
407ff557c27SGlenn Miles
pca955x_gpio_in_handler(void * opaque,int pin,int level)408ff557c27SGlenn Miles static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
409ff557c27SGlenn Miles {
410ff557c27SGlenn Miles
411ff557c27SGlenn Miles PCA955xState *s = PCA955X(opaque);
412ff557c27SGlenn Miles PCA955xClass *k = PCA955X_GET_CLASS(s);
413ff557c27SGlenn Miles
414ff557c27SGlenn Miles assert((pin >= 0) && (pin < k->pin_count));
415ff557c27SGlenn Miles pca955x_set_ext_state(s, pin, level);
416ff557c27SGlenn Miles }
417ff557c27SGlenn Miles
pca955x_realize(DeviceState * dev,Error ** errp)4182df252d8SPhilippe Mathieu-Daudé static void pca955x_realize(DeviceState *dev, Error **errp)
4192df252d8SPhilippe Mathieu-Daudé {
420586f495bSPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(dev);
4212df252d8SPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(dev);
4222df252d8SPhilippe Mathieu-Daudé
4232df252d8SPhilippe Mathieu-Daudé if (!s->description) {
4242df252d8SPhilippe Mathieu-Daudé s->description = g_strdup("pca-unspecified");
4252df252d8SPhilippe Mathieu-Daudé }
426586f495bSPhilippe Mathieu-Daudé
427ff557c27SGlenn Miles qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
428ff557c27SGlenn Miles qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
4292df252d8SPhilippe Mathieu-Daudé }
4302df252d8SPhilippe Mathieu-Daudé
431de531a6bSRichard Henderson static const Property pca955x_properties[] = {
4322df252d8SPhilippe Mathieu-Daudé DEFINE_PROP_STRING("description", PCA955xState, description),
4332df252d8SPhilippe Mathieu-Daudé };
4342df252d8SPhilippe Mathieu-Daudé
pca955x_class_init(ObjectClass * klass,const void * data)435*12d1a768SPhilippe Mathieu-Daudé static void pca955x_class_init(ObjectClass *klass, const void *data)
4365141d415SCédric Le Goater {
4372df252d8SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(klass);
4385141d415SCédric Le Goater I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
4395141d415SCédric Le Goater
440ec17228aSPhilippe Mathieu-Daudé k->event = pca955x_event;
441ec17228aSPhilippe Mathieu-Daudé k->recv = pca955x_recv;
442ec17228aSPhilippe Mathieu-Daudé k->send = pca955x_send;
4432df252d8SPhilippe Mathieu-Daudé dc->realize = pca955x_realize;
4442df252d8SPhilippe Mathieu-Daudé device_class_set_props(dc, pca955x_properties);
445736132e4SPhilippe Mathieu-Daudé }
446736132e4SPhilippe Mathieu-Daudé
447736132e4SPhilippe Mathieu-Daudé static const TypeInfo pca955x_info = {
448736132e4SPhilippe Mathieu-Daudé .name = TYPE_PCA955X,
449736132e4SPhilippe Mathieu-Daudé .parent = TYPE_I2C_SLAVE,
450736132e4SPhilippe Mathieu-Daudé .instance_init = pca955x_initfn,
451736132e4SPhilippe Mathieu-Daudé .instance_size = sizeof(PCA955xState),
452736132e4SPhilippe Mathieu-Daudé .class_init = pca955x_class_init,
453fc1bff95SPhilippe Mathieu-Daudé .class_size = sizeof(PCA955xClass),
454736132e4SPhilippe Mathieu-Daudé .abstract = true,
455736132e4SPhilippe Mathieu-Daudé };
456736132e4SPhilippe Mathieu-Daudé
pca9552_class_init(ObjectClass * oc,const void * data)457*12d1a768SPhilippe Mathieu-Daudé static void pca9552_class_init(ObjectClass *oc, const void *data)
458736132e4SPhilippe Mathieu-Daudé {
459736132e4SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(oc);
460736132e4SPhilippe Mathieu-Daudé PCA955xClass *pc = PCA955X_CLASS(oc);
461736132e4SPhilippe Mathieu-Daudé
462e3d08143SPeter Maydell device_class_set_legacy_reset(dc, pca9552_reset);
4635141d415SCédric Le Goater dc->vmsd = &pca9552_vmstate;
464736132e4SPhilippe Mathieu-Daudé pc->max_reg = PCA9552_LS3;
465736132e4SPhilippe Mathieu-Daudé pc->pin_count = 16;
4665141d415SCédric Le Goater }
4675141d415SCédric Le Goater
4685141d415SCédric Le Goater static const TypeInfo pca9552_info = {
4695141d415SCédric Le Goater .name = TYPE_PCA9552,
470736132e4SPhilippe Mathieu-Daudé .parent = TYPE_PCA955X,
4715141d415SCédric Le Goater .class_init = pca9552_class_init,
4725141d415SCédric Le Goater };
4735141d415SCédric Le Goater
pca955x_register_types(void)474ec17228aSPhilippe Mathieu-Daudé static void pca955x_register_types(void)
4755141d415SCédric Le Goater {
476736132e4SPhilippe Mathieu-Daudé type_register_static(&pca955x_info);
4775141d415SCédric Le Goater type_register_static(&pca9552_info);
4785141d415SCédric Le Goater }
4795141d415SCédric Le Goater
480ec17228aSPhilippe Mathieu-Daudé type_init(pca955x_register_types)
481