1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include "qemu/osdep.h"
28#include "qapi/error.h"
29#include "hw/sysbus.h"
30#include "hw/register.h"
31#include "migration/vmstate.h"
32#include "hw/qdev-properties.h"
33
34#include "qemu/bitops.h"
35#include "qemu/log.h"
36
37#include "hw/fdt_generic_util.h"
38
39#ifndef GIC_PROXY_ERR_DEBUG
40#define GIC_PROXY_ERR_DEBUG 0
41#endif
42
43#define TYPE_XILINX_GIC_PROXY "xlnx.zynqmp-gicp"
44
45#define XILINX_GIC_PROXY(obj) \
46 OBJECT_CHECK(GICProxy, (obj), TYPE_XILINX_GIC_PROXY)
47
48#define MAX_INTS 160
49#define GICP_GROUPS 5
50#define GICP_GROUP_STRIDE 0x14
51
52REG32(GICP0_IRQ_STATUS, 0x0)
53REG32(GICP0_IRQ_MASK, 0x4)
54REG32(GICP0_IRQ_ENABLE, 0x8)
55REG32(GICP0_IRQ_DISABLE, 0xc)
56REG32(GICP0_IRQ_TRIGGER, 0x10)
57 #define R_GICP0_RSVD 0x000000ff
58 #define R_GICP1_RSVD 0
59 #define R_GICP2_RSVD 0
60 #define R_GICP3_RSVD 0x000000ff
61 #define R_GICP4_RSVD 0xf0000000
62REG32(GICP_PMU_IRQ_STATUS, 0xa0)
63REG32(GICP_PMU_IRQ_MASK, 0xa4)
64REG32(GICP_PMU_IRQ_ENABLE, 0xa8)
65REG32(GICP_PMU_IRQ_DISABLE, 0xac)
66REG32(GICP_PMU_IRQ_TRIGGER, 0xb0)
67
68#define R_MAX (R_GICP_PMU_IRQ_TRIGGER + 1)
69
70typedef struct GICProxy {
71 SysBusDevice parent_obj;
72 MemoryRegion iomem;
73 qemu_irq irq;
74
75 uint32_t pinState[GICP_GROUPS];
76 uint32_t regs[R_MAX];
77 RegisterInfo regs_info[R_MAX];
78} GICProxy;
79
80
81
82
83#define GICPN_STATUS_REG(n) ((A_GICP0_IRQ_STATUS + \
84 (n) * GICP_GROUP_STRIDE) >> 2)
85#define GICPN_MASK_REG(n) ((A_GICP0_IRQ_MASK + \
86 (n) * GICP_GROUP_STRIDE) >> 2)
87
88static void gicp_update_irq(void *opaque)
89{
90 GICProxy *s = XILINX_GIC_PROXY(opaque);
91 bool pending = s->regs[R_GICP_PMU_IRQ_STATUS] &
92 ~s->regs[R_GICP_PMU_IRQ_MASK];
93
94 qemu_set_irq(s->irq, pending);
95}
96
97
98
99static void gicp_update(void *opaque, uint8_t nr)
100{
101 GICProxy *s = XILINX_GIC_PROXY(opaque);
102
103 if (s->regs[GICPN_STATUS_REG(nr)] & ~s->regs[GICPN_MASK_REG(nr)]) {
104 s->regs[R_GICP_PMU_IRQ_STATUS] |= 1 << nr;
105 } else {
106 s->regs[R_GICP_PMU_IRQ_STATUS] &= ~(1 << nr);
107 }
108 gicp_update_irq(s);
109}
110
111static void gicp_status_postw(RegisterInfo *reg, uint64_t val64)
112{
113 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
114 unsigned int i;
115
116 for (i = 0; i < GICP_GROUPS; i++) {
117 gicp_update(s, i);
118 }
119}
120
121static void gicp_enable_postw(RegisterInfo *reg, uint64_t val64)
122{
123 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
124 uint32_t val = val64;
125
126 s->regs[R_GICP_PMU_IRQ_MASK] &= ~val;
127 gicp_update_irq(s);
128}
129
130static void gicp_disable_postw(RegisterInfo *reg, uint64_t val64)
131{
132 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
133 uint32_t val = val64;
134
135 s->regs[R_GICP_PMU_IRQ_MASK] |= val;
136 gicp_update_irq(s);
137}
138
139static void gicp_trigger_postw(RegisterInfo *reg, uint64_t val64)
140{
141 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
142 uint32_t val = val64;
143
144 s->regs[R_GICP_PMU_IRQ_STATUS] |= val;
145 gicp_update_irq(s);
146}
147
148
149
150static void gicpn_status_postw(RegisterInfo *reg, uint64_t val64)
151{
152 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
153 uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
154
155 s->regs[GICPN_STATUS_REG(nr)] |= s->pinState[nr];
156 gicp_update(s, nr);
157}
158
159static void gicpn_enable_postw(RegisterInfo *reg, uint64_t val64)
160{
161 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
162 uint32_t val = val64;
163 uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
164
165 s->regs[GICPN_MASK_REG(nr)] &= ~val;
166 gicp_update(s, nr);
167}
168
169static void gicpn_disable_postw(RegisterInfo *reg, uint64_t val64)
170{
171 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
172 uint32_t val = val64;
173 uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
174
175 s->regs[GICPN_MASK_REG(nr)] |= val;
176 gicp_update(s, nr);
177}
178
179static void gicpn_trigger_postw(RegisterInfo *reg, uint64_t val64)
180{
181 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
182 uint32_t val = val64;
183 uint64_t nr = reg->access->addr / GICP_GROUP_STRIDE;
184
185 s->regs[GICPN_STATUS_REG(nr)] |= val;
186 gicp_update(s, nr);
187}
188
189
190static uint64_t gicp_wo_postr(RegisterInfo *reg, uint64_t val64)
191{
192 GICProxy *s = XILINX_GIC_PROXY(reg->opaque);
193 qemu_log_mask(LOG_GUEST_ERROR,
194 "%s: Reading from wo register at %" HWADDR_PRIx "\n",
195 object_get_canonical_path(OBJECT(s)),
196 reg->access->addr);
197 return 0;
198}
199
200
201static uint64_t gicp_raz_hi24_postr(RegisterInfo *reg, uint64_t val64)
202{
203 return val64 & 0xff;
204}
205
206static RegisterAccessInfo gic_proxy_regs_info[] = {
207#define GICPN_REG_DEFS(n) \
208 { .name = "GICP" #n "_IRQ_STATUS", \
209 .addr = A_GICP0_IRQ_STATUS + n * GICP_GROUP_STRIDE, \
210 .w1c = 0xffffffff, \
211 .rsvd = R_GICP ## n ## _RSVD, \
212 .post_write = gicpn_status_postw, \
213 },{ .name = "GICP" #n "_IRQ_MASK", \
214 .addr = A_GICP0_IRQ_MASK + n * GICP_GROUP_STRIDE, \
215 .ro = 0xffffffff, \
216 .rsvd = R_GICP ## n ## _RSVD, \
217 .reset = 0xffffffff, \
218 },{ .name = "GICP" #n "_IRQ_ENABLE", \
219 .addr = A_GICP0_IRQ_ENABLE + n * GICP_GROUP_STRIDE, \
220 .rsvd = R_GICP ## n ## _RSVD, \
221 .post_read = gicp_wo_postr, \
222 .post_write = gicpn_enable_postw, \
223 },{ .name = "GICP" #n "_IRQ_DISABLE", \
224 .addr = A_GICP0_IRQ_DISABLE + n * GICP_GROUP_STRIDE, \
225 .rsvd = R_GICP ## n ## _RSVD, \
226 .post_read = gicp_wo_postr, \
227 .post_write = gicpn_disable_postw, \
228 },{ .name = "GICP" #n "_IRQ_TRIGGER", \
229 .addr = A_GICP0_IRQ_TRIGGER + n * GICP_GROUP_STRIDE, \
230 .rsvd = R_GICP ## n ## _RSVD, \
231 .post_read = gicp_wo_postr, \
232 .post_write = gicpn_trigger_postw, \
233 }
234 GICPN_REG_DEFS(0),
235 GICPN_REG_DEFS(1),
236 GICPN_REG_DEFS(2),
237 GICPN_REG_DEFS(3),
238 GICPN_REG_DEFS(4),
239 { .name = "GICP_PMU_IRQ_STATUS", .addr = A_GICP_PMU_IRQ_STATUS,
240 .w1c = 0x000000ff,
241 .rsvd = 0xffffffe0,
242 .post_read = gicp_raz_hi24_postr,
243 .post_write = gicp_status_postw,
244 },{ .name = "GICP_PMU_IRQ_MASK", .addr = A_GICP_PMU_IRQ_MASK,
245 .ro = 0x000000ff,
246 .rsvd = 0xffffffe0,
247 .reset = 0x000000ff,
248 .post_read = gicp_raz_hi24_postr,
249 },{ .name = "GICP_PMU_IRQ_ENABLE", .addr = A_GICP_PMU_IRQ_ENABLE,
250 .rsvd = 0xffffffe0,
251 .post_read = gicp_wo_postr,
252 .post_write = gicp_enable_postw,
253 },{ .name = "GICP_PMU_IRQ_DISABLE", .addr = A_GICP_PMU_IRQ_DISABLE,
254 .rsvd = 0xffffffe0,
255 .post_read = gicp_wo_postr,
256 .post_write = gicp_disable_postw,
257 },{ .name = "GICP_PMU_IRQ_TRIGGER", .addr = A_GICP_PMU_IRQ_TRIGGER,
258 .rsvd = 0xffffffe0,
259 .post_read = gicp_wo_postr,
260 .post_write = gicp_trigger_postw,
261 }
262};
263
264static void gic_proxy_reset(DeviceState *dev)
265{
266 GICProxy *s = XILINX_GIC_PROXY(dev);
267 unsigned int i;
268
269 for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
270 register_reset(&s->regs_info[i]);
271 }
272}
273
274static const MemoryRegionOps gic_proxy_ops = {
275 .read = register_read_memory,
276 .write = register_write_memory,
277 .endianness = DEVICE_LITTLE_ENDIAN,
278 .valid = {
279 .min_access_size = 4,
280 .max_access_size = 4,
281 },
282};
283
284static void gic_proxy_set_irq(void *opaque, int irq, int level)
285{
286 GICProxy *s = XILINX_GIC_PROXY(opaque);
287 int group = irq / 32;
288 int bit = irq % 32;
289
290 if (level) {
291 s->pinState[group] |= 1 << bit;
292 } else {
293 s->pinState[group] &= ~(1 << bit);
294 }
295 s->regs[GICPN_STATUS_REG(group)] |= s->pinState[group];
296 gicp_update(s, group);
297}
298
299static void gic_proxy_init(Object *obj)
300{
301 GICProxy *s = XILINX_GIC_PROXY(obj);
302 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
303 DeviceState *dev = DEVICE(s);
304 RegisterInfoArray *reg_array;
305
306 memory_region_init(&s->iomem, obj, TYPE_XILINX_GIC_PROXY, R_MAX * 4);
307 reg_array =
308 register_init_block32(DEVICE(obj), gic_proxy_regs_info,
309 ARRAY_SIZE(gic_proxy_regs_info),
310 s->regs_info, s->regs,
311 &gic_proxy_ops,
312 GIC_PROXY_ERR_DEBUG,
313 R_MAX * 4);
314 memory_region_add_subregion(&s->iomem,
315 0x0,
316 ®_array->mem);
317
318
319
320
321
322
323
324
325 qdev_init_gpio_in(dev, gic_proxy_set_irq, MAX_INTS);
326 qdev_init_gpio_out_named(dev, &s->irq, "gicp-irq", 1);
327
328 sysbus_init_mmio(sbd, &s->iomem);
329 sysbus_init_irq(sbd, &s->irq);
330}
331
332static int gic_proxy_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
333 uint32_t *cells, int ncells, int max,
334 Error **errp)
335{
336 int idx;
337
338 if (ncells != 3) {
339 error_setg(errp, "Xilinx GIC Proxy requires 3 interrupt cells, "
340 "%d cells given", ncells);
341 return 0;
342 }
343 idx = cells[1];
344
345 switch (cells[0]) {
346 case 0:
347 if (idx >= MAX_INTS) {
348 error_setg(errp, "Xilinx GIC Proxy has maximum index of %" PRId32
349 ", index %" PRId32 " given", MAX_INTS - 1, idx);
350 return 0;
351 }
352 (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[1]);
353 return 1;
354 default:
355 error_setg(errp, "Invalid cell 0 value in interrupt binding: %d",
356 cells[0]);
357 return 0;
358 }
359};
360
361static const VMStateDescription vmstate_gic_proxy = {
362 .name = TYPE_XILINX_GIC_PROXY,
363 .version_id = 1,
364 .minimum_version_id = 1,
365 .minimum_version_id_old = 1,
366 .fields = (VMStateField[]) {
367 VMSTATE_UINT32_ARRAY(regs, GICProxy, R_MAX),
368 VMSTATE_END_OF_LIST(),
369 }
370};
371
372static const FDTGenericGPIOSet gic_proxy_client_gpios[] = {
373 {
374 .names = &fdt_generic_gpio_name_set_gpio,
375 .gpios = (FDTGenericGPIOConnection[]) {
376 { .name = "gicp-irq", .fdt_index = 0 },
377 { },
378 },
379 },
380 { },
381};
382
383static void gic_proxy_class_init(ObjectClass *oc, void *data)
384{
385 DeviceClass *dc = DEVICE_CLASS(oc);
386 FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
387 FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(oc);
388
389 dc->reset = gic_proxy_reset;
390 dc->vmsd = &vmstate_gic_proxy;
391 fgic->get_irq = gic_proxy_get_irq;
392 fggc->client_gpios = gic_proxy_client_gpios;
393}
394
395static const TypeInfo gic_proxy_info = {
396 .name = TYPE_XILINX_GIC_PROXY,
397 .parent = TYPE_SYS_BUS_DEVICE,
398 .instance_size = sizeof(GICProxy),
399 .instance_init = gic_proxy_init,
400 .class_init = gic_proxy_class_init,
401 .interfaces = (InterfaceInfo[]) {
402 { TYPE_FDT_GENERIC_INTC },
403 { TYPE_FDT_GENERIC_GPIO },
404 { },
405 },
406};
407
408static void gic_proxy_register_types(void)
409{
410 type_register_static(&gic_proxy_info);
411}
412
413type_init(gic_proxy_register_types)
414