xref: /qemu/hw/intc/loongarch_pch_pic.c (revision 513823e7521a09ed7ad1e32e6454bac3b2cbf52d)
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/irq.h"
11 #include "hw/intc/loongarch_pch_pic.h"
12 #include "trace.h"
13 #include "qapi/error.h"
14 
15 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask,
16                                int level)
17 {
18     uint64_t val;
19     int irq;
20 
21     if (level) {
22         val = mask & s->intirr & ~s->int_mask;
23         if (val) {
24             irq = ctz64(val);
25             s->intisr |= MAKE_64BIT_MASK(irq, 1);
26             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
27         }
28     } else {
29         /*
30          * intirr means requested pending irq
31          * do not clear pending irq for edge-triggered on lowering edge
32          */
33         val = mask & s->intisr & ~s->intirr;
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     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(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                 /* marked pending on a rising edge */
55                 s->intirr |= mask;
56             }
57             s->last_intirr |= mask;
58         } else {
59             s->last_intirr &= ~mask;
60         }
61     } else {
62         /* Level triggered */
63         if (level) {
64             s->intirr |= mask;
65             s->last_intirr |= mask;
66         } else {
67             s->intirr &= ~mask;
68             s->last_intirr &= ~mask;
69         }
70     }
71     pch_pic_update_irq(s, mask, level);
72 }
73 
74 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
75                                             unsigned size)
76 {
77     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
78     uint64_t val = 0;
79     uint32_t offset = addr & 0xfff;
80 
81     switch (offset) {
82     case PCH_PIC_INT_ID_LO:
83         val = PCH_PIC_INT_ID_VAL;
84         break;
85     case PCH_PIC_INT_ID_HI:
86         /*
87          * With 7A1000 manual
88          *   bit  0-15 pch irqchip version
89          *   bit 16-31 irq number supported with pch irqchip
90          */
91         val = deposit32(PCH_PIC_INT_ID_VER, 16, 16, s->irq_num - 1);
92         break;
93     case PCH_PIC_INT_MASK_LO:
94         val = (uint32_t)s->int_mask;
95         break;
96     case PCH_PIC_INT_MASK_HI:
97         val = s->int_mask >> 32;
98         break;
99     case PCH_PIC_INT_EDGE_LO:
100         val = (uint32_t)s->intedge;
101         break;
102     case PCH_PIC_INT_EDGE_HI:
103         val = s->intedge >> 32;
104         break;
105     case PCH_PIC_HTMSI_EN_LO:
106         val = (uint32_t)s->htmsi_en;
107         break;
108     case PCH_PIC_HTMSI_EN_HI:
109         val = s->htmsi_en >> 32;
110         break;
111     case PCH_PIC_AUTO_CTRL0_LO:
112     case PCH_PIC_AUTO_CTRL0_HI:
113     case PCH_PIC_AUTO_CTRL1_LO:
114     case PCH_PIC_AUTO_CTRL1_HI:
115         break;
116     default:
117         break;
118     }
119 
120     trace_loongarch_pch_pic_low_readw(size, addr, val);
121     return val;
122 }
123 
124 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
125 {
126     uint64_t mask = 0xffffffff00000000;
127     uint64_t data = target;
128 
129     return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
130 }
131 
132 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
133                                          uint64_t value, unsigned size)
134 {
135     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
136     uint32_t offset, old_valid, data = (uint32_t)value;
137     uint64_t old, int_mask;
138     offset = addr & 0xfff;
139 
140     trace_loongarch_pch_pic_low_writew(size, addr, data);
141 
142     switch (offset) {
143     case PCH_PIC_INT_MASK_LO:
144         old = s->int_mask;
145         s->int_mask = get_writew_val(old, data, 0);
146         old_valid = (uint32_t)old;
147         if (old_valid & ~data) {
148             pch_pic_update_irq(s, (old_valid & ~data), 1);
149         }
150         if (~old_valid & data) {
151             pch_pic_update_irq(s, (~old_valid & data), 0);
152         }
153         break;
154     case PCH_PIC_INT_MASK_HI:
155         old = s->int_mask;
156         s->int_mask = get_writew_val(old, data, 1);
157         old_valid = (uint32_t)(old >> 32);
158         int_mask = old_valid & ~data;
159         if (int_mask) {
160             pch_pic_update_irq(s, int_mask << 32, 1);
161         }
162         int_mask = ~old_valid & data;
163         if (int_mask) {
164             pch_pic_update_irq(s, int_mask << 32, 0);
165         }
166         break;
167     case PCH_PIC_INT_EDGE_LO:
168         s->intedge = get_writew_val(s->intedge, data, 0);
169         break;
170     case PCH_PIC_INT_EDGE_HI:
171         s->intedge = get_writew_val(s->intedge, data, 1);
172         break;
173     case PCH_PIC_INT_CLEAR_LO:
174         if (s->intedge & data) {
175             s->intirr &= (~data);
176             pch_pic_update_irq(s, data, 0);
177             s->intisr &= (~data);
178         }
179         break;
180     case PCH_PIC_INT_CLEAR_HI:
181         value <<= 32;
182         if (s->intedge & value) {
183             s->intirr &= (~value);
184             pch_pic_update_irq(s, value, 0);
185             s->intisr &= (~value);
186         }
187         break;
188     case PCH_PIC_HTMSI_EN_LO:
189         s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
190         break;
191     case PCH_PIC_HTMSI_EN_HI:
192         s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
193         break;
194     case PCH_PIC_AUTO_CTRL0_LO:
195     case PCH_PIC_AUTO_CTRL0_HI:
196     case PCH_PIC_AUTO_CTRL1_LO:
197     case PCH_PIC_AUTO_CTRL1_HI:
198         break;
199     default:
200         break;
201     }
202 }
203 
204 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
205                                         unsigned size)
206 {
207     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
208     uint64_t val = 0;
209     uint32_t offset = addr & 0xfff;
210 
211     switch (offset) {
212     case STATUS_LO_START:
213         val = (uint32_t)(s->intisr & (~s->int_mask));
214         break;
215     case STATUS_HI_START:
216         val = (s->intisr & (~s->int_mask)) >> 32;
217         break;
218     case POL_LO_START:
219         val = (uint32_t)s->int_polarity;
220         break;
221     case POL_HI_START:
222         val = s->int_polarity >> 32;
223         break;
224     default:
225         break;
226     }
227 
228     trace_loongarch_pch_pic_high_readw(size, addr, val);
229     return val;
230 }
231 
232 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
233                                      uint64_t value, unsigned size)
234 {
235     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
236     uint32_t offset, data = (uint32_t)value;
237     offset = addr & 0xfff;
238 
239     trace_loongarch_pch_pic_high_writew(size, addr, data);
240 
241     switch (offset) {
242     case STATUS_LO_START:
243         s->intisr = get_writew_val(s->intisr, data, 0);
244         break;
245     case STATUS_HI_START:
246         s->intisr = get_writew_val(s->intisr, data, 1);
247         break;
248     case POL_LO_START:
249         s->int_polarity = get_writew_val(s->int_polarity, data, 0);
250         break;
251     case POL_HI_START:
252         s->int_polarity = get_writew_val(s->int_polarity, data, 1);
253         break;
254     default:
255         break;
256     }
257 }
258 
259 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
260                                         unsigned size)
261 {
262     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
263     uint64_t val = 0;
264     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
265     int64_t offset_tmp;
266 
267     switch (offset) {
268     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
269         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
270         if (offset_tmp >= 0 && offset_tmp < 64) {
271             val = s->htmsi_vector[offset_tmp];
272         }
273         break;
274     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
275         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
276         if (offset_tmp >= 0 && offset_tmp < 64) {
277             val = s->route_entry[offset_tmp];
278         }
279         break;
280     default:
281         break;
282     }
283 
284     trace_loongarch_pch_pic_readb(size, addr, val);
285     return val;
286 }
287 
288 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
289                                      uint64_t data, unsigned size)
290 {
291     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
292     int32_t offset_tmp;
293     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
294 
295     trace_loongarch_pch_pic_writeb(size, addr, data);
296 
297     switch (offset) {
298     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
299         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
300         if (offset_tmp >= 0 && offset_tmp < 64) {
301             s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
302         }
303         break;
304     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
305         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
306         if (offset_tmp >= 0 && offset_tmp < 64) {
307             s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
308         }
309         break;
310     default:
311         break;
312     }
313 }
314 
315 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
316     .read = loongarch_pch_pic_low_readw,
317     .write = loongarch_pch_pic_low_writew,
318     .valid = {
319         .min_access_size = 4,
320         .max_access_size = 8,
321     },
322     .impl = {
323         .min_access_size = 4,
324         .max_access_size = 4,
325     },
326     .endianness = DEVICE_LITTLE_ENDIAN,
327 };
328 
329 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
330     .read = loongarch_pch_pic_high_readw,
331     .write = loongarch_pch_pic_high_writew,
332     .valid = {
333         .min_access_size = 4,
334         .max_access_size = 8,
335     },
336     .impl = {
337         .min_access_size = 4,
338         .max_access_size = 4,
339     },
340     .endianness = DEVICE_LITTLE_ENDIAN,
341 };
342 
343 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
344     .read = loongarch_pch_pic_readb,
345     .write = loongarch_pch_pic_writeb,
346     .valid = {
347         .min_access_size = 1,
348         .max_access_size = 1,
349     },
350     .impl = {
351         .min_access_size = 1,
352         .max_access_size = 1,
353     },
354     .endianness = DEVICE_LITTLE_ENDIAN,
355 };
356 
357 static void loongarch_pch_pic_reset(DeviceState *d)
358 {
359     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(d);
360     int i;
361 
362     s->int_mask = -1;
363     s->htmsi_en = 0x0;
364     s->intedge  = 0x0;
365     s->intclr   = 0x0;
366     s->auto_crtl0 = 0x0;
367     s->auto_crtl1 = 0x0;
368     for (i = 0; i < 64; i++) {
369         s->route_entry[i] = 0x1;
370         s->htmsi_vector[i] = 0x0;
371     }
372     s->intirr = 0x0;
373     s->intisr = 0x0;
374     s->last_intirr = 0x0;
375     s->int_polarity = 0x0;
376 }
377 
378 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
379 {
380     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
381     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
382     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
383     Error *local_err = NULL;
384 
385     lpc->parent_realize(dev, &local_err);
386     if (local_err) {
387         error_propagate(errp, local_err);
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     memory_region_init_io(&s->iomem32_low, OBJECT(dev),
394                           &loongarch_pch_pic_reg32_low_ops,
395                           s, PCH_PIC_NAME(.reg32_part1), 0x100);
396     memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops,
397                           s, PCH_PIC_NAME(.reg8), 0x2a0);
398     memory_region_init_io(&s->iomem32_high, OBJECT(dev),
399                           &loongarch_pch_pic_reg32_high_ops,
400                           s, PCH_PIC_NAME(.reg32_part2), 0xc60);
401     sysbus_init_mmio(sbd, &s->iomem32_low);
402     sysbus_init_mmio(sbd, &s->iomem8);
403     sysbus_init_mmio(sbd, &s->iomem32_high);
404 
405 }
406 
407 static void loongarch_pic_class_init(ObjectClass *klass, void *data)
408 {
409     DeviceClass *dc = DEVICE_CLASS(klass);
410     LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
411 
412     device_class_set_legacy_reset(dc, loongarch_pch_pic_reset);
413     device_class_set_parent_realize(dc, loongarch_pic_realize,
414                                     &lpc->parent_realize);
415 }
416 
417 static const TypeInfo loongarch_pic_types[] = {
418    {
419         .name               = TYPE_LOONGARCH_PIC,
420         .parent             = TYPE_LOONGARCH_PIC_COMMON,
421         .instance_size      = sizeof(LoongarchPICState),
422         .class_size         = sizeof(LoongarchPICClass),
423         .class_init         = loongarch_pic_class_init,
424     }
425 };
426 
427 DEFINE_TYPES(loongarch_pic_types)
428