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