blob: 2d5e65abfff3a44460e67480f25f146f34b23ed5 [file] [log] [blame]
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +08001/* 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"
Tianrui Zhao270950b2022-12-14 09:57:18 +08009#include "qemu/bitops.h"
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080010#include "hw/sysbus.h"
11#include "hw/loongarch/virt.h"
Tianrui Zhaof4d10ce2022-12-27 11:19:57 +080012#include "hw/pci-host/ls7a.h"
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080013#include "hw/irq.h"
14#include "hw/intc/loongarch_pch_pic.h"
Tianrui Zhao270950b2022-12-14 09:57:18 +080015#include "hw/qdev-properties.h"
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080016#include "migration/vmstate.h"
17#include "trace.h"
Tianrui Zhao270950b2022-12-14 09:57:18 +080018#include "qapi/error.h"
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080019
20static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
21{
Xiaojuan Yang056dac52022-07-15 14:07:37 +080022 uint64_t val;
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080023 int irq;
24
25 if (level) {
26 val = mask & s->intirr & ~s->int_mask;
27 if (val) {
Xiaojuan Yang056dac52022-07-15 14:07:37 +080028 irq = ctz64(val);
29 s->intisr |= MAKE_64BIT_MASK(irq, 1);
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080030 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
31 }
32 } else {
Bibo Mao2948c1f2023-07-07 17:15:57 +080033 /*
34 * intirr means requested pending irq
35 * do not clear pending irq for edge-triggered on lowering edge
36 */
37 val = mask & s->intisr & ~s->intirr;
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080038 if (val) {
Xiaojuan Yang056dac52022-07-15 14:07:37 +080039 irq = ctz64(val);
40 s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080041 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
42 }
43 }
44}
45
46static void pch_pic_irq_handler(void *opaque, int irq, int level)
47{
48 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
49 uint64_t mask = 1ULL << irq;
50
Tianrui Zhao270950b2022-12-14 09:57:18 +080051 assert(irq < s->irq_num);
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080052 trace_loongarch_pch_pic_irq_handler(irq, level);
53
54 if (s->intedge & mask) {
55 /* Edge triggered */
56 if (level) {
57 if ((s->last_intirr & mask) == 0) {
Bibo Mao2948c1f2023-07-07 17:15:57 +080058 /* marked pending on a rising edge */
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080059 s->intirr |= mask;
60 }
61 s->last_intirr |= mask;
62 } else {
63 s->last_intirr &= ~mask;
64 }
65 } else {
66 /* Level triggered */
67 if (level) {
68 s->intirr |= mask;
69 s->last_intirr |= mask;
70 } else {
71 s->intirr &= ~mask;
72 s->last_intirr &= ~mask;
73 }
74 }
75 pch_pic_update_irq(s, mask, level);
76}
77
78static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
79 unsigned size)
80{
81 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
82 uint64_t val = 0;
83 uint32_t offset = addr & 0xfff;
84
85 switch (offset) {
86 case PCH_PIC_INT_ID_LO:
87 val = PCH_PIC_INT_ID_VAL;
88 break;
89 case PCH_PIC_INT_ID_HI:
Tianrui Zhao270950b2022-12-14 09:57:18 +080090 /*
91 * With 7A1000 manual
92 * bit 0-15 pch irqchip version
93 * bit 16-31 irq number supported with pch irqchip
94 */
95 val = deposit32(PCH_PIC_INT_ID_VER, 16, 16, s->irq_num - 1);
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +080096 break;
97 case PCH_PIC_INT_MASK_LO:
98 val = (uint32_t)s->int_mask;
99 break;
100 case PCH_PIC_INT_MASK_HI:
101 val = s->int_mask >> 32;
102 break;
103 case PCH_PIC_INT_EDGE_LO:
104 val = (uint32_t)s->intedge;
105 break;
106 case PCH_PIC_INT_EDGE_HI:
107 val = s->intedge >> 32;
108 break;
109 case PCH_PIC_HTMSI_EN_LO:
110 val = (uint32_t)s->htmsi_en;
111 break;
112 case PCH_PIC_HTMSI_EN_HI:
113 val = s->htmsi_en >> 32;
114 break;
115 case PCH_PIC_AUTO_CTRL0_LO:
116 case PCH_PIC_AUTO_CTRL0_HI:
117 case PCH_PIC_AUTO_CTRL1_LO:
118 case PCH_PIC_AUTO_CTRL1_HI:
119 break;
120 default:
121 break;
122 }
123
124 trace_loongarch_pch_pic_low_readw(size, addr, val);
125 return val;
126}
127
128static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
129{
130 uint64_t mask = 0xffffffff00000000;
131 uint64_t data = target;
132
133 return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
134}
135
136static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
137 uint64_t value, unsigned size)
138{
139 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
140 uint32_t offset, old_valid, data = (uint32_t)value;
141 uint64_t old, int_mask;
142 offset = addr & 0xfff;
143
144 trace_loongarch_pch_pic_low_writew(size, addr, data);
145
146 switch (offset) {
147 case PCH_PIC_INT_MASK_LO:
148 old = s->int_mask;
149 s->int_mask = get_writew_val(old, data, 0);
150 old_valid = (uint32_t)old;
151 if (old_valid & ~data) {
152 pch_pic_update_irq(s, (old_valid & ~data), 1);
153 }
154 if (~old_valid & data) {
155 pch_pic_update_irq(s, (~old_valid & data), 0);
156 }
157 break;
158 case PCH_PIC_INT_MASK_HI:
159 old = s->int_mask;
160 s->int_mask = get_writew_val(old, data, 1);
161 old_valid = (uint32_t)(old >> 32);
162 int_mask = old_valid & ~data;
163 if (int_mask) {
164 pch_pic_update_irq(s, int_mask << 32, 1);
165 }
166 int_mask = ~old_valid & data;
167 if (int_mask) {
168 pch_pic_update_irq(s, int_mask << 32, 0);
169 }
170 break;
171 case PCH_PIC_INT_EDGE_LO:
172 s->intedge = get_writew_val(s->intedge, data, 0);
173 break;
174 case PCH_PIC_INT_EDGE_HI:
175 s->intedge = get_writew_val(s->intedge, data, 1);
176 break;
177 case PCH_PIC_INT_CLEAR_LO:
178 if (s->intedge & data) {
179 s->intirr &= (~data);
180 pch_pic_update_irq(s, data, 0);
181 s->intisr &= (~data);
182 }
183 break;
184 case PCH_PIC_INT_CLEAR_HI:
185 value <<= 32;
186 if (s->intedge & value) {
187 s->intirr &= (~value);
188 pch_pic_update_irq(s, value, 0);
189 s->intisr &= (~value);
190 }
191 break;
192 case PCH_PIC_HTMSI_EN_LO:
193 s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
194 break;
195 case PCH_PIC_HTMSI_EN_HI:
196 s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
197 break;
198 case PCH_PIC_AUTO_CTRL0_LO:
199 case PCH_PIC_AUTO_CTRL0_HI:
200 case PCH_PIC_AUTO_CTRL1_LO:
201 case PCH_PIC_AUTO_CTRL1_HI:
202 break;
203 default:
204 break;
205 }
206}
207
208static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
209 unsigned size)
210{
211 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
212 uint64_t val = 0;
213 uint32_t offset = addr & 0xfff;
214
215 switch (offset) {
216 case STATUS_LO_START:
217 val = (uint32_t)(s->intisr & (~s->int_mask));
218 break;
219 case STATUS_HI_START:
220 val = (s->intisr & (~s->int_mask)) >> 32;
221 break;
222 case POL_LO_START:
223 val = (uint32_t)s->int_polarity;
224 break;
225 case POL_HI_START:
226 val = s->int_polarity >> 32;
227 break;
228 default:
229 break;
230 }
231
232 trace_loongarch_pch_pic_high_readw(size, addr, val);
233 return val;
234}
235
236static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
237 uint64_t value, unsigned size)
238{
239 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
240 uint32_t offset, data = (uint32_t)value;
241 offset = addr & 0xfff;
242
243 trace_loongarch_pch_pic_high_writew(size, addr, data);
244
245 switch (offset) {
246 case STATUS_LO_START:
247 s->intisr = get_writew_val(s->intisr, data, 0);
248 break;
249 case STATUS_HI_START:
250 s->intisr = get_writew_val(s->intisr, data, 1);
251 break;
252 case POL_LO_START:
253 s->int_polarity = get_writew_val(s->int_polarity, data, 0);
254 break;
255 case POL_HI_START:
256 s->int_polarity = get_writew_val(s->int_polarity, data, 1);
257 break;
258 default:
259 break;
260 }
261}
262
263static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
264 unsigned size)
265{
266 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
267 uint64_t val = 0;
268 uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
269 int64_t offset_tmp;
270
271 switch (offset) {
272 case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
273 offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
274 if (offset_tmp >= 0 && offset_tmp < 64) {
275 val = s->htmsi_vector[offset_tmp];
276 }
277 break;
278 case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
279 offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
280 if (offset_tmp >= 0 && offset_tmp < 64) {
281 val = s->route_entry[offset_tmp];
282 }
283 break;
284 default:
285 break;
286 }
287
288 trace_loongarch_pch_pic_readb(size, addr, val);
289 return val;
290}
291
292static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
293 uint64_t data, unsigned size)
294{
295 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
296 int32_t offset_tmp;
297 uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
298
299 trace_loongarch_pch_pic_writeb(size, addr, data);
300
301 switch (offset) {
302 case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
303 offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
304 if (offset_tmp >= 0 && offset_tmp < 64) {
305 s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
306 }
307 break;
308 case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
309 offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
310 if (offset_tmp >= 0 && offset_tmp < 64) {
311 s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
312 }
313 break;
314 default:
315 break;
316 }
317}
318
319static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
320 .read = loongarch_pch_pic_low_readw,
321 .write = loongarch_pch_pic_low_writew,
322 .valid = {
323 .min_access_size = 4,
324 .max_access_size = 8,
325 },
326 .impl = {
327 .min_access_size = 4,
328 .max_access_size = 4,
329 },
330 .endianness = DEVICE_LITTLE_ENDIAN,
331};
332
333static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
334 .read = loongarch_pch_pic_high_readw,
335 .write = loongarch_pch_pic_high_writew,
336 .valid = {
337 .min_access_size = 4,
338 .max_access_size = 8,
339 },
340 .impl = {
341 .min_access_size = 4,
342 .max_access_size = 4,
343 },
344 .endianness = DEVICE_LITTLE_ENDIAN,
345};
346
347static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
348 .read = loongarch_pch_pic_readb,
349 .write = loongarch_pch_pic_writeb,
350 .valid = {
351 .min_access_size = 1,
352 .max_access_size = 1,
353 },
354 .impl = {
355 .min_access_size = 1,
356 .max_access_size = 1,
357 },
358 .endianness = DEVICE_LITTLE_ENDIAN,
359};
360
361static void loongarch_pch_pic_reset(DeviceState *d)
362{
363 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
364 int i;
365
366 s->int_mask = -1;
367 s->htmsi_en = 0x0;
368 s->intedge = 0x0;
369 s->intclr = 0x0;
370 s->auto_crtl0 = 0x0;
371 s->auto_crtl1 = 0x0;
372 for (i = 0; i < 64; i++) {
373 s->route_entry[i] = 0x1;
374 s->htmsi_vector[i] = 0x0;
375 }
376 s->intirr = 0x0;
377 s->intisr = 0x0;
378 s->last_intirr = 0x0;
379 s->int_polarity = 0x0;
380}
381
Tianrui Zhao270950b2022-12-14 09:57:18 +0800382static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp)
383{
384 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev);
385
Tianrui Zhaof4d10ce2022-12-27 11:19:57 +0800386 if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) {
Tianrui Zhao270950b2022-12-14 09:57:18 +0800387 error_setg(errp, "Invalid 'pic_irq_num'");
388 return;
389 }
390
391 qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
392 qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
393}
394
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800395static void loongarch_pch_pic_init(Object *obj)
396{
397 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
398 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
399
400 memory_region_init_io(&s->iomem32_low, obj,
401 &loongarch_pch_pic_reg32_low_ops,
402 s, PCH_PIC_NAME(.reg32_part1), 0x100);
403 memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
404 s, PCH_PIC_NAME(.reg8), 0x2a0);
405 memory_region_init_io(&s->iomem32_high, obj,
406 &loongarch_pch_pic_reg32_high_ops,
407 s, PCH_PIC_NAME(.reg32_part2), 0xc60);
408 sysbus_init_mmio(sbd, &s->iomem32_low);
409 sysbus_init_mmio(sbd, &s->iomem8);
410 sysbus_init_mmio(sbd, &s->iomem32_high);
411
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800412}
413
Tianrui Zhao270950b2022-12-14 09:57:18 +0800414static Property loongarch_pch_pic_properties[] = {
415 DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC, irq_num, 0),
416 DEFINE_PROP_END_OF_LIST(),
417};
418
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800419static const VMStateDescription vmstate_loongarch_pch_pic = {
420 .name = TYPE_LOONGARCH_PCH_PIC,
421 .version_id = 1,
422 .minimum_version_id = 1,
Richard Henderson45b1f812023-12-21 14:16:15 +1100423 .fields = (const VMStateField[]) {
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800424 VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
425 VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
426 VMSTATE_UINT64(intedge, LoongArchPCHPIC),
427 VMSTATE_UINT64(intclr, LoongArchPCHPIC),
428 VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
429 VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
430 VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
431 VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
432 VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
433 VMSTATE_UINT64(intirr, LoongArchPCHPIC),
434 VMSTATE_UINT64(intisr, LoongArchPCHPIC),
435 VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
436 VMSTATE_END_OF_LIST()
437 }
438};
439
440static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
441{
442 DeviceClass *dc = DEVICE_CLASS(klass);
443
Tianrui Zhao270950b2022-12-14 09:57:18 +0800444 dc->realize = loongarch_pch_pic_realize;
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800445 dc->reset = loongarch_pch_pic_reset;
446 dc->vmsd = &vmstate_loongarch_pch_pic;
Tianrui Zhao270950b2022-12-14 09:57:18 +0800447 device_class_set_props(dc, loongarch_pch_pic_properties);
Xiaojuan Yang0f4fcf12022-06-06 20:43:22 +0800448}
449
450static const TypeInfo loongarch_pch_pic_info = {
451 .name = TYPE_LOONGARCH_PCH_PIC,
452 .parent = TYPE_SYS_BUS_DEVICE,
453 .instance_size = sizeof(LoongArchPCHPIC),
454 .instance_init = loongarch_pch_pic_init,
455 .class_init = loongarch_pch_pic_class_init,
456};
457
458static void loongarch_pch_pic_register_types(void)
459{
460 type_register_static(&loongarch_pch_pic_info);
461}
462
463type_init(loongarch_pch_pic_register_types)