qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander


From: Dong, Eddie
Subject: RE: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander
Date: Tue, 7 Feb 2023 01:29:13 +0000


> -----Original Message-----
> From: qemu-devel-bounces+eddie.dong=intel.com@nongnu.org <qemu-
> devel-bounces+eddie.dong=intel.com@nongnu.org> On Behalf Of Titus
> Rwantare
> Sent: Monday, February 6, 2023 11:50 AM
> To: peter.maydell@linaro.org
> Cc: qemu-arm@nongnu.org; qemu-devel@nongnu.org; Titus Rwantare
> <titusr@google.com>; Hao Wu <wuhaotsh@google.com>
> Subject: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander
> 
> This is a simple i2c device that allows i2c capable devices to have GPIOs.

This patch is to implement a device model of I2C to GPIO for PCA_xxx, right?
Or do you implement a general/common I2C to GPIO device? 
I think it is for the former one. In this case, the commit message is not clear.


> 
> Reviewed-by: Hao Wu <wuhaotsh@google.com>
> Signed-off-by: Titus Rwantare <titusr@google.com>
> ---
>  hw/arm/Kconfig                  |   1 +
>  hw/gpio/meson.build             |   1 +
>  hw/gpio/pca_i2c_gpio.c          | 362 ++++++++++++++++++++++++++++++++
>  hw/gpio/trace-events            |   5 +
>  hw/i2c/Kconfig                  |   4 +
>  include/hw/gpio/pca_i2c_gpio.h  |  72 +++++++
>  tests/qtest/meson.build         |   1 +
>  tests/qtest/pca_i2c_gpio-test.c | 169 +++++++++++++++
>  8 files changed, 615 insertions(+)
>  create mode 100644 hw/gpio/pca_i2c_gpio.c  create mode 100644
> include/hw/gpio/pca_i2c_gpio.h  create mode 100644
> tests/qtest/pca_i2c_gpio-test.c
> 
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index
> 2d157de9b8..1b533ddd76 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -418,6 +418,7 @@ config NPCM7XX
>      select SSI
>      select UNIMP
>      select PCA954X
> +    select PCA_I2C_GPIO
> 
>  config FSL_IMX25
>      bool
> diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index
> b726e6d27a..1e5b602002 100644
> --- a/hw/gpio/meson.build
> +++ b/hw/gpio/meson.build
> @@ -12,3 +12,4 @@ softmmu_ss.add(when: 'CONFIG_OMAP', if_true:
> files('omap_gpio.c'))
>  softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c'))
>  softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true:
> files('aspeed_gpio.c'))
>  softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
> +softmmu_ss.add(when: 'CONFIG_PCA_I2C_GPIO', if_true:
> +files('pca_i2c_gpio.c'))
> diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c new file
> mode 100644 index 0000000000..afae497a22
> --- /dev/null
> +++ b/hw/gpio/pca_i2c_gpio.c

Should this file be located under hw/i2c ?

