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 if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_CHANGE)) { 86 for (i = 0; i < k->pin_count; i++) { 87 if (extract32(pins_changed, i, 1)) { 88 unsigned new_state = extract32(pins_status, i, 1); 89 90 /* 91 * We display the state using the PCA logic ("active-high"). 92 * This is not the state of the LED, which signal might be 93 * wired "active-low" on the board. 94 */ 95 trace_pca955x_gpio_change(s->description, i, 96 !new_state, new_state); 97 } 98 } 99 } 100 } 101 102 static void pca955x_update_pin_input(PCA955xState *s) 103 { 104 PCA955xClass *k = PCA955X_GET_CLASS(s); 105 int i; 106 107 for (i = 0; i < k->pin_count; i++) { 108 uint8_t input_reg = PCA9552_INPUT0 + (i / 8); 109 uint8_t input_shift = (i % 8); 110 uint8_t config = pca955x_pin_get_config(s, i); 111 112 switch (config) { 113 case PCA9552_LED_ON: 114 s->regs[input_reg] |= 1 << input_shift; 115 break; 116 case PCA9552_LED_OFF: 117 s->regs[input_reg] &= ~(1 << input_shift); 118 break; 119 case PCA9552_LED_PWM0: 120 case PCA9552_LED_PWM1: 121 /* TODO */ 122 default: 123 break; 124 } 125 } 126 } 127 128 static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) 129 { 130 switch (reg) { 131 case PCA9552_INPUT0: 132 case PCA9552_INPUT1: 133 case PCA9552_PSC0: 134 case PCA9552_PWM0: 135 case PCA9552_PSC1: 136 case PCA9552_PWM1: 137 case PCA9552_LS0: 138 case PCA9552_LS1: 139 case PCA9552_LS2: 140 case PCA9552_LS3: 141 return s->regs[reg]; 142 default: 143 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", 144 __func__, reg); 145 return 0xFF; 146 } 147 } 148 149 static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) 150 { 151 uint16_t pins_status; 152 153 switch (reg) { 154 case PCA9552_PSC0: 155 case PCA9552_PWM0: 156 case PCA9552_PSC1: 157 case PCA9552_PWM1: 158 s->regs[reg] = data; 159 break; 160 161 case PCA9552_LS0: 162 case PCA9552_LS1: 163 case PCA9552_LS2: 164 case PCA9552_LS3: 165 pins_status = pca955x_pins_get_status(s); 166 s->regs[reg] = data; 167 pca955x_update_pin_input(s); 168 pca955x_display_pins_status(s, pins_status); 169 break; 170 171 case PCA9552_INPUT0: 172 case PCA9552_INPUT1: 173 default: 174 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", 175 __func__, reg); 176 } 177 } 178 179 /* 180 * When Auto-Increment is on, the register address is incremented 181 * after each byte is sent to or received by the device. The index 182 * rollovers to 0 when the maximum register address is reached. 183 */ 184 static void pca955x_autoinc(PCA955xState *s) 185 { 186 PCA955xClass *k = PCA955X_GET_CLASS(s); 187 188 if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { 189 uint8_t reg = s->pointer & 0xf; 190 191 reg = (reg + 1) % (k->max_reg + 1); 192 s->pointer = reg | PCA9552_AUTOINC; 193 } 194 } 195 196 static uint8_t pca955x_recv(I2CSlave *i2c) 197 { 198 PCA955xState *s = PCA955X(i2c); 199 uint8_t ret; 200 201 ret = pca955x_read(s, s->pointer & 0xf); 202 203 /* 204 * From the Specs: 205 * 206 * Important Note: When a Read sequence is initiated and the 207 * AI bit is set to Logic Level 1, the Read Sequence MUST 208 * start by a register different from 0. 209 * 210 * I don't know what should be done in this case, so throw an 211 * error. 212 */ 213 if (s->pointer == PCA9552_AUTOINC) { 214 qemu_log_mask(LOG_GUEST_ERROR, 215 "%s: Autoincrement read starting with register 0\n", 216 __func__); 217 } 218 219 pca955x_autoinc(s); 220 221 return ret; 222 } 223 224 static int pca955x_send(I2CSlave *i2c, uint8_t data) 225 { 226 PCA955xState *s = PCA955X(i2c); 227 228 /* First byte sent by is the register address */ 229 if (s->len == 0) { 230 s->pointer = data; 231 s->len++; 232 } else { 233 pca955x_write(s, s->pointer & 0xf, data); 234 235 pca955x_autoinc(s); 236 } 237 238 return 0; 239 } 240 241 static int pca955x_event(I2CSlave *i2c, enum i2c_event event) 242 { 243 PCA955xState *s = PCA955X(i2c); 244 245 s->len = 0; 246 return 0; 247 } 248 249 static void pca955x_get_led(Object *obj, Visitor *v, const char *name, 250 void *opaque, Error **errp) 251 { 252 PCA955xClass *k = PCA955X_GET_CLASS(obj); 253 PCA955xState *s = PCA955X(obj); 254 int led, rc, reg; 255 uint8_t state; 256 257 rc = sscanf(name, "led%2d", &led); 258 if (rc != 1) { 259 error_setg(errp, "%s: error reading %s", __func__, name); 260 return; 261 } 262 if (led < 0 || led > k->pin_count) { 263 error_setg(errp, "%s invalid led %s", __func__, name); 264 return; 265 } 266 /* 267 * Get the LSx register as the qom interface should expose the device 268 * state, not the modeled 'input line' behaviour which would come from 269 * reading the INPUTx reg 270 */ 271 reg = PCA9552_LS0 + led / 4; 272 state = (pca955x_read(s, reg) >> (led % 8)) & 0x3; 273 visit_type_str(v, name, (char **)&led_state[state], errp); 274 } 275 276 /* 277 * Return an LED selector register value based on an existing one, with 278 * the appropriate 2-bit state value set for the given LED number (0-3). 279 */ 280 static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state) 281 { 282 return (oldval & (~(0x3 << (led_num << 1)))) | 283 ((state & 0x3) << (led_num << 1)); 284 } 285 286 static void pca955x_set_led(Object *obj, Visitor *v, const char *name, 287 void *opaque, Error **errp) 288 { 289 PCA955xClass *k = PCA955X_GET_CLASS(obj); 290 PCA955xState *s = PCA955X(obj); 291 Error *local_err = NULL; 292 int led, rc, reg, val; 293 uint8_t state; 294 char *state_str; 295 296 visit_type_str(v, name, &state_str, &local_err); 297 if (local_err) { 298 error_propagate(errp, local_err); 299 return; 300 } 301 rc = sscanf(name, "led%2d", &led); 302 if (rc != 1) { 303 error_setg(errp, "%s: error reading %s", __func__, name); 304 return; 305 } 306 if (led < 0 || led > k->pin_count) { 307 error_setg(errp, "%s invalid led %s", __func__, name); 308 return; 309 } 310 311 for (state = 0; state < ARRAY_SIZE(led_state); state++) { 312 if (!strcmp(state_str, led_state[state])) { 313 break; 314 } 315 } 316 if (state >= ARRAY_SIZE(led_state)) { 317 error_setg(errp, "%s invalid led state %s", __func__, state_str); 318 return; 319 } 320 321 reg = PCA9552_LS0 + led / 4; 322 val = pca955x_read(s, reg); 323 val = pca955x_ledsel(val, led % 4, state); 324 pca955x_write(s, reg, val); 325 } 326 327 static const VMStateDescription pca9552_vmstate = { 328 .name = "PCA9552", 329 .version_id = 0, 330 .minimum_version_id = 0, 331 .fields = (VMStateField[]) { 332 VMSTATE_UINT8(len, PCA955xState), 333 VMSTATE_UINT8(pointer, PCA955xState), 334 VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), 335 VMSTATE_I2C_SLAVE(i2c, PCA955xState), 336 VMSTATE_END_OF_LIST() 337 } 338 }; 339 340 static void pca9552_reset(DeviceState *dev) 341 { 342 PCA955xState *s = PCA955X(dev); 343 344 s->regs[PCA9552_PSC0] = 0xFF; 345 s->regs[PCA9552_PWM0] = 0x80; 346 s->regs[PCA9552_PSC1] = 0xFF; 347 s->regs[PCA9552_PWM1] = 0x80; 348 s->regs[PCA9552_LS0] = 0x55; /* all OFF */ 349 s->regs[PCA9552_LS1] = 0x55; 350 s->regs[PCA9552_LS2] = 0x55; 351 s->regs[PCA9552_LS3] = 0x55; 352 353 pca955x_update_pin_input(s); 354 355 s->pointer = 0xFF; 356 s->len = 0; 357 } 358 359 static void pca955x_initfn(Object *obj) 360 { 361 PCA955xClass *k = PCA955X_GET_CLASS(obj); 362 int led; 363 364 assert(k->pin_count <= PCA955X_PIN_COUNT_MAX); 365 for (led = 0; led < k->pin_count; led++) { 366 char *name; 367 368 name = g_strdup_printf("led%d", led); 369 object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, 370 NULL, NULL); 371 g_free(name); 372 } 373 } 374 375 static void pca955x_realize(DeviceState *dev, Error **errp) 376 { 377 PCA955xState *s = PCA955X(dev); 378 379 if (!s->description) { 380 s->description = g_strdup("pca-unspecified"); 381 } 382 } 383 384 static Property pca955x_properties[] = { 385 DEFINE_PROP_STRING("description", PCA955xState, description), 386 DEFINE_PROP_END_OF_LIST(), 387 }; 388 389 static void pca955x_class_init(ObjectClass *klass, void *data) 390 { 391 DeviceClass *dc = DEVICE_CLASS(klass); 392 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 393 394 k->event = pca955x_event; 395 k->recv = pca955x_recv; 396 k->send = pca955x_send; 397 dc->realize = pca955x_realize; 398 device_class_set_props(dc, pca955x_properties); 399 } 400 401 static const TypeInfo pca955x_info = { 402 .name = TYPE_PCA955X, 403 .parent = TYPE_I2C_SLAVE, 404 .instance_init = pca955x_initfn, 405 .instance_size = sizeof(PCA955xState), 406 .class_init = pca955x_class_init, 407 .abstract = true, 408 }; 409 410 static void pca9552_class_init(ObjectClass *oc, void *data) 411 { 412 DeviceClass *dc = DEVICE_CLASS(oc); 413 PCA955xClass *pc = PCA955X_CLASS(oc); 414 415 dc->reset = pca9552_reset; 416 dc->vmsd = &pca9552_vmstate; 417 pc->max_reg = PCA9552_LS3; 418 pc->pin_count = 16; 419 } 420 421 static const TypeInfo pca9552_info = { 422 .name = TYPE_PCA9552, 423 .parent = TYPE_PCA955X, 424 .class_init = pca9552_class_init, 425 }; 426 427 static void pca955x_register_types(void) 428 { 429 type_register_static(&pca955x_info); 430 type_register_static(&pca9552_info); 431 } 432 433 type_init(pca955x_register_types) 434