diff mbox series

[RFC,1/6] hw/gpio/gpio_transmitter: Add Device

Message ID 20211216015417.1234812-2-komlodi@google.com (mailing list archive)
State New, archived
Headers show
Series Introduce GPIO transmitter and connect to NPCM7xx | expand

Commit Message

Joe Komlodi Dec. 16, 2021, 1:54 a.m. UTC
This adds a GPIO transmitter, a device which takes in the GPIO state of
a GPIO controller and transmits it via chardev.

The purpose of this device is to relay any GPIO changes to external
software that may need to act on them.

To integrate this device into a GPIO controller, the GPIO transmitter
should be initialized like any other device, then any GPIO controllers
being initialized should get a pointer to the transmitter.

To transmit data, the GPIO controller calls google_gpio_tx_transmit()
with the GPIO controller's controller number (i.e. which controller is
currently transmitting out of how many exist in the machine), and the
state of the GPIO pins on that controller.

On an error, the GPIO transmitter doesn't provide any feedback to the
controller and only logs a guest error. This is because it is a
completely separate entity from the GPIO controller and the GPIO
controller doesn't care about it, and because any
errors would be the fault of the external software receiving the GPIO
transmitter packets.

Signed-off-by: Joe Komlodi <komlodi@google.com>
---
 hw/gpio/Kconfig                           |   3 +
 hw/gpio/google_gpio_transmitter.c         | 127 ++++++++++++++++++++++
 hw/gpio/meson.build                       |   1 +
 include/hw/gpio/google_gpio_transmitter.h |  46 ++++++++
 4 files changed, 177 insertions(+)
 create mode 100644 hw/gpio/google_gpio_transmitter.c
 create mode 100644 include/hw/gpio/google_gpio_transmitter.h
diff mbox series

Patch

diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index f0e7405f6e..760646cc3a 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -13,3 +13,6 @@  config GPIO_PWR
 
 config SIFIVE_GPIO
     bool
+
+config GOOGLE_GPIO_TRANSMITTER
+    bool
diff --git a/hw/gpio/google_gpio_transmitter.c b/hw/gpio/google_gpio_transmitter.c
new file mode 100644
index 0000000000..3429121ccb
--- /dev/null
+++ b/hw/gpio/google_gpio_transmitter.c
@@ -0,0 +1,127 @@ 
+/*
+ * Google GPIO Transmitter.
+ *
+ * This is a fake hardware model that does not exist on any board or IC.
+ * The purpose of this model is to aggregate GPIO state changes from a GPIO
+ * controller and transmit them via chardev.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/gpio/google_gpio_transmitter.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#define PACKET_REVISION 0x01
+
+void google_gpio_tx_transmit(GoogleGPIOTXState *s, uint8_t controller,
+                              uint32_t gpios)
+{
+    uint8_t packet[6];
+
+    packet[0] = PACKET_REVISION;
+    packet[1] = controller;
+    memcpy(&packet[2], &gpios, sizeof(gpios));
+    qemu_chr_fe_write_all(&s->chr, packet, ARRAY_SIZE(packet));
+}
+
+static void google_gpio_tx_event(void *opaque, QEMUChrEvent evt)
+{
+    switch (evt) {
+    case CHR_EVENT_OPENED:
+    case CHR_EVENT_CLOSED:
+    case CHR_EVENT_BREAK:
+    case CHR_EVENT_MUX_IN:
+    case CHR_EVENT_MUX_OUT:
+        /*
+         * Ignore events.
+         * Our behavior stays the same regardless of what happens.
+         */
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void google_gpio_tx_receive(void *opaque, const uint8_t *buf, int size)
+{
+    GoogleGPIOTXState *s = GOOGLE_GPIO_TX(opaque);
+
+    switch (buf[0]) {
+    case GPIOTXCODE_OK:
+        break;
+    case GPIOTXCODE_MALFORMED_PKT:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Transmitted malformed packet\n",
+                      object_get_canonical_path(OBJECT(s)));
+        break;
+    case GPIOTXCODE_UNKNOWN_VERSION:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Transmitted malformed packet "
+                      "with a version the recipent can't handle. Sent "
+                      "version %d\n", object_get_canonical_path(OBJECT(s)),
+                      PACKET_REVISION);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown response 0x%x\n",
+                      object_get_canonical_path(OBJECT(s)), buf[0]);
+        break;
+    }
+
+    if (size != 1) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Expects packets only of length 1\n",
+                      object_get_canonical_path(OBJECT(s)));
+    }
+}
+
+static int google_gpio_tx_can_receive(void *opaque)
+{
+    return 1;
+}
+
+static void google_gpio_tx_realize(DeviceState *dev, Error **errp)
+{
+    GoogleGPIOTXState *s = GOOGLE_GPIO_TX(dev);
+
+    qemu_chr_fe_set_handlers(&s->chr, google_gpio_tx_can_receive,
+                             google_gpio_tx_receive,
+                             google_gpio_tx_event,
+                             NULL, OBJECT(s), NULL, true);
+}
+
+static Property google_gpio_properties[] = {
+    DEFINE_PROP_CHR("gpio-chardev", GoogleGPIOTXState, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void google_gpio_tx_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "Google GPIO Controller Transmitter";
+    dc->realize = google_gpio_tx_realize;
+    device_class_set_props(dc, google_gpio_properties);
+}
+
+static const TypeInfo google_gpio_tx_types[] = {
+    {
+        .name = TYPE_GOOGLE_GPIO_TRANSMITTER,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(GoogleGPIOTXState),
+        .class_init = google_gpio_tx_class_init,
+    },
+};
+
+DEFINE_TYPES(google_gpio_tx_types);
diff --git a/include/hw/gpio/google_gpio_transmitter.h b/include/hw/gpio/google_gpio_transmitter.h
new file mode 100644
index 0000000000..fa7d7b3b77
--- /dev/null
+++ b/include/hw/gpio/google_gpio_transmitter.h
@@ -0,0 +1,46 @@ 
+/*
+ * Google GPIO Transmitter.
+ *
+ * This is a fake hardware model that does not exist on any board or IC.
+ * The purpose of this model is to aggregate GPIO state changes from a GPIO
+ * controller and transmit them via chardev.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef GOOGLE_GPIO_TRANSMITTER_H
+#define GOOGLE_GPIO_TRANSMITTER_H
+
+#include "chardev/char-fe.h"
+#include "hw/sysbus.h"
+
+#define TYPE_GOOGLE_GPIO_TRANSMITTER "google.gpio-transmitter"
+#define GOOGLE_GPIO_TX(obj) \
+    OBJECT_CHECK(GoogleGPIOTXState, (obj), TYPE_GOOGLE_GPIO_TRANSMITTER)
+
+#define GPIO_TX_NUM_CONTROLLERS 8
+
+typedef enum {
+    GPIOTXCODE_OK              = 0x00,
+    GPIOTXCODE_MALFORMED_PKT   = 0xe0,
+    GPIOTXCODE_UNKNOWN_VERSION = 0xe1,
+} GoogleGPIOTXCode;
+
+typedef struct {
+    SysBusDevice parent;
+
+    CharBackend chr;
+} GoogleGPIOTXState;
+
+void google_gpio_tx_transmit(GoogleGPIOTXState *s, uint8_t controller,
+                             uint32_t gpios);
+
+#endif /* GOOGLE_GPIO_TRANSMITTER_H */