All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] misc/pca9552: Changes to support powernv10
@ 2023-10-19 20:40 Glenn Miles
  2023-10-19 20:40 ` [PATCH 1/2] misc/pca9552: Fix inverted input status Glenn Miles
  2023-10-19 20:40 ` [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs Glenn Miles
  0 siblings, 2 replies; 10+ messages in thread
From: Glenn Miles @ 2023-10-19 20:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Glenn Miles, qemu-arm, Cédric Le Goater, andrew, Joel Stanley

This is a series of patches targeted at getting the pca9552
model ready for use by the powernv10 machine.

Glenn Miles (2):
  misc/pca9552: Fix inverted input status
  misc/pca9552: Let external devices set pca9552 inputs

 hw/misc/pca9552.c          | 51 +++++++++++++++++++++++++++++++++-----
 include/hw/misc/pca9552.h  |  3 ++-
 tests/qtest/pca9552-test.c |  6 ++---
 3 files changed, 50 insertions(+), 10 deletions(-)

-- 
2.31.1



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-19 20:40 [PATCH 0/2] misc/pca9552: Changes to support powernv10 Glenn Miles
@ 2023-10-19 20:40 ` Glenn Miles
  2023-10-20  2:51   ` Andrew Jeffery
  2023-10-19 20:40 ` [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs Glenn Miles
  1 sibling, 1 reply; 10+ messages in thread
From: Glenn Miles @ 2023-10-19 20:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Glenn Miles, qemu-arm, Cédric Le Goater, andrew, Joel Stanley

The pca9552 INPUT0 and INPUT1 registers are supposed to
hold the logical values of the LED pins.  A logical 0
should be seen in the INPUT0/1 registers for a pin when
its corresponding LSn bits are set to 0, which is also
the state needed for turning on an LED in a typical
usage scenario.  Existing code was doing the opposite
and setting INPUT0/1 bit to a 1 when the LSn bit was
set to 0, so this commit fixes that.

Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
---

Changes from prior version:
    - Added comment regarding pca953X
    - Added cover letter

 hw/misc/pca9552.c          | 18 +++++++++++++-----
 tests/qtest/pca9552-test.c |  6 +++---
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index fff19e369a..445f56a9e8 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
 
 DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
                        TYPE_PCA955X)
-
+/*
+ * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
+ *        chips are the reverse of the PCA953X family of chips.
+ */
 #define PCA9552_LED_ON   0x0
 #define PCA9552_LED_OFF  0x1
 #define PCA9552_LED_PWM0 0x2
@@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
 
         switch (config) {
         case PCA9552_LED_ON:
-            qemu_set_irq(s->gpio[i], 1);
-            s->regs[input_reg] |= 1 << input_shift;
-            break;
-        case PCA9552_LED_OFF:
+            /* Pin is set to 0V to turn on LED */
             qemu_set_irq(s->gpio[i], 0);
             s->regs[input_reg] &= ~(1 << input_shift);
             break;
+        case PCA9552_LED_OFF:
+            /*
+             * Pin is set to Hi-Z to turn off LED and
+             * pullup sets it to a logical 1.
+             */
+            qemu_set_irq(s->gpio[i], 1);
+            s->regs[input_reg] |= 1 << input_shift;
+            break;
         case PCA9552_LED_PWM0:
         case PCA9552_LED_PWM1:
             /* TODO */
diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
index d80ed93cd3..ccca2b3d91 100644
--- a/tests/qtest/pca9552-test.c
+++ b/tests/qtest/pca9552-test.c
@@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
     g_assert_cmphex(value, ==, 0x55);
 
     value = i2c_get8(i2cdev, PCA9552_INPUT0);
-    g_assert_cmphex(value, ==, 0x0);
+    g_assert_cmphex(value, ==, 0xFF);
 
     pca9552_init(i2cdev);
 
@@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
     g_assert_cmphex(value, ==, 0x54);
 
     value = i2c_get8(i2cdev, PCA9552_INPUT0);
-    g_assert_cmphex(value, ==, 0x01);
+    g_assert_cmphex(value, ==, 0xFE);
 
     value = i2c_get8(i2cdev, PCA9552_LS3);
     g_assert_cmphex(value, ==, 0x54);
 
     value = i2c_get8(i2cdev, PCA9552_INPUT1);
-    g_assert_cmphex(value, ==, 0x10);
+    g_assert_cmphex(value, ==, 0xEF);
 }
 
 static void pca9552_register_nodes(void)
-- 
2.31.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs
  2023-10-19 20:40 [PATCH 0/2] misc/pca9552: Changes to support powernv10 Glenn Miles
  2023-10-19 20:40 ` [PATCH 1/2] misc/pca9552: Fix inverted input status Glenn Miles
@ 2023-10-19 20:40 ` Glenn Miles
  2023-10-20  4:48   ` Andrew Jeffery
  1 sibling, 1 reply; 10+ messages in thread
From: Glenn Miles @ 2023-10-19 20:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: Glenn Miles, qemu-arm, Cédric Le Goater, andrew, Joel Stanley

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 |
            ------+------+-----+

Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
---

Changes from previous version:
 - Added #define's for external state values
 - Added logic state table to commit message
 - Added cover letter

 hw/misc/pca9552.c         | 41 ++++++++++++++++++++++++++++++++++-----
 include/hw/misc/pca9552.h |  3 ++-
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 445f56a9e8..ed814d1f98 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"};
 
@@ -116,16 +118,22 @@ static void pca955x_update_pin_input(PCA955xState *s)
         switch (config) {
         case PCA9552_LED_ON:
             /* Pin is set to 0V to turn on LED */
-            qemu_set_irq(s->gpio[i], 0);
+            qemu_set_irq(s->gpio_out[i], 0);
             s->regs[input_reg] &= ~(1 << input_shift);
             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) {
+                qemu_set_irq(s->gpio_out[i], 0);
+                s->regs[input_reg] &= ~(1 << input_shift);
+            } else {
+                qemu_set_irq(s->gpio_out[i], 1);
+                s->regs[input_reg] |= 1 << input_shift;
+            }
             break;
         case PCA9552_LED_PWM0:
         case PCA9552_LED_PWM1:
@@ -340,6 +348,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 +367,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 +390,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 +419,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.31.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-19 20:40 ` [PATCH 1/2] misc/pca9552: Fix inverted input status Glenn Miles
@ 2023-10-20  2:51   ` Andrew Jeffery
  2023-10-20  9:42     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Jeffery @ 2023-10-20  2:51 UTC (permalink / raw)
  To: Glenn Miles, qemu-devel
  Cc: qemu-arm, Cédric Le Goater, Joel Stanley, f4bug

On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> > The pca9552 INPUT0 and INPUT1 registers are supposed to
> > hold the logical values of the LED pins.  A logical 0
> > should be seen in the INPUT0/1 registers for a pin when
> > its corresponding LSn bits are set to 0, which is also
> > the state needed for turning on an LED in a typical
> > usage scenario.  Existing code was doing the opposite
> > and setting INPUT0/1 bit to a 1 when the LSn bit was
> > set to 0, so this commit fixes that.
> > 
> > Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> > ---
> > 
> > Changes from prior version:
> >     - Added comment regarding pca953X
> >     - Added cover letter
> > 
> >  hw/misc/pca9552.c          | 18 +++++++++++++-----
> >  tests/qtest/pca9552-test.c |  6 +++---
> >  2 files changed, 16 insertions(+), 8 deletions(-)
> > 
> > diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> > index fff19e369a..445f56a9e8 100644
> > --- a/hw/misc/pca9552.c
> > +++ b/hw/misc/pca9552.c
> > @@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
> >  
> >  DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
> >                         TYPE_PCA955X)
> > -
> > +/*
> > + * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
> > + *        chips are the reverse of the PCA953X family of chips.
> > + */
> >  #define PCA9552_LED_ON   0x0
> >  #define PCA9552_LED_OFF  0x1
> >  #define PCA9552_LED_PWM0 0x2
> > @@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
> >  
> >          switch (config) {
> >          case PCA9552_LED_ON:
> > -            qemu_set_irq(s->gpio[i], 1);
> > -            s->regs[input_reg] |= 1 << input_shift;
> > -            break;
> > -        case PCA9552_LED_OFF:
> > +            /* Pin is set to 0V to turn on LED */
> >              qemu_set_irq(s->gpio[i], 0);
> >              s->regs[input_reg] &= ~(1 << input_shift);
> >              break;
> > +        case PCA9552_LED_OFF:
> > +            /*
> > +             * Pin is set to Hi-Z to turn off LED and
> > +             * pullup sets it to a logical 1.
> > +             */
> > +            qemu_set_irq(s->gpio[i], 1);
> > +            s->regs[input_reg] |= 1 << input_shift;
> > +            break;

So the witherspoon-bmc machine was a user of the PCA9552 outputs as
LEDs. I guess its LEDs were in the wrong state the whole time? That
looks like the only user though, and shouldn't be negatively affected.

Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>

> >          case PCA9552_LED_PWM0:
> >          case PCA9552_LED_PWM1:
> >              /* TODO */
> > diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c
> > index d80ed93cd3..ccca2b3d91 100644
> > --- a/tests/qtest/pca9552-test.c
> > +++ b/tests/qtest/pca9552-test.c
> > @@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
> >      g_assert_cmphex(value, ==, 0x55);
> >  
> >      value = i2c_get8(i2cdev, PCA9552_INPUT0);
> > -    g_assert_cmphex(value, ==, 0x0);
> > +    g_assert_cmphex(value, ==, 0xFF);
> >  
> >      pca9552_init(i2cdev);
> >  
> > @@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
> >      g_assert_cmphex(value, ==, 0x54);
> >  
> >      value = i2c_get8(i2cdev, PCA9552_INPUT0);
> > -    g_assert_cmphex(value, ==, 0x01);
> > +    g_assert_cmphex(value, ==, 0xFE);
> >  
> >      value = i2c_get8(i2cdev, PCA9552_LS3);
> >      g_assert_cmphex(value, ==, 0x54);
> >  
> >      value = i2c_get8(i2cdev, PCA9552_INPUT1);
> > -    g_assert_cmphex(value, ==, 0x10);
> > +    g_assert_cmphex(value, ==, 0xEF);
> >  }
> >  
> >  static void pca9552_register_nodes(void)




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs
  2023-10-19 20:40 ` [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs Glenn Miles
@ 2023-10-20  4:48   ` Andrew Jeffery
  2023-10-20 17:46     ` Miles Glenn
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Jeffery @ 2023-10-20  4:48 UTC (permalink / raw)
  To: Glenn Miles, qemu-devel; +Cc: qemu-arm, Cédric Le Goater, Joel Stanley

On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> 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 |
>             ------+------+-----+
> 
> Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> ---
> 
> Changes from previous version:
>  - Added #define's for external state values
>  - Added logic state table to commit message
>  - Added cover letter
> 
>  hw/misc/pca9552.c         | 41 ++++++++++++++++++++++++++++++++++-----
>  include/hw/misc/pca9552.h |  3 ++-
>  2 files changed, 38 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> index 445f56a9e8..ed814d1f98 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"};
>  
> @@ -116,16 +118,22 @@ static void pca955x_update_pin_input(PCA955xState *s)
>          switch (config) {
>          case PCA9552_LED_ON:
>              /* Pin is set to 0V to turn on LED */
> -            qemu_set_irq(s->gpio[i], 0);
> +            qemu_set_irq(s->gpio_out[i], 0);

pca955x_update_pin_input() is called by the external GPIO handling path
as well as the I2C command handling path. If the I2C path sets the line
low followed by the device attached to the GPIO setting the line low
then I think each change will issue an event on the outbound GPIO. Is
that desired behaviour? Does it matter?

>              s->regs[input_reg] &= ~(1 << input_shift);
>              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) {
> +                qemu_set_irq(s->gpio_out[i], 0);

Similarly here - it might be the case that both devices have pulled the
line low and now the I2C path is releasing it. Given the external
device had already pulled the line low as well should we expect an
event to occur issued here? Should it matter?

Andrew

> +                s->regs[input_reg] &= ~(1 << input_shift);
> +            } else {
> +                qemu_set_irq(s->gpio_out[i], 1);
> +                s->regs[input_reg] |= 1 << input_shift;
> +            }
>              break;
>          case PCA9552_LED_PWM0:
>          case PCA9552_LED_PWM1:
> @@ -340,6 +348,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 +367,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 +390,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 +419,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 */
>  };
>  



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-20  2:51   ` Andrew Jeffery
@ 2023-10-20  9:42     ` Philippe Mathieu-Daudé
  2023-10-20 16:32       ` Miles Glenn
  0 siblings, 1 reply; 10+ messages in thread
