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 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or 9 * later. See the COPYING file in the top-level directory. 10 */ 11 12 #include "qemu/osdep.h" 13 #include "qemu/log.h" 14 #include "hw/hw.h" 15 #include "hw/misc/pca9552.h" 16 #include "hw/misc/pca9552_regs.h" 17 18 #define PCA9552_LED_ON 0x0 19 #define PCA9552_LED_OFF 0x1 20 #define PCA9552_LED_PWM0 0x2 21 #define PCA9552_LED_PWM1 0x3 22 23 static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin) 24 { 25 uint8_t reg = PCA9552_LS0 + (pin / 4); 26 uint8_t shift = (pin % 4) << 1; 27 28 return extract32(s->regs[reg], shift, 2); 29 } 30 31 static void pca9552_update_pin_input(PCA9552State *s) 32 { 33 int i; 34 35 for (i = 0; i < s->nr_leds; i++) { 36 uint8_t input_reg = PCA9552_INPUT0 + (i / 8); 37 uint8_t input_shift = (i % 8); 38 uint8_t config = pca9552_pin_get_config(s, i); 39 40 switch (config) { 41 case PCA9552_LED_ON: 42 s->regs[input_reg] |= 1 << input_shift; 43 break; 44 case PCA9552_LED_OFF: 45 s->regs[input_reg] &= ~(1 << input_shift); 46 break; 47 case PCA9552_LED_PWM0: 48 case PCA9552_LED_PWM1: 49 /* TODO */ 50 default: 51 break; 52 } 53 } 54 } 55 56 static uint8_t pca9552_read(PCA9552State *s, uint8_t reg) 57 { 58 switch (reg) { 59 case PCA9552_INPUT0: 60 case PCA9552_INPUT1: 61 case PCA9552_PSC0: 62 case PCA9552_PWM0: 63 case PCA9552_PSC1: 64 case PCA9552_PWM1: 65 case PCA9552_LS0: 66 case PCA9552_LS1: 67 case PCA9552_LS2: 68 case PCA9552_LS3: 69 return s->regs[reg]; 70 default: 71 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", 72 __func__, reg); 73 return 0xFF; 74 } 75 } 76 77 static void pca9552_write(PCA9552State *s, uint8_t reg, uint8_t data) 78 { 79 switch (reg) { 80 case PCA9552_PSC0: 81 case PCA9552_PWM0: 82 case PCA9552_PSC1: 83 case PCA9552_PWM1: 84 s->regs[reg] = data; 85 break; 86 87 case PCA9552_LS0: 88 case PCA9552_LS1: 89 case PCA9552_LS2: 90 case PCA9552_LS3: 91 s->regs[reg] = data; 92 pca9552_update_pin_input(s); 93 break; 94 95 case PCA9552_INPUT0: 96 case PCA9552_INPUT1: 97 default: 98 qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", 99 __func__, reg); 100 } 101 } 102 103 /* 104 * When Auto-Increment is on, the register address is incremented 105 * after each byte is sent to or received by the device. The index 106 * rollovers to 0 when the maximum register address is reached. 107 */ 108 static void pca9552_autoinc(PCA9552State *s) 109 { 110 if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { 111 uint8_t reg = s->pointer & 0xf; 112 113 reg = (reg + 1) % (s->max_reg + 1); 114 s->pointer = reg | PCA9552_AUTOINC; 115 } 116 } 117 118 static uint8_t pca9552_recv(I2CSlave *i2c) 119 { 120 PCA9552State *s = PCA9552(i2c); 121 uint8_t ret; 122 123 ret = pca9552_read(s, s->pointer & 0xf); 124 125 /* 126 * From the Specs: 127 * 128 * Important Note: When a Read sequence is initiated and the 129 * AI bit is set to Logic Level 1, the Read Sequence MUST 130 * start by a register different from 0. 131 * 132 * I don't know what should be done in this case, so throw an 133 * error. 134 */ 135 if (s->pointer == PCA9552_AUTOINC) { 136 qemu_log_mask(LOG_GUEST_ERROR, 137 "%s: Autoincrement read starting with register 0\n", 138 __func__); 139 } 140 141 pca9552_autoinc(s); 142 143 return ret; 144 } 145 146 static int pca9552_send(I2CSlave *i2c, uint8_t data) 147 { 148 PCA9552State *s = PCA9552(i2c); 149 150 /* First byte sent by is the register address */ 151 if (s->len == 0) { 152 s->pointer = data; 153 s->len++; 154 } else { 155 pca9552_write(s, s->pointer & 0xf, data); 156 157 pca9552_autoinc(s); 158 } 159 160 return 0; 161 } 162 163 static int pca9552_event(I2CSlave *i2c, enum i2c_event event) 164 { 165 PCA9552State *s = PCA9552(i2c); 166 167 s->len = 0; 168 return 0; 169 } 170 171 static const VMStateDescription pca9552_vmstate = { 172 .name = "PCA9552", 173 .version_id = 0, 174 .minimum_version_id = 0, 175 .fields = (VMStateField[]) { 176 VMSTATE_UINT8(len, PCA9552State), 177 VMSTATE_UINT8(pointer, PCA9552State), 178 VMSTATE_UINT8_ARRAY(regs, PCA9552State, PCA9552_NR_REGS), 179 VMSTATE_I2C_SLAVE(i2c, PCA9552State), 180 VMSTATE_END_OF_LIST() 181 } 182 }; 183 184 static void pca9552_reset(DeviceState *dev) 185 { 186 PCA9552State *s = PCA9552(dev); 187 188 s->regs[PCA9552_PSC0] = 0xFF; 189 s->regs[PCA9552_PWM0] = 0x80; 190 s->regs[PCA9552_PSC1] = 0xFF; 191 s->regs[PCA9552_PWM1] = 0x80; 192 s->regs[PCA9552_LS0] = 0x55; /* all OFF */ 193 s->regs[PCA9552_LS1] = 0x55; 194 s->regs[PCA9552_LS2] = 0x55; 195 s->regs[PCA9552_LS3] = 0x55; 196 197 pca9552_update_pin_input(s); 198 199 s->pointer = 0xFF; 200 s->len = 0; 201 } 202 203 static void pca9552_initfn(Object *obj) 204 { 205 PCA9552State *s = PCA9552(obj); 206 207 /* If support for the other PCA955X devices are implemented, these 208 * constant values might be part of class structure describing the 209 * PCA955X device 210 */ 211 s->max_reg = PCA9552_LS3; 212 s->nr_leds = 16; 213 } 214 215 static void pca9552_class_init(ObjectClass *klass, void *data) 216 { 217 DeviceClass *dc = DEVICE_CLASS(klass); 218 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 219 220 k->event = pca9552_event; 221 k->recv = pca9552_recv; 222 k->send = pca9552_send; 223 dc->reset = pca9552_reset; 224 dc->vmsd = &pca9552_vmstate; 225 } 226 227 static const TypeInfo pca9552_info = { 228 .name = TYPE_PCA9552, 229 .parent = TYPE_I2C_SLAVE, 230 .instance_init = pca9552_initfn, 231 .instance_size = sizeof(PCA9552State), 232 .class_init = pca9552_class_init, 233 }; 234 235 static void pca9552_register_types(void) 236 { 237 type_register_static(&pca9552_info); 238 } 239 240 type_init(pca9552_register_types) 241