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. 7*736132e4SPhilippe 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" 165141d415SCédric Le Goater #include "hw/misc/pca9552.h" 175141d415SCédric Le Goater #include "hw/misc/pca9552_regs.h" 18d6454270SMarkus Armbruster #include "migration/vmstate.h" 19a90d8f84SJoel Stanley #include "qapi/error.h" 20a90d8f84SJoel Stanley #include "qapi/visitor.h" 215141d415SCédric Le Goater 22*736132e4SPhilippe Mathieu-Daudé typedef struct PCA955xClass { 23*736132e4SPhilippe Mathieu-Daudé /*< private >*/ 24*736132e4SPhilippe Mathieu-Daudé I2CSlaveClass parent_class; 25*736132e4SPhilippe Mathieu-Daudé /*< public >*/ 26*736132e4SPhilippe Mathieu-Daudé 27*736132e4SPhilippe Mathieu-Daudé uint8_t pin_count; 28*736132e4SPhilippe Mathieu-Daudé uint8_t max_reg; 29*736132e4SPhilippe Mathieu-Daudé } PCA955xClass; 30*736132e4SPhilippe Mathieu-Daudé 31*736132e4SPhilippe Mathieu-Daudé #define PCA955X_CLASS(klass) \ 32*736132e4SPhilippe Mathieu-Daudé OBJECT_CLASS_CHECK(PCA955xClass, (klass), TYPE_PCA955X) 33*736132e4SPhilippe Mathieu-Daudé #define PCA955X_GET_CLASS(obj) \ 34*736132e4SPhilippe Mathieu-Daudé OBJECT_GET_CLASS(PCA955xClass, (obj), TYPE_PCA955X) 35*736132e4SPhilippe Mathieu-Daudé 365141d415SCédric Le Goater #define PCA9552_LED_ON 0x0 375141d415SCédric Le Goater #define PCA9552_LED_OFF 0x1 385141d415SCédric Le Goater #define PCA9552_LED_PWM0 0x2 395141d415SCédric Le Goater #define PCA9552_LED_PWM1 0x3 405141d415SCédric Le Goater 41a90d8f84SJoel Stanley static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; 42a90d8f84SJoel Stanley 43ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin) 445141d415SCédric Le Goater { 455141d415SCédric Le Goater uint8_t reg = PCA9552_LS0 + (pin / 4); 465141d415SCédric Le Goater uint8_t shift = (pin % 4) << 1; 475141d415SCédric Le Goater 485141d415SCédric Le Goater return extract32(s->regs[reg], shift, 2); 495141d415SCédric Le Goater } 505141d415SCédric Le Goater 51ec17228aSPhilippe Mathieu-Daudé static void pca955x_update_pin_input(PCA955xState *s) 525141d415SCédric Le Goater { 53*736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(s); 545141d415SCédric Le Goater int i; 555141d415SCédric Le Goater 56*736132e4SPhilippe Mathieu-Daudé for (i = 0; i < k->pin_count; i++) { 575141d415SCédric Le Goater uint8_t input_reg = PCA9552_INPUT0 + (i / 8); 585141d415SCédric Le Goater uint8_t input_shift = (i % 8); 59ec17228aSPhilippe Mathieu-Daudé uint8_t config = pca955x_pin_get_config(s, i); 605141d415SCédric Le Goater 615141d415SCédric Le Goater switch (config) { 625141d415SCédric Le Goater case PCA9552_LED_ON: 635141d415SCédric Le Goater s->regs[input_reg] |= 1 << input_shift; 645141d415SCédric Le Goater break; 655141d415SCédric Le Goater case PCA9552_LED_OFF: 665141d415SCédric Le Goater s->regs[input_reg] &= ~(1 << input_shift); 675141d415SCédric Le Goater break; 685141d415SCédric Le Goater case PCA9552_LED_PWM0: 695141d415SCédric Le Goater case PCA9552_LED_PWM1: 705141d415SCédric Le Goater /* TODO */ 715141d415SCédric Le Goater default: 725141d415SCédric Le Goater break; 735141d415SCédric Le Goater } 745141d415SCédric Le Goater } 755141d415SCédric Le Goater } 765141d415SCédric Le Goater 77ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) 785141d415SCédric Le Goater { 795141d415SCédric Le Goater switch (reg) { 805141d415SCédric Le Goater case PCA9552_INPUT0: 815141d415SCédric Le Goater case PCA9552_INPUT1: 825141d415SCédric Le Goater case PCA9552_PSC0: 835141d415SCédric Le Goater case PCA9552_PWM0: 845141d415SCédric Le Goater case PCA9552_PSC1: 855141d415SCédric Le Goater case PCA9552_PWM1: 865141d415SCédric Le Goater case PCA9552_LS0: 875141d415SCédric Le Goater case PCA9552_LS1: 885141d415SCédric Le Goater case PCA9552_LS2: 895141d415SCédric Le Goater case PCA9552_LS3: 905141d415SCédric Le Goater return s->regs[reg]; 915141d415SCédric Le Goater default: 925141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", 935141d415SCédric Le Goater __func__, reg); 945141d415SCédric Le Goater return 0xFF; 955141d415SCédric Le Goater } 965141d415SCédric Le Goater } 975141d415SCédric Le Goater 98ec17228aSPhilippe Mathieu-Daudé static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) 995141d415SCédric Le Goater { 1005141d415SCédric Le Goater switch (reg) { 1015141d415SCédric Le Goater case PCA9552_PSC0: 1025141d415SCédric Le Goater case PCA9552_PWM0: 1035141d415SCédric Le Goater case PCA9552_PSC1: 1045141d415SCédric Le Goater case PCA9552_PWM1: 1055141d415SCédric Le Goater s->regs[reg] = data; 1065141d415SCédric Le Goater break; 1075141d415SCédric Le Goater 1085141d415SCédric Le Goater case PCA9552_LS0: 1095141d415SCédric Le Goater case PCA9552_LS1: 1105141d415SCédric Le Goater case PCA9552_LS2: 1115141d415SCédric Le Goater case PCA9552_LS3: 1125141d415SCédric Le Goater s->regs[reg] = data; 113ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s); 1145141d415SCédric Le Goater break; 1155141d415SCédric Le Goater 1165141d415SCédric Le Goater case PCA9552_INPUT0: 1175141d415SCédric Le Goater case PCA9552_INPUT1: 1185141d415SCédric Le Goater default: 1195141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", 1205141d415SCédric Le Goater __func__, reg); 1215141d415SCédric Le Goater } 1225141d415SCédric Le Goater } 1235141d415SCédric Le Goater 1245141d415SCédric Le Goater /* 1255141d415SCédric Le Goater * When Auto-Increment is on, the register address is incremented 1265141d415SCédric Le Goater * after each byte is sent to or received by the device. The index 1275141d415SCédric Le Goater * rollovers to 0 when the maximum register address is reached. 1285141d415SCédric Le Goater */ 129ec17228aSPhilippe Mathieu-Daudé static void pca955x_autoinc(PCA955xState *s) 1305141d415SCédric Le Goater { 131*736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(s); 132*736132e4SPhilippe Mathieu-Daudé 1335141d415SCédric Le Goater if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { 1345141d415SCédric Le Goater uint8_t reg = s->pointer & 0xf; 1355141d415SCédric Le Goater 136*736132e4SPhilippe Mathieu-Daudé reg = (reg + 1) % (k->max_reg + 1); 1375141d415SCédric Le Goater s->pointer = reg | PCA9552_AUTOINC; 1385141d415SCédric Le Goater } 1395141d415SCédric Le Goater } 1405141d415SCédric Le Goater 141ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_recv(I2CSlave *i2c) 1425141d415SCédric Le Goater { 143ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1445141d415SCédric Le Goater uint8_t ret; 1455141d415SCédric Le Goater 146ec17228aSPhilippe Mathieu-Daudé ret = pca955x_read(s, s->pointer & 0xf); 1475141d415SCédric Le Goater 1485141d415SCédric Le Goater /* 1495141d415SCédric Le Goater * From the Specs: 1505141d415SCédric Le Goater * 1515141d415SCédric Le Goater * Important Note: When a Read sequence is initiated and the 1525141d415SCédric Le Goater * AI bit is set to Logic Level 1, the Read Sequence MUST 1535141d415SCédric Le Goater * start by a register different from 0. 1545141d415SCédric Le Goater * 1555141d415SCédric Le Goater * I don't know what should be done in this case, so throw an 1565141d415SCédric Le Goater * error. 1575141d415SCédric Le Goater */ 1585141d415SCédric Le Goater if (s->pointer == PCA9552_AUTOINC) { 1595141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 1605141d415SCédric Le Goater "%s: Autoincrement read starting with register 0\n", 1615141d415SCédric Le Goater __func__); 1625141d415SCédric Le Goater } 1635141d415SCédric Le Goater 164ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s); 1655141d415SCédric Le Goater 1665141d415SCédric Le Goater return ret; 1675141d415SCédric Le Goater } 1685141d415SCédric Le Goater 169ec17228aSPhilippe Mathieu-Daudé static int pca955x_send(I2CSlave *i2c, uint8_t data) 1705141d415SCédric Le Goater { 171ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1725141d415SCédric Le Goater 1735141d415SCédric Le Goater /* First byte sent by is the register address */ 1745141d415SCédric Le Goater if (s->len == 0) { 1755141d415SCédric Le Goater s->pointer = data; 1765141d415SCédric Le Goater s->len++; 1775141d415SCédric Le Goater } else { 178ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, s->pointer & 0xf, data); 1795141d415SCédric Le Goater 180ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s); 1815141d415SCédric Le Goater } 1825141d415SCédric Le Goater 1835141d415SCédric Le Goater return 0; 1845141d415SCédric Le Goater } 1855141d415SCédric Le Goater 186ec17228aSPhilippe Mathieu-Daudé static int pca955x_event(I2CSlave *i2c, enum i2c_event event) 1875141d415SCédric Le Goater { 188ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1895141d415SCédric Le Goater 1905141d415SCédric Le Goater s->len = 0; 1915141d415SCédric Le Goater return 0; 1925141d415SCédric Le Goater } 1935141d415SCédric Le Goater 194ec17228aSPhilippe Mathieu-Daudé static void pca955x_get_led(Object *obj, Visitor *v, const char *name, 195a90d8f84SJoel Stanley void *opaque, Error **errp) 196a90d8f84SJoel Stanley { 197*736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj); 198ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj); 199a90d8f84SJoel Stanley int led, rc, reg; 200a90d8f84SJoel Stanley uint8_t state; 201a90d8f84SJoel Stanley 202a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led); 203a90d8f84SJoel Stanley if (rc != 1) { 204a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name); 205a90d8f84SJoel Stanley return; 206a90d8f84SJoel Stanley } 207*736132e4SPhilippe Mathieu-Daudé if (led < 0 || led > k->pin_count) { 208a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name); 209a90d8f84SJoel Stanley return; 210a90d8f84SJoel Stanley } 211a90d8f84SJoel Stanley /* 212a90d8f84SJoel Stanley * Get the LSx register as the qom interface should expose the device 213a90d8f84SJoel Stanley * state, not the modeled 'input line' behaviour which would come from 214a90d8f84SJoel Stanley * reading the INPUTx reg 215a90d8f84SJoel Stanley */ 216a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4; 217ec17228aSPhilippe Mathieu-Daudé state = (pca955x_read(s, reg) >> (led % 8)) & 0x3; 218a90d8f84SJoel Stanley visit_type_str(v, name, (char **)&led_state[state], errp); 219a90d8f84SJoel Stanley } 220a90d8f84SJoel Stanley 221a90d8f84SJoel Stanley /* 222a90d8f84SJoel Stanley * Return an LED selector register value based on an existing one, with 223a90d8f84SJoel Stanley * the appropriate 2-bit state value set for the given LED number (0-3). 224a90d8f84SJoel Stanley */ 225a90d8f84SJoel Stanley static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state) 226a90d8f84SJoel Stanley { 227a90d8f84SJoel Stanley return (oldval & (~(0x3 << (led_num << 1)))) | 228a90d8f84SJoel Stanley ((state & 0x3) << (led_num << 1)); 229a90d8f84SJoel Stanley } 230a90d8f84SJoel Stanley 231ec17228aSPhilippe Mathieu-Daudé static void pca955x_set_led(Object *obj, Visitor *v, const char *name, 232a90d8f84SJoel Stanley void *opaque, Error **errp) 233a90d8f84SJoel Stanley { 234*736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj); 235ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj); 236a90d8f84SJoel Stanley Error *local_err = NULL; 237a90d8f84SJoel Stanley int led, rc, reg, val; 238a90d8f84SJoel Stanley uint8_t state; 239a90d8f84SJoel Stanley char *state_str; 240a90d8f84SJoel Stanley 241a90d8f84SJoel Stanley visit_type_str(v, name, &state_str, &local_err); 242a90d8f84SJoel Stanley if (local_err) { 243a90d8f84SJoel Stanley error_propagate(errp, local_err); 244a90d8f84SJoel Stanley return; 245a90d8f84SJoel Stanley } 246a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led); 247a90d8f84SJoel Stanley if (rc != 1) { 248a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name); 249a90d8f84SJoel Stanley return; 250a90d8f84SJoel Stanley } 251*736132e4SPhilippe Mathieu-Daudé if (led < 0 || led > k->pin_count) { 252a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name); 253a90d8f84SJoel Stanley return; 254a90d8f84SJoel Stanley } 255a90d8f84SJoel Stanley 256a90d8f84SJoel Stanley for (state = 0; state < ARRAY_SIZE(led_state); state++) { 257a90d8f84SJoel Stanley if (!strcmp(state_str, led_state[state])) { 258a90d8f84SJoel Stanley break; 259a90d8f84SJoel Stanley } 260a90d8f84SJoel Stanley } 261a90d8f84SJoel Stanley if (state >= ARRAY_SIZE(led_state)) { 262a90d8f84SJoel Stanley error_setg(errp, "%s invalid led state %s", __func__, state_str); 263a90d8f84SJoel Stanley return; 264a90d8f84SJoel Stanley } 265a90d8f84SJoel Stanley 266a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4; 267ec17228aSPhilippe Mathieu-Daudé val = pca955x_read(s, reg); 268a90d8f84SJoel Stanley val = pca955x_ledsel(val, led % 4, state); 269ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, reg, val); 270a90d8f84SJoel Stanley } 271a90d8f84SJoel Stanley 2725141d415SCédric Le Goater static const VMStateDescription pca9552_vmstate = { 2735141d415SCédric Le Goater .name = "PCA9552", 2745141d415SCédric Le Goater .version_id = 0, 2755141d415SCédric Le Goater .minimum_version_id = 0, 2765141d415SCédric Le Goater .fields = (VMStateField[]) { 277ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(len, PCA955xState), 278ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(pointer, PCA955xState), 279ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), 280ec17228aSPhilippe Mathieu-Daudé VMSTATE_I2C_SLAVE(i2c, PCA955xState), 2815141d415SCédric Le Goater VMSTATE_END_OF_LIST() 2825141d415SCédric Le Goater } 2835141d415SCédric Le Goater }; 2845141d415SCédric Le Goater 2855141d415SCédric Le Goater static void pca9552_reset(DeviceState *dev) 2865141d415SCédric Le Goater { 287ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(dev); 2885141d415SCédric Le Goater 2895141d415SCédric Le Goater s->regs[PCA9552_PSC0] = 0xFF; 2905141d415SCédric Le Goater s->regs[PCA9552_PWM0] = 0x80; 2915141d415SCédric Le Goater s->regs[PCA9552_PSC1] = 0xFF; 2925141d415SCédric Le Goater s->regs[PCA9552_PWM1] = 0x80; 2935141d415SCédric Le Goater s->regs[PCA9552_LS0] = 0x55; /* all OFF */ 2945141d415SCédric Le Goater s->regs[PCA9552_LS1] = 0x55; 2955141d415SCédric Le Goater s->regs[PCA9552_LS2] = 0x55; 2965141d415SCédric Le Goater s->regs[PCA9552_LS3] = 0x55; 2975141d415SCédric Le Goater 298ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s); 2995141d415SCédric Le Goater 3005141d415SCédric Le Goater s->pointer = 0xFF; 3015141d415SCédric Le Goater s->len = 0; 3025141d415SCédric Le Goater } 3035141d415SCédric Le Goater 304ec17228aSPhilippe Mathieu-Daudé static void pca955x_initfn(Object *obj) 3055141d415SCédric Le Goater { 306*736132e4SPhilippe Mathieu-Daudé PCA955xClass *k = PCA955X_GET_CLASS(obj); 307a90d8f84SJoel Stanley int led; 3085141d415SCédric Le Goater 309*736132e4SPhilippe Mathieu-Daudé assert(k->pin_count <= PCA955X_PIN_COUNT_MAX); 310*736132e4SPhilippe Mathieu-Daudé for (led = 0; led < k->pin_count; led++) { 311a90d8f84SJoel Stanley char *name; 312a90d8f84SJoel Stanley 313a90d8f84SJoel Stanley name = g_strdup_printf("led%d", led); 314ec17228aSPhilippe Mathieu-Daudé object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, 315d2623129SMarkus Armbruster NULL, NULL); 316a90d8f84SJoel Stanley g_free(name); 317a90d8f84SJoel Stanley } 3185141d415SCédric Le Goater } 3195141d415SCédric Le Goater 320*736132e4SPhilippe Mathieu-Daudé static void pca955x_class_init(ObjectClass *klass, void *data) 3215141d415SCédric Le Goater { 3225141d415SCédric Le Goater I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 3235141d415SCédric Le Goater 324ec17228aSPhilippe Mathieu-Daudé k->event = pca955x_event; 325ec17228aSPhilippe Mathieu-Daudé k->recv = pca955x_recv; 326ec17228aSPhilippe Mathieu-Daudé k->send = pca955x_send; 327*736132e4SPhilippe Mathieu-Daudé } 328*736132e4SPhilippe Mathieu-Daudé 329*736132e4SPhilippe Mathieu-Daudé static const TypeInfo pca955x_info = { 330*736132e4SPhilippe Mathieu-Daudé .name = TYPE_PCA955X, 331*736132e4SPhilippe Mathieu-Daudé .parent = TYPE_I2C_SLAVE, 332*736132e4SPhilippe Mathieu-Daudé .instance_init = pca955x_initfn, 333*736132e4SPhilippe Mathieu-Daudé .instance_size = sizeof(PCA955xState), 334*736132e4SPhilippe Mathieu-Daudé .class_init = pca955x_class_init, 335*736132e4SPhilippe Mathieu-Daudé .abstract = true, 336*736132e4SPhilippe Mathieu-Daudé }; 337*736132e4SPhilippe Mathieu-Daudé 338*736132e4SPhilippe Mathieu-Daudé static void pca9552_class_init(ObjectClass *oc, void *data) 339*736132e4SPhilippe Mathieu-Daudé { 340*736132e4SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(oc); 341*736132e4SPhilippe Mathieu-Daudé PCA955xClass *pc = PCA955X_CLASS(oc); 342*736132e4SPhilippe Mathieu-Daudé 3435141d415SCédric Le Goater dc->reset = pca9552_reset; 3445141d415SCédric Le Goater dc->vmsd = &pca9552_vmstate; 345*736132e4SPhilippe Mathieu-Daudé pc->max_reg = PCA9552_LS3; 346*736132e4SPhilippe Mathieu-Daudé pc->pin_count = 16; 3475141d415SCédric Le Goater } 3485141d415SCédric Le Goater 3495141d415SCédric Le Goater static const TypeInfo pca9552_info = { 3505141d415SCédric Le Goater .name = TYPE_PCA9552, 351*736132e4SPhilippe Mathieu-Daudé .parent = TYPE_PCA955X, 3525141d415SCédric Le Goater .class_init = pca9552_class_init, 3535141d415SCédric Le Goater }; 3545141d415SCédric Le Goater 355ec17228aSPhilippe Mathieu-Daudé static void pca955x_register_types(void) 3565141d415SCédric Le Goater { 357*736132e4SPhilippe Mathieu-Daudé type_register_static(&pca955x_info); 3585141d415SCédric Le Goater type_register_static(&pca9552_info); 3595141d415SCédric Le Goater } 3605141d415SCédric Le Goater 361ec17228aSPhilippe Mathieu-Daudé type_init(pca955x_register_types) 362