From: Philippe Mathieu-Daudé @ 2023-10-20  9:42 UTC (permalink / raw)
  To: Andrew Jeffery, Glenn Miles, qemu-devel
  Cc: qemu-arm, Cédric Le Goater, Joel Stanley, f4bug

On 20/10/23 04:51, Andrew Jeffery wrote:
> On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
>>> The pca9552 INPUT0 and INPUT1 registers are supposed to
>>> hold the logical values of the LED pins.  A logical 0
>>> should be seen in the INPUT0/1 registers for a pin when
>>> its corresponding LSn bits are set to 0, which is also
>>> the state needed for turning on an LED in a typical
>>> usage scenario.  Existing code was doing the opposite
>>> and setting INPUT0/1 bit to a 1 when the LSn bit was
>>> set to 0, so this commit fixes that.
>>>
>>> Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
>>> ---
>>>
>>> Changes from prior version:
>>>      - Added comment regarding pca953X
>>>      - Added cover letter
>>>
>>>   hw/misc/pca9552.c          | 18 +++++++++++++-----
>>>   tests/qtest/pca9552-test.c |  6 +++---
>>>   2 files changed, 16 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
>>> index fff19e369a..445f56a9e8 100644
>>> --- a/hw/misc/pca9552.c
>>> +++ b/hw/misc/pca9552.c
>>> @@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
>>>   
>>>   DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
>>>                          TYPE_PCA955X)
>>> -
>>> +/*
>>> + * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
>>> + *        chips are the reverse of the PCA953X family of chips.
>>> + */
>>>   #define PCA9552_LED_ON   0x0
>>>   #define PCA9552_LED_OFF  0x1
>>>   #define PCA9552_LED_PWM0 0x2
>>> @@ -112,13 +115,18 @@ static void pca955x_update_pin_input(PCA955xState *s)
>>>   
>>>           switch (config) {
>>>           case PCA9552_LED_ON:
>>> -            qemu_set_irq(s->gpio[i], 1);
>>> -            s->regs[input_reg] |= 1 << input_shift;
>>> -            break;
>>> -        case PCA9552_LED_OFF:
>>> +            /* Pin is set to 0V to turn on LED */
>>>               qemu_set_irq(s->gpio[i], 0);
>>>               s->regs[input_reg] &= ~(1 << input_shift);
>>>               break;
>>> +        case PCA9552_LED_OFF:
>>> +            /*
>>> +             * Pin is set to Hi-Z to turn off LED and
>>> +             * pullup sets it to a logical 1.
>>> +             */
>>> +            qemu_set_irq(s->gpio[i], 1);
>>> +            s->regs[input_reg] |= 1 << input_shift;
>>> +            break;
> 
> So the witherspoon-bmc machine was a user of the PCA9552 outputs as
> LEDs. I guess its LEDs were in the wrong state the whole time? That
> looks like the only user though, and shouldn't be negatively affected.

Usually GPIO polarity is a machine/board property, not a device
one.

We could use the LED API (hw/misc/led.h) which initialize each
output with GpioPolarity.



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-20  9:42     ` Philippe Mathieu-Daudé
@ 2023-10-20 16:32       ` Miles Glenn
  2023-10-23 23:34         ` Andrew Jeffery
  0 siblings, 1 reply; 10+ messages in thread
From: Miles Glenn @ 2023-10-20 16:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Andrew Jeffery, qemu-devel
  Cc: qemu-arm, Cédric Le Goater, Joel Stanley, f4bug

On Fri, 2023-10-20 at 11:42 +0200, Philippe Mathieu-Daudé wrote:
> On 20/10/23 04:51, Andrew Jeffery wrote:
> > On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> > > > The pca9552 INPUT0 and INPUT1 registers are supposed to
> > > > hold the logical values of the LED pins.  A logical 0
> > > > should be seen in the INPUT0/1 registers for a pin when
> > > > its corresponding LSn bits are set to 0, which is also
> > > > the state needed for turning on an LED in a typical
> > > > usage scenario.  Existing code was doing the opposite
> > > > and setting INPUT0/1 bit to a 1 when the LSn bit was
> > > > set to 0, so this commit fixes that.
> > > > 
> > > > Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> > > > ---
> > > > 
> > > > Changes from prior version:
> > > >      - Added comment regarding pca953X
> > > >      - Added cover letter
> > > > 
> > > >   hw/misc/pca9552.c          | 18 +++++++++++++-----
> > > >   tests/qtest/pca9552-test.c |  6 +++---
> > > >   2 files changed, 16 insertions(+), 8 deletions(-)
> > > > 
> > > > diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> > > > index fff19e369a..445f56a9e8 100644
> > > > --- a/hw/misc/pca9552.c
> > > > +++ b/hw/misc/pca9552.c
> > > > @@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
> > > >   
> > > >   DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
> > > >                          TYPE_PCA955X)
> > > > -
> > > > +/*
> > > > + * Note:  The LED_ON and LED_OFF configuration values for the
> > > > PCA955X
> > > > + *        chips are the reverse of the PCA953X family of
> > > > chips.
> > > > + */
> > > >   #define PCA9552_LED_ON   0x0
> > > >   #define PCA9552_LED_OFF  0x1
> > > >   #define PCA9552_LED_PWM0 0x2
> > > > @@ -112,13 +115,18 @@ static void
> > > > pca955x_update_pin_input(PCA955xState *s)
> > > >   
> > > >           switch (config) {
> > > >           case PCA9552_LED_ON:
> > > > -            qemu_set_irq(s->gpio[i], 1);
> > > > -            s->regs[input_reg] |= 1 << input_shift;
> > > > -            break;
> > > > -        case PCA9552_LED_OFF:
> > > > +            /* Pin is set to 0V to turn on LED */
> > > >               qemu_set_irq(s->gpio[i], 0);
> > > >               s->regs[input_reg] &= ~(1 << input_shift);
> > > >               break;
> > > > +        case PCA9552_LED_OFF:
> > > > +            /*
> > > > +             * Pin is set to Hi-Z to turn off LED and
> > > > +             * pullup sets it to a logical 1.
> > > > +             */
> > > > +            qemu_set_irq(s->gpio[i], 1);
> > > > +            s->regs[input_reg] |= 1 << input_shift;
> > > > +            break;
> > 
> > So the witherspoon-bmc machine was a user of the PCA9552 outputs as
> > LEDs. I guess its LEDs were in the wrong state the whole time? That
> > looks like the only user though, and shouldn't be negatively
> > affected.
> 
> Usually GPIO polarity is a machine/board property, not a device
> one.
> 
> We could use the LED API (hw/misc/led.h) which initialize each
> output with GpioPolarity.
> 

Thanks for your comments!   This piqued my curiosity so I decided to
run a test with the witherspoon-bmc machine.  Without my changes, I ran
the following command to turn off LED13 on the pca9552 which I had
previously set to "on":

  qom-set /machine/unattached/device[18] led13 off

I had GDB connected at the time with a breakpoint set on
led_set_state() so that I could see what was happening.  Due to the
inversion bug, I expected the pca9552 code to set the pin low and also
set the irq low, which it did.  The connected LED's on this pca9552
were all configured as GPIO_POLARITY_ACTIVE_LOW, so I expected that
setting the irq state low would actually turn on the LED.  Instead it
turned off the LED.

Reviewing the LED code, I believe the problem lies here:

static void led_set_state_gpio_handler(void *opaque, int line, int
new_state)
{
    LEDState *s = LED(opaque);

    assert(line == 0);
    led_set_state(s, !!new_state != s->gpio_active_high);
}


In my test, new_state was 0 and gpio_active_high was 0, resulting in
the boolean expression of ( 0 != 0 ) which is false and results in
turning off the LED.  So, this looks like a bug to me.

For another simpler example, if the LED polarity was set to
GPIO_POLARITY_ACTIVE_HIGH, then s->gpio_active_high would be 1.  Then,
if we set the irq line to 1, wouldn't we expect the LED to turn on? 
However, as the code stands, it would actually turn the LED off.  So, I
think we can remove one of the "!"'s from in front of new_state.  Then,
if the LED is active high and the irq line is set high, it would turn
on the LED.  Correct?

Thanks,

Glenn



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs
  2023-10-20  4:48   ` Andrew Jeffery
@ 2023-10-20 17:46     ` Miles Glenn
  0 siblings, 0 replies; 10+ messages in thread
From: Miles Glenn @ 2023-10-20 17:46 UTC (permalink / raw)
  To: Andrew Jeffery, qemu-devel; +Cc: qemu-arm, Cédric Le Goater, Joel Stanley

On Fri, 2023-10-20 at 15:18 +1030, Andrew Jeffery wrote:
> On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> > 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 |
> >             ------+------+-----+
> > 
> > Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> > ---
> > 
> > Changes from previous version:
> >  - Added #define's for external state values
> >  - Added logic state table to commit message
> >  - Added cover letter
> > 
> >  hw/misc/pca9552.c         | 41 ++++++++++++++++++++++++++++++++++-
> > ----
> >  include/hw/misc/pca9552.h |  3 ++-
> >  2 files changed, 38 insertions(+), 6 deletions(-)
> > 
> > diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> > index 445f56a9e8..ed814d1f98 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"};
> >  
> > @@ -116,16 +118,22 @@ static void
> > pca955x_update_pin_input(PCA955xState *s)
> >          switch (config) {
> >          case PCA9552_LED_ON:
> >              /* Pin is set to 0V to turn on LED */
> > -            qemu_set_irq(s->gpio[i], 0);
> > +            qemu_set_irq(s->gpio_out[i], 0);
> 
> pca955x_update_pin_input() is called by the external GPIO handling
> path
> as well as the I2C command handling path. If the I2C path sets the
> line
> low followed by the device attached to the GPIO setting the line low
> then I think each change will issue an event on the outbound GPIO. Is
> that desired behaviour? Does it matter?
> 

I think these questions probably depend a lot on the recieving device,
but at best, I think it's inefficient and at worst, depending on the
recieving device, it could lead to bugs, so I'll add a check.

 
> >              s->regs[input_reg] &= ~(1 << input_shift);
> >              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) {
> > +                qemu_set_irq(s->gpio_out[i], 0);
> 
> Similarly here - it might be the case that both devices have pulled
> the
> line low and now the I2C path is releasing it. Given the external
> device had already pulled the line low as well should we expect an
> event to occur issued here? Should it matter?
> 
> Andrew
> 

See previous response.

Thanks for the review!

Glenn

> > +                s->regs[input_reg] &= ~(1 << input_shift);
> > +            } else {
> > +                qemu_set_irq(s->gpio_out[i], 1);
> > +                s->regs[input_reg] |= 1 << input_shift;
> > +            }
> >              break;
> >          case PCA9552_LED_PWM0:
> >          case PCA9552_LED_PWM1:
> > @@ -340,6 +348,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 +367,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 +390,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 +419,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 */
> >  };
> >  



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-20 16:32       ` Miles Glenn
@ 2023-10-23 23:34         ` Andrew Jeffery
  2023-10-24 16:46           ` Miles Glenn
  0 siblings, 1 reply; 10+ messages in thread
From: Andrew Jeffery @ 2023-10-23 23:34 UTC (permalink / raw)
  To: Miles Glenn, Philippe Mathieu-Daudé, qemu-devel
  Cc: qemu-arm, Cédric Le Goater, Joel Stanley, f4bug

On Fri, 2023-10-20 at 11:32 -0500, Miles Glenn wrote:
> On Fri, 2023-10-20 at 11:42 +0200, Philippe Mathieu-Daudé wrote:
> > On 20/10/23 04:51, Andrew Jeffery wrote:
> > > On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> > > > > The pca9552 INPUT0 and INPUT1 registers are supposed to
> > > > > hold the logical values of the LED pins.  A logical 0
> > > > > should be seen in the INPUT0/1 registers for a pin when
> > > > > its corresponding LSn bits are set to 0, which is also
> > > > > the state needed for turning on an LED in a typical
> > > > > usage scenario.  Existing code was doing the opposite
> > > > > and setting INPUT0/1 bit to a 1 when the LSn bit was
> > > > > set to 0, so this commit fixes that.
> > > > > 
> > > > > Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> > > > > ---
> > > > > 
> > > > > Changes from prior version:
> > > > >      - Added comment regarding pca953X
> > > > >      - Added cover letter
> > > > > 
> > > > >   hw/misc/pca9552.c          | 18 +++++++++++++-----
> > > > >   tests/qtest/pca9552-test.c |  6 +++---
> > > > >   2 files changed, 16 insertions(+), 8 deletions(-)
> > > > > 
> > > > > diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> > > > > index fff19e369a..445f56a9e8 100644
> > > > > --- a/hw/misc/pca9552.c
> > > > > +++ b/hw/misc/pca9552.c
> > > > > @@ -36,7 +36,10 @@ typedef struct PCA955xClass PCA955xClass;
> > > > >   
> > > > >   DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
> > > > >                          TYPE_PCA955X)
> > > > > -
> > > > > +/*
> > > > > + * Note:  The LED_ON and LED_OFF configuration values for the
> > > > > PCA955X
> > > > > + *        chips are the reverse of the PCA953X family of
> > > > > chips.
> > > > > + */
> > > > >   #define PCA9552_LED_ON   0x0
> > > > >   #define PCA9552_LED_OFF  0x1
> > > > >   #define PCA9552_LED_PWM0 0x2
> > > > > @@ -112,13 +115,18 @@ static void
> > > > > pca955x_update_pin_input(PCA955xState *s)
> > > > >   
> > > > >           switch (config) {
> > > > >           case PCA9552_LED_ON:
> > > > > -            qemu_set_irq(s->gpio[i], 1);
> > > > > -            s->regs[input_reg] |= 1 << input_shift;
> > > > > -            break;
> > > > > -        case PCA9552_LED_OFF:
> > > > > +            /* Pin is set to 0V to turn on LED */
> > > > >               qemu_set_irq(s->gpio[i], 0);
> > > > >               s->regs[input_reg] &= ~(1 << input_shift);
> > > > >               break;
> > > > > +        case PCA9552_LED_OFF:
> > > > > +            /*
> > > > > +             * Pin is set to Hi-Z to turn off LED and
> > > > > +             * pullup sets it to a logical 1.
> > > > > +             */
> > > > > +            qemu_set_irq(s->gpio[i], 1);
> > > > > +            s->regs[input_reg] |= 1 << input_shift;
> > > > > +            break;
> > > 
> > > So the witherspoon-bmc machine was a user of the PCA9552 outputs as
> > > LEDs. I guess its LEDs were in the wrong state the whole time? That
> > > looks like the only user though, and shouldn't be negatively
> > > affected.
> > 
> > Usually GPIO polarity is a machine/board property, not a device
> > one.
> > 
> > We could use the LED API (hw/misc/led.h) which initialize each
> > output with GpioPolarity.
> > 
> 
> Thanks for your comments!   This piqued my curiosity so I decided to
> run a test with the witherspoon-bmc machine.  Without my changes, I ran
> the following command to turn off LED13 on the pca9552 which I had
> previously set to "on":
> 
>   qom-set /machine/unattached/device[18] led13 off
> 
> I had GDB connected at the time with a breakpoint set on
> led_set_state() so that I could see what was happening.  Due to the
> inversion bug, I expected the pca9552 code to set the pin low and also
> set the irq low, which it did.  The connected LED's on this pca9552
> were all configured as GPIO_POLARITY_ACTIVE_LOW, so I expected that
> setting the irq state low would actually turn on the LED.  Instead it
> turned off the LED.
> 
> Reviewing the LED code, I believe the problem lies here:
> 
> static void led_set_state_gpio_handler(void *opaque, int line, int
> new_state)
> {
>     LEDState *s = LED(opaque);
> 
>     assert(line == 0);
>     led_set_state(s, !!new_state != s->gpio_active_high);
> }
> 
> 
> In my test, new_state was 0 and gpio_active_high was 0, resulting in
> the boolean expression of ( 0 != 0 ) which is false and results in
> turning off the LED.  So, this looks like a bug to me.
> 
> For another simpler example, if the LED polarity was set to
> GPIO_POLARITY_ACTIVE_HIGH, then s->gpio_active_high would be 1.  Then,
> if we set the irq line to 1, wouldn't we expect the LED to turn on? 
> However, as the code stands, it would actually turn the LED off.  So, I
> think we can remove one of the "!"'s from in front of new_state.  Then,
> if the LED is active high and the irq line is set high, it would turn
> on the LED.  Correct?

Seems reasonable to me. Looks like Philippe added the LED behaviour in
Fddb67f6402b8 ("hw/misc/led: Allow connecting from GPIO output"), so
his input would be helpful. Perhaps send a fix for review?

Andrew


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] misc/pca9552: Fix inverted input status
  2023-10-23 23:34         ` Andrew Jeffery
@ 2023-10-24 16:46           ` Miles Glenn
  0 siblings, 0 replies; 10+ messages in thread
