1
2
3
4
5
6
7
8
9#include "qemu/osdep.h"
10#include "hw/sysbus.h"
11#include "sysemu/sysemu.h"
12#include "sysemu/dma.h"
13#include "qemu/log.h"
14#include "qapi/qmp/qerror.h"
15#include "qapi/error.h"
16#include "hw/qdev.h"
17
18#include "hw/fdt_generic_util.h"
19
20#include "hw/remote-port-proto.h"
21#include "hw/remote-port-device.h"
22
23#define TYPE_REMOTE_PORT_GPIO "remote-port-gpio"
24#define REMOTE_PORT_GPIO(obj) \
25 OBJECT_CHECK(RemotePortGPIO, (obj), TYPE_REMOTE_PORT_GPIO)
26
27#define MAX_GPIOS 164
28#define CACHE_INVALID -1
29
30typedef struct RemotePortGPIO {
31
32 SysBusDevice parent;
33
34
35 int8_t cache[MAX_GPIOS];
36 uint32_t num_gpios;
37 qemu_irq *gpio_out;
38 uint16_t cell_offset_irq_num;
39
40 uint64_t current_id;
41
42 uint32_t rp_dev;
43 struct RemotePort *rp;
44} RemotePortGPIO;
45
46static void rp_gpio_handler(void *opaque, int irq, int level)
47{
48 RemotePortGPIO *s = opaque;
49 struct rp_pkt pkt;
50 size_t len;
51 int64_t clk;
52
53
54 if (s->cache[irq] != CACHE_INVALID && s->cache[irq] == level) {
55 return;
56 }
57
58 s->cache[irq] = level;
59
60 clk = rp_normalized_vmclk(s->rp);
61 len = rp_encode_interrupt(s->current_id++, s->rp_dev, &pkt.interrupt, clk,
62 irq, 0, level);
63 rp_write(s->rp, (void *)&pkt, len);
64}
65
66static void rp_gpio_interrupt(RemotePortDevice *s, struct rp_pkt *pkt)
67{
68 RemotePortGPIO *rpg = REMOTE_PORT_GPIO(s);
69
70 qemu_set_irq(rpg->gpio_out[pkt->interrupt.line], pkt->interrupt.val);
71}
72
73static void rp_gpio_reset(DeviceState *dev)
74{
75 RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
76
77
78 memset(s->cache, CACHE_INVALID, s->num_gpios);
79}
80
81static void rp_gpio_realize(DeviceState *dev, Error **errp)
82{
83 RemotePortGPIO *s = REMOTE_PORT_GPIO(dev);
84 unsigned int i;
85
86 s->gpio_out = g_new0(qemu_irq, s->num_gpios);
87 qdev_init_gpio_out(dev, s->gpio_out, s->num_gpios);
88 qdev_init_gpio_in(dev, rp_gpio_handler, s->num_gpios);
89
90 for (i = 0; i < s->num_gpios; i++) {
91 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->gpio_out[i]);
92 }
93}
94
95static void rp_gpio_init(Object *obj)
96{
97 RemotePortGPIO *rpms = REMOTE_PORT_GPIO(obj);
98
99 object_property_add_link(obj, "rp-adaptor0", "remote-port",
100 (Object **)&rpms->rp,
101 qdev_prop_allow_set_link_before_realize,
102 OBJ_PROP_LINK_UNREF_ON_RELEASE,
103 &error_abort);
104}
105
106static Property rp_properties[] = {
107 DEFINE_PROP_UINT32("rp-chan0", RemotePortGPIO, rp_dev, 0),
108 DEFINE_PROP_UINT32("num-gpios", RemotePortGPIO, num_gpios, 16),
109 DEFINE_PROP_UINT16("cell-offset-irq-num", RemotePortGPIO,
110 cell_offset_irq_num, 0),
111 DEFINE_PROP_END_OF_LIST(),
112};
113
114static int rp_fdt_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
115 uint32_t *cells, int ncells, int max,
116 Error **errp)
117{
118 RemotePortGPIO *s = REMOTE_PORT_GPIO(obj);
119
120 if (cells[s->cell_offset_irq_num] >= s->num_gpios) {
121 error_setg(errp, "RP-GPIO was setup for %u interrupts: index %"
122 PRIu32 " requested", s->num_gpios,
123 cells[s->cell_offset_irq_num]);
124 return 0;
125 }
126
127 (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[s->cell_offset_irq_num]);
128 return 1;
129};
130
131static void rp_gpio_class_init(ObjectClass *oc, void *data)
132{
133 RemotePortDeviceClass *rpdc = REMOTE_PORT_DEVICE_CLASS(oc);
134 DeviceClass *dc = DEVICE_CLASS(oc);
135 FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
136
137 rpdc->ops[RP_CMD_interrupt] = rp_gpio_interrupt;
138 dc->reset = rp_gpio_reset;
139 dc->realize = rp_gpio_realize;
140 dc->props = rp_properties;
141 fgic->get_irq = rp_fdt_get_irq;
142}
143
144static const TypeInfo rp_info = {
145 .name = TYPE_REMOTE_PORT_GPIO,
146 .parent = TYPE_SYS_BUS_DEVICE,
147 .instance_size = sizeof(RemotePortGPIO),
148 .instance_init = rp_gpio_init,
149 .class_init = rp_gpio_class_init,
150 .interfaces = (InterfaceInfo[]) {
151 { TYPE_REMOTE_PORT_DEVICE },
152 { TYPE_FDT_GENERIC_INTC },
153 { },
154 },
155};
156
157static void rp_register_types(void)
158{
159 type_register_static(&rp_info);
160}
161
162type_init(rp_register_types)
163