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
28
29
30#include "qemu/osdep.h"
31#include "qapi/error.h"
32#include "hw/sysbus.h"
33#include "hw/register.h"
34#include "qemu/bitops.h"
35#include "qemu/log.h"
36
37#include "hw/fdt_generic_util.h"
38
39#define TYPE_XILINX_ZYNQMP_INTC_REDIRECT "xlnx.zynqmp-intc-redirect"
40
41#define XILINX_ZYNQMP_INTC_REDIRECT(obj) \
42 OBJECT_CHECK(INTCRedirect, (obj), TYPE_XILINX_ZYNQMP_INTC_REDIRECT)
43
44#define NUM_LINES_FROM_GIC 4
45
46typedef struct INTCRedirect {
47
48 DeviceState parent;
49
50
51 qemu_irq cpu_out[4];
52 qemu_irq pmu_out;
53
54 bool cpu_pwrdwn_en;
55 uint8_t irq_in;
56
57} INTCRedirect;
58
59static void intc_redirect_update_irqs(void *opaque)
60{
61 INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
62 unsigned int i;
63
64
65
66
67 if (s->cpu_pwrdwn_en) {
68 qemu_set_irq(s->pmu_out, !!s->irq_in);
69 } else {
70 for (i = 0; i < NUM_LINES_FROM_GIC; i++) {
71 qemu_set_irq(s->cpu_out[i], !!(s->irq_in & (1 << i)));
72 }
73 }
74}
75
76static void intc_redirect_in_from_gic(void *opaque, int irq, int level)
77{
78 INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
79
80 s->irq_in = deposit32(s->irq_in, irq, 1, level);
81 intc_redirect_update_irqs(s);
82}
83
84static void intc_redirect_pwr_cntrl_enable(void *opaque, int irq, int level)
85{
86 INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(opaque);
87
88 s->cpu_pwrdwn_en = level;
89 intc_redirect_update_irqs(s);
90}
91
92static void intc_redirect_init(Object *obj)
93{
94 INTCRedirect *s = XILINX_ZYNQMP_INTC_REDIRECT(obj);
95 DeviceState *dev = DEVICE(obj);
96
97 qdev_init_gpio_in_named(dev, intc_redirect_in_from_gic, "gic_in", 4);
98 qdev_init_gpio_out_named(dev, s->cpu_out, "cpu_out", 4);
99 qdev_init_gpio_out_named(dev, &s->pmu_out, "pmu_out", 1);
100 qdev_init_gpio_in_named(dev, intc_redirect_pwr_cntrl_enable,
101 "cpu_pwrdwn_en", 1);
102}
103
104static int intc_redirect_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
105 uint32_t *cells, int ncells, int max,
106 Error **errp)
107{
108 if (cells[0] >= NUM_LINES_FROM_GIC) {
109 error_setg(errp, "ZynqMP intc redirect only supports %u interrupts,"
110 "index %" PRIu32 " requested", NUM_LINES_FROM_GIC, cells[0]);
111 return 0;
112 }
113
114 *irqs = qdev_get_gpio_in(DEVICE(obj), cells[0]);
115 return 1;
116};
117
118
119
120
121static const FDTGenericGPIOSet intc_redirect_client_gpios[] = {
122 {
123 .names = &fdt_generic_gpio_name_set_interrupts,
124 .gpios = (FDTGenericGPIOConnection[]) {
125 { .name = "cpu_out", .range = 4 },
126 { },
127 },
128 },
129 {
130 .names = &fdt_generic_gpio_name_set_gpio,
131 .gpios = (FDTGenericGPIOConnection[]) {
132 { .name = "pmu_out", .fdt_index = 0, },
133 { .name = "cpu_pwrdwn_en", .fdt_index = 1, },
134 { },
135 },
136 },
137 { },
138};
139static const FDTGenericGPIOSet intc_redirect_controller_gpios[] = {
140 {
141 .names = &fdt_generic_gpio_name_set_interrupts,
142 .gpios = (FDTGenericGPIOConnection[]) {
143 { .name = "gic_in", .range = 4 },
144 { },
145 },
146 },
147 { },
148};
149
150static void intc_redirect_class_init(ObjectClass *oc, void *data)
151{
152 FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(oc);
153 FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(oc);
154
155 fgic->get_irq = intc_redirect_get_irq;
156 fggc->client_gpios = intc_redirect_client_gpios;
157 fggc->controller_gpios = intc_redirect_controller_gpios;
158}
159
160static const TypeInfo intc_redirect_info = {
161 .name = TYPE_XILINX_ZYNQMP_INTC_REDIRECT,
162 .parent = TYPE_DEVICE,
163 .instance_size = sizeof(INTCRedirect),
164 .instance_init = intc_redirect_init,
165 .class_init = intc_redirect_class_init,
166 .interfaces = (InterfaceInfo[]) {
167 { TYPE_FDT_GENERIC_INTC },
168 { TYPE_FDT_GENERIC_GPIO },
169 { },
170 },
171};
172
173static void intc_redirect_register_types(void)
174{
175 type_register_static(&intc_redirect_info);
176}
177
178type_init(intc_redirect_register_types)
179