Re: [PATCH v5 7/9] misc: Add a pca9554 GPIO device model
On Wed, 2023-11-22 at 09:55 +0100, Cédric Le Goater wrote: > On 11/21/23 20:09, Glenn Miles wrote: > > Specs are available here: > > > > https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf > > > > This is a simple model supporting the basic registers for GPIO > > mode. The device also supports an interrupt output line but the > > model does not yet support this. > > > > Reviewed-by: Cédric Le Goater > > My R-b was on patch "ppc/pnv: Add a pca9554 I2C device to powernv10- > rainier". > Not on the pca9554 model itself. > > Thanks, > > C. > Yikes! Sorry about that. I wonder if there's a good tool out there to help with tracking this type of information. Thanks, Glenn > > > > Signed-off-by: Glenn Miles > > --- > > > > No change from previous version > > > > MAINTAINERS| 10 +- > > hw/misc/pca9554.c | 328 > > + > > include/hw/misc/pca9554.h | 36 > > include/hw/misc/pca9554_regs.h | 19 ++ > > 4 files changed, 391 insertions(+), 2 deletions(-) > > create mode 100644 hw/misc/pca9554.c > > create mode 100644 include/hw/misc/pca9554.h > > create mode 100644 include/hw/misc/pca9554_regs.h > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 695e0bd34f..4d1c991691 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -1155,9 +1155,7 @@ R: Joel Stanley > > L: qemu-...@nongnu.org > > S: Maintained > > F: hw/*/*aspeed* > > -F: hw/misc/pca9552.c > > F: include/hw/*/*aspeed* > > -F: include/hw/misc/pca9552*.h > > F: hw/net/ftgmac100.c > > F: include/hw/net/ftgmac100.h > > F: docs/system/arm/aspeed.rst > > @@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv* > > F: pc-bios/skiboot.lid > > F: tests/qtest/pnv* > > > > +pca955x > > +M: Glenn Miles > > +L: qemu-...@nongnu.org > > +L: qemu-...@nongnu.org > > +S: Odd Fixes > > +F: hw/misc/pca955*.c > > +F: include/hw/misc/pca955*.h > > + > > virtex_ml507 > > M: Edgar E. Iglesias > > L: qemu-...@nongnu.org > > diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c > > new file mode 100644 > > index 00..778b32e443 > > --- /dev/null > > +++ b/hw/misc/pca9554.c > > @@ -0,0 +1,328 @@ > > +/* > > + * PCA9554 I/O port > > + * > > + * Copyright (c) 2023, IBM Corporation. > > + * > > + * SPDX-License-Identifier: GPL-2.0-or-later > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qemu/log.h" > > +#include "qemu/module.h" > > +#include "qemu/bitops.h" > > +#include "hw/qdev-properties.h" > > +#include "hw/misc/pca9554.h" > > +#include "hw/misc/pca9554_regs.h" > > +#include "hw/irq.h" > > +#include "migration/vmstate.h" > > +#include "qapi/error.h" > > +#include "qapi/visitor.h" > > +#include "trace.h" > > +#include "qom/object.h" > > + > > +struct PCA9554Class { > > +/*< private >*/ > > +I2CSlaveClass parent_class; > > +/*< public >*/ > > +}; > > +typedef struct PCA9554Class PCA9554Class; > > + > > +DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554, > > + TYPE_PCA9554) > > + > > +#define PCA9554_PIN_LOW 0x0 > > +#define PCA9554_PIN_HIZ 0x1 > > + > > +static const char *pin_state[] = {"low", "high"}; > > + > > +static void pca9554_update_pin_input(PCA9554State *s) > > +{ > > +int i; > > +uint8_t config = s->regs[PCA9554_CONFIG]; > > +uint8_t output = s->regs[PCA9554_OUTPUT]; > > +uint8_t internal_state = config | output; > > + > > +for (i = 0; i < PCA9554_PIN_COUNT; i++) { > > +uint8_t bit_mask = 1 << i; > > +uint8_t internal_pin_state = (internal_state >> i) & 0x1; > > +uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; > > +uint8_t new_value; > > + > > +switch (internal_pin_state) { > > +case PCA9554_PIN_LOW: > > +s->regs[PCA9554_INPUT] &= ~bit_mask; > > +break; > > +case PCA9554_PIN_HIZ: > > +/* > > + * pullup sets it to a logical 1 unless > > + * external device drives it low. > > + */ > > +if (s->ext_state[i] == PCA9554_PIN_LOW) { > > +s->regs[PCA9554_INPUT] &= ~bit_mask; > > +} else { > > +s->regs[PCA9554_INPUT] |= bit_mask; > > +} > > +break; > > +default: > > +break; > > +} > > + > > +/* update irq state only if pin state changed */ > > +new_value = s->regs[PCA9554_INPUT] & bit_mask; > > +if (new_value != old_value) { > > +if (new_value) { > > +/* changed from 0 to 1 */ > > +qemu_set_irq(s->gpio_out[i], 1); > > +} else { > > +/* changed from 1 to 0 */ > > +qemu_set_irq(s->gpio_out[i], 0); > > +} > > +} > > +} > > +} > > + > > +static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) > > +{ > > +switch (reg) { > > +case PCA9554_INPUT: > > +return s->regs[PCA9554_INPUT] ^
Re: [PATCH v5 7/9] misc: Add a pca9554 GPIO device model
On 11/21/23 20:09, Glenn Miles wrote: Specs are available here: https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf This is a simple model supporting the basic registers for GPIO mode. The device also supports an interrupt output line but the model does not yet support this. Reviewed-by: Cédric Le Goater Signed-off-by: Glenn Miles --- Reviewed-by: Cédric Le Goater Thanks, C. No change from previous version MAINTAINERS| 10 +- hw/misc/pca9554.c | 328 + include/hw/misc/pca9554.h | 36 include/hw/misc/pca9554_regs.h | 19 ++ 4 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 hw/misc/pca9554.c create mode 100644 include/hw/misc/pca9554.h create mode 100644 include/hw/misc/pca9554_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 695e0bd34f..4d1c991691 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1155,9 +1155,7 @@ R: Joel Stanley L: qemu-...@nongnu.org S: Maintained F: hw/*/*aspeed* -F: hw/misc/pca9552.c F: include/hw/*/*aspeed* -F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst @@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +pca955x +M: Glenn Miles +L: qemu-...@nongnu.org +L: qemu-...@nongnu.org +S: Odd Fixes +F: hw/misc/pca955*.c +F: include/hw/misc/pca955*.h + virtex_ml507 M: Edgar E. Iglesias L: qemu-...@nongnu.org diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c new file mode 100644 index 00..778b32e443 --- /dev/null +++ b/hw/misc/pca9554.c @@ -0,0 +1,328 @@ +/* + * PCA9554 I/O port + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "hw/qdev-properties.h" +#include "hw/misc/pca9554.h" +#include "hw/misc/pca9554_regs.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "trace.h" +#include "qom/object.h" + +struct PCA9554Class { +/*< private >*/ +I2CSlaveClass parent_class; +/*< public >*/ +}; +typedef struct PCA9554Class PCA9554Class; + +DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554, + TYPE_PCA9554) + +#define PCA9554_PIN_LOW 0x0 +#define PCA9554_PIN_HIZ 0x1 + +static const char *pin_state[] = {"low", "high"}; + +static void pca9554_update_pin_input(PCA9554State *s) +{ +int i; +uint8_t config = s->regs[PCA9554_CONFIG]; +uint8_t output = s->regs[PCA9554_OUTPUT]; +uint8_t internal_state = config | output; + +for (i = 0; i < PCA9554_PIN_COUNT; i++) { +uint8_t bit_mask = 1 << i; +uint8_t internal_pin_state = (internal_state >> i) & 0x1; +uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; +uint8_t new_value; + +switch (internal_pin_state) { +case PCA9554_PIN_LOW: +s->regs[PCA9554_INPUT] &= ~bit_mask; +break; +case PCA9554_PIN_HIZ: +/* + * pullup sets it to a logical 1 unless + * external device drives it low. + */ +if (s->ext_state[i] == PCA9554_PIN_LOW) { +s->regs[PCA9554_INPUT] &= ~bit_mask; +} else { +s->regs[PCA9554_INPUT] |= bit_mask; +} +break; +default: +break; +} + +/* update irq state only if pin state changed */ +new_value = s->regs[PCA9554_INPUT] & bit_mask; +if (new_value != old_value) { +if (new_value) { +/* changed from 0 to 1 */ +qemu_set_irq(s->gpio_out[i], 1); +} else { +/* changed from 1 to 0 */ +qemu_set_irq(s->gpio_out[i], 0); +} +} +} +} + +static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) +{ +switch (reg) { +case PCA9554_INPUT: +return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY]; +case PCA9554_OUTPUT: +case PCA9554_POLARITY: +case PCA9554_CONFIG: +return s->regs[reg]; +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); +return 0xFF; +} +} + +static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) +{ +switch (reg) { +case PCA9554_OUTPUT: +case PCA9554_CONFIG: +s->regs[reg] = data; +pca9554_update_pin_input(s); +break; +case PCA9554_POLARITY: +s->regs[reg] = data; +break; +case PCA9554_INPUT: +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); +} +} + +static uint8_t pca9554_recv(I2CSlave *i2c) +{ +PCA9554State *s = PCA9554(i2c); +uint8_t ret; + +
Re: [PATCH v5 7/9] misc: Add a pca9554 GPIO device model
On 11/21/23 20:09, Glenn Miles wrote: Specs are available here: https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf This is a simple model supporting the basic registers for GPIO mode. The device also supports an interrupt output line but the model does not yet support this. Reviewed-by: Cédric Le Goater My R-b was on patch "ppc/pnv: Add a pca9554 I2C device to powernv10-rainier". Not on the pca9554 model itself. Thanks, C. Signed-off-by: Glenn Miles --- No change from previous version MAINTAINERS| 10 +- hw/misc/pca9554.c | 328 + include/hw/misc/pca9554.h | 36 include/hw/misc/pca9554_regs.h | 19 ++ 4 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 hw/misc/pca9554.c create mode 100644 include/hw/misc/pca9554.h create mode 100644 include/hw/misc/pca9554_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 695e0bd34f..4d1c991691 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1155,9 +1155,7 @@ R: Joel Stanley L: qemu-...@nongnu.org S: Maintained F: hw/*/*aspeed* -F: hw/misc/pca9552.c F: include/hw/*/*aspeed* -F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst @@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +pca955x +M: Glenn Miles +L: qemu-...@nongnu.org +L: qemu-...@nongnu.org +S: Odd Fixes +F: hw/misc/pca955*.c +F: include/hw/misc/pca955*.h + virtex_ml507 M: Edgar E. Iglesias L: qemu-...@nongnu.org diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c new file mode 100644 index 00..778b32e443 --- /dev/null +++ b/hw/misc/pca9554.c @@ -0,0 +1,328 @@ +/* + * PCA9554 I/O port + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "hw/qdev-properties.h" +#include "hw/misc/pca9554.h" +#include "hw/misc/pca9554_regs.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "trace.h" +#include "qom/object.h" + +struct PCA9554Class { +/*< private >*/ +I2CSlaveClass parent_class; +/*< public >*/ +}; +typedef struct PCA9554Class PCA9554Class; + +DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554, + TYPE_PCA9554) + +#define PCA9554_PIN_LOW 0x0 +#define PCA9554_PIN_HIZ 0x1 + +static const char *pin_state[] = {"low", "high"}; + +static void pca9554_update_pin_input(PCA9554State *s) +{ +int i; +uint8_t config = s->regs[PCA9554_CONFIG]; +uint8_t output = s->regs[PCA9554_OUTPUT]; +uint8_t internal_state = config | output; + +for (i = 0; i < PCA9554_PIN_COUNT; i++) { +uint8_t bit_mask = 1 << i; +uint8_t internal_pin_state = (internal_state >> i) & 0x1; +uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; +uint8_t new_value; + +switch (internal_pin_state) { +case PCA9554_PIN_LOW: +s->regs[PCA9554_INPUT] &= ~bit_mask; +break; +case PCA9554_PIN_HIZ: +/* + * pullup sets it to a logical 1 unless + * external device drives it low. + */ +if (s->ext_state[i] == PCA9554_PIN_LOW) { +s->regs[PCA9554_INPUT] &= ~bit_mask; +} else { +s->regs[PCA9554_INPUT] |= bit_mask; +} +break; +default: +break; +} + +/* update irq state only if pin state changed */ +new_value = s->regs[PCA9554_INPUT] & bit_mask; +if (new_value != old_value) { +if (new_value) { +/* changed from 0 to 1 */ +qemu_set_irq(s->gpio_out[i], 1); +} else { +/* changed from 1 to 0 */ +qemu_set_irq(s->gpio_out[i], 0); +} +} +} +} + +static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) +{ +switch (reg) { +case PCA9554_INPUT: +return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY]; +case PCA9554_OUTPUT: +case PCA9554_POLARITY: +case PCA9554_CONFIG: +return s->regs[reg]; +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); +return 0xFF; +} +} + +static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) +{ +switch (reg) { +case PCA9554_OUTPUT: +case PCA9554_CONFIG: +s->regs[reg] = data; +pca9554_update_pin_input(s); +break; +case PCA9554_POLARITY: +s->regs[reg] = data; +break; +case PCA9554_INPUT: +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); +} +} + +static uint8_t
[PATCH v5 7/9] misc: Add a pca9554 GPIO device model
Specs are available here: https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf This is a simple model supporting the basic registers for GPIO mode. The device also supports an interrupt output line but the model does not yet support this. Reviewed-by: Cédric Le Goater Signed-off-by: Glenn Miles --- No change from previous version MAINTAINERS| 10 +- hw/misc/pca9554.c | 328 + include/hw/misc/pca9554.h | 36 include/hw/misc/pca9554_regs.h | 19 ++ 4 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 hw/misc/pca9554.c create mode 100644 include/hw/misc/pca9554.h create mode 100644 include/hw/misc/pca9554_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 695e0bd34f..4d1c991691 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1155,9 +1155,7 @@ R: Joel Stanley L: qemu-...@nongnu.org S: Maintained F: hw/*/*aspeed* -F: hw/misc/pca9552.c F: include/hw/*/*aspeed* -F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst @@ -1526,6 +1524,14 @@ F: include/hw/pci-host/pnv* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +pca955x +M: Glenn Miles +L: qemu-...@nongnu.org +L: qemu-...@nongnu.org +S: Odd Fixes +F: hw/misc/pca955*.c +F: include/hw/misc/pca955*.h + virtex_ml507 M: Edgar E. Iglesias L: qemu-...@nongnu.org diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c new file mode 100644 index 00..778b32e443 --- /dev/null +++ b/hw/misc/pca9554.c @@ -0,0 +1,328 @@ +/* + * PCA9554 I/O port + * + * Copyright (c) 2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitops.h" +#include "hw/qdev-properties.h" +#include "hw/misc/pca9554.h" +#include "hw/misc/pca9554_regs.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "trace.h" +#include "qom/object.h" + +struct PCA9554Class { +/*< private >*/ +I2CSlaveClass parent_class; +/*< public >*/ +}; +typedef struct PCA9554Class PCA9554Class; + +DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554, + TYPE_PCA9554) + +#define PCA9554_PIN_LOW 0x0 +#define PCA9554_PIN_HIZ 0x1 + +static const char *pin_state[] = {"low", "high"}; + +static void pca9554_update_pin_input(PCA9554State *s) +{ +int i; +uint8_t config = s->regs[PCA9554_CONFIG]; +uint8_t output = s->regs[PCA9554_OUTPUT]; +uint8_t internal_state = config | output; + +for (i = 0; i < PCA9554_PIN_COUNT; i++) { +uint8_t bit_mask = 1 << i; +uint8_t internal_pin_state = (internal_state >> i) & 0x1; +uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; +uint8_t new_value; + +switch (internal_pin_state) { +case PCA9554_PIN_LOW: +s->regs[PCA9554_INPUT] &= ~bit_mask; +break; +case PCA9554_PIN_HIZ: +/* + * pullup sets it to a logical 1 unless + * external device drives it low. + */ +if (s->ext_state[i] == PCA9554_PIN_LOW) { +s->regs[PCA9554_INPUT] &= ~bit_mask; +} else { +s->regs[PCA9554_INPUT] |= bit_mask; +} +break; +default: +break; +} + +/* update irq state only if pin state changed */ +new_value = s->regs[PCA9554_INPUT] & bit_mask; +if (new_value != old_value) { +if (new_value) { +/* changed from 0 to 1 */ +qemu_set_irq(s->gpio_out[i], 1); +} else { +/* changed from 1 to 0 */ +qemu_set_irq(s->gpio_out[i], 0); +} +} +} +} + +static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) +{ +switch (reg) { +case PCA9554_INPUT: +return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY]; +case PCA9554_OUTPUT: +case PCA9554_POLARITY: +case PCA9554_CONFIG: +return s->regs[reg]; +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); +return 0xFF; +} +} + +static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) +{ +switch (reg) { +case PCA9554_OUTPUT: +case PCA9554_CONFIG: +s->regs[reg] = data; +pca9554_update_pin_input(s); +break; +case PCA9554_POLARITY: +s->regs[reg] = data; +break; +case PCA9554_INPUT: +default: +qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); +} +} + +static uint8_t pca9554_recv(I2CSlave *i2c) +{ +PCA9554State *s = PCA9554(i2c); +uint8_t ret; + +ret = pca9554_read(s, s->pointer & 0x3); + +return ret; +} + +static int pca9554_send(I2CSlave *i2c, uint8_t