[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 24/49] misc/pca9552: Let external devices set pca9552 inputs
From: |
Nicholas Piggin |
Subject: |
[PULL 24/49] misc/pca9552: Let external devices set pca9552 inputs |
Date: |
Mon, 19 Feb 2024 18:29:13 +1000 |
From: Glenn Miles <milesg@linux.vnet.ibm.com>
Allow external devices to drive pca9552 input pins by adding
input GPIO's to the model. This allows a device to connect
its output GPIO's to the pca9552 input GPIO's.
In order for an external device to set the state of a pca9552
pin, the pin must first be configured for high impedance (LED
is off). If the pca9552 pin is configured to drive the pin low
(LED is on), then external input will be ignored.
Here is a table describing the logical state of a pca9552 pin
given the state being driven by the pca9552 and an external device:
PCA9552
Configured
State
| Hi-Z | Low |
------+------+-----+
External Hi-Z | Hi | Low |
Device ------+------+-----+
State Low | Low | Low |
------+------+-----+
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
hw/misc/pca9552.c | 50 +++++++++++++++++++++++++++++++++------
include/hw/misc/pca9552.h | 3 ++-
2 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index f00a149d61..2ae13af35e 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -44,6 +44,8 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
#define PCA9552_LED_OFF 0x1
#define PCA9552_LED_PWM0 0x2
#define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW 0x0
+#define PCA9552_PIN_HIZ 0x1
static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
@@ -110,22 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
for (i = 0; i < k->pin_count; i++) {
uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
- uint8_t input_shift = (i % 8);
+ uint8_t bit_mask = 1 << (i % 8);
uint8_t config = pca955x_pin_get_config(s, i);
+ uint8_t old_value = s->regs[input_reg] & bit_mask;
+ uint8_t new_value;
switch (config) {
case PCA9552_LED_ON:
/* Pin is set to 0V to turn on LED */
- qemu_set_irq(s->gpio[i], 0);
- s->regs[input_reg] &= ~(1 << input_shift);
+ s->regs[input_reg] &= ~bit_mask;
break;
case PCA9552_LED_OFF:
/*
* Pin is set to Hi-Z to turn off LED and
- * pullup sets it to a logical 1.
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
*/
- qemu_set_irq(s->gpio[i], 1);
- s->regs[input_reg] |= 1 << input_shift;
+ if (s->ext_state[i] == PCA9552_PIN_LOW) {
+ s->regs[input_reg] &= ~bit_mask;
+ } else {
+ s->regs[input_reg] |= bit_mask;
+ }
break;
case PCA9552_LED_PWM0:
case PCA9552_LED_PWM1:
@@ -133,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
default:
break;
}
+
+ /* update irq state only if pin state changed */
+ new_value = s->regs[input_reg] & bit_mask;
+ if (new_value != old_value) {
+ qemu_set_irq(s->gpio_out[i], !!new_value);
+ }
}
}
@@ -340,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
VMSTATE_UINT8(len, PCA955xState),
VMSTATE_UINT8(pointer, PCA955xState),
VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+ VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
VMSTATE_I2C_SLAVE(i2c, PCA955xState),
VMSTATE_END_OF_LIST()
}
@@ -358,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
s->regs[PCA9552_LS2] = 0x55;
s->regs[PCA9552_LS3] = 0x55;
+ memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
pca955x_update_pin_input(s);
s->pointer = 0xFF;
@@ -380,6 +395,26 @@ static void pca955x_initfn(Object *obj)
}
}
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+ if (s->ext_state[pin] != level) {
+ uint16_t pins_status = pca955x_pins_get_status(s);
+ s->ext_state[pin] = level;
+ pca955x_update_pin_input(s);
+ pca955x_display_pins_status(s, pins_status);
+ }
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+ PCA955xState *s = PCA955X(opaque);
+ PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+ assert((pin >= 0) && (pin < k->pin_count));
+ pca955x_set_ext_state(s, pin, level);
+}
+
static void pca955x_realize(DeviceState *dev, Error **errp)
{
PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -389,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
s->description = g_strdup("pca-unspecified");
}
- qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+ qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
+ qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
}
static Property pca955x_properties[] = {
diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h
index b6f4e264fe..c36525f0c3 100644
--- a/include/hw/misc/pca9552.h
+++ b/include/hw/misc/pca9552.h
@@ -30,7 +30,8 @@ struct PCA955xState {
uint8_t pointer;
uint8_t regs[PCA955X_NR_REGS];
- qemu_irq gpio[PCA955X_PIN_COUNT_MAX];
+ qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
+ uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
char *description; /* For debugging purpose only */
};
--
2.42.0
- [PULL 11/49] ppc/pnv: Update skiboot to v7.1, (continued)
- [PULL 11/49] ppc/pnv: Update skiboot to v7.1, Nicholas Piggin, 2024/02/19
- [PULL 14/49] hw/ppc/spapr_hcall: Allow elision of softmmu_resize_hpt_prep, Nicholas Piggin, 2024/02/19
- [PULL 15/49] hw/ppc/spapr_hcall: Rename {softmmu -> vhyp_mmu}_resize_hpt_pr, Nicholas Piggin, 2024/02/19
- [PULL 16/49] hw/ppc/spapr: Rename 'softmmu' -> 'vhyp_mmu', Nicholas Piggin, 2024/02/19
- [PULL 18/49] ppc/spapr: Initialize max_cpus limit to SPAPR_IRQ_NR_IPIS., Nicholas Piggin, 2024/02/19
- [PULL 17/49] ppc/spapr: Introduce SPAPR_IRQ_NR_IPIS to refer IRQ range for CPU IPIs., Nicholas Piggin, 2024/02/19
- [PULL 19/49] ppc/spapr: change pseries machine default to POWER10 CPU, Nicholas Piggin, 2024/02/19
- [PULL 20/49] spapr: Tag pseries-2.1 - 2.11 machines as deprecated, Nicholas Piggin, 2024/02/19
- [PULL 22/49] hw/pci-host/raven.c: Mark raven_io_ops as implementing unaligned accesses, Nicholas Piggin, 2024/02/19
- [PULL 23/49] misc/pca9552: Fix inverted input status, Nicholas Piggin, 2024/02/19
- [PULL 24/49] misc/pca9552: Let external devices set pca9552 inputs,
Nicholas Piggin <=
- [PULL 21/49] ppc/pnv: Change powernv default to powernv10, Nicholas Piggin, 2024/02/19
- [PULL 25/49] ppc/pnv: New powernv10-rainier machine type, Nicholas Piggin, 2024/02/19
- [PULL 26/49] ppc/pnv: Add pca9552 to powernv10-rainier for PCIe hotplug power control, Nicholas Piggin, 2024/02/19
- [PULL 27/49] ppc/pnv: Wire up pca9552 GPIO pins for PCIe hotplug power control, Nicholas Piggin, 2024/02/19
- [PULL 28/49] ppc/pnv: Use resettable interface to reset child I2C buses, Nicholas Piggin, 2024/02/19
- [PULL 30/49] ppc/pnv: Add a pca9554 I2C device to powernv10-rainier, Nicholas Piggin, 2024/02/19
- [PULL 31/49] ppc/pnv: Test pnv i2c master and connected devices, Nicholas Piggin, 2024/02/19
- [PULL 33/49] hw/ppc: Add N1 chiplet model, Nicholas Piggin, 2024/02/19
- [PULL 29/49] misc: Add a pca9554 GPIO device model, Nicholas Piggin, 2024/02/19
- [PULL 32/49] hw/ppc: Add pnv nest pervasive common chiplet model, Nicholas Piggin, 2024/02/19