@samitouri / QOSamiQemu / commits / a11babc632

hw/gpio: pca9552: expose GPIO pins as pin%d QOM properties

The PCA9552 exposes its LED channels as led%d QOM string properties, but the GPIO variants (PCA9535/PCA9555) inherited the same led%d interface, which drives the LED selector registers and is meaningless for a plain I/O expander. Add pin%d string properties ("low"/"high") for the GPIO variants, mirroring the standalone pca9555 model: - reading returns the raw pin logic level from the INPUT register; - writing drives the external input level, but only for pins the guest has configured as inputs (writes to output pins are ignored with a LOG_UNIMP message). The LED variant keeps its led%d properties. 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-9-814575bc076b@free.fr Signed-off-by: Cédric Le Goater <clg@redhat.com>

Emmanuel Blot committed Jul 9, 2026 at 17:23 UTC a11babc63217a5e3e8a9a7350654e35755f6ad11
1 file changed +86 -5
hw/gpio/pca9552.c
+86 -5
@@ -68,6 +68,7 @@ struct PCA955xClass {
68 #define PCA9552_PIN_HIZ 0x1
69
70 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
71 +static const char *pin_state[] = {"low", "high"};
72
73 static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
74 {
@@ -428,6 +429,79 @@ static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
429 pca955x_write(s, reg, val);
430 }
431
432 +static void pca955x_set_ext_state(PCA955xState *s, int pin, int level);
433 +
434 +static void pca955x_get_pin(Object *obj, Visitor *v, const char *name,
435 + void *opaque, Error **errp)
436 +{
437 + PCA955xClass *k = PCA955X_GET_CLASS(obj);
438 + PCA955xState *s = PCA955X(obj);
439 + int pin, rc;
440 + uint8_t input_reg, state;
441 +
442 + rc = sscanf(name, "pin%2d", &pin);
443 + if (rc != 1) {
444 + error_setg(errp, "%s: error reading %s", __func__, name);
445 + return;
446 + }
447 + if (pin < 0 || pin >= k->pin_count) {
448 + error_setg(errp, "%s invalid pin %s", __func__, name);
449 + return;
450 + }
451 +
452 + /*
453 + * Report the raw pin logic level; polarity inversion is a read-time
454 + * transform applied to the INPUT register, not to the pin state itself.
455 + */
456 + input_reg = PCA9535_INPUT0 + (pin / 8);
457 + state = (s->regs[input_reg] >> (pin % 8)) & 0x1;
458 + visit_type_str(v, name, (char **)&pin_state[state], errp);
459 +}
460 +
461 +static void pca955x_set_pin(Object *obj, Visitor *v, const char *name,
462 + void *opaque, Error **errp)
463 +{
464 + PCA955xClass *k = PCA955X_GET_CLASS(obj);
465 + PCA955xState *s = PCA955X(obj);
466 + int pin, rc;
467 + uint8_t state, config_reg;
468 + g_autofree char *state_str = NULL;
469 +
470 + if (!visit_type_str(v, name, &state_str, errp)) {
471 + return;
472 + }
473 + rc = sscanf(name, "pin%2d", &pin);
474 + if (rc != 1) {
475 + error_setg(errp, "%s: error reading %s", __func__, name);
476 + return;
477 + }
478 + if (pin < 0 || pin >= k->pin_count) {
479 + error_setg(errp, "%s invalid pin %s", __func__, name);
480 + return;
481 + }
482 +
483 + for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
484 + if (!strcmp(state_str, pin_state[state])) {
485 + break;
486 + }
487 + }
488 + if (state >= ARRAY_SIZE(pin_state)) {
489 + error_setg(errp, "%s invalid pin state %s", __func__, state_str);
490 + return;
491 + }
492 +
493 + /* Only input-configured pins can be driven by an external device. */
494 + config_reg = PCA9535_CONFIG0 + (pin / 8);
495 + if (!((s->regs[config_reg] >> (pin % 8)) & 0x1)) {
496 + qemu_log_mask(LOG_UNIMP,
497 + "%s: pin %d is configured as output, ignoring set\n",
498 + s->description, pin);
499 + return;
500 + }
501 +
502 + pca955x_set_ext_state(s, pin, state != PCA9552_PIN_LOW);
503 +}
504 +
505 static const VMStateDescription pca9552_vmstate = {
506 .name = "PCA9552",
507 .version_id = 0,
@@ -485,15 +559,22 @@ static void pca9535_reset_hold(Object *obj, ResetType type)
559 static void pca955x_initfn(Object *obj)
560 {
561 PCA955xClass *k = PCA955X_GET_CLASS(obj);
488 - int led;
562
563 assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
491 - for (led = 0; led < k->pin_count; led++) {
564 + for (int ix = 0; ix < k->pin_count; ix++) {
565 char *name;
566
494 - name = g_strdup_printf("led%d", led);
495 - object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led,
496 - NULL, NULL);
567 + if (k->has_led_support) {
568 + /* LED variant: expose the LED selector state as led%d. */
569 + name = g_strdup_printf("led%d", ix);
570 + object_property_add(obj, name, "bool",
571 + pca955x_get_led, pca955x_set_led, NULL, NULL);
572 + } else {
573 + /* GPIO variant: expose the pin logic level as pin%d. */
574 + name = g_strdup_printf("pin%d", ix);
575 + object_property_add(obj, name, "str",
576 + pca955x_get_pin, pca955x_set_pin, NULL, NULL);
577 + }
578 g_free(name);
579 }
580 }