diff mbox series

[v2,2/4] hw/gpio: add PCA9538 8-bit GPIO expander

Message ID 20230208224339.270589-3-titusr@google.com
State New
Headers show
Series PCA I2C GPIO expanders | expand

Commit Message

Titus Rwantare Feb. 8, 2023, 10:43 p.m. UTC
The 8-bit expander has different register offsets than the 16-bit one,
  making them incompatible.

Reviewed-by: Hao Wu <wuhaotsh@google.com>
Signed-off-by: Titus Rwantare <titusr@google.com>
---
 hw/gpio/pca_i2c_gpio.c         | 94 ++++++++++++++++++++++++++++++++++
 include/hw/gpio/pca_i2c_gpio.h |  7 +++
 2 files changed, 101 insertions(+)

Comments

Philippe Mathieu-Daudé Feb. 9, 2023, 7:57 a.m. UTC | #1
On 8/2/23 23:43, Titus Rwantare wrote:
>    The 8-bit expander has different register offsets than the 16-bit one,
>    making them incompatible.

Following extract/deposit API suggestion on previous patch, an
alternatively is to use PCAGPIOClass::input_port/output_port/...
offset fields.

> Reviewed-by: Hao Wu <wuhaotsh@google.com>
> Signed-off-by: Titus Rwantare <titusr@google.com>
> ---
>   hw/gpio/pca_i2c_gpio.c         | 94 ++++++++++++++++++++++++++++++++++
>   include/hw/gpio/pca_i2c_gpio.h |  7 +++
>   2 files changed, 101 insertions(+)


> diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
> index 99322959e1..3ab7d19a97 100644
> --- a/include/hw/gpio/pca_i2c_gpio.h
> +++ b/include/hw/gpio/pca_i2c_gpio.h
> @@ -19,6 +19,7 @@
>   
>   #define PCA_I2C_MAX_PINS                     16
>   #define PCA6416_NUM_PINS                     16
> +#define PCA9538_NUM_PINS                     8
>   
>   typedef struct PCAGPIOClass {
>       I2CSlaveClass parent;
> @@ -62,8 +63,14 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
>   #define PCA6416_CONFIGURATION_PORT_0         0x06 /* read/write */
>   #define PCA6416_CONFIGURATION_PORT_1         0x07 /* read/write */
>   
> +#define PCA9538_INPUT_PORT                   0x00 /* read */
> +#define PCA9538_OUTPUT_PORT                  0x01 /* read/write */
> +#define PCA9538_POLARITY_INVERSION_PORT      0x02 /* read/write */
> +#define PCA9538_CONFIGURATION_PORT           0x03 /* read/write */

Something like this maybe:

     static uint8_t pca_i2c_gpio_recv(I2CSlave *i2c)
     {
         PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
         PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(i2c);
         unsigned shift = (ps->command) & pc->shift ? 8 : 0;
         uint8_t data;

         if (ps->command == pc->input_port) {
             data = extract16(ps->curr_input, shift, 8);
         } else if (ps->command == pc->output_port) {
             data = extract16(ps->curr_input, shift, 8);
         } else if (...) {

Maybe I'm over-engineering :)
diff mbox series

Patch

diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c
index 434a759453..fa69523556 100644
--- a/hw/gpio/pca_i2c_gpio.c
+++ b/hw/gpio/pca_i2c_gpio.c
@@ -143,6 +143,41 @@  static uint8_t pca6416_recv(I2CSlave *i2c)
     return data;
 }
 
+/* slave to master */
+static uint8_t pca9538_recv(I2CSlave *i2c)
+{
+    PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+    uint8_t data;
+
+    switch (ps->command) {
+    case PCA9538_INPUT_PORT:
+        data = ps->curr_input;
+        break;
+
+    case PCA9538_OUTPUT_PORT:
+        data = ps->new_output;
+        break;
+
+    case PCA9538_POLARITY_INVERSION_PORT:
+        data = ps->polarity_inv;
+        break;
+
+    case PCA9538_CONFIGURATION_PORT:
+        data = ps->config;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: reading from unsupported register 0x%02x",
+                      __func__, ps->command);
+        data = 0xFF;
+        break;
+    }
+
+    trace_pca_i2c_recv(DEVICE(ps)->canonical_path, ps->command, data);
+    return data;
+}
+
 /* master to slave */
 static int pca6416_send(I2CSlave *i2c, uint8_t data)
 {
@@ -203,6 +238,47 @@  static int pca6416_send(I2CSlave *i2c, uint8_t data)
     return 0;
 }
 
+/* master to slave */
+static int pca9538_send(I2CSlave *i2c, uint8_t data)
+{
+    PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
+    if (ps->i2c_cmd) {
+        ps->command = data;
+        ps->i2c_cmd = false;
+        return 0;
+    }
+
+    trace_pca_i2c_send(DEVICE(ps)->canonical_path, ps->command, data);
+
+    switch (ps->command) {
+    case PCA9538_INPUT_PORT:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: writing to read only reg: 0x%02x",
+                      __func__, ps->command);
+        break;
+    case PCA9538_OUTPUT_PORT:
+        ps->new_output = data;
+        break;
+
+    case PCA9538_POLARITY_INVERSION_PORT:
+        ps->polarity_inv = data;
+        break;
+
+    case PCA9538_CONFIGURATION_PORT:
+        ps->config = data;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: writing to unsupported register\n",
+                      __func__);
+        return -1;
+    }
+
+    pca_i2c_update_irqs(ps);
+
+    return 0;
+}
+
 static int pca_i2c_event(I2CSlave *i2c, enum i2c_event event)
 {
     PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
@@ -337,6 +413,19 @@  static void pca6416_gpio_class_init(ObjectClass *klass, void *data)
     k->send = pca6416_send;
 }
 
