From nobody Fri May 17 05:00:06 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=google.com ARC-Seal: i=1; a=rsa-sha256; t=1675713083; cv=none; d=zohomail.com; s=zohoarc; b=EQ0oq3DugnrDuFo1ncI9sR0PvGRhLHf5CNFhdvACSwCcPogJxmUkAnpfYD8igGyxRMvrVbjvovCoN2a/vbSAdttU6CyaecupCkTL+jROG7nUc8BiQrdEOSQfljNuowAdhTSUdcABp3s5Am+6BeentGuk3vHkS5ky0FQzcpERSSs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1675713083; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=+guheRGNxwsLT53HDTDKJqAj8KCTAUkh+fSSePFZRRA=; b=iyYb/67fTh4AL/TwNeK/YNNjqW/ynw8qVAl4Nz7aVYobLn4KLsviWQHtioQFMnTjtHVueyx3MMGH4BN2/Wms5DoPX16x+wUL475BNIsawbgwDWUQd2G6kecWvSCL47hCPr6yNJRM4hTK24TuU9g5Z7ETCrnxjfunRIA7d6r+pY8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1675713083422736.9845169535587; Mon, 6 Feb 2023 11:51:23 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pP7Uv-0007gl-6m; Mon, 06 Feb 2023 14:49:53 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <32FnhYwYKCjcmbmnlkZhhZeX.VhfjXfn-WXoXeghgZgn.hkZ@flex--titusr.bounces.google.com>) id 1pP7Ut-0007f8-0G for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:51 -0500 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <32FnhYwYKCjcmbmnlkZhhZeX.VhfjXfn-WXoXeghgZgn.hkZ@flex--titusr.bounces.google.com>) id 1pP7Uq-0007sn-6l for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:50 -0500 Received: by mail-yb1-xb4a.google.com with SMTP id z9-20020a25ba49000000b007d4416e3667so12625184ybj.23 for ; Mon, 06 Feb 2023 11:49:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+guheRGNxwsLT53HDTDKJqAj8KCTAUkh+fSSePFZRRA=; b=BN/fWqtqI4ZgKmI9BbtSMM5Wqa7DaToEUtSOeEKAy55OlWsXqGAJDnYPVdOK2qSn2O vUk5ICZnnewoBlPw7clMUnq0Q0n4Ty4isjhuJRxK0hcKBvAKstglCZ5ztrV6akX0Mx3c 8y28oanCo3I9N19jzIkXbYe0r3TW/4L6BCPp/ksc+mS3koExr0qLVGAAWaEX+q9Da1ND C6FADvushIxiGK4Nx9p1hDBoFlFy5gHbjxAujrFIRjnCICp+awA+n6MsrKlDRpkIXazD mVVx3lyzSWByU3e1LY31LvolxUVw9hMd/gRVGamLmHMd2rw2kGx3PwuHI6lTvHKLHqY+ cpGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+guheRGNxwsLT53HDTDKJqAj8KCTAUkh+fSSePFZRRA=; b=nclPiqdpXoMoNB8zWVW2lEgwokQwoAZ3znrKydGkoC1KUqSh08MX6BwDLr9ec2VwKT 90mOkO9TwYRZpO8pyU8e3W8orcbgUpFdo+Z0rqEpaRDVwsvBbasTSTtRYC7FDXopr5ul GZ4p5FnCjF1jBocM7DN/+WCT+IJjArFsUK2RPdM9t6S0R3FAAgKAvh1xadHDGiIGMAjW SLsRPDgRUeBLsFAzLjIyY6TS1bRcDY+bMAgTipPBtfP4LsKVnUDZazVx/ehuyMEXDiAy dTnfBfd1u1V3OtQW8iAgvgMdUpg9sn7FKXmPh6vriu/G17JNaVHQuS3o1S02hsoNmykh BBwg== X-Gm-Message-State: AO0yUKX3jr5rrlod+foUgSty0mvOLUhsGYfRxpzxD/p4ER+7GTFvTSev d7xCeo1CE2/6pImEFEZXhRZUdA/hjRo= X-Google-Smtp-Source: AK7set8l7w18I4EsWsPbvUFothQXJLbazvbwrhpsqP2V8FqidHws0wI9ht1SokqMWB0HLCsEPRNE4zsIqS4= X-Received: from titusr.c.googlers.com ([fda3:e722:ac3:cc00:14:4d90:c0a8:22b8]) (user=titusr job=sendgmr) by 2002:a05:6902:30d:b0:881:39d0:46ee with SMTP id b13-20020a056902030d00b0088139d046eemr91253ybs.522.1675712984253; Mon, 06 Feb 2023 11:49:44 -0800 (PST) Date: Mon, 6 Feb 2023 19:49:34 +0000 In-Reply-To: <20230206194936.168843-1-titusr@google.com> Mime-Version: 1.0 References: <20230206194936.168843-1-titusr@google.com> X-Mailer: git-send-email 2.39.1.519.gcb327c4b5f-goog Message-ID: <20230206194936.168843-2-titusr@google.com> Subject: [PATCH 1/3] hw/gpio: add PCA6414 i2c GPIO expander From: Titus Rwantare To: peter.maydell@linaro.org Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Titus Rwantare , Hao Wu Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=32FnhYwYKCjcmbmnlkZhhZeX.VhfjXfn-WXoXeghgZgn.hkZ@flex--titusr.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @google.com) X-ZM-MESSAGEID: 1675713084452100001 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This is a simple i2c device that allows i2c capable devices to have GPIOs. Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare --- 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 =20 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 @@ -0,0 +1,362 @@ +/* + * NXP PCA I2C GPIO Expanders + * + * Low-voltage translating 16-bit I2C/SMBus GPIO expander with interrupt o= utput, + * 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 configurat= ion 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 inp= uts + * 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 inpu= t or + * an output by the Configuration register. + */ +static void pca_i2c_update_irqs(PCAGPIOState *ps) +{ + PCAGPIOClass *pc =3D PCA_I2C_GPIO_GET_CLASS(ps); + uint16_t out_diff =3D ps->new_output ^ ps->curr_output; + uint16_t in_diff =3D ps->new_input ^ ps->curr_input; + uint16_t mask, pin_i; + + if (in_diff || out_diff) { + for (int i =3D 0; i < pc->num_pins; i++) { + mask =3D BIT(i); + /* pin must be configured as an output to be set here */ + if (out_diff & ~ps->config & mask) { + pin_i =3D mask & ps->new_output; + qemu_set_irq(ps->output[i], pin_i > 0); + ps->curr_output &=3D ~mask; + ps->curr_output |=3D pin_i; + } + + if (in_diff & mask) { + ps->curr_input &=3D ~mask; + ps->curr_input |=3D mask & ps->new_input; + } + } + /* make diff =3D 0 */ + ps->new_input =3D ps->curr_input; + } +} + +static void pca_i2c_irq_handler(void *opaque, int n, int level) +{ + PCAGPIOState *ps =3D opaque; + PCAGPIOClass *pc =3D PCA_I2C_GPIO_GET_CLASS(opaque); + uint16_t mask =3D BIT(n); + + g_assert(n < pc->num_pins); + g_assert(n >=3D 0); + + ps->new_input &=3D ~mask; + + if (level > 0) { + ps->new_input |=3D BIT(n); + } + + pca_i2c_update_irqs(ps); +} + +/* slave to master */ +static uint8_t pca6416_recv(I2CSlave *i2c) +{ + PCAGPIOState *ps =3D PCA_I2C_GPIO(i2c); + uint8_t data; + + switch (ps->command) { + case PCA6416_INPUT_PORT_0: + data =3D ps->curr_input; + break; + + case PCA6416_INPUT_PORT_1: + data =3D 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 =3D ps->new_output; + break; + + case PCA6416_OUTPUT_PORT_1: + data =3D ps->new_output >> 8; + break; + + case PCA6416_POLARITY_INVERSION_PORT_0: + data =3D ps->polarity_inv; + break; + + case PCA6416_POLARITY_INVERSION_PORT_1: + data =3D ps->polarity_inv >> 8; + break; + + case PCA6416_CONFIGURATION_PORT_0: + data =3D ps->config; + break; + + case PCA6416_CONFIGURATION_PORT_1: + data =3D ps->config >> 8; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register 0x%02x", + __func__, ps->command); + data =3D 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 =3D PCA_I2C_GPIO(i2c); + if (ps->i2c_cmd) { + ps->command =3D data; + ps->i2c_cmd =3D 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%0= 2x", + __func__, ps->command); + break; + + case PCA6416_OUTPUT_PORT_0: + ps->new_output &=3D 0xFF00; + ps->new_output |=3D data; + break; + + case PCA6416_OUTPUT_PORT_1: + ps->new_output &=3D 0xFF; + ps->new_output |=3D data << 8; + break; + + case PCA6416_POLARITY_INVERSION_PORT_0: + ps->polarity_inv &=3D 0xFF00; + ps->polarity_inv |=3D data; + break; + + case PCA6416_POLARITY_INVERSION_PORT_1: + ps->polarity_inv &=3D 0xFF; + ps->polarity_inv |=3D data << 8; + break; + + case PCA6416_CONFIGURATION_PORT_0: + ps->config &=3D 0xFF00; + ps->config |=3D data; + break; + + case PCA6416_CONFIGURATION_PORT_1: + ps->config &=3D 0xFF; + ps->config |=3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D 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 =3D PCA_I2C_GPIO(dev); + pca_i2c_update_irqs(ps); +} + +static const VMStateDescription vmstate_pca_i2c_gpio =3D { + .name =3D TYPE_PCA_I2C_GPIO, + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (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 =3D DEVICE_CLASS(klass); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(klass); + PCAGPIOClass *pc =3D PCA_I2C_GPIO_CLASS(klass); + + dc->desc =3D "PCA6416 16-bit I/O expander"; + dc->realize =3D pca_i2c_realize; + dc->vmsd =3D &vmstate_pca_i2c_gpio; + + k->event =3D pca_i2c_event; + k->recv =3D pca6416_recv; + k->send =3D pca6416_send; + + pc->num_pins =3D PCA6416_NUM_PINS; +} + +static void pca_i2c_gpio_init(Object *obj) +{ + PCAGPIOState *ps =3D PCA_I2C_GPIO(obj); + PCAGPIOClass *pc =3D PCA_I2C_GPIO_GET_CLASS(obj); + DeviceState *dev =3D DEVICE(obj); + + ps->new_output =3D PCA_I2C_OUTPUT_DEFAULT; + ps->config =3D 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[] =3D { + { + .name =3D TYPE_PCA_I2C_GPIO, + .parent =3D TYPE_I2C_SLAVE, + .instance_size =3D sizeof(PCAGPIOState), + .instance_init =3D pca_i2c_gpio_init, + .class_size =3D sizeof(PCAGPIOClass), + .abstract =3D true, + }, + { + .name =3D TYPE_PCA6416_GPIO, + .parent =3D TYPE_PCA_I2C_GPIO, + .class_init =3D 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 " v= alue 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%" PRIx= 8 " data 0x%" PRIx8 +pca_i2c_send(const char *id, uint8_t cmd, uint8_t data) "%s cmd: 0x%" PRIx= 8 " 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 =20 +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 o= utput, + * 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-tes= t.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 =3D qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, property); + g_assert(qdict_haskey(response, "return")); + ret =3D 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 =3D 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 =3D (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 =3D i2c_get8(i2cdev, PCA6416_INPUT_PORT_0); + g_assert_cmphex(value, =3D=3D, 0xAA); + value =3D i2c_get8(i2cdev, PCA6416_INPUT_PORT_1); + g_assert_cmphex(value, =3D=3D, 0xAA); + + qmp_value =3D qmp_pca_gpio_get(TEST_ID, "gpio_input"); + g_assert_cmphex(qmp_value, =3D=3D, 0xAAAA); +} +static void test_config(void *obj, void *data, QGuestAllocator *alloc) +{ + + QI2CDevice *i2cdev =3D (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 =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0); + g_assert_cmphex(value, =3D=3D, PCA_CONFIG_BYTE); + + value =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1); + g_assert_cmphex(value, =3D=3D, 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 =3D i2c_get8(i2cdev, PCA6416_INPUT_PORT_0); + g_assert_cmphex(value & PCA_CONFIG_BYTE, =3D=3D, 0x55); + value =3D i2c_get8(i2cdev, PCA6416_INPUT_PORT_1); + g_assert_cmphex(value & PCA_CONFIG_BYTE, =3D=3D, 0x55); + qmp_value =3D qmp_pca_gpio_get(TEST_ID, "gpio_input"); + g_assert_cmphex(qmp_value & PCA_CONFIG_WORD, =3D=3D, 0x5555); + + /* + * i2c will return the value written to the output register, not the v= alues + * of the output pins, so we check only the configured pins + */ + qmp_pca_gpio_set(TEST_ID, "gpio_output", 0xFFFF); + value =3D i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_0); + g_assert_cmphex(value & ~PCA_CONFIG_BYTE, =3D=3D, 0xAA); + value =3D i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_1); + g_assert_cmphex(value & ~PCA_CONFIG_BYTE, =3D=3D, 0xAA); + + qmp_value =3D qmp_pca_gpio_get(TEST_ID, "gpio_output"); + g_assert_cmphex(qmp_value & ~PCA_CONFIG_WORD, =3D=3D, 0xAAAA); +} + +static void test_set_output(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev =3D (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 =3D i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_0); + g_assert_cmphex(value, =3D=3D, 0x55); + value =3D i2c_get8(i2cdev, PCA6416_OUTPUT_PORT_1); + g_assert_cmphex(value, =3D=3D, 0x55); + + qmp_value =3D qmp_pca_gpio_get(TEST_ID, "gpio_output"); + g_assert_cmphex(qmp_value, =3D=3D, 0x5555); +} + +static void test_tx_rx(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev =3D (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 =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0); + g_assert_cmphex(value, =3D=3D, 0xFF); + + value =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1); + g_assert_cmphex(value, =3D=3D, 0xFF); + + value =3D i2c_get8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_0); + g_assert_cmphex(value, =3D=3D, 0); + + value =3D i2c_get8(i2cdev, PCA6416_POLARITY_INVERSION_PORT_1); + g_assert_cmphex(value, =3D=3D, 0); + + i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_0, 0xAB); + value =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_0); + g_assert_cmphex(value, =3D=3D, 0xAB); + + i2c_set8(i2cdev, PCA6416_CONFIGURATION_PORT_1, 0xBC); + value =3D i2c_get8(i2cdev, PCA6416_CONFIGURATION_PORT_1); + g_assert_cmphex(value, =3D=3D, 0xBC); + +} + +static void pca_i2c_gpio_register_nodes(void) +{ + QOSGraphEdgeOptions opts =3D { + .extra_device_opts =3D "id=3D" TEST_ID ",address=3D0x78" + }; + 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); --=20 2.39.1.519.gcb327c4b5f-goog From nobody Fri May 17 05:00:06 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=google.com ARC-Seal: i=1; a=rsa-sha256; t=1675713056; cv=none; d=zohomail.com; s=zohoarc; b=KU5WSSnToU7KotaAaQ47V6AL9oKP65vOuvoapRsjpKwqMeHoMDJqivQurRbZjB4ZeyTjfi7WNkvXLamqexvooaUQ8zQwF7VC0PYrnhWzY9oAGNguQqS9kaOwIvXMZkkGRU4YcMbwgeiatkclyT3JuM6vlqqZareHz+XWV929ChU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1675713056; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=5svBDPpOEX5xuVnze/3gbbDHfyiQ5kPXXMWNym3ZBK0=; b=DytUuLlGHD0jdjWTXn6Ju2I1obaORyM30gaExkb8TqQ1TvjggnhFz6FGSVJ3QIYPPNWoPi6gLD3V/ssPr2moVYeTh0fw4kc06xucr2sL5LesaysCFVnTcLjgI7JOzxcRl4UzDc+o2zyeKoCdTSxjTw689XKnvGXeWz8oP5iLLCw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1675713056724695.9433480110936; Mon, 6 Feb 2023 11:50:56 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pP7Ut-0007fO-Dm; Mon, 06 Feb 2023 14:49:51 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <32VnhYwYKCjgncnomlaiiafY.WigkYgo-XYpYfhihaho.ila@flex--titusr.bounces.google.com>) id 1pP7Us-0007ep-E1 for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:50 -0500 Received: from mail-yb1-xb4a.google.com ([2607:f8b0:4864:20::b4a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <32VnhYwYKCjgncnomlaiiafY.WigkYgo-XYpYfhihaho.ila@flex--titusr.bounces.google.com>) id 1pP7Uq-0007ss-Ko for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:50 -0500 Received: by mail-yb1-xb4a.google.com with SMTP id s7-20020a257707000000b0085600c7c70cso12418522ybc.5 for ; Mon, 06 Feb 2023 11:49:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=5svBDPpOEX5xuVnze/3gbbDHfyiQ5kPXXMWNym3ZBK0=; b=Q4CPINzb+aucGoGxaelTg/5fnrUgDRu2nBLvC1A47hANN4FduC8hUSfMM2S6kn8jrV EMA1UR5fqvkU12rEBEQ8RcJB8Czzgzo/Z1G4V1PHRn8MMwlRjKdLluWcqXthkccRFwsQ EIck/iMG7OsosP9gua1WQKQ6H54WuZzZDv8f6j9HPMdUqtnlmU1ImK3y89+4j8gkABjQ 5Cx/Ei1c0t3Ypdk2QysEek6u0aux22Karru9CKNonG9BL+Wrt+axtuJbcEaIyEbFQkcr ywNd6mNTb3aiCQe/8MvJDspYVP1bIhB2vULtex8fTrleRWUBbAKoHCDYCgL+PCoa6AgS Exng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=5svBDPpOEX5xuVnze/3gbbDHfyiQ5kPXXMWNym3ZBK0=; b=U5WdOtY8LQv/84tdpGe7Lp7iWtsMlrOAABWESLOnJHHDfTp3MKagfFbIPAYIuT/aYZ RmK8TbYhqP4UVoCN08EcJD0Q5eCw1s0NWf8Q6+qXjIA+5AQWxt5Nb0SXHpuNaZ9JJwKS X4eh8nqXmEDuBJJD4GpVDYZ017CAEwhP7vXBSUqotvGfy4ab9QaDSyeaICLDzMGV/5Kh lsjid3O7BY+YsGa/gKnFa87rfH75FRe9xMBCo38oJdb/h+yKVqSNsJS5/RWD4AfYgnFW sg3157Tv374NjVcph0dVOLc3McuAL9QTtoCeP3y3XmNDPMCzmU2nDcNfNHGoxOF7BebL AoQQ== X-Gm-Message-State: AO0yUKVhidYg29QBFuAaOs3k3lU8B66MhJwAN/GTOWko8cltc0wQwkuM Sqa4TnihaBUivNTEbm5LPD4nb4khamU= X-Google-Smtp-Source: AK7set9jG3+CDmkAzrYO/ZhyxdhHKVJ4gwL0Fwtq6moO5xgIhXEE3gfhYNJ1LX3V53+6czNIqwjnCV+BtUc= X-Received: from titusr.c.googlers.com ([fda3:e722:ac3:cc00:14:4d90:c0a8:22b8]) (user=titusr job=sendgmr) by 2002:a81:a14a:0:b0:52a:9cc9:492f with SMTP id y71-20020a81a14a000000b0052a9cc9492fmr38832ywg.351.1675712985096; Mon, 06 Feb 2023 11:49:45 -0800 (PST) Date: Mon, 6 Feb 2023 19:49:35 +0000 In-Reply-To: <20230206194936.168843-1-titusr@google.com> Mime-Version: 1.0 References: <20230206194936.168843-1-titusr@google.com> X-Mailer: git-send-email 2.39.1.519.gcb327c4b5f-goog Message-ID: <20230206194936.168843-3-titusr@google.com> Subject: [PATCH 2/3] hw/gpio: add PCA9538 8-bit GPIO expander From: Titus Rwantare To: peter.maydell@linaro.org Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Titus Rwantare , Hao Wu Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::b4a; envelope-from=32VnhYwYKCjgncnomlaiiafY.WigkYgo-XYpYfhihaho.ila@flex--titusr.bounces.google.com; helo=mail-yb1-xb4a.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @google.com) X-ZM-MESSAGEID: 1675713058239100005 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The 8-bit expander has different register offsets than the 16-bit one, making them incompatible. Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare --- hw/gpio/pca_i2c_gpio.c | 98 ++++++++++++++++++++++++++++++++++ include/hw/gpio/pca_i2c_gpio.h | 7 +++ 2 files changed, 105 insertions(+) diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c index afae497a22..f77d8d7e84 100644 --- a/hw/gpio/pca_i2c_gpio.c +++ b/hw/gpio/pca_i2c_gpio.c @@ -141,6 +141,41 @@ static uint8_t pca6416_recv(I2CSlave *i2c) return data; } =20 +/* slave to master */ +static uint8_t pca9538_recv(I2CSlave *i2c) +{ + PCAGPIOState *ps =3D PCA_I2C_GPIO(i2c); + uint8_t data; + + switch (ps->command) { + case PCA9538_INPUT_PORT: + data =3D ps->curr_input; + break; + + case PCA9538_OUTPUT_PORT: + data =3D ps->new_output; + break; + + case PCA9538_POLARITY_INVERSION_PORT: + data =3D ps->polarity_inv; + break; + + case PCA9538_CONFIGURATION_PORT: + data =3D ps->config; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: reading from unsupported register 0x%02x", + __func__, ps->command); + data =3D 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) { @@ -201,6 +236,47 @@ static int pca6416_send(I2CSlave *i2c, uint8_t data) return 0; } =20 +/* master to slave */ +static int pca9538_send(I2CSlave *i2c, uint8_t data) +{ + PCAGPIOState *ps =3D PCA_I2C_GPIO(i2c); + if (ps->i2c_cmd) { + ps->command =3D data; + ps->i2c_cmd =3D 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%0= 2x", + __func__, ps->command); + break; + case PCA9538_OUTPUT_PORT: + ps->new_output =3D data; + break; + + case PCA9538_POLARITY_INVERSION_PORT: + ps->polarity_inv =3D data; + break; + + case PCA9538_CONFIGURATION_PORT: + ps->config =3D 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 =3D PCA_I2C_GPIO(i2c); @@ -321,6 +397,23 @@ static void pca6416_gpio_class_init(ObjectClass *klass= , void *data) pc->num_pins =3D PCA6416_NUM_PINS; } =20 +static void pca9538_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(klass); + PCAGPIOClass *pc =3D PCA_I2C_GPIO_CLASS(klass); + + dc->desc =3D "PCA9538 8-bit I/O expander"; + dc->realize =3D pca_i2c_realize; + dc->vmsd =3D &vmstate_pca_i2c_gpio; + + k->event =3D pca_i2c_event; + k->recv =3D pca9538_recv; + k->send =3D pca9538_send; + + pc->num_pins =3D PCA9538_NUM_PINS; +} + static void pca_i2c_gpio_init(Object *obj) { PCAGPIOState *ps =3D PCA_I2C_GPIO(obj); @@ -357,6 +450,11 @@ static const TypeInfo pca_gpio_types[] =3D { .parent =3D TYPE_PCA_I2C_GPIO, .class_init =3D pca6416_gpio_class_init, }, + { + .name =3D TYPE_PCA9538_GPIO, + .parent =3D TYPE_PCA_I2C_GPIO, + .class_init =3D pca9538_gpio_class_init, + }, }; =20 DEFINE_TYPES(pca_gpio_types); diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h index a10897c0e0..8cd268e8f0 100644 --- a/include/hw/gpio/pca_i2c_gpio.h +++ b/include/hw/gpio/pca_i2c_gpio.h @@ -18,6 +18,7 @@ #include "qom/object.h" =20 #define PCA6416_NUM_PINS 16 +#define PCA9538_NUM_PINS 8 =20 typedef struct PCAGPIOClass { I2CSlaveClass parent; @@ -61,6 +62,11 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_= GPIO) #define PCA6416_CONFIGURATION_PORT_0 0x06 /* read/write */ #define PCA6416_CONFIGURATION_PORT_1 0x07 /* read/write */ =20 +#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 PCA6416_OUTPUT_DEFAULT 0xFFFF #define PCA6416_CONFIG_DEFAULT 0xFFFF =20 @@ -68,5 +74,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_G= PIO) #define PCA_I2C_CONFIG_DEFAULT 0xFFFF =20 #define TYPE_PCA6416_GPIO "pca6416" +#define TYPE_PCA9538_GPIO "pca9538" =20 #endif --=20 2.39.1.519.gcb327c4b5f-goog From nobody Fri May 17 05:00:06 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=google.com ARC-Seal: i=1; a=rsa-sha256; t=1675713054; cv=none; d=zohomail.com; s=zohoarc; b=nvmGIDS8yjOqrKfTgBvVGxiY60HAS/ljDz+LglVlpTXQBSeIgy2cbSOoekJdpsiy+P51HLrjVR6qFw9eFzl59KzQAPwD8PpKbSoi92pTSYE+jVHGEw9l8R+UbCcgpePEKtQX5hHbKkGUICKWMASyF1b20ZdoOWdBWEZlG/uxA/k= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1675713054; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=9ABrUA5krIv7LfvsXEqxczxaHvxnH2HruiF60Riq0HE=; b=MtB1rHDtxxDE3y2JK0kDiRZSbVYyajyAxyqoa1epvWDxG4RtQQGqzaLsCBP2teTk+3I2qrp437ZjZTroB9s7IHDsGJfTZk/EABOxFWHf39/1EDs6zx7TSYyuztUQiibdowbBKlfuq2ljhiY5iCTjYC2vIIw0gimnotVHF56nOWQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1675713054989705.4048225619848; Mon, 6 Feb 2023 11:50:54 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pP7Uw-0007hj-FQ; Mon, 06 Feb 2023 14:49:54 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <32lnhYwYKCjkodopnmbjjbgZ.XjhlZhp-YZqZgijibip.jmb@flex--titusr.bounces.google.com>) id 1pP7Ut-0007fz-SD for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:51 -0500 Received: from mail-yb1-xb49.google.com ([2607:f8b0:4864:20::b49]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <32lnhYwYKCjkodopnmbjjbgZ.XjhlZhp-YZqZgijibip.jmb@flex--titusr.bounces.google.com>) id 1pP7Ur-0007tL-Gy for qemu-devel@nongnu.org; Mon, 06 Feb 2023 14:49:51 -0500 Received: by mail-yb1-xb49.google.com with SMTP id z9-20020a25ba49000000b007d4416e3667so12625266ybj.23 for ; Mon, 06 Feb 2023 11:49:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=9ABrUA5krIv7LfvsXEqxczxaHvxnH2HruiF60Riq0HE=; b=DlSNsRBhoPRYVT3NrHAWLtMTJbIOqrAPghBm/W/wmopCerf1PPI+LCDcFniockR3xo h4V+1/xvy4obUp4DOKeGFEAljQ3k5IYy/heXP2e8MRhPgSG1/iOuWJQEsNdhNcs7fMkn y+lOlLikRPChnkkB9XUkNb1zltKe/eMtYMN/Yc4UBeF0so5Ue4ekHwJIwFt7p650vLaD Lc1gwdcnQJyWHjyBkl7I/1N/7ucNfjpL9JETj37GT9zwTpLwSKOaZ7JWmJDDhhOVdFAB /toT77JzmeT+pn4BkqsZ60D/jIVAgTbKIFXPsV7PXrm9eW3HV3aEMdwxA23K9nzhSbhV lH1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=9ABrUA5krIv7LfvsXEqxczxaHvxnH2HruiF60Riq0HE=; b=XzAnbDIWvc0o2XQAp77vpiu4e951UD55fCxrOEYDzGXvU3yJ0wrpQvnnMugbow/zdH jiaNhctRBi8b2frDCHOty239+VeSLAey8WShWx8FKn3/XfY+FFk6UCq9dzibfFBJ8iwp lQU+xV1mG6KaBnF9trl5mmVDg9fmDZYrIuapgGVAmzCvevM35l+psk876GC3cjwYCNp3 i+7dErYkjo9V9MqZtXpbT4PtVIaeR0a2k9dw0EFtGGNnxT7WWWVYp1tRDPJba8v9F5uq W9XxAIo1nbGC8PbZ9Xs3ZuXszh50c88d8IOObAYa2aW2gXDeBL3jsu9PbzBpw1ElSJRZ fUQQ== X-Gm-Message-State: AO0yUKVJTU/YW2Hjn1/oy70wbek0YgM40wzLVPM0AxK4iAUAuaM3UPdB k7B9YqkE6mubcivgBms9vlyHSJhVGcA= X-Google-Smtp-Source: AK7set9YgjPQchLIhO5KNC7lISmGcAO/WrKpPLMcem5LI8bghFmIUSFERMCdGs+AFp6dGVl+y9pmn6rR4Zk= X-Received: from titusr.c.googlers.com ([fda3:e722:ac3:cc00:14:4d90:c0a8:22b8]) (user=titusr job=sendgmr) by 2002:a25:dc6:0:b0:89a:3c0a:d6de with SMTP id 189-20020a250dc6000000b0089a3c0ad6demr0ybn.11.1675712986886; Mon, 06 Feb 2023 11:49:46 -0800 (PST) Date: Mon, 6 Feb 2023 19:49:36 +0000 In-Reply-To: <20230206194936.168843-1-titusr@google.com> Mime-Version: 1.0 References: <20230206194936.168843-1-titusr@google.com> X-Mailer: git-send-email 2.39.1.519.gcb327c4b5f-goog Message-ID: <20230206194936.168843-4-titusr@google.com> Subject: [PATCH 3/3] hw/gpio: add PCA9536 i2c gpio expander From: Titus Rwantare To: peter.maydell@linaro.org Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org, Titus Rwantare , Hao Wu Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::b49; envelope-from=32lnhYwYKCjkodopnmbjjbgZ.XjhlZhp-YZqZgijibip.jmb@flex--titusr.bounces.google.com; helo=mail-yb1-xb49.google.com X-Spam_score_int: -95 X-Spam_score: -9.6 X-Spam_bar: --------- X-Spam_report: (-9.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @google.com) X-ZM-MESSAGEID: 1675713056536100003 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This device has the same register layout as the pca9538, but 4 fewer gpio pins. This commit lowers the number of pins intialised, and reuses the pca9539 logic. Reviewed-by: Hao Wu Signed-off-by: Titus Rwantare --- hw/gpio/pca_i2c_gpio.c | 22 ++++++++++++++++++++++ include/hw/gpio/pca_i2c_gpio.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/hw/gpio/pca_i2c_gpio.c b/hw/gpio/pca_i2c_gpio.c index f77d8d7e84..66dd1d3369 100644 --- a/hw/gpio/pca_i2c_gpio.c +++ b/hw/gpio/pca_i2c_gpio.c @@ -414,6 +414,23 @@ static void pca9538_gpio_class_init(ObjectClass *klass= , void *data) pc->num_pins =3D PCA9538_NUM_PINS; } =20 +static void pca9536_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(klass); + PCAGPIOClass *pc =3D PCA_I2C_GPIO_CLASS(klass); + + dc->desc =3D "PCA9536 4-bit I/O expander"; + dc->realize =3D pca_i2c_realize; + dc->vmsd =3D &vmstate_pca_i2c_gpio; + + k->event =3D pca_i2c_event; + k->recv =3D pca9538_recv; + k->send =3D pca9538_send; + + pc->num_pins =3D PCA9536_NUM_PINS; +} + static void pca_i2c_gpio_init(Object *obj) { PCAGPIOState *ps =3D PCA_I2C_GPIO(obj); @@ -455,6 +472,11 @@ static const TypeInfo pca_gpio_types[] =3D { .parent =3D TYPE_PCA_I2C_GPIO, .class_init =3D pca9538_gpio_class_init, }, + { + .name =3D TYPE_PCA9536_GPIO, + .parent =3D TYPE_PCA_I2C_GPIO, + .class_init =3D pca9536_gpio_class_init, + }, }; =20 DEFINE_TYPES(pca_gpio_types); diff --git a/include/hw/gpio/pca_i2c_gpio.h b/include/hw/gpio/pca_i2c_gpio.h index 8cd268e8f0..3d2a88ba7b 100644 --- a/include/hw/gpio/pca_i2c_gpio.h +++ b/include/hw/gpio/pca_i2c_gpio.h @@ -19,6 +19,7 @@ =20 #define PCA6416_NUM_PINS 16 #define PCA9538_NUM_PINS 8 +#define PCA9536_NUM_PINS 4 =20 typedef struct PCAGPIOClass { I2CSlaveClass parent; @@ -75,5 +76,6 @@ OBJECT_DECLARE_TYPE(PCAGPIOState, PCAGPIOClass, PCA_I2C_G= PIO) =20 #define TYPE_PCA6416_GPIO "pca6416" #define TYPE_PCA9538_GPIO "pca9538" +#define TYPE_PCA9536_GPIO "pca9536" =20 #endif --=20 2.39.1.519.gcb327c4b5f-goog