master
c 359 lines 10.2 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QEMU Loongson 7A1000 I/O interrupt controller.
4 *
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu/bitops.h"
10 #include "qemu/log.h"
11 #include "hw/core/irq.h"
12 #include "hw/intc/loongarch_pch_pic.h"
13 #include "system/kvm.h"
14 #include "trace.h"
15 #include "qapi/error.h"
16
17 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask,
18 int level)
19 {
20 uint64_t val;
21 int irq;
22 uint8_t vector;
23
24 if (level) {
25 val = mask & s->intirr & ~s->int_mask;
26 if (val) {
27 irq = ctz64(val);
28 vector = s->htmsi_vector[irq];
29 if (vector >= s->irq_num) {
30 qemu_log_mask(LOG_GUEST_ERROR,
31 "%s: htmsi_vector[%d]=%u out of range\n",
32 __func__, irq, vector);
33 return;
34 }
35 s->intisr |= MAKE_64BIT_MASK(irq, 1);
36 qemu_set_irq(s->parent_irq[vector], 1);
37 }
38 } else {
39 /*
40 * intirr means requested pending irq
41 * do not clear pending irq for edge-triggered on lowering edge
42 */
43 val = mask & s->intisr & ~s->intirr;
44 if (val) {
45 irq = ctz64(val);
46 vector = s->htmsi_vector[irq];
47 if (vector >= s->irq_num) {
48 qemu_log_mask(LOG_GUEST_ERROR,
49 "%s: htmsi_vector[%d]=%u out of range\n",
50 __func__, irq, vector);
51 return;
52 }
53 s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
54 qemu_set_irq(s->parent_irq[vector], 0);
55 }
56 }
57 }
58
59 static void pch_pic_irq_handler(void *opaque, int irq, int level)
60 {
61 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
62 uint64_t mask = 1ULL << irq;
63
64 assert(irq < s->irq_num);
65 trace_loongarch_pch_pic_irq_handler(irq, level);
66
67 if (kvm_irqchip_in_kernel()) {
68 kvm_set_irq(kvm_state, irq, !!level);
69 return;
70 }
71
72 if (s->intedge & mask) {
73 /* Edge triggered */
74 if (level) {
75 if ((s->last_intirr & mask) == 0) {
76 /* marked pending on a rising edge */
77 s->intirr |= mask;
78 }
79 s->last_intirr |= mask;
80 } else {
81 s->last_intirr &= ~mask;
82 }
83 } else {
84 /* Level triggered */
85 if (level) {
86 s->intirr |= mask;
87 s->last_intirr |= mask;
88 } else {
89 s->intirr &= ~mask;
90 s->last_intirr &= ~mask;
91 }
92 }
93 pch_pic_update_irq(s, mask, level);
94 }
95
96 static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask)
97 {
98 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
99 uint64_t val = 0;
100 uint32_t offset;
101
102 offset = addr & 7;
103 addr -= offset;
104 switch (addr) {
105 case PCH_PIC_INT_ID:
106 val = cpu_to_le64(s->id.data);
107 break;
108 case PCH_PIC_INT_MASK:
109 val = s->int_mask;
110 break;
111 case PCH_PIC_INT_EDGE:
112 val = s->intedge;
113 break;
114 case PCH_PIC_HTMSI_EN:
115 val = s->htmsi_en;
116 break;
117 case PCH_PIC_AUTO_CTRL0:
118 case PCH_PIC_AUTO_CTRL1:
119 /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */
120 break;
121 case PCH_PIC_INT_STATUS:
122 val = s->intisr & (~s->int_mask);
123 break;
124 case PCH_PIC_INT_POL:
125 val = s->int_polarity;
126 break;
127 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
128 val = ldq_le_p(&s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]);
129 break;
130 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
131 val = ldq_le_p(&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]);
132 break;
133 default:
134 qemu_log_mask(LOG_GUEST_ERROR,
135 "pch_pic_read: Bad address 0x%"PRIx64"\n", addr);
136 break;
137 }
138
139 return (val >> (offset * 8)) & field_mask;
140 }
141
142 static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value,
143 uint64_t field_mask)
144 {
145 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
146 uint32_t offset;
147 uint64_t old, mask, data;
148 void *ptemp;
149
150 offset = addr & 7;
151 addr -= offset;
152 mask = field_mask << (offset * 8);
153 data = (value & field_mask) << (offset * 8);
154 switch (addr) {
155 case PCH_PIC_INT_MASK:
156 old = s->int_mask;
157 s->int_mask = (old & ~mask) | data;
158 if (old & ~data) {
159 pch_pic_update_irq(s, old & ~data, 1);
160 }
161
162 if (~old & data) {
163 pch_pic_update_irq(s, ~old & data, 0);
164 }
165 break;
166 case PCH_PIC_INT_EDGE:
167 s->intedge = (s->intedge & ~mask) | data;
168 break;
169 case PCH_PIC_INT_CLEAR:
170 if (s->intedge & data) {
171 s->intirr &= ~data;
172 pch_pic_update_irq(s, data, 0);
173 s->intisr &= ~data;
174 }
175 break;
176 case PCH_PIC_HTMSI_EN:
177 s->htmsi_en = (s->htmsi_en & ~mask) | data;
178 break;
179 case PCH_PIC_AUTO_CTRL0:
180 case PCH_PIC_AUTO_CTRL1:
181 /* Discard auto_ctrl access */
182 break;
183 case PCH_PIC_INT_POL:
184 s->int_polarity = (s->int_polarity & ~mask) | data;
185 break;
186 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
187 ptemp = &s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC];
188 stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data);
189 break;
190 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
191 ptemp = (uint64_t *)&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY];
192 stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data);
193 break;
194 default:
195 qemu_log_mask(LOG_GUEST_ERROR,
196 "pch_pic_write: Bad address 0x%"PRIx64"\n", addr);
197 break;
198 }
199 }
200
201 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr,
202 unsigned size)
203 {
204 uint64_t val = 0;
205
206 switch (size) {
207 case 1:
208 val = pch_pic_read(opaque, addr, UCHAR_MAX);
209 break;
210 case 2:
211 val = pch_pic_read(opaque, addr, USHRT_MAX);
212 break;
213 case 4:
214 val = pch_pic_read(opaque, addr, UINT_MAX);
215 break;
216 case 8:
217 val = pch_pic_read(opaque, addr, UINT64_MAX);
218 break;
219 default:
220 qemu_log_mask(LOG_GUEST_ERROR,
221 "loongarch_pch_pic_read: Bad size %d\n", size);
222 break;
223 }
224
225 trace_loongarch_pch_pic_read(size, addr, val);
226 return val;
227 }
228
229 static void loongarch_pch_pic_write(void *opaque, hwaddr addr,
230 uint64_t value, unsigned size)
231 {
232 trace_loongarch_pch_pic_write(size, addr, value);
233
234 switch (size) {
235 case 1:
236 pch_pic_write(opaque, addr, value, UCHAR_MAX);
237 break;
238 case 2:
239 pch_pic_write(opaque, addr, value, USHRT_MAX);
240 break;
241 break;
242 case 4:
243 pch_pic_write(opaque, addr, value, UINT_MAX);
244 break;
245 case 8:
246 pch_pic_write(opaque, addr, value, UINT64_MAX);
247 break;
248 default:
249 qemu_log_mask(LOG_GUEST_ERROR,
250 "loongarch_pch_pic_write: Bad size %d\n", size);
251 break;
252 }
253 }
254
255 static const MemoryRegionOps loongarch_pch_pic_ops = {
256 .read = loongarch_pch_pic_read,
257 .write = loongarch_pch_pic_write,
258 .valid = {
259 .min_access_size = 1,
260 .max_access_size = 8,
261 /*
262 * PCH PIC device would not work correctly if the guest was doing
263 * unaligned access. This might not be a limitation on the real
264 * device but in practice there is no reason for a guest to access
265 * this device unaligned.
266 */
267 .unaligned = false,
268 },
269 .impl = {
270 .min_access_size = 1,
271 .max_access_size = 8,
272 },
273 .endianness = DEVICE_LITTLE_ENDIAN,
274 };
275
276 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
277 {
278 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
279
280 if (lpc->parent_phases.hold) {
281 lpc->parent_phases.hold(obj, type);
282 }
283
284 if (kvm_irqchip_in_kernel()) {
285 kvm_pic_put(obj, 0);
286 }
287 }
288
289 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
290 {
291 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
292 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
293 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
294 Error *local_err = NULL;
295
296 lpc->parent_realize(dev, &local_err);
297 if (local_err) {
298 error_propagate(errp, local_err);
299 return;
300 }
301
302 qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
303 qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
304
305 if (kvm_irqchip_in_kernel()) {
306 kvm_pic_realize(dev, errp);
307 } else {
308 memory_region_init_io(&s->iomem, OBJECT(dev),
309 &loongarch_pch_pic_ops,
310 s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE);
311 sysbus_init_mmio(sbd, &s->iomem);
312 }
313 }
314
315 static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque)
316 {
317 if (kvm_irqchip_in_kernel()) {
318 return kvm_pic_get(opaque);
319 }
320
321 return 0;
322 }
323
324 static int loongarch_pic_post_load(LoongArchPICCommonState *opaque,
325 int version_id)
326 {
327 if (kvm_irqchip_in_kernel()) {
328 return kvm_pic_put(opaque, version_id);
329 }
330
331 return 0;
332 }
333
334 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
335 {
336 DeviceClass *dc = DEVICE_CLASS(klass);
337 LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
338 LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass);
339 ResettableClass *rc = RESETTABLE_CLASS(klass);
340
341 resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
342 NULL, &lpc->parent_phases);
343 device_class_set_parent_realize(dc, loongarch_pic_realize,
344 &lpc->parent_realize);
345 lpcc->pre_save = loongarch_pic_pre_save;
346 lpcc->post_load = loongarch_pic_post_load;
347 }
348
349 static const TypeInfo loongarch_pic_types[] = {
350 {
351 .name = TYPE_LOONGARCH_PIC,
352 .parent = TYPE_LOONGARCH_PIC_COMMON,
353 .instance_size = sizeof(LoongarchPICState),
354 .class_size = sizeof(LoongarchPICClass),
355 .class_init = loongarch_pic_class_init,
356 }
357 };
358
359 DEFINE_TYPES(loongarch_pic_types)