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