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. 75141d415SCédric Le Goater * 85141d415SCédric Le Goater * This work is licensed under the terms of the GNU GPL, version 2 or 95141d415SCédric Le Goater * later. See the COPYING file in the top-level directory. 105141d415SCédric Le Goater */ 115141d415SCédric Le Goater 125141d415SCédric Le Goater #include "qemu/osdep.h" 135141d415SCédric Le Goater #include "qemu/log.h" 140b8fa32fSMarkus Armbruster #include "qemu/module.h" 155141d415SCédric Le Goater #include "hw/misc/pca9552.h" 165141d415SCédric Le Goater #include "hw/misc/pca9552_regs.h" 17d6454270SMarkus Armbruster #include "migration/vmstate.h" 18a90d8f84SJoel Stanley #include "qapi/error.h" 19a90d8f84SJoel Stanley #include "qapi/visitor.h" 205141d415SCédric Le Goater 215141d415SCédric Le Goater #define PCA9552_LED_ON 0x0 225141d415SCédric Le Goater #define PCA9552_LED_OFF 0x1 235141d415SCédric Le Goater #define PCA9552_LED_PWM0 0x2 245141d415SCédric Le Goater #define PCA9552_LED_PWM1 0x3 255141d415SCédric Le Goater 26a90d8f84SJoel Stanley static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; 27a90d8f84SJoel Stanley 28*ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin) 295141d415SCédric Le Goater { 305141d415SCédric Le Goater uint8_t reg = PCA9552_LS0 + (pin / 4); 315141d415SCédric Le Goater uint8_t shift = (pin % 4) << 1; 325141d415SCédric Le Goater 335141d415SCédric Le Goater return extract32(s->regs[reg], shift, 2); 345141d415SCédric Le Goater } 355141d415SCédric Le Goater 36*ec17228aSPhilippe Mathieu-Daudé static void pca955x_update_pin_input(PCA955xState *s) 375141d415SCédric Le Goater { 385141d415SCédric Le Goater int i; 395141d415SCédric Le Goater 408208335bSPhilippe Mathieu-Daudé for (i = 0; i < s->pin_count; i++) { 415141d415SCédric Le Goater uint8_t input_reg = PCA9552_INPUT0 + (i / 8); 425141d415SCédric Le Goater uint8_t input_shift = (i % 8); 43*ec17228aSPhilippe Mathieu-Daudé uint8_t config = pca955x_pin_get_config(s, i); 445141d415SCédric Le Goater 455141d415SCédric Le Goater switch (config) { 465141d415SCédric Le Goater case PCA9552_LED_ON: 475141d415SCédric Le Goater s->regs[input_reg] |= 1 << input_shift; 485141d415SCédric Le Goater break; 495141d415SCédric Le Goater case PCA9552_LED_OFF: 505141d415SCédric Le Goater s->regs[input_reg] &= ~(1 << input_shift); 515141d415SCédric Le Goater break; 525141d415SCédric Le Goater case PCA9552_LED_PWM0: 535141d415SCédric Le Goater case PCA9552_LED_PWM1: 545141d415SCédric Le Goater /* TODO */ 555141d415SCédric Le Goater default: 565141d415SCédric Le Goater break; 575141d415SCédric Le Goater } 585141d415SCédric Le Goater } 595141d415SCédric Le Goater } 605141d415SCédric Le Goater 61*ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) 625141d415SCédric Le Goater { 635141d415SCédric Le Goater switch (reg) { 645141d415SCédric Le Goater case PCA9552_INPUT0: 655141d415SCédric Le Goater case PCA9552_INPUT1: 665141d415SCédric Le Goater case PCA9552_PSC0: 675141d415SCédric Le Goater case PCA9552_PWM0: 685141d415SCédric Le Goater case PCA9552_PSC1: 695141d415SCédric Le Goater case PCA9552_PWM1: 705141d415SCédric Le Goater case PCA9552_LS0: 715141d415SCédric Le Goater case PCA9552_LS1: 725141d415SCédric Le Goater case PCA9552_LS2: 735141d415SCédric Le Goater case PCA9552_LS3: 745141d415SCédric Le Goater return s->regs[reg]; 755141d415SCédric Le Goater default: 765141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", 775141d415SCédric Le Goater __func__, reg); 785141d415SCédric Le Goater return 0xFF; 795141d415SCédric Le Goater } 805141d415SCédric Le Goater } 815141d415SCédric Le Goater 82*ec17228aSPhilippe Mathieu-Daudé static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) 835141d415SCédric Le Goater { 845141d415SCédric Le Goater switch (reg) { 855141d415SCédric Le Goater case PCA9552_PSC0: 865141d415SCédric Le Goater case PCA9552_PWM0: 875141d415SCédric Le Goater case PCA9552_PSC1: 885141d415SCédric Le Goater case PCA9552_PWM1: 895141d415SCédric Le Goater s->regs[reg] = data; 905141d415SCédric Le Goater break; 915141d415SCédric Le Goater 925141d415SCédric Le Goater case PCA9552_LS0: 935141d415SCédric Le Goater case PCA9552_LS1: 945141d415SCédric Le Goater case PCA9552_LS2: 955141d415SCédric Le Goater case PCA9552_LS3: 965141d415SCédric Le Goater s->regs[reg] = data; 97*ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s); 985141d415SCédric Le Goater break; 995141d415SCédric Le Goater 1005141d415SCédric Le Goater case PCA9552_INPUT0: 1015141d415SCédric Le Goater case PCA9552_INPUT1: 1025141d415SCédric Le Goater default: 1035141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", 1045141d415SCédric Le Goater __func__, reg); 1055141d415SCédric Le Goater } 1065141d415SCédric Le Goater } 1075141d415SCédric Le Goater 1085141d415SCédric Le Goater /* 1095141d415SCédric Le Goater * When Auto-Increment is on, the register address is incremented 1105141d415SCédric Le Goater * after each byte is sent to or received by the device. The index 1115141d415SCédric Le Goater * rollovers to 0 when the maximum register address is reached. 1125141d415SCédric Le Goater */ 113*ec17228aSPhilippe Mathieu-Daudé static void pca955x_autoinc(PCA955xState *s) 1145141d415SCédric Le Goater { 1155141d415SCédric Le Goater if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { 1165141d415SCédric Le Goater uint8_t reg = s->pointer & 0xf; 1175141d415SCédric Le Goater 1185141d415SCédric Le Goater reg = (reg + 1) % (s->max_reg + 1); 1195141d415SCédric Le Goater s->pointer = reg | PCA9552_AUTOINC; 1205141d415SCédric Le Goater } 1215141d415SCédric Le Goater } 1225141d415SCédric Le Goater 123*ec17228aSPhilippe Mathieu-Daudé static uint8_t pca955x_recv(I2CSlave *i2c) 1245141d415SCédric Le Goater { 125*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1265141d415SCédric Le Goater uint8_t ret; 1275141d415SCédric Le Goater 128*ec17228aSPhilippe Mathieu-Daudé ret = pca955x_read(s, s->pointer & 0xf); 1295141d415SCédric Le Goater 1305141d415SCédric Le Goater /* 1315141d415SCédric Le Goater * From the Specs: 1325141d415SCédric Le Goater * 1335141d415SCédric Le Goater * Important Note: When a Read sequence is initiated and the 1345141d415SCédric Le Goater * AI bit is set to Logic Level 1, the Read Sequence MUST 1355141d415SCédric Le Goater * start by a register different from 0. 1365141d415SCédric Le Goater * 1375141d415SCédric Le Goater * I don't know what should be done in this case, so throw an 1385141d415SCédric Le Goater * error. 1395141d415SCédric Le Goater */ 1405141d415SCédric Le Goater if (s->pointer == PCA9552_AUTOINC) { 1415141d415SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, 1425141d415SCédric Le Goater "%s: Autoincrement read starting with register 0\n", 1435141d415SCédric Le Goater __func__); 1445141d415SCédric Le Goater } 1455141d415SCédric Le Goater 146*ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s); 1475141d415SCédric Le Goater 1485141d415SCédric Le Goater return ret; 1495141d415SCédric Le Goater } 1505141d415SCédric Le Goater 151*ec17228aSPhilippe Mathieu-Daudé static int pca955x_send(I2CSlave *i2c, uint8_t data) 1525141d415SCédric Le Goater { 153*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1545141d415SCédric Le Goater 1555141d415SCédric Le Goater /* First byte sent by is the register address */ 1565141d415SCédric Le Goater if (s->len == 0) { 1575141d415SCédric Le Goater s->pointer = data; 1585141d415SCédric Le Goater s->len++; 1595141d415SCédric Le Goater } else { 160*ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, s->pointer & 0xf, data); 1615141d415SCédric Le Goater 162*ec17228aSPhilippe Mathieu-Daudé pca955x_autoinc(s); 1635141d415SCédric Le Goater } 1645141d415SCédric Le Goater 1655141d415SCédric Le Goater return 0; 1665141d415SCédric Le Goater } 1675141d415SCédric Le Goater 168*ec17228aSPhilippe Mathieu-Daudé static int pca955x_event(I2CSlave *i2c, enum i2c_event event) 1695141d415SCédric Le Goater { 170*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(i2c); 1715141d415SCédric Le Goater 1725141d415SCédric Le Goater s->len = 0; 1735141d415SCédric Le Goater return 0; 1745141d415SCédric Le Goater } 1755141d415SCédric Le Goater 176*ec17228aSPhilippe Mathieu-Daudé static void pca955x_get_led(Object *obj, Visitor *v, const char *name, 177a90d8f84SJoel Stanley void *opaque, Error **errp) 178a90d8f84SJoel Stanley { 179*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj); 180a90d8f84SJoel Stanley int led, rc, reg; 181a90d8f84SJoel Stanley uint8_t state; 182a90d8f84SJoel Stanley 183a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led); 184a90d8f84SJoel Stanley if (rc != 1) { 185a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name); 186a90d8f84SJoel Stanley return; 187a90d8f84SJoel Stanley } 1888208335bSPhilippe Mathieu-Daudé if (led < 0 || led > s->pin_count) { 189a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name); 190a90d8f84SJoel Stanley return; 191a90d8f84SJoel Stanley } 192a90d8f84SJoel Stanley /* 193a90d8f84SJoel Stanley * Get the LSx register as the qom interface should expose the device 194a90d8f84SJoel Stanley * state, not the modeled 'input line' behaviour which would come from 195a90d8f84SJoel Stanley * reading the INPUTx reg 196a90d8f84SJoel Stanley */ 197a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4; 198*ec17228aSPhilippe Mathieu-Daudé state = (pca955x_read(s, reg) >> (led % 8)) & 0x3; 199a90d8f84SJoel Stanley visit_type_str(v, name, (char **)&led_state[state], errp); 200a90d8f84SJoel Stanley } 201a90d8f84SJoel Stanley 202a90d8f84SJoel Stanley /* 203a90d8f84SJoel Stanley * Return an LED selector register value based on an existing one, with 204a90d8f84SJoel Stanley * the appropriate 2-bit state value set for the given LED number (0-3). 205a90d8f84SJoel Stanley */ 206a90d8f84SJoel Stanley static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state) 207a90d8f84SJoel Stanley { 208a90d8f84SJoel Stanley return (oldval & (~(0x3 << (led_num << 1)))) | 209a90d8f84SJoel Stanley ((state & 0x3) << (led_num << 1)); 210a90d8f84SJoel Stanley } 211a90d8f84SJoel Stanley 212*ec17228aSPhilippe Mathieu-Daudé static void pca955x_set_led(Object *obj, Visitor *v, const char *name, 213a90d8f84SJoel Stanley void *opaque, Error **errp) 214a90d8f84SJoel Stanley { 215*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj); 216a90d8f84SJoel Stanley Error *local_err = NULL; 217a90d8f84SJoel Stanley int led, rc, reg, val; 218a90d8f84SJoel Stanley uint8_t state; 219a90d8f84SJoel Stanley char *state_str; 220a90d8f84SJoel Stanley 221a90d8f84SJoel Stanley visit_type_str(v, name, &state_str, &local_err); 222a90d8f84SJoel Stanley if (local_err) { 223a90d8f84SJoel Stanley error_propagate(errp, local_err); 224a90d8f84SJoel Stanley return; 225a90d8f84SJoel Stanley } 226a90d8f84SJoel Stanley rc = sscanf(name, "led%2d", &led); 227a90d8f84SJoel Stanley if (rc != 1) { 228a90d8f84SJoel Stanley error_setg(errp, "%s: error reading %s", __func__, name); 229a90d8f84SJoel Stanley return; 230a90d8f84SJoel Stanley } 2318208335bSPhilippe Mathieu-Daudé if (led < 0 || led > s->pin_count) { 232a90d8f84SJoel Stanley error_setg(errp, "%s invalid led %s", __func__, name); 233a90d8f84SJoel Stanley return; 234a90d8f84SJoel Stanley } 235a90d8f84SJoel Stanley 236a90d8f84SJoel Stanley for (state = 0; state < ARRAY_SIZE(led_state); state++) { 237a90d8f84SJoel Stanley if (!strcmp(state_str, led_state[state])) { 238a90d8f84SJoel Stanley break; 239a90d8f84SJoel Stanley } 240a90d8f84SJoel Stanley } 241a90d8f84SJoel Stanley if (state >= ARRAY_SIZE(led_state)) { 242a90d8f84SJoel Stanley error_setg(errp, "%s invalid led state %s", __func__, state_str); 243a90d8f84SJoel Stanley return; 244a90d8f84SJoel Stanley } 245a90d8f84SJoel Stanley 246a90d8f84SJoel Stanley reg = PCA9552_LS0 + led / 4; 247*ec17228aSPhilippe Mathieu-Daudé val = pca955x_read(s, reg); 248a90d8f84SJoel Stanley val = pca955x_ledsel(val, led % 4, state); 249*ec17228aSPhilippe Mathieu-Daudé pca955x_write(s, reg, val); 250a90d8f84SJoel Stanley } 251a90d8f84SJoel Stanley 2525141d415SCédric Le Goater static const VMStateDescription pca9552_vmstate = { 2535141d415SCédric Le Goater .name = "PCA9552", 2545141d415SCédric Le Goater .version_id = 0, 2555141d415SCédric Le Goater .minimum_version_id = 0, 2565141d415SCédric Le Goater .fields = (VMStateField[]) { 257*ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(len, PCA955xState), 258*ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8(pointer, PCA955xState), 259*ec17228aSPhilippe Mathieu-Daudé VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), 260*ec17228aSPhilippe Mathieu-Daudé VMSTATE_I2C_SLAVE(i2c, PCA955xState), 2615141d415SCédric Le Goater VMSTATE_END_OF_LIST() 2625141d415SCédric Le Goater } 2635141d415SCédric Le Goater }; 2645141d415SCédric Le Goater 2655141d415SCédric Le Goater static void pca9552_reset(DeviceState *dev) 2665141d415SCédric Le Goater { 267*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(dev); 2685141d415SCédric Le Goater 2695141d415SCédric Le Goater s->regs[PCA9552_PSC0] = 0xFF; 2705141d415SCédric Le Goater s->regs[PCA9552_PWM0] = 0x80; 2715141d415SCédric Le Goater s->regs[PCA9552_PSC1] = 0xFF; 2725141d415SCédric Le Goater s->regs[PCA9552_PWM1] = 0x80; 2735141d415SCédric Le Goater s->regs[PCA9552_LS0] = 0x55; /* all OFF */ 2745141d415SCédric Le Goater s->regs[PCA9552_LS1] = 0x55; 2755141d415SCédric Le Goater s->regs[PCA9552_LS2] = 0x55; 2765141d415SCédric Le Goater s->regs[PCA9552_LS3] = 0x55; 2775141d415SCédric Le Goater 278*ec17228aSPhilippe Mathieu-Daudé pca955x_update_pin_input(s); 2795141d415SCédric Le Goater 2805141d415SCédric Le Goater s->pointer = 0xFF; 2815141d415SCédric Le Goater s->len = 0; 2825141d415SCédric Le Goater } 2835141d415SCédric Le Goater 284*ec17228aSPhilippe Mathieu-Daudé static void pca955x_initfn(Object *obj) 2855141d415SCédric Le Goater { 286*ec17228aSPhilippe Mathieu-Daudé PCA955xState *s = PCA955X(obj); 287a90d8f84SJoel Stanley int led; 2885141d415SCédric Le Goater 2895141d415SCédric Le Goater /* If support for the other PCA955X devices are implemented, these 2905141d415SCédric Le Goater * constant values might be part of class structure describing the 2915141d415SCédric Le Goater * PCA955X device 2925141d415SCédric Le Goater */ 2935141d415SCédric Le Goater s->max_reg = PCA9552_LS3; 2948208335bSPhilippe Mathieu-Daudé s->pin_count = 16; 295a90d8f84SJoel Stanley 2968208335bSPhilippe Mathieu-Daudé for (led = 0; led < s->pin_count; led++) { 297a90d8f84SJoel Stanley char *name; 298a90d8f84SJoel Stanley 299a90d8f84SJoel Stanley name = g_strdup_printf("led%d", led); 300*ec17228aSPhilippe Mathieu-Daudé object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, 301d2623129SMarkus Armbruster NULL, NULL); 302a90d8f84SJoel Stanley g_free(name); 303a90d8f84SJoel Stanley } 3045141d415SCédric Le Goater } 3055141d415SCédric Le Goater 3065141d415SCédric Le Goater static void pca9552_class_init(ObjectClass *klass, void *data) 3075141d415SCédric Le Goater { 3085141d415SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 3095141d415SCédric Le Goater I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 3105141d415SCédric Le Goater 311*ec17228aSPhilippe Mathieu-Daudé k->event = pca955x_event; 312*ec17228aSPhilippe Mathieu-Daudé k->recv = pca955x_recv; 313*ec17228aSPhilippe Mathieu-Daudé k->send = pca955x_send; 3145141d415SCédric Le Goater dc->reset = pca9552_reset; 3155141d415SCédric Le Goater dc->vmsd = &pca9552_vmstate; 3165141d415SCédric Le Goater } 3175141d415SCédric Le Goater 3185141d415SCédric Le Goater static const TypeInfo pca9552_info = { 3195141d415SCédric Le Goater .name = TYPE_PCA9552, 3205141d415SCédric Le Goater .parent = TYPE_I2C_SLAVE, 321*ec17228aSPhilippe Mathieu-Daudé .instance_init = pca955x_initfn, 322*ec17228aSPhilippe Mathieu-Daudé .instance_size = sizeof(PCA955xState), 3235141d415SCédric Le Goater .class_init = pca9552_class_init, 3245141d415SCédric Le Goater }; 3255141d415SCédric Le Goater 326*ec17228aSPhilippe Mathieu-Daudé static void pca955x_register_types(void) 3275141d415SCédric Le Goater { 3285141d415SCédric Le Goater type_register_static(&pca9552_info); 3295141d415SCédric Le Goater } 3305141d415SCédric Le Goater 331*ec17228aSPhilippe Mathieu-Daudé type_init(pca955x_register_types) 332