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