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