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
