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