From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
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 <edgar.iglesias@amd.com>
Signed-off-by: Takahiro Nakata <takahiro.nakata.wr@renesas.com>
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
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 <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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 <edgar.iglesias@xilinx.com>
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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