@samitouri / QOSamiQemu / commits / f8e1f6488f

hw/gpio: pca9552: conform GPIO command handling to the datasheet

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 <emmanuel.blot@free.fr> Reviewed-by: Glenn Miles <milesg@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-8-814575bc076b@free.fr Signed-off-by: Cédric Le Goater <clg@redhat.com>

Emmanuel Blot committed Jul 9, 2026 at 17:23 UTC f8e1f6488f2b9f79b37c9c40db17f9be1beea032
1 file changed +31 -6
hw/gpio/pca9552.c
+31 -6
@@ -259,14 +259,26 @@ static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data)
259 }
260
261 /*
262 - * When Auto-Increment is on, the register address is incremented
263 - * after each byte is sent to or received by the device. The index
264 - * rollovers to 0 when the maximum register address is reached.
262 + * Advance the command pointer after each byte sent to or received from the
263 + * device.
264 + *
265 + * The LED variant auto-increments only when the AI bit (bit 4) is set in the
266 + * command byte, rolling over to 0 once the maximum register address is
267 + * reached.
268 + *
269 + * The GPIO variants auto-increment on every access, toggling bit 0 so the
270 + * pointer stays within the addressed register pair
271 + * (input/output/polarity/config), as specified by their datasheet.
272 */
273 static void pca955x_autoinc(PCA955xState *s)
274 {
275 PCA955xClass *k = PCA955X_GET_CLASS(s);
276
277 + if (!k->has_led_support) {
278 + s->pointer ^= 0x1;
279 + return;
280 + }
281 +
282 if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) {
283 uint8_t reg = s->pointer & 0xf;
284
@@ -275,12 +287,25 @@ static void pca955x_autoinc(PCA955xState *s)
287 }
288 }
289
290 +/*
291 + * The LED variant addresses its registers with a 4-bit command field, while
292 + * the GPIO variants only decode 3 bits (the command wraps into the 8-register
293 + * window).
294 + */
295 +static inline uint8_t pca955x_cmd_reg(PCA955xState *s)
296 +{
297 + PCA955xClass *k = PCA955X_GET_CLASS(s);
298 +
299 + return s->pointer & (k->has_led_support ? 0xf : 0x7);
300 +}
301 +
302 static uint8_t pca955x_recv(I2CSlave *i2c)
303 {
304 PCA955xState *s = PCA955X(i2c);
305 + PCA955xClass *k = PCA955X_GET_CLASS(s);
306 uint8_t ret;
307
283 - ret = pca955x_read(s, s->pointer & 0xf);
308 + ret = pca955x_read(s, pca955x_cmd_reg(s));
309
310 /*
311 * From the Specs:
@@ -292,7 +317,7 @@ static uint8_t pca955x_recv(I2CSlave *i2c)
317 * I don't know what should be done in this case, so throw an
318 * error.
319 */
295 - if (s->pointer == PCA9552_AUTOINC) {
320 + if (k->has_led_support && s->pointer == PCA9552_AUTOINC) {
321 qemu_log_mask(LOG_GUEST_ERROR,
322 "%s: Autoincrement read starting with register 0\n",
323 __func__);
@@ -312,7 +337,7 @@ static int pca955x_send(I2CSlave *i2c, uint8_t data)
337 s->pointer = data;
338 s->len++;
339 } else {
315 - pca955x_write(s, s->pointer & 0xf, data);
340 + pca955x_write(s, pca955x_cmd_reg(s), data);
341
342 pca955x_autoinc(s);
343 }