From: Miles Glenn @ 2023-10-24 16:46 UTC (permalink / raw)
  To: Andrew Jeffery, Philippe Mathieu-Daudé, qemu-devel
  Cc: qemu-arm, Cédric Le Goater, Joel Stanley, f4bug

On Tue, 2023-10-24 at 10:04 +1030, Andrew Jeffery wrote:
> On Fri, 2023-10-20 at 11:32 -0500, Miles Glenn wrote:
> > On Fri, 2023-10-20 at 11:42 +0200, Philippe Mathieu-Daudé wrote:
> > > On 20/10/23 04:51, Andrew Jeffery wrote:
> > > > On Thu, 2023-10-19 at 15:40 -0500, Glenn Miles wrote:
> > > > > > The pca9552 INPUT0 and INPUT1 registers are supposed to
> > > > > > hold the logical values of the LED pins.  A logical 0
> > > > > > should be seen in the INPUT0/1 registers for a pin when
> > > > > > its corresponding LSn bits are set to 0, which is also
> > > > > > the state needed for turning on an LED in a typical
> > > > > > usage scenario.  Existing code was doing the opposite
> > > > > > and setting INPUT0/1 bit to a 1 when the LSn bit was
> > > > > > set to 0, so this commit fixes that.
> > > > > > 
> > > > > > Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
> > > > > > ---
> > > > > > 
> > > > > > Changes from prior version:
> > > > > >      - Added comment regarding pca953X
> > > > > >      - Added cover letter
> > > > > > 
> > > > > >   hw/misc/pca9552.c          | 18 +++++++++++++-----
> > > > > >   tests/qtest/pca9552-test.c |  6 +++---
> > > > > >   2 files changed, 16 insertions(+), 8 deletions(-)
> > > > > > 
> > > > > > diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
> > > > > > index fff19e369a..445f56a9e8 100644
> > > > > > --- a/hw/misc/pca9552.c
> > > > > > +++ b/hw/misc/pca9552.c
> > > > > > @@ -36,7 +36,10 @@ typedef struct PCA955xClass
> > > > > > PCA955xClass;
> > > > > >   
> > > > > >   DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
> > > > > >                          TYPE_PCA955X)
> > > > > > -
> > > > > > +/*
> > > > > > + * Note:  The LED_ON and LED_OFF configuration values for
> > > > > > the
> > > > > > PCA955X
> > > > > > + *        chips are the reverse of the PCA953X family of
> > > > > > chips.
> > > > > > + */
> > > > > >   #define PCA9552_LED_ON   0x0
> > > > > >   #define PCA9552_LED_OFF  0x1
> > > > > >   #define PCA9552_LED_PWM0 0x2
> > > > > > @@ -112,13 +115,18 @@ static void
> > > > > > pca955x_update_pin_input(PCA955xState *s)
> > > > > >   
> > > > > >           switch (config) {
> > > > > >           case PCA9552_LED_ON:
> > > > > > -            qemu_set_irq(s->gpio[i], 1);
> > > > > > -            s->regs[input_reg] |= 1 << input_shift;
> > > > > > -            break;
> > > > > > -        case PCA9552_LED_OFF:
> > > > > > +            /* Pin is set to 0V to turn on LED */
> > > > > >               qemu_set_irq(s->gpio[i], 0);
> > > > > >               s->regs[input_reg] &= ~(1 << input_shift);
> > > > > >               break;
> > > > > > +        case PCA9552_LED_OFF:
> > > > > > +            /*
> > > > > > +             * Pin is set to Hi-Z to turn off LED and
> > > > > > +             * pullup sets it to a logical 1.
> > > > > > +             */
> > > > > > +            qemu_set_irq(s->gpio[i], 1);
> > > > > > +            s->regs[input_reg] |= 1 << input_shift;
> > > > > > +            break;
> > > > 
> > > > So the witherspoon-bmc machine was a user of the PCA9552
> > > > outputs as
> > > > LEDs. I guess its LEDs were in the wrong state the whole time?
> > > > That
> > > > looks like the only user though, and shouldn't be negatively
> > > > affected.
> > > 
> > > Usually GPIO polarity is a machine/board property, not a device
> > > one.
> > > 
> > > We could use the LED API (hw/misc/led.h) which initialize each
> > > output with GpioPolarity.
> > > 
> > 
> > Thanks for your comments!   This piqued my curiosity so I decided
> > to
> > run a test with the witherspoon-bmc machine.  Without my changes, I
> > ran
> > the following command to turn off LED13 on the pca9552 which I had
> > previously set to "on":
> > 
> >   qom-set /machine/unattached/device[18] led13 off
> > 
> > I had GDB connected at the time with a breakpoint set on
> > led_set_state() so that I could see what was happening.  Due to the
> > inversion bug, I expected the pca9552 code to set the pin low and
> > also
> > set the irq low, which it did.  The connected LED's on this pca9552
> > were all configured as GPIO_POLARITY_ACTIVE_LOW, so I expected that
> > setting the irq state low would actually turn on the LED.  Instead
> > it
> > turned off the LED.
> > 
> > Reviewing the LED code, I believe the problem lies here:
> > 
> > static void led_set_state_gpio_handler(void *opaque, int line, int
> > new_state)
> > {
> >     LEDState *s = LED(opaque);
> > 
> >     assert(line == 0);
> >     led_set_state(s, !!new_state != s->gpio_active_high);
> > }
> > 
> > 
> > In my test, new_state was 0 and gpio_active_high was 0, resulting
> > in
> > the boolean expression of ( 0 != 0 ) which is false and results in
> > turning off the LED.  So, this looks like a bug to me.
> > 
> > For another simpler example, if the LED polarity was set to
> > GPIO_POLARITY_ACTIVE_HIGH, then s->gpio_active_high would be
> > 1.  Then,
> > if we set the irq line to 1, wouldn't we expect the LED to turn
> > on? 
> > However, as the code stands, it would actually turn the LED
> > off.  So, I
> > think we can remove one of the "!"'s from in front of
> > new_state.  Then,
> > if the LED is active high and the irq line is set high, it would
> > turn
> > on the LED.  Correct?
> 
> Seems reasonable to me. Looks like Philippe added the LED behaviour
> in
> Fddb67f6402b8 ("hw/misc/led: Allow connecting from GPIO output"), so
> his input would be helpful. Perhaps send a fix for review?
> 
> Andrew

Sounds good.  I'll do that.

Glenn



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-10-24 16:47 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-19 20:40 [PATCH 0/2] misc/pca9552: Changes to support powernv10 Glenn Miles
2023-10-19 20:40 ` [PATCH 1/2] misc/pca9552: Fix inverted input status Glenn Miles
2023-10-20  2:51   ` Andrew Jeffery
2023-10-20  9:42     ` Philippe Mathieu-Daudé
2023-10-20 16:32       ` Miles Glenn
2023-10-23 23:34         ` Andrew Jeffery
2023-10-24 16:46           ` Miles Glenn
2023-10-19 20:40 ` [PATCH 2/2] misc/pca9552: Let external devices set pca9552 inputs Glenn Miles
2023-10-20  4:48   ` Andrew Jeffery
2023-10-20 17:46     ` Miles Glenn

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.