xref: /openbmc/qemu/hw/intc/loongarch_pch_pic.c (revision ab9bbee3c7da3d5dcce03bd7379bc7d05243a9af)
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:
83         val = PCH_PIC_INT_ID_VAL;
84         break;
85     case PCH_PIC_INT_ID + 4:
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:
94         val = (uint32_t)s->int_mask;
95         break;
96     case PCH_PIC_INT_MASK + 4:
97         val = s->int_mask >> 32;
98         break;
99     case PCH_PIC_INT_EDGE:
100         val = (uint32_t)s->intedge;
101         break;
102     case PCH_PIC_INT_EDGE + 4:
103         val = s->intedge >> 32;
104         break;
105     case PCH_PIC_HTMSI_EN:
106         val = (uint32_t)s->htmsi_en;
107         break;
108     case PCH_PIC_HTMSI_EN + 4:
109         val = s->htmsi_en >> 32;
110         break;
111     case PCH_PIC_AUTO_CTRL0:
112     case PCH_PIC_AUTO_CTRL0 + 4:
113     case PCH_PIC_AUTO_CTRL1:
114     case PCH_PIC_AUTO_CTRL1 + 4:
115         /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */
116         break;
117     default:
118         break;
119     }
120 
121     trace_loongarch_pch_pic_low_readw(size, addr, val);
122     return val;
123 }
124 
125 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
126 {
127     uint64_t mask = 0xffffffff00000000;
128     uint64_t data = target;
129 
130     return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
131 }
132 
133 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
134                                          uint64_t value, unsigned size)
135 {
136     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
137     uint32_t offset, old_valid, data = (uint32_t)value;
138     uint64_t old, int_mask;
139     offset = addr & 0xfff;
140 
141     trace_loongarch_pch_pic_low_writew(size, addr, data);
142 
143     switch (offset) {
144     case PCH_PIC_INT_MASK:
145         old = s->int_mask;
146         s->int_mask = get_writew_val(old, data, 0);
147         old_valid = (uint32_t)old;
148         if (old_valid & ~data) {
149             pch_pic_update_irq(s, (old_valid & ~data), 1);
150         }
151         if (~old_valid & data) {
152             pch_pic_update_irq(s, (~old_valid & data), 0);
153         }
154         break;
155     case PCH_PIC_INT_MASK + 4:
156         old = s->int_mask;
157         s->int_mask = get_writew_val(old, data, 1);
158         old_valid = (uint32_t)(old >> 32);
159         int_mask = old_valid & ~data;
160         if (int_mask) {
161             pch_pic_update_irq(s, int_mask << 32, 1);
162         }
163         int_mask = ~old_valid & data;
164         if (int_mask) {
165             pch_pic_update_irq(s, int_mask << 32, 0);
166         }
167         break;
168     case PCH_PIC_INT_EDGE:
169         s->intedge = get_writew_val(s->intedge, data, 0);
170         break;
171     case PCH_PIC_INT_EDGE + 4:
172         s->intedge = get_writew_val(s->intedge, data, 1);
173         break;
174     case PCH_PIC_INT_CLEAR:
175         if (s->intedge & data) {
176             s->intirr &= (~data);
177             pch_pic_update_irq(s, data, 0);
178             s->intisr &= (~data);
179         }
180         break;
181     case PCH_PIC_INT_CLEAR + 4:
182         value <<= 32;
183         if (s->intedge & value) {
184             s->intirr &= (~value);
185             pch_pic_update_irq(s, value, 0);
186             s->intisr &= (~value);
187         }
188         break;
189     case PCH_PIC_HTMSI_EN:
190         s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
191         break;
192     case PCH_PIC_HTMSI_EN + 4:
193         s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
194         break;
195     case PCH_PIC_AUTO_CTRL0:
196     case PCH_PIC_AUTO_CTRL0 + 4:
197     case PCH_PIC_AUTO_CTRL1:
198     case PCH_PIC_AUTO_CTRL1 + 4:
199         /* discard auto_ctrl access */
200         break;
201     default:
202         break;
203     }
204 }
205 
206 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
207                                         unsigned size)
208 {
209     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
210     uint64_t val = 0;
211     uint32_t offset = addr & 0xfff;
212 
213     switch (offset) {
214     case STATUS_LO_START:
215         val = (uint32_t)(s->intisr & (~s->int_mask));
216         break;
217     case STATUS_HI_START:
218         val = (s->intisr & (~s->int_mask)) >> 32;
219         break;
220     case POL_LO_START:
221         val = (uint32_t)s->int_polarity;
222         break;
223     case POL_HI_START:
224         val = s->int_polarity >> 32;
225         break;
226     default:
227         break;
228     }
229 
230     trace_loongarch_pch_pic_high_readw(size, addr, val);
231     return val;
232 }
233 
234 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
235                                      uint64_t value, unsigned size)
236 {
237     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
238     uint32_t offset, data = (uint32_t)value;
239     offset = addr & 0xfff;
240 
241     trace_loongarch_pch_pic_high_writew(size, addr, data);
242 
243     switch (offset) {
244     case STATUS_LO_START:
245         s->intisr = get_writew_val(s->intisr, data, 0);
246         break;
247     case STATUS_HI_START:
248         s->intisr = get_writew_val(s->intisr, data, 1);
249         break;
250     case POL_LO_START:
251         s->int_polarity = get_writew_val(s->int_polarity, data, 0);
252         break;
253     case POL_HI_START:
254         s->int_polarity = get_writew_val(s->int_polarity, data, 1);
255         break;
256     default:
257         break;
258     }
259 }
260 
261 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
262                                         unsigned size)
263 {
264     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
265     uint64_t val = 0;
266     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
267     int64_t offset_tmp;
268 
269     switch (offset) {
270     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
271         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
272         if (offset_tmp >= 0 && offset_tmp < 64) {
273             val = s->htmsi_vector[offset_tmp];
274         }
275         break;
276     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
277         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
278         if (offset_tmp >= 0 && offset_tmp < 64) {
279             val = s->route_entry[offset_tmp];
280         }
281         break;
282     default:
283         break;
284     }
285 
286     trace_loongarch_pch_pic_readb(size, addr, val);
287     return val;
288 }
289 
290 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
291                                      uint64_t data, unsigned size)
292 {
293     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
294     int32_t offset_tmp;
295     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
296 
297     trace_loongarch_pch_pic_writeb(size, addr, data);
298 
299     switch (offset) {
300     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
301         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
302         if (offset_tmp >= 0 && offset_tmp < 64) {
303             s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
304         }
305         break;
306     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
307         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
308         if (offset_tmp >= 0 && offset_tmp < 64) {
309             s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
310         }
311         break;
312     default:
313         break;
314     }
315 }
316 
317 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
318     .read = loongarch_pch_pic_low_readw,
319     .write = loongarch_pch_pic_low_writew,
320     .valid = {
321         .min_access_size = 4,
322         .max_access_size = 8,
323     },
324     .impl = {
325         .min_access_size = 4,
326         .max_access_size = 4,
327     },
328     .endianness = DEVICE_LITTLE_ENDIAN,
329 };
330 
331 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
332     .read = loongarch_pch_pic_high_readw,
333     .write = loongarch_pch_pic_high_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_reg8_ops = {
346     .read = loongarch_pch_pic_readb,
347     .write = loongarch_pch_pic_writeb,
348     .valid = {
349         .min_access_size = 1,
350         .max_access_size = 1,
351     },
352     .impl = {
353         .min_access_size = 1,
354         .max_access_size = 1,
355     },
356     .endianness = DEVICE_LITTLE_ENDIAN,
357 };
358 
359 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
360 {
361     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
362 
363     if (lpc->parent_phases.hold) {
364         lpc->parent_phases.hold(obj, type);
365     }
366 }
367 
368 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
369 {
370     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
371     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
372     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
373     Error *local_err = NULL;
374 
375     lpc->parent_realize(dev, &local_err);
376     if (local_err) {
377         error_propagate(errp, local_err);
378         return;
379     }
380 
381     qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
382     qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
383     memory_region_init_io(&s->iomem32_low, OBJECT(dev),
384                           &loongarch_pch_pic_reg32_low_ops,
385                           s, PCH_PIC_NAME(.reg32_part1), 0x100);
386     memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops,
387                           s, PCH_PIC_NAME(.reg8), 0x2a0);
388     memory_region_init_io(&s->iomem32_high, OBJECT(dev),
389                           &loongarch_pch_pic_reg32_high_ops,
390                           s, PCH_PIC_NAME(.reg32_part2), 0xc60);
391     sysbus_init_mmio(sbd, &s->iomem32_low);
392     sysbus_init_mmio(sbd, &s->iomem8);
393     sysbus_init_mmio(sbd, &s->iomem32_high);
394 
395 }
396 
397 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
398 {
399     DeviceClass *dc = DEVICE_CLASS(klass);
400     LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
401     ResettableClass *rc = RESETTABLE_CLASS(klass);
402 
403     resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
404                                        NULL, &lpc->parent_phases);
405     device_class_set_parent_realize(dc, loongarch_pic_realize,
406                                     &lpc->parent_realize);
407 }
408 
409 static const TypeInfo loongarch_pic_types[] = {
410    {
411         .name               = TYPE_LOONGARCH_PIC,
412         .parent             = TYPE_LOONGARCH_PIC_COMMON,
413         .instance_size      = sizeof(LoongarchPICState),
414         .class_size         = sizeof(LoongarchPICClass),
415         .class_init         = loongarch_pic_class_init,
416     }
417 };
418 
419 DEFINE_TYPES(loongarch_pic_types)
420