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 "hw/sysbus.h"
11 #include "hw/loongarch/virt.h"
12 #include "hw/pci-host/ls7a.h"
13 #include "hw/irq.h"
14 #include "hw/intc/loongarch_pch_pic.h"
15 #include "hw/qdev-properties.h"
16 #include "migration/vmstate.h"
17 #include "trace.h"
18 #include "qapi/error.h"
19
pch_pic_update_irq(LoongArchPCHPIC * s,uint64_t mask,int level)20 static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
21 {
22 uint64_t val;
23 int irq;
24
25 if (level) {
26 val = mask & s->intirr & ~s->int_mask;
27 if (val) {
28 irq = ctz64(val);
29 s->intisr |= MAKE_64BIT_MASK(irq, 1);
30 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
31 }
32 } else {
33 /*
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;
38 if (val) {
39 irq = ctz64(val);
40 s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
41 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
42 }
43 }
44 }
45
pch_pic_irq_handler(void * opaque,int irq,int level)46 static 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
51 assert(irq < s->irq_num);
52 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) {
58 /* marked pending on a rising edge */
59 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
loongarch_pch_pic_low_readw(void * opaque,hwaddr addr,unsigned size)78 static 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:
90 /*
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);
96 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
get_writew_val(uint64_t value,uint32_t target,bool hi)128 static 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
loongarch_pch_pic_low_writew(void * opaque,hwaddr addr,uint64_t value,unsigned size)136 static 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
loongarch_pch_pic_high_readw(void * opaque,hwaddr addr,unsigned size)208 static 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
loongarch_pch_pic_high_writew(void * opaque,hwaddr addr,uint64_t value,unsigned size)236 static 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
loongarch_pch_pic_readb(void * opaque,hwaddr addr,unsigned size)263 static 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
loongarch_pch_pic_writeb(void * opaque,hwaddr addr,uint64_t data,unsigned size)292 static 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
319 static 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
333 static 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
347 static 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
loongarch_pch_pic_reset(DeviceState * d)361 static 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
loongarch_pch_pic_realize(DeviceState * dev,Error ** errp)382 static void loongarch_pch_pic_realize(DeviceState *dev, Error **errp)
383 {
384 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(dev);
385
386 if (!s->irq_num || s->irq_num > VIRT_PCH_PIC_IRQ_NUM) {
387 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
loongarch_pch_pic_init(Object * obj)395 static 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
412 }
413
414 static 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
419 static const VMStateDescription vmstate_loongarch_pch_pic = {
420 .name = TYPE_LOONGARCH_PCH_PIC,
421 .version_id = 1,
422 .minimum_version_id = 1,
423 .fields = (const VMStateField[]) {
424 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
loongarch_pch_pic_class_init(ObjectClass * klass,void * data)440 static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
441 {
442 DeviceClass *dc = DEVICE_CLASS(klass);
443
444 dc->realize = loongarch_pch_pic_realize;
445 dc->reset = loongarch_pch_pic_reset;
446 dc->vmsd = &vmstate_loongarch_pch_pic;
447 device_class_set_props(dc, loongarch_pch_pic_properties);
448 }
449
450 static 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
loongarch_pch_pic_register_types(void)458 static void loongarch_pch_pic_register_types(void)
459 {
460 type_register_static(&loongarch_pch_pic_info);
461 }
462
463 type_init(loongarch_pch_pic_register_types)
464