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 = 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
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;
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
loongarch_pch_pic_read(void * opaque,hwaddr addr,unsigned size)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
loongarch_pch_pic_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)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
loongarch_pic_reset_hold(Object * obj,ResetType type)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
loongarch_pic_realize(DeviceState * dev,Error ** errp)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
loongarch_pic_pre_save(LoongArchPICCommonState * opaque)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
loongarch_pic_post_load(LoongArchPICCommonState * opaque,int version_id)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
loongarch_pic_class_init(ObjectClass * klass,const void * data)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