xref: /qemu/hw/intc/loongarch_pch_pic.c (revision d01d42ccc9510c039b2e4ec49af164e374eab154)
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 
pch_pic_update_irq(LoongArchPICCommonState * s,uint64_t mask,int level)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 
pch_pic_irq_handler(void * opaque,int irq,int level)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 
pch_pic_read(void * opaque,hwaddr addr,uint64_t field_mask)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 = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC);
114         break;
115     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
116         val = *(uint64_t *)(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 
pch_pic_write(void * opaque,hwaddr addr,uint64_t value,uint64_t field_mask)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, *ptemp;
133 
134     offset = addr & 7;
135     addr -= offset;
136     mask = field_mask << (offset * 8);
137     data = (value & field_mask) << (offset * 8);
138     switch (addr) {
139     case PCH_PIC_INT_MASK:
140         old = s->int_mask;
141         s->int_mask = (old & ~mask) | data;
142         if (old & ~data) {
143             pch_pic_update_irq(s, old & ~data, 1);
144         }
145 
146         if (~old & data) {
147             pch_pic_update_irq(s, ~old & data, 0);
148         }
149         break;
150     case PCH_PIC_INT_EDGE:
151         s->intedge = (s->intedge & ~mask) | data;
152         break;
153     case PCH_PIC_INT_CLEAR:
154         if (s->intedge & data) {
155             s->intirr &= ~data;
156             pch_pic_update_irq(s, data, 0);
157             s->intisr &= ~data;
158         }
159         break;
160     case PCH_PIC_HTMSI_EN:
161         s->htmsi_en = (s->htmsi_en & ~mask) | data;
162         break;
163     case PCH_PIC_AUTO_CTRL0:
164     case PCH_PIC_AUTO_CTRL1:
165         /* Discard auto_ctrl access */
166         break;
167     case PCH_PIC_INT_POL:
168         s->int_polarity = (s->int_polarity & ~mask) | data;
169         break;
170     case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END:
171         ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC);
172         *ptemp = (*ptemp & ~mask) | data;
173         break;
174     case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END:
175         ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY);
176         *ptemp = (*ptemp & ~mask) | data;
177         break;
178     default:
179         qemu_log_mask(LOG_GUEST_ERROR,
180                       "pch_pic_write: Bad address 0x%"PRIx64"\n", addr);
181         break;
182     }
183 }
184 
loongarch_pch_pic_read(void * opaque,hwaddr addr,unsigned size)185 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr,
186                                        unsigned size)
187 {
188     uint64_t val = 0;
189 
190     switch (size) {
191     case 1:
192         val = pch_pic_read(opaque, addr, UCHAR_MAX);
193         break;
194     case 2:
195         val = pch_pic_read(opaque, addr, USHRT_MAX);
196         break;
197     case 4:
198         val = pch_pic_read(opaque, addr, UINT_MAX);
199         break;
200     case 8:
201         val = pch_pic_read(opaque, addr, UINT64_MAX);
202         break;
203     default:
204         qemu_log_mask(LOG_GUEST_ERROR,
205                       "loongarch_pch_pic_read: Bad size %d\n", size);
206         break;
207     }
208 
209     trace_loongarch_pch_pic_read(size, addr, val);
210     return val;
211 }
212 
loongarch_pch_pic_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)213 static void loongarch_pch_pic_write(void *opaque, hwaddr addr,
214                                     uint64_t value, unsigned size)
215 {
216     trace_loongarch_pch_pic_write(size, addr, value);
217 
218     switch (size) {
219     case 1:
220         pch_pic_write(opaque, addr, value, UCHAR_MAX);
221         break;
222     case 2:
223         pch_pic_write(opaque, addr, value, USHRT_MAX);
224         break;
225         break;
226     case 4:
227         pch_pic_write(opaque, addr, value, UINT_MAX);
228         break;
229     case 8:
230         pch_pic_write(opaque, addr, value, UINT64_MAX);
231         break;
232     default:
233         qemu_log_mask(LOG_GUEST_ERROR,
234                       "loongarch_pch_pic_write: Bad size %d\n", size);
235         break;
236     }
237 }
238 
239 static const MemoryRegionOps loongarch_pch_pic_ops = {
240     .read = loongarch_pch_pic_read,
241     .write = loongarch_pch_pic_write,
242     .valid = {
243         .min_access_size = 1,
244         .max_access_size = 8,
245         /*
246          * PCH PIC device would not work correctly if the guest was doing
247          * unaligned access. This might not be a limitation on the real
248          * device but in practice there is no reason for a guest to access
249          * this device unaligned.
250          */
251         .unaligned = false,
252     },
253     .impl = {
254         .min_access_size = 1,
255         .max_access_size = 8,
256     },
257     .endianness = DEVICE_LITTLE_ENDIAN,
258 };
259 
loongarch_pic_reset_hold(Object * obj,ResetType type)260 static void loongarch_pic_reset_hold(Object *obj, ResetType type)
261 {
262     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj);
263 
264     if (lpc->parent_phases.hold) {
265         lpc->parent_phases.hold(obj, type);
266     }
267 
268     if (kvm_irqchip_in_kernel()) {
269         kvm_pic_put(obj, 0);
270     }
271 }
272 
loongarch_pic_realize(DeviceState * dev,Error ** errp)273 static void loongarch_pic_realize(DeviceState *dev, Error **errp)
274 {
275     LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
276     LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
277     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
278     Error *local_err = NULL;
279 
280     lpc->parent_realize(dev, &local_err);
281     if (local_err) {
282         error_propagate(errp, local_err);
283         return;
284     }
285 
286     qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
287     qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
288 
289     if (kvm_irqchip_in_kernel()) {
290         kvm_pic_realize(dev, errp);
291     } else {
292         memory_region_init_io(&s->iomem, OBJECT(dev),
293                               &loongarch_pch_pic_ops,
294                               s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE);
295         sysbus_init_mmio(sbd, &s->iomem);
296     }
297 }
298 
loongarch_pic_pre_save(LoongArchPICCommonState * opaque)299 static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque)
300 {
301     if (kvm_irqchip_in_kernel()) {
302         return kvm_pic_get(opaque);
303     }
304 
305     return 0;
306 }
307 
loongarch_pic_post_load(LoongArchPICCommonState * opaque,int version_id)308 static int loongarch_pic_post_load(LoongArchPICCommonState *opaque,
309                                    int version_id)
310 {
311     if (kvm_irqchip_in_kernel()) {
312         return kvm_pic_put(opaque, version_id);
313     }
314 
315     return 0;
316 }
317 
loongarch_pic_class_init(ObjectClass * klass,const void * data)318 static void loongarch_pic_class_init(ObjectClass *klass, const void *data)
319 {
320     DeviceClass *dc = DEVICE_CLASS(klass);
321     LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
322     LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass);
323     ResettableClass *rc = RESETTABLE_CLASS(klass);
324 
325     resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold,
326                                        NULL, &lpc->parent_phases);
327     device_class_set_parent_realize(dc, loongarch_pic_realize,
328                                     &lpc->parent_realize);
329     lpcc->pre_save = loongarch_pic_pre_save;
330     lpcc->post_load = loongarch_pic_post_load;
331 }
332 
333 static const TypeInfo loongarch_pic_types[] = {
334    {
335         .name               = TYPE_LOONGARCH_PIC,
336         .parent             = TYPE_LOONGARCH_PIC_COMMON,
337         .instance_size      = sizeof(LoongarchPICState),
338         .class_size         = sizeof(LoongarchPICClass),
339         .class_init         = loongarch_pic_class_init,
340     }
341 };
342 
343 DEFINE_TYPES(loongarch_pic_types)
344