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