1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * QEMU Loongson 7A1000 I/O interrupt controller.
5 * Copyright (C) 2021 Loongson Technology Corporation Limited
8 #include "qemu/osdep.h"
9 #include "qemu/bitops.h"
10 #include "hw/sysbus.h"
11 #include "hw/loongarch/virt.h"
12 #include "hw/pci-host/ls7a.h"
14 #include "hw/intc/loongarch_pch_pic.h"
15 #include "hw/qdev-properties.h"
16 #include "migration/vmstate.h"
18 #include "qapi/error.h"
20 static void pch_pic_update_irq(LoongArchPCHPIC
*s
, uint64_t mask
, int level
)
26 val
= mask
& s
->intirr
& ~s
->int_mask
;
29 s
->intisr
|= MAKE_64BIT_MASK(irq
, 1);
30 qemu_set_irq(s
->parent_irq
[s
->htmsi_vector
[irq
]], 1);
34 * intirr means requested pending irq
35 * do not clear pending irq for edge-triggered on lowering edge
37 val
= mask
& s
->intisr
& ~s
->intirr
;
40 s
->intisr
&= ~MAKE_64BIT_MASK(irq
, 1);
41 qemu_set_irq(s
->parent_irq
[s
->htmsi_vector
[irq
]], 0);
46 static void pch_pic_irq_handler(void *opaque
, int irq
, int level
)
48 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
49 uint64_t mask
= 1ULL << irq
;
51 assert(irq
< s
->irq_num
);
52 trace_loongarch_pch_pic_irq_handler(irq
, level
);
54 if (s
->intedge
& mask
) {
57 if ((s
->last_intirr
& mask
) == 0) {
58 /* marked pending on a rising edge */
61 s
->last_intirr
|= mask
;
63 s
->last_intirr
&= ~mask
;
69 s
->last_intirr
|= mask
;
72 s
->last_intirr
&= ~mask
;
75 pch_pic_update_irq(s
, mask
, level
);
78 static uint64_t loongarch_pch_pic_low_readw(void *opaque
, hwaddr addr
,
81 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
83 uint32_t offset
= addr
& 0xfff;
86 case PCH_PIC_INT_ID_LO
:
87 val
= PCH_PIC_INT_ID_VAL
;
89 case PCH_PIC_INT_ID_HI
:
92 * bit 0-15 pch irqchip version
93 * bit 16-31 irq number supported with pch irqchip
95 val
= deposit32(PCH_PIC_INT_ID_VER
, 16, 16, s
->irq_num
- 1);
97 case PCH_PIC_INT_MASK_LO
:
98 val
= (uint32_t)s
->int_mask
;
100 case PCH_PIC_INT_MASK_HI
:
101 val
= s
->int_mask
>> 32;
103 case PCH_PIC_INT_EDGE_LO
:
104 val
= (uint32_t)s
->intedge
;
106 case PCH_PIC_INT_EDGE_HI
:
107 val
= s
->intedge
>> 32;
109 case PCH_PIC_HTMSI_EN_LO
:
110 val
= (uint32_t)s
->htmsi_en
;
112 case PCH_PIC_HTMSI_EN_HI
:
113 val
= s
->htmsi_en
>> 32;
115 case PCH_PIC_AUTO_CTRL0_LO
:
116 case PCH_PIC_AUTO_CTRL0_HI
:
117 case PCH_PIC_AUTO_CTRL1_LO
:
118 case PCH_PIC_AUTO_CTRL1_HI
:
124 trace_loongarch_pch_pic_low_readw(size
, addr
, val
);
128 static uint64_t get_writew_val(uint64_t value
, uint32_t target
, bool hi
)
130 uint64_t mask
= 0xffffffff00000000;
131 uint64_t data
= target
;
133 return hi
? (value
& ~mask
) | (data
<< 32) : (value
& mask
) | data
;
136 static void loongarch_pch_pic_low_writew(void *opaque
, hwaddr addr
,
137 uint64_t value
, unsigned size
)
139 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
140 uint32_t offset
, old_valid
, data
= (uint32_t)value
;
141 uint64_t old
, int_mask
;
142 offset
= addr
& 0xfff;
144 trace_loongarch_pch_pic_low_writew(size
, addr
, data
);
147 case PCH_PIC_INT_MASK_LO
:
149 s
->int_mask
= get_writew_val(old
, data
, 0);
150 old_valid
= (uint32_t)old
;
151 if (old_valid
& ~data
) {
152 pch_pic_update_irq(s
, (old_valid
& ~data
), 1);
154 if (~old_valid
& data
) {
155 pch_pic_update_irq(s
, (~old_valid
& data
), 0);
158 case PCH_PIC_INT_MASK_HI
:
160 s
->int_mask
= get_writew_val(old
, data
, 1);
161 old_valid
= (uint32_t)(old
>> 32);
162 int_mask
= old_valid
& ~data
;
164 pch_pic_update_irq(s
, int_mask
<< 32, 1);
166 int_mask
= ~old_valid
& data
;
168 pch_pic_update_irq(s
, int_mask
<< 32, 0);
171 case PCH_PIC_INT_EDGE_LO
:
172 s
->intedge
= get_writew_val(s
->intedge
, data
, 0);
174 case PCH_PIC_INT_EDGE_HI
:
175 s
->intedge
= get_writew_val(s
->intedge
, data
, 1);
177 case PCH_PIC_INT_CLEAR_LO
:
178 if (s
->intedge
& data
) {
179 s
->intirr
&= (~data
);
180 pch_pic_update_irq(s
, data
, 0);
181 s
->intisr
&= (~data
);
184 case PCH_PIC_INT_CLEAR_HI
:
186 if (s
->intedge
& value
) {
187 s
->intirr
&= (~value
);
188 pch_pic_update_irq(s
, value
, 0);
189 s
->intisr
&= (~value
);
192 case PCH_PIC_HTMSI_EN_LO
:
193 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 0);
195 case PCH_PIC_HTMSI_EN_HI
:
196 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 1);
198 case PCH_PIC_AUTO_CTRL0_LO
:
199 case PCH_PIC_AUTO_CTRL0_HI
:
200 case PCH_PIC_AUTO_CTRL1_LO
:
201 case PCH_PIC_AUTO_CTRL1_HI
:
208 static uint64_t loongarch_pch_pic_high_readw(void *opaque
, hwaddr addr
,
211 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
213 uint32_t offset
= addr
& 0xfff;
216 case STATUS_LO_START
:
217 val
= (uint32_t)(s
->intisr
& (~s
->int_mask
));
219 case STATUS_HI_START
:
220 val
= (s
->intisr
& (~s
->int_mask
)) >> 32;
223 val
= (uint32_t)s
->int_polarity
;
226 val
= s
->int_polarity
>> 32;
232 trace_loongarch_pch_pic_high_readw(size
, addr
, val
);
236 static void loongarch_pch_pic_high_writew(void *opaque
, hwaddr addr
,
237 uint64_t value
, unsigned size
)
239 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
240 uint32_t offset
, data
= (uint32_t)value
;
241 offset
= addr
& 0xfff;
243 trace_loongarch_pch_pic_high_writew(size
, addr
, data
);
246 case STATUS_LO_START
:
247 s
->intisr
= get_writew_val(s
->intisr
, data
, 0);
249 case STATUS_HI_START
:
250 s
->intisr
= get_writew_val(s
->intisr
, data
, 1);
253 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 0);
256 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 1);
263 static uint64_t loongarch_pch_pic_readb(void *opaque
, hwaddr addr
,
266 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
268 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET
;
272 case PCH_PIC_HTMSI_VEC_OFFSET
... PCH_PIC_HTMSI_VEC_END
:
273 offset_tmp
= offset
- PCH_PIC_HTMSI_VEC_OFFSET
;
274 if (offset_tmp
>= 0 && offset_tmp
< 64) {
275 val
= s
->htmsi_vector
[offset_tmp
];
278 case PCH_PIC_ROUTE_ENTRY_OFFSET
... PCH_PIC_ROUTE_ENTRY_END
:
279 offset_tmp
= offset
- PCH_PIC_ROUTE_ENTRY_OFFSET
;
280 if (offset_tmp
>= 0 && offset_tmp
< 64) {
281 val
= s
->route_entry
[offset_tmp
];
288 trace_loongarch_pch_pic_readb(size
, addr
, val
);
292 static void loongarch_pch_pic_writeb(void *opaque
, hwaddr addr
,
293 uint64_t data
, unsigned size
)
295 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
297 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET
;
299 trace_loongarch_pch_pic_writeb(size
, addr
, data
);
302 case PCH_PIC_HTMSI_VEC_OFFSET
... PCH_PIC_HTMSI_VEC_END
:
303 offset_tmp
= offset
- PCH_PIC_HTMSI_VEC_OFFSET
;
304 if (offset_tmp
>= 0 && offset_tmp
< 64) {
305 s
->htmsi_vector
[offset_tmp
] = (uint8_t)(data
& 0xff);
308 case PCH_PIC_ROUTE_ENTRY_OFFSET
... PCH_PIC_ROUTE_ENTRY_END
:
309 offset_tmp
= offset
- PCH_PIC_ROUTE_ENTRY_OFFSET
;
310 if (offset_tmp
>= 0 && offset_tmp
< 64) {
311 s
->route_entry
[offset_tmp
] = (uint8_t)(data
& 0xff);
319 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops
= {
320 .read
= loongarch_pch_pic_low_readw
,
321 .write
= loongarch_pch_pic_low_writew
,
323 .min_access_size
= 4,
324 .max_access_size
= 8,
327 .min_access_size
= 4,
328 .max_access_size
= 4,
330 .endianness
= DEVICE_LITTLE_ENDIAN
,
333 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops
= {
334 .read
= loongarch_pch_pic_high_readw
,
335 .write
= loongarch_pch_pic_high_writew
,
337 .min_access_size
= 4,
338 .max_access_size
= 8,
341 .min_access_size
= 4,
342 .max_access_size
= 4,
344 .endianness
= DEVICE_LITTLE_ENDIAN
,
347 static const MemoryRegionOps loongarch_pch_pic_reg8_ops
= {
348 .read
= loongarch_pch_pic_readb
,
349 .write
= loongarch_pch_pic_writeb
,
351 .min_access_size
= 1,
352 .max_access_size
= 1,
355 .min_access_size
= 1,
356 .max_access_size
= 1,
358 .endianness
= DEVICE_LITTLE_ENDIAN
,
361 static void loongarch_pch_pic_reset(DeviceState
*d
)
363 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(d
);
372 for (i
= 0; i
< 64; i
++) {
373 s
->route_entry
[i
] = 0x1;
374 s
->htmsi_vector
[i
] = 0x0;
378 s
->last_intirr
= 0x0;
379 s
->int_polarity
= 0x0;
382 static void loongarch_pch_pic_realize(DeviceState
*dev
, Error
**errp
)
384 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(dev
);
386 if (!s
->irq_num
|| s
->irq_num
> VIRT_PCH_PIC_IRQ_NUM
) {
387 error_setg(errp
, "Invalid 'pic_irq_num'");
391 qdev_init_gpio_out(dev
, s
->parent_irq
, s
->irq_num
);
392 qdev_init_gpio_in(dev
, pch_pic_irq_handler
, s
->irq_num
);
395 static void loongarch_pch_pic_init(Object
*obj
)
397 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(obj
);
398 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
400 memory_region_init_io(&s
->iomem32_low
, obj
,
401 &loongarch_pch_pic_reg32_low_ops
,
402 s
, PCH_PIC_NAME(.reg32_part1
), 0x100);
403 memory_region_init_io(&s
->iomem8
, obj
, &loongarch_pch_pic_reg8_ops
,
404 s
, PCH_PIC_NAME(.reg8
), 0x2a0);
405 memory_region_init_io(&s
->iomem32_high
, obj
,
406 &loongarch_pch_pic_reg32_high_ops
,
407 s
, PCH_PIC_NAME(.reg32_part2
), 0xc60);
408 sysbus_init_mmio(sbd
, &s
->iomem32_low
);
409 sysbus_init_mmio(sbd
, &s
->iomem8
);
410 sysbus_init_mmio(sbd
, &s
->iomem32_high
);
414 static Property loongarch_pch_pic_properties
[] = {
415 DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC
, irq_num
, 0),
416 DEFINE_PROP_END_OF_LIST(),
419 static const VMStateDescription vmstate_loongarch_pch_pic
= {
420 .name
= TYPE_LOONGARCH_PCH_PIC
,
422 .minimum_version_id
= 1,
423 .fields
= (VMStateField
[]) {
424 VMSTATE_UINT64(int_mask
, LoongArchPCHPIC
),
425 VMSTATE_UINT64(htmsi_en
, LoongArchPCHPIC
),
426 VMSTATE_UINT64(intedge
, LoongArchPCHPIC
),
427 VMSTATE_UINT64(intclr
, LoongArchPCHPIC
),
428 VMSTATE_UINT64(auto_crtl0
, LoongArchPCHPIC
),
429 VMSTATE_UINT64(auto_crtl1
, LoongArchPCHPIC
),
430 VMSTATE_UINT8_ARRAY(route_entry
, LoongArchPCHPIC
, 64),
431 VMSTATE_UINT8_ARRAY(htmsi_vector
, LoongArchPCHPIC
, 64),
432 VMSTATE_UINT64(last_intirr
, LoongArchPCHPIC
),
433 VMSTATE_UINT64(intirr
, LoongArchPCHPIC
),
434 VMSTATE_UINT64(intisr
, LoongArchPCHPIC
),
435 VMSTATE_UINT64(int_polarity
, LoongArchPCHPIC
),
436 VMSTATE_END_OF_LIST()
440 static void loongarch_pch_pic_class_init(ObjectClass
*klass
, void *data
)
442 DeviceClass
*dc
= DEVICE_CLASS(klass
);
444 dc
->realize
= loongarch_pch_pic_realize
;
445 dc
->reset
= loongarch_pch_pic_reset
;
446 dc
->vmsd
= &vmstate_loongarch_pch_pic
;
447 device_class_set_props(dc
, loongarch_pch_pic_properties
);
450 static const TypeInfo loongarch_pch_pic_info
= {
451 .name
= TYPE_LOONGARCH_PCH_PIC
,
452 .parent
= TYPE_SYS_BUS_DEVICE
,
453 .instance_size
= sizeof(LoongArchPCHPIC
),
454 .instance_init
= loongarch_pch_pic_init
,
455 .class_init
= loongarch_pch_pic_class_init
,
458 static void loongarch_pch_pic_register_types(void)
460 type_register_static(&loongarch_pch_pic_info
);
463 type_init(loongarch_pch_pic_register_types
)