1 /* 2 * PCA9552 I2C LED blinker 3 * 4 * https://www.nxp.com/docs/en/application-note/AN264.pdf 5 * 6 * Copyright (c) 2017-2018, IBM Corporation. 7 * Copyright (c) 2020 Philippe Mathieu-Daudé 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or 10 * later. See the COPYING file in the top-level directory. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu/log.h" 15 #include "qemu/module.h" 16 #include "qemu/bitops.h" 17 #include "hw/qdev-properties.h" 18 #include "hw/misc/pca9552.h" 19 #include "hw/misc/pca9552_regs.h" 20 #include "migration/vmstate.h" 21 #include "qapi/error.h" 22 #include "qapi/visitor.h" 23 #include "trace.h" 24 25 typedef struct PCA955xClass { 26 /*< private >*/ 27 I2CSlaveClass parent_class; 28 /*< public >*/ 29 30 uint8_t pin_count; 31 uint8_t max_reg; 32 } PCA955xClass; 33 34 #define PCA955X_CLASS(klass) \ 35 OBJECT_CLASS_CHECK(PCA955xClass, (klass), TYPE_PCA955X) 36 #define PCA955X_GET_CLASS(obj) \ 37 OBJECT_GET_CLASS(PCA955xClass, (obj), TYPE_PCA955X) 38 39 #define PCA9552_LED_ON 0x0 40 #define PCA9552_LED_OFF 0x1 41 #define PCA9552_LED_PWM0 0x2 42 #define PCA9552_LED_PWM1 0x3 43 44 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; 45 46 static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin) 47 { 48 uint8_t reg = PCA9552_LS0 + (pin / 4); 49 uint8_t shift = (pin % 4) << 1; 50 51 return extract32(s->regs[reg], shift, 2); 52 } 53 54 /* Return INPUT status (bit #N belongs to GPIO #N) */ 55 static uint16_t pca955x_pins_get_status(PCA955xState *s) 56 { 57 return (s->regs[PCA9552_INPUT1] << 8) | s->regs[PCA9552_INPUT0]; 58 } 59 60 static void pca955x_display_pins_status(PCA955xState *s, 61 uint16_t previous_pins_status) 62 { 63 PCA955xClass *k = PCA955X_GET_CLASS(s); 64 uint16_t pins_status, pins_changed; 65 int i; 66 67 pins_status = pca955x_pins_get_status(s); 68 pins_changed = previous_pins_status ^ pins_status; 69 if (!pins_changed) { 70 return; 71 } 72 if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) { 73 char *buf = g_newa(char, k->pin_count + 1); 74 75 for (i = 0; i < k->pin_count; i++) { 76 if (extract32(pins_status, i, 1)) { 77 buf[i] = '*'; 78 } else { 79 buf[i] = '.'; 80 } 81 } 82 buf[i] = '\0'; 83 trace_pca955x_gpio_status(s->description, buf); 84 } 85 } 86 87 static void pca955x_update_pin_input(PCA955xState *s) 88 { 89 PCA955xClass *k = PCA955X_GET_CLASS(s); 90 int i; 91 92 for (i = 0; i < k->pin_count; i++) { 93 uint8_t input_reg = PCA9552_INPUT0 + (i / 8); 94 uint8_t input_shift = (i % 8); 95 uint8_t config = pca955x_pin_get_config(s, i); 96 97 switch (config) { 98 case PCA9552_LED_ON: 99 s->regs[input_reg] |= 1 << input_shift; 100 break; 101 case PCA9552_LED_OFF: 102 s->regs[input_reg] &= ~(1 << input_shift); 103 break; 104 case PCA9552_LED_PWM0: 105 case PCA9552_LED_PWM1: 106 /* TODO */ 107 default: 108 break; 109 } 110 } 111 } 112 113 static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) 114 { 115 switch (reg) { 116 case PCA9552_INPUT0: 117 case PCA9552_INPUT1: 118 case PCA9552_PSC0: 119 case PCA9552_PWM0: 120 case PCA9552_PSC1: 121 case PCA9552_PWM1: 122 case PCA9552_LS0: 123 case PCA9552_LS1: 124 case PCA9552_LS2: 125 case PCA9552_LS3: 126 return s->regs[reg]; 127 default: 128 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", 129 __func__, reg); 130 return 0xFF; 131 } 132 } 133 134 static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) 135 { 136 uint16_t pins_status; 137 138 switch (reg) { 139 case PCA9552_PSC0: 140 case PCA9552_PWM0: 141 case PCA9552_PSC1: 142 case PCA9552_PWM1: 143 s->regs[reg] = data; 144 break; 145 146 case PCA9552_LS0: 147 case PCA9552_LS1: 148 case PCA9552_LS2: 149 case PCA9552_LS3: 150 pins_status = pca955x_pins_get_status(s); 151 s->regs[reg] = data; 152 pca955x_update_pin_input(s); 153 pca955x_display_pins_status(s, pins_status); 154 break; 155 156 case PCA9552_INPUT0: 157 case PCA9552_INPUT1: 158 default: 159 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", 160 __func__, reg); 161 } 162 } 163 164 /* 165 * When Auto-Increment is on, the register address is incremented 166 * after each byte is sent to or received by the device. The index 167 * rollovers to 0 when the maximum register address is reached. 168 */ 169 static void pca955x_autoinc(PCA955xState *s) 170 { 171 PCA955xClass *k = PCA955X_GET_CLASS(s); 172 173 if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { 174 uint8_t reg = s->pointer & 0xf; 175 176 reg = (reg + 1) % (k->max_reg + 1); 177 s->pointer = reg | PCA9552_AUTOINC; 178 } 179 } 180 181 static uint8_t pca955x_recv(I2CSlave *i2c) 182 { 183 PCA955xState *s = PCA955X(i2c); 184 uint8_t ret; 185 186 ret = pca955x_read(s, s->pointer & 0xf); 187 188 /* 189 * From the Specs: 190 * 191 * Important Note: When a Read sequence is initiated and the 192 * AI bit is set to Logic Level 1, the Read Sequence MUST 193 * start by a register different from 0. 194 * 195 * I don't know what should be done in this case, so throw an 196 * error. 197 */ 198 if (s->pointer == PCA9552_AUTOINC) { 199 qemu_log_mask(LOG_GUEST_ERROR, 200 "%s: Autoincrement read starting with register 0\n", 201 __func__); 202 } 203 204 pca955x_autoinc(s); 205 206 return ret; 207 } 208 209 static int pca955x_send(I2CSlave *i2c, uint8_t data) 210 { 211 PCA955xState *s = PCA955X(i2c); 212 213 /* First byte sent by is the register address */ 214 if (s->len == 0) { 215 s->pointer = data; 216 s->len++; 217 } else { 218 pca955x_write(s, s->pointer & 0xf, data); 219 220 pca955x_autoinc(s); 221 } 222 223 return 0; 224 } 225 226 static int pca955x_event(I2CSlave *i2c, enum i2c_event event) 227 { 228 PCA955xState *s = PCA955X(i2c); 229 230 s->len = 0; 231 return 0; 232 } 233 234 static void pca955x_get_led(Object *obj, Visitor *v, const char *name, 235 void *opaque, Error **errp) 236 { 237 PCA955xClass *k = PCA955X_GET_CLASS(obj); 238 PCA955xState *s = PCA955X(obj); 239 int led, rc, reg; 240 uint8_t state; 241 242 rc = sscanf(name, "led%2d", &led); 243 if (rc != 1) { 244 error_setg(errp, "%s: error reading %s", __func__, name); 245 return; 246 } 247 if (led < 0 || led > k->pin_count) { 248 error_setg(errp, "%s invalid led %s", __func__, name); 249 return; 250 } 251 /* 252 * Get the LSx register as the qom interface should expose the device 253 * state, not the modeled 'input line' behaviour which would come from 254 * reading the INPUTx reg 255 */ 256 reg = PCA9552_LS0 + led / 4; 257 state = (pca955x_read(s, reg) >> (led % 8)) & 0x3; 258 visit_type_str(v, name, (char **)&led_state[state], errp); 259 } 260 261 /* 262 * Return an LED selector register value based on an existing one, with 263 * the appropriate 2-bit state value set for the given LED number (0-3). 264 */ 265 static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state) 266 { 267 return (oldval & (~(0x3 << (led_num << 1)))) | 268 ((state & 0x3) << (led_num << 1)); 269 } 270 271 static void pca955x_set_led(Object *obj, Visitor *v, const char *name, 272 void *opaque, Error **errp) 273 { 274 PCA955xClass *k = PCA955X_GET_CLASS(obj); 275 PCA955xState *s = PCA955X(obj); 276 Error *local_err = NULL; 277 int led, rc, reg, val; 278 uint8_t state; 279 char *state_str; 280 281 visit_type_str(v, name, &state_str, &local_err); 282 if (local_err) { 283 error_propagate(errp, local_err); 284 return; 285 } 286 rc = sscanf(name, "led%2d", &led); 287 if (rc != 1) { 288 error_setg(errp, "%s: error reading %s", __func__, name); 289 return; 290 } 291 if (led < 0 || led > k->pin_count) { 292 error_setg(errp, "%s invalid led %s", __func__, name); 293 return; 294 } 295 296 for (state = 0; state < ARRAY_SIZE(led_state); state++) { 297 if (!strcmp(state_str, led_state[state])) { 298 break; 299 } 300 } 301 if (state >= ARRAY_SIZE(led_state)) { 302 error_setg(errp, "%s invalid led state %s", __func__, state_str); 303 return; 304 } 305 306 reg = PCA9552_LS0 + led / 4; 307 val = pca955x_read(s, reg); 308 val = pca955x_ledsel(val, led % 4, state); 309 pca955x_write(s, reg, val); 310 } 311 312 static const VMStateDescription pca9552_vmstate = { 313 .name = "PCA9552", 314 .version_id = 0, 315 .minimum_version_id = 0, 316 .fields = (VMStateField[]) { 317 VMSTATE_UINT8(len, PCA955xState), 318 VMSTATE_UINT8(pointer, PCA955xState), 319 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), 320 VMSTATE_I2C_SLAVE(i2c, PCA955xState), 321 VMSTATE_END_OF_LIST() 322 } 323 }; 324 325 static void pca9552_reset(DeviceState *dev) 326 { 327 PCA955xState *s = PCA955X(dev); 328 329 s->regs[PCA9552_PSC0] = 0xFF; 330 s->regs[PCA9552_PWM0] = 0x80; 331 s->regs[PCA9552_PSC1] = 0xFF; 332 s->regs[PCA9552_PWM1] = 0x80; 333 s->regs[PCA9552_LS0] = 0x55; /* all OFF */ 334 s->regs[PCA9552_LS1] = 0x55; 335 s->regs[PCA9552_LS2] = 0x55; 336 s->regs[PCA9552_LS3] = 0x55; 337 338 pca955x_update_pin_input(s); 339 340 s->pointer = 0xFF; 341 s->len = 0; 342 } 343 344 static void pca955x_initfn(Object *obj) 345 { 346 PCA955xClass *k = PCA955X_GET_CLASS(obj); 347 int led; 348 349 assert(k->pin_count <= PCA955X_PIN_COUNT_MAX); 350 for (led = 0; led < k->pin_count; led++) { 351 char *name; 352 353 name = g_strdup_printf("led%d", led); 354 object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, 355 NULL, NULL); 356 g_free(name); 357 } 358 } 359 360 static void pca955x_realize(DeviceState *dev, Error **errp) 361 { 362 PCA955xState *s = PCA955X(dev); 363 364 if (!s->description) { 365 s->description = g_strdup("pca-unspecified"); 366 } 367 } 368 369 static Property pca955x_properties[] = { 370 DEFINE_PROP_STRING("description", PCA955xState, description), 371 DEFINE_PROP_END_OF_LIST(), 372 }; 373 374 static void pca955x_class_init(ObjectClass *klass, void *data) 375 { 376 DeviceClass *dc = DEVICE_CLASS(klass); 377 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 378 379 k->event = pca955x_event; 380 k->recv = pca955x_recv; 381 k->send = pca955x_send; 382 dc->realize = pca955x_realize; 383 device_class_set_props(dc, pca955x_properties); 384 } 385 386 static const TypeInfo pca955x_info = { 387 .name = TYPE_PCA955X, 388 .parent = TYPE_I2C_SLAVE, 389 .instance_init = pca955x_initfn, 390 .instance_size = sizeof(PCA955xState), 391 .class_init = pca955x_class_init, 392 .abstract = true, 393 }; 394 395 static void pca9552_class_init(ObjectClass *oc, void *data) 396 { 397 DeviceClass *dc = DEVICE_CLASS(oc); 398 PCA955xClass *pc = PCA955X_CLASS(oc); 399 400 dc->reset = pca9552_reset; 401 dc->vmsd = &pca9552_vmstate; 402 pc->max_reg = PCA9552_LS3; 403 pc->pin_count = 16; 404 } 405 406 static const TypeInfo pca9552_info = { 407 .name = TYPE_PCA9552, 408 .parent = TYPE_PCA955X, 409 .class_init = pca9552_class_init, 410 }; 411 412 static void pca955x_register_types(void) 413 { 414 type_register_static(&pca955x_info); 415 type_register_static(&pca9552_info); 416 } 417 418 type_init(pca955x_register_types) 419