From: Ruslan Ruslichenko <[email protected]>

This patch adds the `remote-port-gpio` device, which enables
GPIO levels exchange between QEMU and the remote peer.

It maps QEMU's `qemu_irq` lines (GPIOs) to Remote Port `RP_CMD_interrupt`
packets, enabling the synchronization of interrupt lines and logic signals
across the simulation.

This is essential for wiring up interrupt controllers or discrete logic
signals (reset lines, enable pins).

Signed-off-by: Edgar E. Iglesias <[email protected]>
Signed-off-by: Takahiro Nakata <[email protected]>
Signed-off-by: Ruslan Ruslichenko <[email protected]>
---
 hw/core/remote-port-gpio.c         | 183 +++++++++++++++++++++++++++++
 hw/core/trace-events               |   4 +
 include/hw/core/remote-port-gpio.h |  33 ++++++
 3 files changed, 220 insertions(+)
 create mode 100644 hw/core/remote-port-gpio.c
 create mode 100644 include/hw/core/remote-port-gpio.h

diff --git a/hw/core/remote-port-gpio.c b/hw/core/remote-port-gpio.c
new file mode 100644
index 0000000000..b9bdbcc5a5
--- /dev/null
+++ b/hw/core/remote-port-gpio.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <[email protected]>
+ * Written by Peter Crosthwaite <[email protected]>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/sysbus.h"
+#include "system/system.h"
+#include "system/dma.h"
+#include "qemu/log.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/irq.h"
+#include "trace.h"
+
+#include "hw/core/remote-port.h"
+#include "hw/core/remote-port-proto.h"
+#include "hw/core/remote-port-gpio.h"
+
+#define CACHE_INVALID -1
+
+static void rp_gpio_handler(void *opaque, int irq, int level)
+{
+    RemotePortGPIO *s = opaque;
+    struct rp_pkt pkt;
+    size_t len;
+    int64_t clk;
+    uint32_t id = rp_new_id(s->rp);
+    uint32_t flags = s->posted_updates ? RP_PKT_FLAGS_posted : 0;
+
+    /* If we hit the cache, return early.  */
+    if (s->cache[irq] != CACHE_INVALID && s->cache[irq] == level) {
+        return;
+    }
+    /* Update the cache and update the remote peer.  */
+    s->cache[irq] = level;
+
+    clk = rp_normalized_vmclk(s->rp);
+    len = rp_encode_interrupt_f(id, s->rp_dev, &pkt.interrupt, clk,
+                              irq, 0, level, flags);
+
+    trace_remote_port_gpio_tx_interrupt(id, flags, s->rp_dev, 0, irq, level);
+
+    if (s->peer->caps.wire_posted_updates && !s->posted_updates) {
+        rp_rsp_mutex_lock(s->rp);
+    }
+
+    rp_write(s->rp, (void *)&pkt, len);
+
+    /*
+     * If peer supports posted updates it will respect our flag and
+     * not respond.
+     */
+    if (s->peer->caps.wire_posted_updates && !s->posted_updates) {
+        RemotePortRespSlot *rsp_slot;
+        struct rp_pkt_interrupt *intr;
+
+        rsp_slot = rp_dev_wait_resp(s->rp, s->rp_dev, id);
+        assert(rsp_slot->rsp.pkt->hdr.id == id);
+
+        intr = &rsp_slot->rsp.pkt->interrupt;
+        trace_remote_port_gpio_rx_interrupt(intr->hdr.id, intr->hdr.flags,
+            intr->hdr.dev, intr->vector, intr->line, intr->val);
+
+        rp_resp_slot_done(s->rp, rsp_slot);
+        rp_rsp_mutex_unlock(s->rp);
+    }
+}
+
+static void rp_gpio_interrupt(RemotePortDevice *rpdev, struct rp_pkt *pkt)
+{
+    RemotePortGPIO *s = REMOTE_PORT_GPIO(rpdev);
+
+    trace_remote_port_gpio_rx_interrupt(pkt->hdr.id, pkt->hdr.flags,
+        pkt->hdr.dev, pkt->interrupt.vector, pkt->interrupt.line,
+        pkt->interrupt.val);
+
+    qemu_set_irq(s->gpio_out[pkt->interrupt.line], pkt->interrupt.val);
+
+    if (s->peer->caps.wire_posted_updates
+        && !(pkt->hdr.flags & RP_PKT_FLAGS_posted)) {
+        RemotePortDynPkt rsp = {0};
+        size_t len;
+
+        /* Need to reply.  */
+        rp_dpkt_alloc(&rsp, sizeof(struct rp_pkt_interrupt));
+        len = rp_encode_interrupt_f(pkt->hdr.id, pkt->hdr.dev,
+                                    &rsp.pkt->interrupt,
+                                    pkt->interrupt.timestamp,
+                                    pkt->interrupt.line,
+                                    pkt->interrupt.vector,
+                                    pkt->interrupt.val,
+                                    pkt->hdr.flags | RP_PKT_FLAGS_response);
+
+        trace_remote_port_gpio_tx_interrupt(pkt->hdr.id,
+            pkt->hdr.flags | RP_PKT_FLAGS_response, pkt->hdr.dev,
+            pkt->interrupt.vector, pkt->interrupt.line, pkt->interrupt.val);
+
+        rp_write(s->rp, (void *)rsp.pkt, len);
+    }
+}
+
+static void rp_gpio_reset(DeviceState *dev)
+{
+    RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
+
+    /* Mark as invalid.  */
+    memset(s->cache, CACHE_INVALID, s->num_gpios);
+}
+
+static void rp_gpio_realize(DeviceState *dev, Error **errp)
+{
+    RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
+    unsigned int i;
+
+    s->peer = rp_get_peer(s->rp);
+
+    s->gpio_out = g_new0(qemu_irq, s->num_gpios);
+    qdev_init_gpio_out(dev, s->gpio_out, s->num_gpios);
+    qdev_init_gpio_in(dev, rp_gpio_handler, s->num_gpios);
+
+    for (i = 0; i < s->num_gpios; i++) {
+        sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gpio_out[i]);
+    }
+}
+
+static void rp_prop_allow_set_link(const Object *obj, const char *name,
+                                   Object *val, Error **errp)
+{
+}
+
+static void rp_gpio_init(Object *obj)
+{
+    RemotePortGPIO *rpms = REMOTE_PORT_GPIO(obj);
+
+    object_property_add_link(obj, "rp-adaptor0", "remote-port",
+                             (Object **)&rpms->rp,
+                             rp_prop_allow_set_link,
+                             OBJ_PROP_LINK_STRONG);
+}
+
+static Property rp_properties[] = {
+    DEFINE_PROP_UINT32("rp-chan0", RemotePortGPIO, rp_dev, 0),
+    DEFINE_PROP_UINT32("num-gpios", RemotePortGPIO, num_gpios, 16),
+    DEFINE_PROP_UINT16("cell-offset-irq-num", RemotePortGPIO,
+                       cell_offset_irq_num, 0),
+    DEFINE_PROP_BOOL("posted-updates", RemotePortGPIO, posted_updates, true),
+};
+
+static void rp_gpio_class_init(ObjectClass *oc, const void *data)
+{
+    RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    rpdc->ops[RP_CMD_interrupt] = rp_gpio_interrupt;
+    dc->legacy_reset = rp_gpio_reset;
+    dc->realize = rp_gpio_realize;
+    device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
+}
+
+static const TypeInfo rp_info = {
+    .name          = TYPE_REMOTE_PORT_GPIO,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(RemotePortGPIO),
+    .instance_init = rp_gpio_init,
+    .class_init    = rp_gpio_class_init,
+    .interfaces    = (InterfaceInfo[]) {
+        { TYPE_REMOTE_PORT_DEVICE },
+        { },
+    },
+};
+
+static void rp_register_types(void)
+{
+    type_register_static(&rp_info);
+}
+
+type_init(rp_register_types)
diff --git a/hw/core/trace-events b/hw/core/trace-events
index ee77916010..fb3125b0eb 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -40,3 +40,7 @@ remote_port_memory_master_rx_busaccess(const char *cmd, 
uint32_t id, uint32_t fl
 # remote-port-memory-slave.c
 remote_port_memory_slave_tx_busaccess(const char *cmd, uint32_t id, uint32_t 
flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, 
id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", 
len=0x%"PRIx32", attr=0x%"PRIx64
 remote_port_memory_slave_rx_busaccess(const char *cmd, uint32_t id, uint32_t 
flags, uint32_t dev, uint64_t addr, uint32_t len, uint64_t attr) "cmd=%s, 
id=0x%"PRIx32", flags=0x%"PRIx32", dev=0x%"PRIx32", addr=0x%"PRIx64", 
len=0x%"PRIx32", attr=0x%"PRIx64
+
+# remote-port-memory-gpio.c
+remote_port_gpio_tx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, 
uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", 
flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", 
level=0x%"PRIx32
+remote_port_gpio_rx_interrupt(uint32_t id, uint32_t flags, uint32_t dev, 
uint64_t vector, uint32_t irq, uint32_t val) "id=0x%"PRIx32", 
flags=0x%"PRIx32", dev=0x%"PRIx32", vector=0x%"PRIx64", irq=0x%"PRIx32", 
level=0x%"PRIx32
diff --git a/include/hw/core/remote-port-gpio.h 
b/include/hw/core/remote-port-gpio.h
new file mode 100644
index 0000000000..940e2ec4fd
--- /dev/null
+++ b/include/hw/core/remote-port-gpio.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2013 Xilinx Inc
+ * Written by Edgar E. Iglesias <[email protected]>
+ * Written by Peter Crosthwaite <[email protected]>
+ *
+ * This code is licensed under the GNU GPL.
+ */
+#ifndef REMOTE_PORT_GPIO_H
+#define REMOTE_PORT_GPIO_H
+
+#define TYPE_REMOTE_PORT_GPIO "remote-port-gpio"
+#define REMOTE_PORT_GPIO(obj) \
+        OBJECT_CHECK(RemotePortGPIO, (obj), TYPE_REMOTE_PORT_GPIO)
+
+#define MAX_GPIOS 960
+
+typedef struct RemotePortGPIO {
+    /* private */
+    SysBusDevice parent;
+    /* public */
+
+    int8_t cache[MAX_GPIOS];
+    uint32_t num_gpios;
+    qemu_irq *gpio_out;
+    uint16_t cell_offset_irq_num;
+
+    bool posted_updates;
+    uint32_t rp_dev;
+    struct RemotePort *rp;
+    struct rp_peer_state *peer;
+} RemotePortGPIO;
+#endif
-- 
2.43.0


Reply via email to