> @@ -0,0 +1,362 @@
> +/*
> + * NXP PCA I2C GPIO Expanders
> + *
> + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with
> +interrupt output,
> + * reset, and configuration registers
> + *
> + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
> + *
> + * Copyright 2023 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * These devices, by default, are configured to input only. The
> +configuration is
> + * settable through qom/qmp, or i2c.To set some pins as inputs before
> +boot, use
> + * the following in the board file of the machine:
> + *      object_property_set_uint(Object *obj, const char *name,
> + *                               uint64_t value, Error **errp);
> + * specifying name as "gpio_config" and the value as a bitfield of the
> +inputs
> + * e.g. for the pca6416, a value of 0xFFF0, sets pins 0-3 as outputs
> +and
> + * 4-15 as inputs.
> + * This value can also be set at runtime through qmp externally, or by
> + * writing to the config register using i2c.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/gpio/pca_i2c_gpio.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +#include "qapi/visitor.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "trace.h"
> +
> +/*
> + * compare new_output to curr_output and update irq to match
> new_output
> + *
> + * The Input port registers (registers 0 and 1) reflect the incoming
> +logic
> + * levels of the pins, regardless of whether the pin is defined as an
> +input or
> + * an output by the Configuration register.
> + */
> +static void pca_i2c_update_irqs(PCAGPIOState *ps) {
> +    PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(ps);
> +    uint16_t out_diff = ps->new_output ^ ps->curr_output;
> +    uint16_t in_diff = ps->new_input ^ ps->curr_input;
> +    uint16_t mask, pin_i;
> +
> +    if (in_diff || out_diff) {
The spec says " A pin configured as an output cannot cause an interrupt".
Do we need to check out_diff here?

> +        for (int i = 0; i < pc->num_pins; i++) {
At least for PCA_6416A, the state of pins are described in UINT16. We can check
all together rather than bit scan/pin scan.

> +            mask = BIT(i);
> +            /* pin must be configured as an output to be set here */
> +            if (out_diff & ~ps->config & mask) {
> +                pin_i = mask & ps->new_output;
> +                qemu_set_irq(ps->output[i], pin_i > 0);
> +                ps->curr_output &= ~mask;
> +                ps->curr_output |= pin_i;
> +            }
> +
> +            if (in_diff & mask) {
> +                ps->curr_input &= ~mask;
> +                ps->curr_input |= mask & ps->new_input;
> +            }
> +        }
> +        /* make diff = 0 */
> +        ps->new_input = ps->curr_input;
> +    }
> +}
> +
> +static void pca_i2c_irq_handler(void *opaque, int n, int level) {
> +    PCAGPIOState *ps = opaque;
> +    PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(opaque);
> +    uint16_t mask = BIT(n);
> +
> +    g_assert(n < pc->num_pins);
> +    g_assert(n >= 0);
> +
> +    ps->new_input &= ~mask;
> +
> +    if (level > 0) {
> +        ps->new_input |= BIT(n);
> +    }
> +
> +    pca_i2c_update_irqs(ps);
> +}
> +
> +/* slave to master */
> +static uint8_t pca6416_recv(I2CSlave *i2c) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(i2c);
> +    uint8_t data;
> +
> +    switch (ps->command) {
If we use an array to define the state (or union with an array access),
we can avoid the duplicated code below.

> +    case PCA6416_INPUT_PORT_0:
> +        data = ps->curr_input;
> +        break;
> +
> +    case PCA6416_INPUT_PORT_1:
> +        data = ps->curr_input >> 8;
> +        break;
> +
> +    /*
> +     * i2c reads to the output registers reflect the values written
> +     * NOT the actual values of the gpios
> +     */
> +    case PCA6416_OUTPUT_PORT_0:
> +        data = ps->new_output;
> +        break;
> +
> +    case PCA6416_OUTPUT_PORT_1:
> +        data = ps->new_output >> 8;
> +        break;
> +
> +    case PCA6416_POLARITY_INVERSION_PORT_0:
> +        data = ps->polarity_inv;
> +        break;
> +
> +    case PCA6416_POLARITY_INVERSION_PORT_1:
> +        data = ps->polarity_inv >> 8;
> +        break;
> +
> +    case PCA6416_CONFIGURATION_PORT_0:
> +        data = ps->config;
> +        break;
> +
> +    case PCA6416_CONFIGURATION_PORT_1:
> +        data = ps->config >> 8;
> +        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) {
> +    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 PCA6416_INPUT_PORT_0:
> +    case PCA6416_INPUT_PORT_1:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: writing to read only reg:
> 0x%02x",
> +                      __func__, ps->command);
> +        break;
> +
> +    case PCA6416_OUTPUT_PORT_0:
> +        ps->new_output &= 0xFF00;
> +        ps->new_output |= data;
> +        break;
> +
> +    case PCA6416_OUTPUT_PORT_1:
> +        ps->new_output &= 0xFF;
> +        ps->new_output |= data << 8;
> +        break;
> +
> +    case PCA6416_POLARITY_INVERSION_PORT_0:
> +        ps->polarity_inv &= 0xFF00;
> +        ps->polarity_inv |= data;
> +        break;
> +
> +    case PCA6416_POLARITY_INVERSION_PORT_1:
> +        ps->polarity_inv &= 0xFF;
> +        ps->polarity_inv |= data << 8;
> +        break;
> +
> +    case PCA6416_CONFIGURATION_PORT_0:
> +        ps->config &= 0xFF00;
> +        ps->config |= data;
> +        break;
> +
> +    case PCA6416_CONFIGURATION_PORT_1:
> +        ps->config &= 0xFF;
> +        ps->config |= data << 8;
> +        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);
> +
> +    switch (event) {
> +    case I2C_START_RECV:
> +        trace_pca_i2c_event(DEVICE(ps)->canonical_path, "START_RECV");
> +        break;
> +
> +    case I2C_START_SEND:
> +        trace_pca_i2c_event(DEVICE(ps)->canonical_path, "START_SEND");
> +        ps->i2c_cmd = true;
> +        break;
> +
> +    case I2C_FINISH:
> +        trace_pca_i2c_event(DEVICE(ps)->canonical_path, "FINISH");
> +        break;
> +
> +    case I2C_NACK:
> +        trace_pca_i2c_event(DEVICE(ps)->canonical_path, "NACK");
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: unknown event
> 0x%x\n",
> +                      DEVICE(ps)->canonical_path, __func__, event);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +static void pca_i2c_config_get(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    visit_type_uint16(v, name, &ps->config, errp); }
> +
> +static void pca_i2c_config_set(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    if (!visit_type_uint16(v, name, &ps->config, errp)) {
> +        return;
> +    }
> +    pca_i2c_update_irqs(ps);
> +}
> +
> +
> +static void pca_i2c_input_get(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    visit_type_uint16(v, name, &ps->curr_input, errp); }
> +
> +static void pca_i2c_input_set(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    if (!visit_type_uint16(v, name, &ps->new_input, errp)) {
> +        return;
> +    }
> +    pca_i2c_update_irqs(ps);
> +}
> +
> +static void pca_i2c_output_get(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    visit_type_uint16(v, name, &ps->curr_output, errp); }
> +
> +static void pca_i2c_output_set(Object *obj, Visitor *v, const char *name,
> +                                void *opaque, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    if (!visit_type_uint16(v, name, &ps->new_output, errp)) {
> +        return;
> +    }
> +    pca_i2c_update_irqs(ps);
> +}
> +
> +static void pca_i2c_realize(DeviceState *dev, Error **errp) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(dev);
> +    pca_i2c_update_irqs(ps);
> +}
> +
> +static const VMStateDescription vmstate_pca_i2c_gpio = {
> +    .name = TYPE_PCA_I2C_GPIO,
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_I2C_SLAVE(parent, PCAGPIOState),
> +        VMSTATE_UINT16(polarity_inv, PCAGPIOState),
> +        VMSTATE_UINT16(config, PCAGPIOState),
> +        VMSTATE_UINT16(curr_input, PCAGPIOState),
> +        VMSTATE_UINT16(curr_output, PCAGPIOState),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void pca6416_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 = "PCA6416 16-bit I/O expander";
> +    dc->realize = pca_i2c_realize;
> +    dc->vmsd = &vmstate_pca_i2c_gpio;
> +
> +    k->event = pca_i2c_event;
> +    k->recv = pca6416_recv;
> +    k->send = pca6416_send;
> +
> +    pc->num_pins = PCA6416_NUM_PINS;
> +}
> +
> +static void pca_i2c_gpio_init(Object *obj) {
> +    PCAGPIOState *ps = PCA_I2C_GPIO(obj);
> +    PCAGPIOClass *pc = PCA_I2C_GPIO_GET_CLASS(obj);
> +    DeviceState *dev = DEVICE(obj);
> +
> +    ps->new_output = PCA_I2C_OUTPUT_DEFAULT;
> +    ps->config = PCA_I2C_CONFIG_DEFAULT;
> +
> +    object_property_add(obj, "gpio_input", "uint16",
> +                        pca_i2c_input_get,
> +                        pca_i2c_input_set, NULL, NULL);
> +    object_property_add(obj, "gpio_output", "uint16",
> +                        pca_i2c_output_get,
> +                        pca_i2c_output_set, NULL, NULL);
> +    object_property_add(obj, "gpio_config", "uint16",
> +                        pca_i2c_config_get,
> +                        pca_i2c_config_set, NULL, NULL);
> +    qdev_init_gpio_in(dev, pca_i2c_irq_handler, pc->num_pins);
> +    qdev_init_gpio_out(dev, ps->output, pc->num_pins); }
> +
> +static const TypeInfo pca_gpio_types[] = {
> +    {
> +    .name = TYPE_PCA_I2C_GPIO,
> +    .parent = TYPE_I2C_SLAVE,
> +    .instance_size = sizeof(PCAGPIOState),
> +    .instance_init = pca_i2c_gpio_init,
> +    .class_size = sizeof(PCAGPIOClass),
> +    .abstract = true,
> +    },
> +    {
> +    .name = TYPE_PCA6416_GPIO,
> +    .parent = TYPE_PCA_I2C_GPIO,
> +    .class_init = pca6416_gpio_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(pca_gpio_types);
> diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index
> 9736b362ac..3fb0685096 100644
> --- a/hw/gpio/trace-events
> +++ b/hw/gpio/trace-events
> @@ -31,3 +31,8 @@ sifive_gpio_update_output_irq(int64_t line, int64_t
> value) "line %" PRIi64 " val  # aspeed_gpio.c  aspeed_gpio_read(uint64_t
> offset, uint64_t value) "offset: 0x%" PRIx64 " value 0x%" PRIx64
> aspeed_gpio_write(uint64_t offset, uint64_t value) "offset: 0x%" PRIx64 "
> value 0x%" PRIx64
> +
> +# pca_i2c_gpio.c
> +pca_i2c_event(const char *id, const char *event) "%s: %s"
> +pca_i2c_recv(const char *id, uint8_t cmd, uint8_t data) "%s cmd: 0x%"
> +PRIx8 " data 0x%" PRIx8 pca_i2c_send(const char *id, uint8_t cmd,
> +uint8_t data) "%s cmd: 0x%" PRIx8 " data 0x%" PRIx8
> diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 14886b35da..b59a79fddf
> 100644
> --- a/hw/i2c/Kconfig
> +++ b/hw/i2c/Kconfig
> @@ -42,6 +42,10 @@ config PCA954X
>      bool
>      select I2C
> 
> +config PCA_I2C_GPIO
> +    bool
> +    select I2C
> +
>  config PMBUS
>      bool
>      select SMBUS
> diff --git a/include/hw/gpio/pca_i2c_gpio.h
> b/include/hw/gpio/pca_i2c_gpio.h new file mode 100644 index
> 0000000000..a10897c0e0
> --- /dev/null
> +++ b/include/hw/gpio/pca_i2c_gpio.h
> @@ -0,0 +1,72 @@
> +/*
> + * NXP PCA6416A
> + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with
> +interrupt output,
> + * reset, and configuration registers
> + *
> + * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
> + *
> + * Note: Polarity inversion emulation not implemented
> + *
> + * Copyright 2021 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later  */ #ifndef PCA_I2C_GPIO_H
> +#define PCA_I2C_GPIO_H
> +
> +#include "hw/i2c/i2c.h"
> +#include "qom/object.h"
> +
> +#define PCA6416_NUM_PINS         16
> +
> +typedef struct PCAGPIOClass {
> +    I2CSlaveClass parent;
> +
> +    uint8_t num_pins;
> +} PCAGPIOClass;
> +
> +typedef struct PCAGPIOState {
> +    I2CSlave parent;
> +
> +    uint16_t polarity_inv;
> +    uint16_t config;
> +
> +    /* the values of the gpio pins are mirrored in these integers */
> +    uint16_t curr_input;
> +    uint16_t curr_output;
> +    uint16_t new_input;
> +    uint16_t new_output;
> +
> +    /*
> +     * Note that these outputs need to be consumed by some other input
> +     * to be useful, qemu ignores writes to disconnected gpio pins
> +     */
> +    qemu_irq output[PCA6416_NUM_PINS];
> +
> +    /* i2c transaction info */
> +    uint8_t command;
> +    bool i2c_cmd;
> +
> +} PCAGPIOState;
> +
> +#define TYPE_PCA_I2C_GPIO "pca_i2c_gpio"
> +OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_GPIO)
> +
> +#define PCA6416_INPUT_PORT_0                 0x00 /* read */
> +#define PCA6416_INPUT_PORT_1                 0x01 /* read */
> +#define PCA6416_OUTPUT_PORT_0                0x02 /* read/write */
> +#define PCA6416_OUTPUT_PORT_1                0x03 /* read/write */
> +#define PCA6416_POLARITY_INVERSION_PORT_0    0x04 /* read/write */
> +#define PCA6416_POLARITY_INVERSION_PORT_1    0x05 /* read/write */
> +#define PCA6416_CONFIGURATION_PORT_0         0x06 /* read/write */
> +#define PCA6416_CONFIGURATION_PORT_1         0x07 /* read/write */
> +
> +#define PCA6416_OUTPUT_DEFAULT               0xFFFF
> +#define PCA6416_CONFIG_DEFAULT               0xFFFF
> +
> +#define PCA_I2C_OUTPUT_DEFAULT               0xFFFF
> +#define PCA_I2C_CONFIG_DEFAULT               0xFFFF
> +
> +#define TYPE_PCA6416_GPIO "pca6416"
> +
> +#endif
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index
> e97616d327..49f406af6b 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -241,6 +241,7 @@ qos_test_ss.add(
>    'ne2000-test.c',
>    'tulip-test.c',
>    'nvme-test.c',
> +  'pca_i2c_gpio-test.c',
>    'pca9552-test.c',
>    'pci-test.c',
>    'pcnet-test.c',
> diff --git a/tests/qtest/pca_i2c_gpio-test.c b/tests/qtest/pca_i2c_gpio-test.c
> new file mode 100644 index 0000000000..041ca34e22
> --- /dev/null
> +++ b/tests/qtest/pca_i2c_gpio-test.c
> @@ -0,0 +1,169 @@
> +/*
> + * QTest for PCA I2C GPIO expanders
> + *
> + * Copyright 2021 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later  */
> +
> +#include "qemu/osdep.h"
> +#include "hw/gpio/pca_i2c_gpio.h"
> +#include "libqtest-single.h"
> +#include "libqos/i2c.h"
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qnum.h"
> +#include "qemu/bitops.h"
> +
> +
> +#define TEST_ID "pca_i2c_gpio-test"
> +#define PCA_CONFIG_BYTE     0x55
> +#define PCA_CONFIG_WORD     0x5555
> +
> +static uint16_t qmp_pca_gpio_get(const char *id, const char *property)
> +{
> +    QDict *response;
> +    uint16_t ret;
> +    response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
> +                   "'property': %s } }", id, property);
> +    g_assert(qdict_haskey(response, "return"));
> +    ret = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
> +    qobject_unref(response);
> +    return ret;
> +}
> +
> +static void qmp_pca_gpio_set(const char *id, const char *property,
> +                             uint16_t value) {
> +    QDict *response;
> +
> +    response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
> +                   "'property': %s, 'value': %u } }",
> +                   id, property, value);
> +    g_assert(qdict_haskey(response, "return"));
> +    qobject_unref(response);
> +}
> +
> +static void test_set_input(void *obj, void *data, QGuestAllocator
> +*alloc) {
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +    uint8_t value;
> +    uint16_t qmp_value;
> +    /* configure pins to be inputs */
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0, 0xFF);
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1, 0xFF);
> +
> +    qmp_pca_gpio_set(TEST_ID, "gpio_input", 0xAAAA);
> +    value = i2c_get8(i2cdev, PCA6416_INPUT_PORT_0);
> +    g_assert_cmphex(value, ==, 0xAA);
> +    value = i2c_get8(i2cdev, PCA6416_INPUT_PORT_1);
> +    g_assert_cmphex(value, ==, 0xAA);
> +
> +    qmp_value = qmp_pca_gpio_get(TEST_ID, "gpio_input");
> +    g_assert_cmphex(qmp_value, ==, 0xAAAA); } static void
> +test_config(void *obj, void *data, QGuestAllocator *alloc) {
> +
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +    uint8_t value;
> +    uint16_t qmp_value;
> +    /* configure half the pins to be inputs */
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0,
> PCA_CONFIG_BYTE);
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1,
> PCA_CONFIG_BYTE);
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0);
> +    g_assert_cmphex(value, ==, PCA_CONFIG_BYTE);
> +
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1);
> +    g_assert_cmphex(value, ==, PCA_CONFIG_BYTE);
> +
> +    /* the pins that match the config should be set, the rest are undef */
> +    qmp_pca_gpio_set(TEST_ID, "gpio_input", 0xFFFF);
> +    value = i2c_get8(i2cdev, PCA6416_INPUT_PORT_0);
> +    g_assert_cmphex(value & PCA_CONFIG_BYTE, ==, 0x55);
> +    value = i2c_get8(i2cdev, PCA6416_INPUT_PORT_1);
> +    g_assert_cmphex(value & PCA_CONFIG_BYTE, ==, 0x55);
> +    qmp_value = qmp_pca_gpio_get(TEST_ID, "gpio_input");
> +    g_assert_cmphex(qmp_value & PCA_CONFIG_WORD, ==, 0x5555);
> +
> +    /*
> +     * i2c will return the value written to the output register, not the 
> values
> +     * of the output pins, so we check only the configured pins
> +     */
> +    qmp_pca_gpio_set(TEST_ID, "gpio_output", 0xFFFF);
> +    value = i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_0);
> +    g_assert_cmphex(value & ~PCA_CONFIG_BYTE, ==, 0xAA);
> +    value = i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_1);
> +    g_assert_cmphex(value & ~PCA_CONFIG_BYTE, ==, 0xAA);
> +
> +    qmp_value = qmp_pca_gpio_get(TEST_ID, "gpio_output");
> +    g_assert_cmphex(qmp_value & ~PCA_CONFIG_WORD, ==, 0xAAAA); }
> +
> +static void test_set_output(void *obj, void *data, QGuestAllocator
> +*alloc) {
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +    uint8_t value;
> +    uint16_t qmp_value;
> +    /* configure pins to be outputs */
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0, 0);
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1, 0);
> +
> +    qmp_pca_gpio_set(TEST_ID, "gpio_output", 0x5555);
> +    value = i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_0);
> +    g_assert_cmphex(value, ==, 0x55);
> +    value = i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_1);
> +    g_assert_cmphex(value, ==, 0x55);
> +
> +    qmp_value = qmp_pca_gpio_get(TEST_ID, "gpio_output");
> +    g_assert_cmphex(qmp_value, ==, 0x5555); }
> +
> +static void test_tx_rx(void *obj, void *data, QGuestAllocator *alloc) {
> +    QI2CDevice *i2cdev = (QI2CDevice *)obj;
> +    uint8_t value;
> +
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0, 0xFF);
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1, 0xFF);
> +    i2c_set8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_0, 0);
> +    i2c_set8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_1, 0);
> +
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0);
> +    g_assert_cmphex(value, ==, 0xFF);
> +
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1);
> +    g_assert_cmphex(value, ==, 0xFF);
> +
> +    value = i2c_get8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_0);
> +    g_assert_cmphex(value, ==, 0);
> +
> +    value = i2c_get8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_1);
> +    g_assert_cmphex(value, ==, 0);
> +
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0, 0xAB);
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0);
> +    g_assert_cmphex(value, ==, 0xAB);
> +
> +    i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1, 0xBC);
> +    value = i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1);
> +    g_assert_cmphex(value, ==, 0xBC);
> +
> +}
> +
> +static void pca_i2c_gpio_register_nodes(void) {
> +    QOSGraphEdgeOptions opts = {
> +        .extra_device_opts = "id=" TEST_ID ",address=0x78"
> +    };
> +    add_qi2c_address(&opts, &(QI2CAddress) { 0x78 });
> +    g_test_set_nonfatal_assertions();
> +
> +    qos_node_create_driver("pca6416", i2c_device_create);
> +    qos_node_consumes("pca6416", "i2c-bus", &opts);
> +
> +    qos_add_test("tx-rx", "pca6416", test_tx_rx, NULL);
> +    qos_add_test("set_output_gpio", "pca6416", test_set_output, NULL);
> +    qos_add_test("set_input_gpio", "pca6416", test_set_input, NULL);
> +    qos_add_test("follow_gpio_config", "pca6416", test_config, NULL); }
> +libqos_init(pca_i2c_gpio_register_nodes);
> --
> 2.39.1.519.gcb327c4b5f-goog
> 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]