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