1de0c7d54SGlenn Miles /*
2de0c7d54SGlenn Miles * PCA9554 I/O port
3de0c7d54SGlenn Miles *
4de0c7d54SGlenn Miles * Copyright (c) 2023, IBM Corporation.
5de0c7d54SGlenn Miles *
6de0c7d54SGlenn Miles * SPDX-License-Identifier: GPL-2.0-or-later
7de0c7d54SGlenn Miles */
8de0c7d54SGlenn Miles
9de0c7d54SGlenn Miles #include "qemu/osdep.h"
10de0c7d54SGlenn Miles #include "qemu/log.h"
11de0c7d54SGlenn Miles #include "qemu/module.h"
12de0c7d54SGlenn Miles #include "qemu/bitops.h"
13de0c7d54SGlenn Miles #include "hw/qdev-properties.h"
146328d8ffSCédric Le Goater #include "hw/gpio/pca9554.h"
156328d8ffSCédric Le Goater #include "hw/gpio/pca9554_regs.h"
16de0c7d54SGlenn Miles #include "hw/irq.h"
17de0c7d54SGlenn Miles #include "migration/vmstate.h"
18de0c7d54SGlenn Miles #include "qapi/error.h"
19de0c7d54SGlenn Miles #include "qapi/visitor.h"
20de0c7d54SGlenn Miles #include "trace.h"
21de0c7d54SGlenn Miles #include "qom/object.h"
22de0c7d54SGlenn Miles
23de0c7d54SGlenn Miles struct PCA9554Class {
24de0c7d54SGlenn Miles /*< private >*/
25de0c7d54SGlenn Miles I2CSlaveClass parent_class;
26de0c7d54SGlenn Miles /*< public >*/
27de0c7d54SGlenn Miles };
28de0c7d54SGlenn Miles typedef struct PCA9554Class PCA9554Class;
29de0c7d54SGlenn Miles
30de0c7d54SGlenn Miles DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
31de0c7d54SGlenn Miles TYPE_PCA9554)
32de0c7d54SGlenn Miles
33de0c7d54SGlenn Miles #define PCA9554_PIN_LOW 0x0
34de0c7d54SGlenn Miles #define PCA9554_PIN_HIZ 0x1
35de0c7d54SGlenn Miles
36de0c7d54SGlenn Miles static const char *pin_state[] = {"low", "high"};
37de0c7d54SGlenn Miles
pca9554_update_pin_input(PCA9554State * s)38de0c7d54SGlenn Miles static void pca9554_update_pin_input(PCA9554State *s)
39de0c7d54SGlenn Miles {
40de0c7d54SGlenn Miles int i;
41de0c7d54SGlenn Miles uint8_t config = s->regs[PCA9554_CONFIG];
42de0c7d54SGlenn Miles uint8_t output = s->regs[PCA9554_OUTPUT];
43de0c7d54SGlenn Miles uint8_t internal_state = config | output;
44de0c7d54SGlenn Miles
45de0c7d54SGlenn Miles for (i = 0; i < PCA9554_PIN_COUNT; i++) {
46de0c7d54SGlenn Miles uint8_t bit_mask = 1 << i;
47de0c7d54SGlenn Miles uint8_t internal_pin_state = (internal_state >> i) & 0x1;
48de0c7d54SGlenn Miles uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
49de0c7d54SGlenn Miles uint8_t new_value;
50de0c7d54SGlenn Miles
51de0c7d54SGlenn Miles switch (internal_pin_state) {
52de0c7d54SGlenn Miles case PCA9554_PIN_LOW:
53de0c7d54SGlenn Miles s->regs[PCA9554_INPUT] &= ~bit_mask;
54de0c7d54SGlenn Miles break;
55de0c7d54SGlenn Miles case PCA9554_PIN_HIZ:
56de0c7d54SGlenn Miles /*
57de0c7d54SGlenn Miles * pullup sets it to a logical 1 unless
58de0c7d54SGlenn Miles * external device drives it low.
59de0c7d54SGlenn Miles */
60de0c7d54SGlenn Miles if (s->ext_state[i] == PCA9554_PIN_LOW) {
61de0c7d54SGlenn Miles s->regs[PCA9554_INPUT] &= ~bit_mask;
62de0c7d54SGlenn Miles } else {
63de0c7d54SGlenn Miles s->regs[PCA9554_INPUT] |= bit_mask;
64de0c7d54SGlenn Miles }
65de0c7d54SGlenn Miles break;
66de0c7d54SGlenn Miles default:
67de0c7d54SGlenn Miles break;
68de0c7d54SGlenn Miles }
69de0c7d54SGlenn Miles
70de0c7d54SGlenn Miles /* update irq state only if pin state changed */
71de0c7d54SGlenn Miles new_value = s->regs[PCA9554_INPUT] & bit_mask;
72de0c7d54SGlenn Miles if (new_value != old_value) {
73de0c7d54SGlenn Miles if (new_value) {
74de0c7d54SGlenn Miles /* changed from 0 to 1 */
75de0c7d54SGlenn Miles qemu_set_irq(s->gpio_out[i], 1);
76de0c7d54SGlenn Miles } else {
77de0c7d54SGlenn Miles /* changed from 1 to 0 */
78de0c7d54SGlenn Miles qemu_set_irq(s->gpio_out[i], 0);
79de0c7d54SGlenn Miles }
80de0c7d54SGlenn Miles }
81de0c7d54SGlenn Miles }
82de0c7d54SGlenn Miles }
83de0c7d54SGlenn Miles
pca9554_read(PCA9554State * s,uint8_t reg)84de0c7d54SGlenn Miles static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
85de0c7d54SGlenn Miles {
86de0c7d54SGlenn Miles switch (reg) {
87de0c7d54SGlenn Miles case PCA9554_INPUT:
88de0c7d54SGlenn Miles return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
89de0c7d54SGlenn Miles case PCA9554_OUTPUT:
90de0c7d54SGlenn Miles case PCA9554_POLARITY:
91de0c7d54SGlenn Miles case PCA9554_CONFIG:
92de0c7d54SGlenn Miles return s->regs[reg];
93de0c7d54SGlenn Miles default:
94de0c7d54SGlenn Miles qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
95de0c7d54SGlenn Miles __func__, reg);
96de0c7d54SGlenn Miles return 0xFF;
97de0c7d54SGlenn Miles }
98de0c7d54SGlenn Miles }
99de0c7d54SGlenn Miles
pca9554_write(PCA9554State * s,uint8_t reg,uint8_t data)100de0c7d54SGlenn Miles static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
101de0c7d54SGlenn Miles {
102de0c7d54SGlenn Miles switch (reg) {
103de0c7d54SGlenn Miles case PCA9554_OUTPUT:
104de0c7d54SGlenn Miles case PCA9554_CONFIG:
105de0c7d54SGlenn Miles s->regs[reg] = data;
106de0c7d54SGlenn Miles pca9554_update_pin_input(s);
107de0c7d54SGlenn Miles break;
108de0c7d54SGlenn Miles case PCA9554_POLARITY:
109de0c7d54SGlenn Miles s->regs[reg] = data;
110de0c7d54SGlenn Miles break;
111de0c7d54SGlenn Miles case PCA9554_INPUT:
112de0c7d54SGlenn Miles default:
113de0c7d54SGlenn Miles qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
114de0c7d54SGlenn Miles __func__, reg);
115de0c7d54SGlenn Miles }
116de0c7d54SGlenn Miles }
117de0c7d54SGlenn Miles
pca9554_recv(I2CSlave * i2c)118de0c7d54SGlenn Miles static uint8_t pca9554_recv(I2CSlave *i2c)
119de0c7d54SGlenn Miles {
120de0c7d54SGlenn Miles PCA9554State *s = PCA9554(i2c);
121de0c7d54SGlenn Miles
122720a0e41SMarkus Armbruster return pca9554_read(s, s->pointer & 0x3);
123de0c7d54SGlenn Miles }
124de0c7d54SGlenn Miles
pca9554_send(I2CSlave * i2c,uint8_t data)125de0c7d54SGlenn Miles static int pca9554_send(I2CSlave *i2c, uint8_t data)
126de0c7d54SGlenn Miles {
127de0c7d54SGlenn Miles PCA9554State *s = PCA9554(i2c);
128de0c7d54SGlenn Miles
129de0c7d54SGlenn Miles /* First byte sent by is the register address */
130de0c7d54SGlenn Miles if (s->len == 0) {
131de0c7d54SGlenn Miles s->pointer = data;
132de0c7d54SGlenn Miles s->len++;
133de0c7d54SGlenn Miles } else {
134de0c7d54SGlenn Miles pca9554_write(s, s->pointer & 0x3, data);
135de0c7d54SGlenn Miles }
136de0c7d54SGlenn Miles
137de0c7d54SGlenn Miles return 0;
138de0c7d54SGlenn Miles }
139de0c7d54SGlenn Miles
pca9554_event(I2CSlave * i2c,enum i2c_event event)140de0c7d54SGlenn Miles static int pca9554_event(I2CSlave *i2c, enum i2c_event event)
141de0c7d54SGlenn Miles {
142de0c7d54SGlenn Miles PCA9554State *s = PCA9554(i2c);
143de0c7d54SGlenn Miles
144de0c7d54SGlenn Miles s->len = 0;
145de0c7d54SGlenn Miles return 0;
146de0c7d54SGlenn Miles }
147de0c7d54SGlenn Miles
pca9554_get_pin(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)148de0c7d54SGlenn Miles static void pca9554_get_pin(Object *obj, Visitor *v, const char *name,
149de0c7d54SGlenn Miles void *opaque, Error **errp)
150de0c7d54SGlenn Miles {
151de0c7d54SGlenn Miles PCA9554State *s = PCA9554(obj);
152de0c7d54SGlenn Miles int pin, rc;
153de0c7d54SGlenn Miles uint8_t state;
154de0c7d54SGlenn Miles
155de0c7d54SGlenn Miles rc = sscanf(name, "pin%2d", &pin);
156de0c7d54SGlenn Miles if (rc != 1) {
157de0c7d54SGlenn Miles error_setg(errp, "%s: error reading %s", __func__, name);
158de0c7d54SGlenn Miles return;
159de0c7d54SGlenn Miles }
160c67f7580SPeter Maydell if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
161de0c7d54SGlenn Miles error_setg(errp, "%s invalid pin %s", __func__, name);
162de0c7d54SGlenn Miles return;
163de0c7d54SGlenn Miles }
164de0c7d54SGlenn Miles
165de0c7d54SGlenn Miles state = pca9554_read(s, PCA9554_CONFIG);
166de0c7d54SGlenn Miles state |= pca9554_read(s, PCA9554_OUTPUT);
167de0c7d54SGlenn Miles state = (state >> pin) & 0x1;
168de0c7d54SGlenn Miles visit_type_str(v, name, (char **)&pin_state[state], errp);
169de0c7d54SGlenn Miles }
170de0c7d54SGlenn Miles
pca9554_set_pin(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)171de0c7d54SGlenn Miles static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
172de0c7d54SGlenn Miles void *opaque, Error **errp)
173de0c7d54SGlenn Miles {
174de0c7d54SGlenn Miles PCA9554State *s = PCA9554(obj);
175de0c7d54SGlenn Miles int pin, rc, val;
176de0c7d54SGlenn Miles uint8_t state, mask;
177de0c7d54SGlenn Miles char *state_str;
178de0c7d54SGlenn Miles
179de0c7d54SGlenn Miles if (!visit_type_str(v, name, &state_str, errp)) {
180de0c7d54SGlenn Miles return;
181de0c7d54SGlenn Miles }
182de0c7d54SGlenn Miles rc = sscanf(name, "pin%2d", &pin);
183de0c7d54SGlenn Miles if (rc != 1) {
184de0c7d54SGlenn Miles error_setg(errp, "%s: error reading %s", __func__, name);
185de0c7d54SGlenn Miles return;
186de0c7d54SGlenn Miles }
187c67f7580SPeter Maydell if (pin < 0 || pin >= PCA9554_PIN_COUNT) {
188de0c7d54SGlenn Miles error_setg(errp, "%s invalid pin %s", __func__, name);
189de0c7d54SGlenn Miles return;
190de0c7d54SGlenn Miles }
191de0c7d54SGlenn Miles
192de0c7d54SGlenn Miles for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
193de0c7d54SGlenn Miles if (!strcmp(state_str, pin_state[state])) {
194de0c7d54SGlenn Miles break;
195de0c7d54SGlenn Miles }
196de0c7d54SGlenn Miles }
197de0c7d54SGlenn Miles if (state >= ARRAY_SIZE(pin_state)) {
198de0c7d54SGlenn Miles error_setg(errp, "%s invalid pin state %s", __func__, state_str);
199de0c7d54SGlenn Miles return;
200de0c7d54SGlenn Miles }
201de0c7d54SGlenn Miles
202de0c7d54SGlenn Miles /* First, modify the output register bit */
203de0c7d54SGlenn Miles val = pca9554_read(s, PCA9554_OUTPUT);
204de0c7d54SGlenn Miles mask = 0x1 << pin;
205de0c7d54SGlenn Miles if (state == PCA9554_PIN_LOW) {
206de0c7d54SGlenn Miles val &= ~(mask);
207de0c7d54SGlenn Miles } else {
208de0c7d54SGlenn Miles val |= mask;
209de0c7d54SGlenn Miles }
210de0c7d54SGlenn Miles pca9554_write(s, PCA9554_OUTPUT, val);
211de0c7d54SGlenn Miles
212de0c7d54SGlenn Miles /* Then, clear the config register bit for output mode */
213de0c7d54SGlenn Miles val = pca9554_read(s, PCA9554_CONFIG);
214de0c7d54SGlenn Miles val &= ~mask;
215de0c7d54SGlenn Miles pca9554_write(s, PCA9554_CONFIG, val);
216de0c7d54SGlenn Miles }
217de0c7d54SGlenn Miles
218de0c7d54SGlenn Miles static const VMStateDescription pca9554_vmstate = {
219de0c7d54SGlenn Miles .name = "PCA9554",
220de0c7d54SGlenn Miles .version_id = 0,
221de0c7d54SGlenn Miles .minimum_version_id = 0,
222de0c7d54SGlenn Miles .fields = (VMStateField[]) {
223de0c7d54SGlenn Miles VMSTATE_UINT8(len, PCA9554State),
224de0c7d54SGlenn Miles VMSTATE_UINT8(pointer, PCA9554State),
225de0c7d54SGlenn Miles VMSTATE_UINT8_ARRAY(regs, PCA9554State, PCA9554_NR_REGS),
226de0c7d54SGlenn Miles VMSTATE_UINT8_ARRAY(ext_state, PCA9554State, PCA9554_PIN_COUNT),
227de0c7d54SGlenn Miles VMSTATE_I2C_SLAVE(i2c, PCA9554State),
228de0c7d54SGlenn Miles VMSTATE_END_OF_LIST()
229de0c7d54SGlenn Miles }
230de0c7d54SGlenn Miles };
231de0c7d54SGlenn Miles
pca9554_reset(DeviceState * dev)232de0c7d54SGlenn Miles static void pca9554_reset(DeviceState *dev)
233de0c7d54SGlenn Miles {
234de0c7d54SGlenn Miles PCA9554State *s = PCA9554(dev);
235de0c7d54SGlenn Miles
236de0c7d54SGlenn Miles s->regs[PCA9554_INPUT] = 0xFF;
237de0c7d54SGlenn Miles s->regs[PCA9554_OUTPUT] = 0xFF;
238de0c7d54SGlenn Miles s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */
239de0c7d54SGlenn Miles s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */
240de0c7d54SGlenn Miles
241de0c7d54SGlenn Miles memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT);
242de0c7d54SGlenn Miles pca9554_update_pin_input(s);
243de0c7d54SGlenn Miles
244de0c7d54SGlenn Miles s->pointer = 0x0;
245de0c7d54SGlenn Miles s->len = 0;
246de0c7d54SGlenn Miles }
247de0c7d54SGlenn Miles
pca9554_initfn(Object * obj)248de0c7d54SGlenn Miles static void pca9554_initfn(Object *obj)
249de0c7d54SGlenn Miles {
250de0c7d54SGlenn Miles int pin;
251de0c7d54SGlenn Miles
252de0c7d54SGlenn Miles for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) {
253de0c7d54SGlenn Miles char *name;
254de0c7d54SGlenn Miles
255de0c7d54SGlenn Miles name = g_strdup_printf("pin%d", pin);
256de0c7d54SGlenn Miles object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin,
257de0c7d54SGlenn Miles NULL, NULL);
258de0c7d54SGlenn Miles g_free(name);
259de0c7d54SGlenn Miles }
260de0c7d54SGlenn Miles }
261de0c7d54SGlenn Miles
pca9554_set_ext_state(PCA9554State * s,int pin,int level)262de0c7d54SGlenn Miles static void pca9554_set_ext_state(PCA9554State *s, int pin, int level)
263de0c7d54SGlenn Miles {
264de0c7d54SGlenn Miles if (s->ext_state[pin] != level) {
265de0c7d54SGlenn Miles s->ext_state[pin] = level;
266de0c7d54SGlenn Miles pca9554_update_pin_input(s);
267de0c7d54SGlenn Miles }
268de0c7d54SGlenn Miles }
269de0c7d54SGlenn Miles
pca9554_gpio_in_handler(void * opaque,int pin,int level)270de0c7d54SGlenn Miles static void pca9554_gpio_in_handler(void *opaque, int pin, int level)
271de0c7d54SGlenn Miles {
272de0c7d54SGlenn Miles
273de0c7d54SGlenn Miles PCA9554State *s = PCA9554(opaque);
274de0c7d54SGlenn Miles
275de0c7d54SGlenn Miles assert((pin >= 0) && (pin < PCA9554_PIN_COUNT));
276de0c7d54SGlenn Miles pca9554_set_ext_state(s, pin, level);
277de0c7d54SGlenn Miles }
278de0c7d54SGlenn Miles
pca9554_realize(DeviceState * dev,Error ** errp)279de0c7d54SGlenn Miles static void pca9554_realize(DeviceState *dev, Error **errp)
280de0c7d54SGlenn Miles {
281de0c7d54SGlenn Miles PCA9554State *s = PCA9554(dev);
282de0c7d54SGlenn Miles
283de0c7d54SGlenn Miles if (!s->description) {
284de0c7d54SGlenn Miles s->description = g_strdup("pca9554");
285de0c7d54SGlenn Miles }
286de0c7d54SGlenn Miles
287de0c7d54SGlenn Miles qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT);
288de0c7d54SGlenn Miles qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT);
289de0c7d54SGlenn Miles }
290de0c7d54SGlenn Miles
291de531a6bSRichard Henderson static const Property pca9554_properties[] = {
292de0c7d54SGlenn Miles DEFINE_PROP_STRING("description", PCA9554State, description),
293de0c7d54SGlenn Miles };
294de0c7d54SGlenn Miles
pca9554_class_init(ObjectClass * klass,const void * data)295*12d1a768SPhilippe Mathieu-Daudé static void pca9554_class_init(ObjectClass *klass, const void *data)
296de0c7d54SGlenn Miles {
297de0c7d54SGlenn Miles DeviceClass *dc = DEVICE_CLASS(klass);
298de0c7d54SGlenn Miles I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
299de0c7d54SGlenn Miles
300de0c7d54SGlenn Miles k->event = pca9554_event;
301de0c7d54SGlenn Miles k->recv = pca9554_recv;
302de0c7d54SGlenn Miles k->send = pca9554_send;
303de0c7d54SGlenn Miles dc->realize = pca9554_realize;
304e3d08143SPeter Maydell device_class_set_legacy_reset(dc, pca9554_reset);
305de0c7d54SGlenn Miles dc->vmsd = &pca9554_vmstate;
306de0c7d54SGlenn Miles device_class_set_props(dc, pca9554_properties);
307de0c7d54SGlenn Miles }
308de0c7d54SGlenn Miles
309de0c7d54SGlenn Miles static const TypeInfo pca9554_info = {
310de0c7d54SGlenn Miles .name = TYPE_PCA9554,
311de0c7d54SGlenn Miles .parent = TYPE_I2C_SLAVE,
312de0c7d54SGlenn Miles .instance_init = pca9554_initfn,
313de0c7d54SGlenn Miles .instance_size = sizeof(PCA9554State),
314de0c7d54SGlenn Miles .class_init = pca9554_class_init,
315de0c7d54SGlenn Miles .class_size = sizeof(PCA9554Class),
316de0c7d54SGlenn Miles .abstract = false,
317de0c7d54SGlenn Miles };
318de0c7d54SGlenn Miles
pca9554_register_types(void)319de0c7d54SGlenn Miles static void pca9554_register_types(void)
320de0c7d54SGlenn Miles {
321de0c7d54SGlenn Miles type_register_static(&pca9554_info);
322de0c7d54SGlenn Miles }
323de0c7d54SGlenn Miles
324de0c7d54SGlenn Miles type_init(pca9554_register_types)
325