@samitouri / QOSamiQemu / commits / 469daf3eef

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