xref: /qemu/hw/intc/loongarch_pch_pic.c (revision c3bef3b4)
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)
459