[PULL 41/83] hw/gpio: pca9552: conform GPIO command handling to the datasheet
Cédric Le Goater <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
From: Emmanuel Blot <[email protected]> The PCA9535/PCA9555 GPIO expanders share the PCA955X command dispatch path with the PCA9552 LED blinker, but their register access differs from the LED variant: - Auto-increment happens on every access and toggles bit 0 so the pointer stays within the addressed register pair (input, output, polarity, config); there is no AI enable bit. - The command byte only decodes 3 bits, so addresses beyond the last register alias back into the 8-register window instead of faulting. Branch the auto-increment and command-decode logic on has_led_support so the GPIO variants follow their datasheet while the PCA9552 behaviour is left untouched. Signed-off-by: Emmanuel Blot <[email protected]> Reviewed-by: Glenn Miles <[email protected]> Link: https://lore.kernel.org/qemu-devel/[email protected] Signed-off-by: Cédric Le Goater <[email protected]> --- hw/gpio/pca9552.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 24f646541582..54a5945d9252 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -259,14 +259,26 @@ static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) } /* - * When Auto-Increment is on, the register address is incremented - * after each byte is sent to or received by the device. The index - * rollovers to 0 when the maximum register address is reached. + * Advance the command pointer after each byte sent to or received from the + * device. + * + * The LED variant auto-increments only when the AI bit (bit 4) is set in the + * command byte, rolling over to 0 once the maximum register address is + * reached. + * + * The GPIO variants auto-increment on every access, toggling bit 0 so the + * pointer stays within the addressed register pair + * (input/output/polarity/config), as specified by their datasheet. */ static void pca955x_autoinc(PCA955xState *s) { PCA955xClass *k = PCA955X_GET_CLASS(s); + if (!k->has_led_support) { + s->pointer ^= 0x1; + return; + } + if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { uint8_t reg = s->pointer & 0xf; @@ -275,12 +287,25 @@ static void pca955x_autoinc(PCA955xState *s) } } +/* + * The LED variant addresses its registers with a 4-bit command field, while + * the GPIO variants only decode 3 bits (the command wraps into the 8-register + * window). + */ +static inline uint8_t pca955x_cmd_reg(PCA955xState *s) +{ + PCA955xClass *k = PCA955X_GET_CLASS(s); + + return s->pointer & (k->has_led_support ? 0xf : 0x7); +} + static uint8_t pca955x_recv(I2CSlave *i2c) { PCA955xState *s = PCA955X(i2c); + PCA955xClass *k = PCA955X_GET_CLASS(s); uint8_t ret; - ret = pca955x_read(s, s->pointer & 0xf); + ret = pca955x_read(s, pca955x_cmd_reg(s)); /* * From the Specs: @@ -292,7 +317,7 @@ static uint8_t pca955x_recv(I2CSlave *i2c) * I don't know what should be done in this case, so throw an * error. */ - if (s->pointer == PCA9552_AUTOINC) { + if (k->has_led_support && s->pointer == PCA9552_AUTOINC) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Autoincrement read starting with register 0\n", __func__); @@ -312,7 +337,7 @@ static int pca955x_send(I2CSlave *i2c, uint8_t data) s->pointer = data; s->len++; } else { - pca955x_write(s, s->pointer & 0xf, data); + pca955x_write(s, pca955x_cmd_reg(s), data); pca955x_autoinc(s); } -- 2.55.0