hw/gpio: pca9552: apply input polarity inversion on read
The PCA9535 polarity inversion register inverts the value read back from the input port for every pin, regardless of its direction, and does not affect the output drive or the physical pin level. Store the raw pin level in the input register and apply the polarity inversion when the input port is read, instead of XORing it into the stored value of output-configured pins only. The interrupt output now reflects the raw pin level, matching the datasheet. 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-7-814575bc076b@free.fr Signed-off-by: Cédric Le Goater <clg@redhat.com>
Emmanuel Blot committed
Jul 9, 2026 at 17:23 UTC
469daf3eeff15d43c3d28d07555b4f134909b49e
1 file changed
+18
-7
hw/gpio/pca9552.c
+18
-7
@@ -167,9 +167,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
167
/* PCA9535: Simple GPIO behavior */
168
uint8_t config_reg = PCA9535_CONFIG0 + (i / 8);
169
uint8_t output_reg = PCA9535_OUTPUT0 + (i / 8);
170
- uint8_t polarity_reg = PCA9535_POLARITY0 + (i / 8);
170
172
- /* Check if pin is configured as input */
171
+ /*
172
+ * The input register holds the raw pin logic level; the
173
+ * polarity inversion register is only applied when the input
174
+ * port is read (see pca955x_read()).
175
+ */
176
if (s->regs[config_reg] & bit_mask) {
177
/* Input mode - reflect external state */
178
if (s->ext_state[i] == PCA9552_PIN_LOW) {
@@ -179,12 +182,8 @@ static void pca955x_update_pin_input(PCA955xState *s)
182
}
183
} else {
184
/* Output mode - reflect output register value */
182
- uint8_t output_bit = s->regs[output_reg] & bit_mask;
183
- uint8_t polarity_bit = s->regs[polarity_reg] & bit_mask;
184
-
185
- /* Apply polarity inversion if set */
185
s->regs[input_reg] = (s->regs[input_reg] & ~bit_mask) |
187
- ((output_bit ^ polarity_bit) & bit_mask);
186
+ (s->regs[output_reg] & bit_mask);
187
}
188
}
189
@@ -206,6 +205,18 @@ static uint8_t pca955x_read(PCA955xState *s, uint8_t reg)
205
return 0xFF;
206
}
207
208
+ /*
209
+ * On the GPIO variants, reading an input port returns the raw pin
210
+ * levels XORed with the polarity inversion register, as specified by
211
+ * the datasheet.
212
+ */
213
+ if (!k->has_led_support &&
214
+ (reg == PCA9535_INPUT0 || reg == PCA9535_INPUT1)) {
215
+ uint8_t polarity_reg = PCA9535_POLARITY0 + (reg - PCA9535_INPUT0);
216
+
217
+ return s->regs[reg] ^ s->regs[polarity_reg];
218
+ }
219
+
220
return s->regs[reg];
221
}
222