+static void pca9538_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+    PCAGPIOClass *pc = PCA_I2C_GPIO_CLASS(klass);
+
+    dc->desc = "PCA9538 8-bit I/O expander";
+    pc->num_pins = PCA9538_NUM_PINS;
+
+    k->recv = pca9538_recv;
+    k->send = pca9538_send;
+}
+
 static void pca_i2c_gpio_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -383,6 +472,11 @@  static const TypeInfo pca_gpio_types[] = {
     .parent = TYPE_PCA_I2C_GPIO,
     .class_init = pca6416_gpio_class_init,
     },
+    {
+    .name = TYPE_PCA9538_GPIO,
+    .parent = TYPE_PCA_I2C_GPIO,
+    .class_init = pca9538_gpio_class_init,
+    },
 };
 
 DEFINE_TYPES(pca_gpio_types);
diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h
index 99322959e1..3ab7d19a97 100644
--- a/include/hw/gpio/pca_i2c_gpio.h
+++ b/include/hw/gpio/pca_i2c_gpio.h
@@ -19,6 +19,7 @@ 
 
 #define PCA_I2C_MAX_PINS                     16
 #define PCA6416_NUM_PINS                     16
+#define PCA9538_NUM_PINS                     8
 
 typedef struct PCAGPIOClass {
     I2CSlaveClass parent;
@@ -62,8 +63,14 @@  OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
 #define PCA6416_CONFIGURATION_PORT_0         0x06 /* read/write */
 #define PCA6416_CONFIGURATION_PORT_1         0x07 /* read/write */
 
+#define PCA9538_INPUT_PORT                   0x00 /* read */
+#define PCA9538_OUTPUT_PORT                  0x01 /* read/write */
+#define PCA9538_POLARITY_INVERSION_PORT      0x02 /* read/write */
+#define PCA9538_CONFIGURATION_PORT           0x03 /* read/write */
+
 #define PCA_I2C_CONFIG_DEFAULT               0
 
 #define TYPE_PCA6416_GPIO "pca6416"
+#define TYPE_PCA9538_GPIO "pca9538"
 
 #endif