icount: Take iothread lock when running QEMU timers
[qemu/ar7.git] / hw / intc / loongarch_pch_pic.c
blob3380b09807c9dcdc53c63976c71281ad97436990
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QEMU Loongson 7A1000 I/O interrupt controller.
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
6 */
8 #include "qemu/osdep.h"
9 #include "hw/sysbus.h"
10 #include "hw/loongarch/virt.h"
11 #include "hw/irq.h"
12 #include "hw/intc/loongarch_pch_pic.h"
13 #include "migration/vmstate.h"
14 #include "trace.h"
16 static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
18 uint64_t val;
19 int irq;
21 if (level) {
22 val = mask & s->intirr & ~s->int_mask;
23 if (val) {
24 irq = ctz64(val);
25 s->intisr |= MAKE_64BIT_MASK(irq, 1);
26 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
28 } else {
29 val = mask & s->intisr;
30 if (val) {
31 irq = ctz64(val);
32 s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
33 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
38 static void pch_pic_irq_handler(void *opaque, int irq, int level)
40 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
41 uint64_t mask = 1ULL << irq;
43 assert(irq < PCH_PIC_IRQ_NUM);
44 trace_loongarch_pch_pic_irq_handler(irq, level);
46 if (s->intedge & mask) {
47 /* Edge triggered */
48 if (level) {
49 if ((s->last_intirr & mask) == 0) {
50 s->intirr |= mask;
52 s->last_intirr |= mask;
53 } else {
54 s->last_intirr &= ~mask;
56 } else {
57 /* Level triggered */
58 if (level) {
59 s->intirr |= mask;
60 s->last_intirr |= mask;
61 } else {
62 s->intirr &= ~mask;
63 s->last_intirr &= ~mask;
66 pch_pic_update_irq(s, mask, level);
69 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
70 unsigned size)
72 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
73 uint64_t val = 0;
74 uint32_t offset = addr & 0xfff;
76 switch (offset) {
77 case PCH_PIC_INT_ID_LO:
78 val = PCH_PIC_INT_ID_VAL;
79 break;
80 case PCH_PIC_INT_ID_HI:
81 val = PCH_PIC_INT_ID_NUM;
82 break;
83 case PCH_PIC_INT_MASK_LO:
84 val = (uint32_t)s->int_mask;
85 break;
86 case PCH_PIC_INT_MASK_HI:
87 val = s->int_mask >> 32;
88 break;
89 case PCH_PIC_INT_EDGE_LO:
90 val = (uint32_t)s->intedge;
91 break;
92 case PCH_PIC_INT_EDGE_HI:
93 val = s->intedge >> 32;
94 break;
95 case PCH_PIC_HTMSI_EN_LO:
96 val = (uint32_t)s->htmsi_en;
97 break;
98 case PCH_PIC_HTMSI_EN_HI:
99 val = s->htmsi_en >> 32;
100 break;
101 case PCH_PIC_AUTO_CTRL0_LO:
102 case PCH_PIC_AUTO_CTRL0_HI:
103 case PCH_PIC_AUTO_CTRL1_LO:
104 case PCH_PIC_AUTO_CTRL1_HI:
105 break;
106 default:
107 break;
110 trace_loongarch_pch_pic_low_readw(size, addr, val);
111 return val;
114 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
116 uint64_t mask = 0xffffffff00000000;
117 uint64_t data = target;
119 return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
122 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
123 uint64_t value, unsigned size)
125 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
126 uint32_t offset, old_valid, data = (uint32_t)value;
127 uint64_t old, int_mask;
128 offset = addr & 0xfff;
130 trace_loongarch_pch_pic_low_writew(size, addr, data);
132 switch (offset) {
133 case PCH_PIC_INT_MASK_LO:
134 old = s->int_mask;
135 s->int_mask = get_writew_val(old, data, 0);
136 old_valid = (uint32_t)old;
137 if (old_valid & ~data) {
138 pch_pic_update_irq(s, (old_valid & ~data), 1);
140 if (~old_valid & data) {
141 pch_pic_update_irq(s, (~old_valid & data), 0);
143 break;
144 case PCH_PIC_INT_MASK_HI:
145 old = s->int_mask;
146 s->int_mask = get_writew_val(old, data, 1);
147 old_valid = (uint32_t)(old >> 32);
148 int_mask = old_valid & ~data;
149 if (int_mask) {
150 pch_pic_update_irq(s, int_mask << 32, 1);
152 int_mask = ~old_valid & data;
153 if (int_mask) {
154 pch_pic_update_irq(s, int_mask << 32, 0);
156 break;
157 case PCH_PIC_INT_EDGE_LO:
158 s->intedge = get_writew_val(s->intedge, data, 0);
159 break;
160 case PCH_PIC_INT_EDGE_HI:
161 s->intedge = get_writew_val(s->intedge, data, 1);
162 break;
163 case PCH_PIC_INT_CLEAR_LO:
164 if (s->intedge & data) {
165 s->intirr &= (~data);
166 pch_pic_update_irq(s, data, 0);
167 s->intisr &= (~data);
169 break;
170 case PCH_PIC_INT_CLEAR_HI:
171 value <<= 32;
172 if (s->intedge & value) {
173 s->intirr &= (~value);
174 pch_pic_update_irq(s, value, 0);
175 s->intisr &= (~value);
177 break;
178 case PCH_PIC_HTMSI_EN_LO:
179 s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
180 break;
181 case PCH_PIC_HTMSI_EN_HI:
182 s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
183 break;
184 case PCH_PIC_AUTO_CTRL0_LO:
185 case PCH_PIC_AUTO_CTRL0_HI:
186 case PCH_PIC_AUTO_CTRL1_LO:
187 case PCH_PIC_AUTO_CTRL1_HI:
188 break;
189 default:
190 break;
194 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
195 unsigned size)
197 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
198 uint64_t val = 0;
199 uint32_t offset = addr & 0xfff;
201 switch (offset) {
202 case STATUS_LO_START:
203 val = (uint32_t)(s->intisr & (~s->int_mask));
204 break;
205 case STATUS_HI_START:
206 val = (s->intisr & (~s->int_mask)) >> 32;
207 break;
208 case POL_LO_START:
209 val = (uint32_t)s->int_polarity;
210 break;
211 case POL_HI_START:
212 val = s->int_polarity >> 32;
213 break;
214 default:
215 break;
218 trace_loongarch_pch_pic_high_readw(size, addr, val);
219 return val;
222 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
223 uint64_t value, unsigned size)
225 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
226 uint32_t offset, data = (uint32_t)value;
227 offset = addr & 0xfff;
229 trace_loongarch_pch_pic_high_writew(size, addr, data);
231 switch (offset) {
232 case STATUS_LO_START:
233 s->intisr = get_writew_val(s->intisr, data, 0);
234 break;
235 case STATUS_HI_START:
236 s->intisr = get_writew_val(s->intisr, data, 1);
237 break;
238 case POL_LO_START:
239 s->int_polarity = get_writew_val(s->int_polarity, data, 0);
240 break;
241 case POL_HI_START:
242 s->int_polarity = get_writew_val(s->int_polarity, data, 1);
243 break;
244 default:
245 break;
249 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
250 unsigned size)
252 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
253 uint64_t val = 0;
254 uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
255 int64_t offset_tmp;
257 switch (offset) {
258 case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
259 offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
260 if (offset_tmp >= 0 && offset_tmp < 64) {
261 val = s->htmsi_vector[offset_tmp];
263 break;
264 case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
265 offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
266 if (offset_tmp >= 0 && offset_tmp < 64) {
267 val = s->route_entry[offset_tmp];
269 break;
270 default:
271 break;
274 trace_loongarch_pch_pic_readb(size, addr, val);
275 return val;
278 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
279 uint64_t data, unsigned size)
281 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
282 int32_t offset_tmp;
283 uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
285 trace_loongarch_pch_pic_writeb(size, addr, data);
287 switch (offset) {
288 case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
289 offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
290 if (offset_tmp >= 0 && offset_tmp < 64) {
291 s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
293 break;
294 case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
295 offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
296 if (offset_tmp >= 0 && offset_tmp < 64) {
297 s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
299 break;
300 default:
301 break;
305 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
306 .read = loongarch_pch_pic_low_readw,
307 .write = loongarch_pch_pic_low_writew,
308 .valid = {
309 .min_access_size = 4,
310 .max_access_size = 8,
312 .impl = {
313 .min_access_size = 4,
314 .max_access_size = 4,
316 .endianness = DEVICE_LITTLE_ENDIAN,
319 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
320 .read = loongarch_pch_pic_high_readw,
321 .write = loongarch_pch_pic_high_writew,
322 .valid = {
323 .min_access_size = 4,
324 .max_access_size = 8,
326 .impl = {
327 .min_access_size = 4,
328 .max_access_size = 4,
330 .endianness = DEVICE_LITTLE_ENDIAN,
333 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
334 .read = loongarch_pch_pic_readb,
335 .write = loongarch_pch_pic_writeb,
336 .valid = {
337 .min_access_size = 1,
338 .max_access_size = 1,
340 .impl = {
341 .min_access_size = 1,
342 .max_access_size = 1,
344 .endianness = DEVICE_LITTLE_ENDIAN,
347 static void loongarch_pch_pic_reset(DeviceState *d)
349 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
350 int i;
352 s->int_mask = -1;
353 s->htmsi_en = 0x0;
354 s->intedge = 0x0;
355 s->intclr = 0x0;
356 s->auto_crtl0 = 0x0;
357 s->auto_crtl1 = 0x0;
358 for (i = 0; i < 64; i++) {
359 s->route_entry[i] = 0x1;
360 s->htmsi_vector[i] = 0x0;
362 s->intirr = 0x0;
363 s->intisr = 0x0;
364 s->last_intirr = 0x0;
365 s->int_polarity = 0x0;
368 static void loongarch_pch_pic_init(Object *obj)
370 LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
371 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
373 memory_region_init_io(&s->iomem32_low, obj,
374 &loongarch_pch_pic_reg32_low_ops,
375 s, PCH_PIC_NAME(.reg32_part1), 0x100);
376 memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
377 s, PCH_PIC_NAME(.reg8), 0x2a0);
378 memory_region_init_io(&s->iomem32_high, obj,
379 &loongarch_pch_pic_reg32_high_ops,
380 s, PCH_PIC_NAME(.reg32_part2), 0xc60);
381 sysbus_init_mmio(sbd, &s->iomem32_low);
382 sysbus_init_mmio(sbd, &s->iomem8);
383 sysbus_init_mmio(sbd, &s->iomem32_high);
385 qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM);
386 qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM);
389 static const VMStateDescription vmstate_loongarch_pch_pic = {
390 .name = TYPE_LOONGARCH_PCH_PIC,
391 .version_id = 1,
392 .minimum_version_id = 1,
393 .fields = (VMStateField[]) {
394 VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
395 VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
396 VMSTATE_UINT64(intedge, LoongArchPCHPIC),
397 VMSTATE_UINT64(intclr, LoongArchPCHPIC),
398 VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
399 VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
400 VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
401 VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
402 VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
403 VMSTATE_UINT64(intirr, LoongArchPCHPIC),
404 VMSTATE_UINT64(intisr, LoongArchPCHPIC),
405 VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
406 VMSTATE_END_OF_LIST()
410 static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
412 DeviceClass *dc = DEVICE_CLASS(klass);
414 dc->reset = loongarch_pch_pic_reset;
415 dc->vmsd = &vmstate_loongarch_pch_pic;
418 static const TypeInfo loongarch_pch_pic_info = {
419 .name = TYPE_LOONGARCH_PCH_PIC,
420 .parent = TYPE_SYS_BUS_DEVICE,
421 .instance_size = sizeof(LoongArchPCHPIC),
422 .instance_init = loongarch_pch_pic_init,
423 .class_init = loongarch_pch_pic_class_init,
426 static void loongarch_pch_pic_register_types(void)
428 type_register_static(&loongarch_pch_pic_info);
431 type_init(loongarch_pch_pic_register_types)