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