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