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