xref: /openbmc/qemu/hw/intc/loongarch_pch_pic.c (revision c4fa97c7f216fc80b09a5d32be847ff8d502cba6)
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 "system/kvm.h"
14 #include "trace.h"
15 #include "qapi/error.h"
16 
17 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask,
18                                int level)
19 {
20     uint64_t val;
21     int irq;
22 
23     if (level) {
24         val = mask & s->intirr & ~s->int_mask;
25         if (val) {
26             irq = ctz64(val);
27             s->intisr |= MAKE_64BIT_MASK(irq, 1);
28             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
29         }
30     } else {
31         /*
32          * intirr means requested pending irq
33          * do not clear pending irq for edge-triggered on lowering edge
34          */
35         val = mask & s->intisr & ~s->intirr;
36         if (val) {
37             irq = ctz64(val);
38             s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
39             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
40         }
41     }
42 }
43 
44 static void pch_pic_irq_handler(void *opaque, int irq, int level)
45 {
46     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
47     uint64_t mask = 1ULL << irq;
48 
49     assert(irq < s->irq_num);
50     trace_loongarch_pch_pic_irq_handler(irq, level);
51 
52     if (kvm_irqchip_in_kernel()) {
53         kvm_set_irq(kvm_state, irq, !!level);
54         return;
55     }
56 
57     if (s->intedge & mask) {
58         /* Edge triggered */
59         if (level) {
60             if ((s->last_intirr & mask) == 0) {
61                 /* marked pending on a rising edge */
62                 s->intirr |= mask;
63             }
64             s->last_intirr |= mask;
65         } else {
66             s->last_intirr &= ~mask;
67         }
68     } else {
69         /* Level triggered */
70         if (level) {
71             s->intirr |= mask;
72             s->last_intirr |= mask;
73         } else {
74             s->intirr &= ~mask;
75             s->last_intirr &= ~mask;
76         }
77     }
78     pch_pic_update_irq(s, mask, level);
79 }
80 
81 static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask)
82 {
83     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
84     uint64_t val = 0;
85     uint32_t offset;
86 
87     offset = addr & 7;
88     addr -= offset;
89     switch (addr) {
90     case PCH_PIC_INT_ID:
91         val = cpu_to_le64(s->id.data);
92         break;
93     case PCH_PIC_INT_MASK:
94         val = s->int_mask;
95         break;
96     case PCH_PIC_INT_EDGE:
97         val = s->intedge;
98         break;
99     case PCH_PIC_HTMSI_EN:
100         val = s->htmsi_en;
101         break;
102     case PCH_PIC_AUTO_CTRL0:
103     case PCH_PIC_AUTO_CTRL1:
104         /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */
105         break;
106     case PCH_PIC_INT_STATUS:
107         val = s->intisr & (~s->int_mask);
108         break;
109     case PCH_PIC_INT_POL:
110         val = s->int_polarity;
111         break;
112     case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
113         val = ldq_le_p(&s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]);
114         break;
115     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
116         val = ldq_le_p(&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]);
117         break;
118     default:
119         qemu_log_mask(LOG_GUEST_ERROR,
120                       "pch_pic_read: Bad address 0x%"PRIx64"\n", addr);
121         break;
122     }
123 
124     return (val >> (offset * 8)) & field_mask;
125 }
126 
127 static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value,
128                           uint64_t field_mask)
129 {
130     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
131     uint32_t offset;
132     uint64_t old, mask, data;
133     void *ptemp;
134 
135     offset = addr & 7;
136     addr -= offset;
137     mask = field_mask << (offset * 8);
138     data = (value & field_mask) << (offset * 8);
139     switch (addr) {
140     case PCH_PIC_INT_MASK:
141         old = s->int_mask;
142         s->int_mask = (old & ~mask) | data;
143         if (old & ~data) {
144             pch_pic_update_irq(s, old & ~data, 1);
145         }
146 
147         if (~old & data) {
148             pch_pic_update_irq(s, ~old & data, 0);
149         }
150         break;
151     case PCH_PIC_INT_EDGE:
152         s->intedge = (s->intedge & ~mask) | data;
153         break;
154     case PCH_PIC_INT_CLEAR:
155         if (s->intedge & data) {
156             s->intirr &= ~data;
157             pch_pic_update_irq(s, data, 0);
158             s->intisr &= ~data;
159         }
160         break;
161     case PCH_PIC_HTMSI_EN:
162         s->htmsi_en = (s->htmsi_en & ~mask) | data;
163         break;
164     case PCH_PIC_AUTO_CTRL0:
165     case PCH_PIC_AUTO_CTRL1:
166         /* Discard auto_ctrl access */
167         break;
168     case PCH_PIC_INT_POL:
169         s->int_polarity = (s->int_polarity & ~mask) | data;
170         break;
171     case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
172         ptemp = &s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC];
173         stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data);
174         break;
175     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
176         ptemp = (uint64_t *)&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY];
177         stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data);
178         break;
179     default:
180         qemu_log_mask(LOG_GUEST_ERROR,
181                       "pch_pic_write: Bad address 0x%"PRIx64"\n", addr);
182         break;
183     }
184 }
185 
186 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr,
187                                        unsigned size)
188 {
189     uint64_t val = 0;
190 
191     switch (size) {
192     case 1:
193         val = pch_pic_read(opaque, addr, UCHAR_MAX);
194         break;
195     case 2:
196         val = pch_pic_read(opaque, addr, USHRT_MAX);
197         break;
198     case 4:
199         val = pch_pic_read(opaque, addr, UINT_MAX);
200         break;
201     case 8:
202         val = pch_pic_read(opaque, addr, UINT64_MAX);
203         break;
204     default:
205         qemu_log_mask(LOG_GUEST_ERROR,
206                       "loongarch_pch_pic_read: Bad size %d\n", size);
207         break;
208     }
209 
210     trace_loongarch_pch_pic_read(size, addr, val);
211     return val;
212 }
213 
214 static void loongarch_pch_pic_write(void *opaque, hwaddr addr,
215                                     uint64_t value, unsigned size)
216 {
217     trace_loongarch_pch_pic_write(size, addr, value);
218 
219     switch (size) {
220     case 1:
221         pch_pic_write(opaque, addr, value, UCHAR_MAX);
222         break;
223     case 2:
224         pch_pic_write(opaque, addr, value, USHRT_MAX);
225         break;
226         break;
227     case 4:
228         pch_pic_write(opaque, addr, value, UINT_MAX);
229         break;
230     case 8:
231         pch_pic_write(opaque, addr, value, UINT64_MAX);
232         break;
233     default:
234         qemu_log_mask(LOG_GUEST_ERROR,
235                       "loongarch_pch_pic_write: Bad size %d\n", size);
236         break;
237     }
238 }
239 
240 static const MemoryRegionOps loongarch_pch_pic_ops = {
241     .read = loongarch_pch_pic_read,
242     .write = loongarch_pch_pic_write,
243     .valid = {
244         .min_access_size = 1,
245         .max_access_size = 8,
246         /*
247          * PCH PIC device would not work correctly if the guest was doing
248          * unaligned access. This might not be a limitation on the real
249          * device but in practice there is no reason for a guest to access
250          * this device unaligned.
251          */
252         .unaligned = false,
253     },
254     .impl = {
255         .min_access_size = 1,
256         .max_access_size = 8,
257     },
258     .endianness = DEVICE_LITTLE_ENDIAN,
259 };
260 
261 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
262 {
263     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
264 
265     if (lpc->parent_phases.hold) {
266         lpc->parent_phases.hold(obj, type);
267     }
268 
269     if (kvm_irqchip_in_kernel()) {
270         kvm_pic_put(obj, 0);
271     }
272 }
273 
274 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
275 {
276     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
277     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
278     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
279     Error *local_err = NULL;
280 
281     lpc->parent_realize(dev, &local_err);
282     if (local_err) {
283         error_propagate(errp, local_err);
284         return;
285     }
286 
287     qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
288     qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
289 
290     if (kvm_irqchip_in_kernel()) {
291         kvm_pic_realize(dev, errp);
292     } else {
293         memory_region_init_io(&s->iomem, OBJECT(dev),
294                               &loongarch_pch_pic_ops,
295                               s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE);
296         sysbus_init_mmio(sbd, &s->iomem);
297     }
298 }
299 
300 static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque)
301 {
302     if (kvm_irqchip_in_kernel()) {
303         return kvm_pic_get(opaque);
304     }
305 
306     return 0;
307 }
308 
309 static int loongarch_pic_post_load(LoongArchPICCommonState *opaque,
310                                    int version_id)
311 {
312     if (kvm_irqchip_in_kernel()) {
313         return kvm_pic_put(opaque, version_id);
314     }
315 
316     return 0;
317 }
318 
319 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
320 {
321     DeviceClass *dc = DEVICE_CLASS(klass);
322     LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
323     LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass);
324     ResettableClass *rc = RESETTABLE_CLASS(klass);
325 
326     resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
327                                        NULL, &lpc->parent_phases);
328     device_class_set_parent_realize(dc, loongarch_pic_realize,
329                                     &lpc->parent_realize);
330     lpcc->pre_save = loongarch_pic_pre_save;
331     lpcc->post_load = loongarch_pic_post_load;
332 }
333 
334 static const TypeInfo loongarch_pic_types[] = {
335    {
336         .name               = TYPE_LOONGARCH_PIC,
337         .parent             = TYPE_LOONGARCH_PIC_COMMON,
338         .instance_size      = sizeof(LoongarchPICState),
339         .class_size         = sizeof(LoongarchPICClass),
340         .class_init         = loongarch_pic_class_init,
341     }
342 };
343 
344 DEFINE_TYPES(loongarch_pic_types)
345