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);
33 val
= mask
& s
->intisr
;
36 s
->intisr
&= ~MAKE_64BIT_MASK(irq
, 1);
37 qemu_set_irq(s
->parent_irq
[s
->htmsi_vector
[irq
]], 0);
42 static void pch_pic_irq_handler(void *opaque
, int irq
, int level
)
44 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
45 uint64_t mask
= 1ULL << irq
;
47 assert(irq
< s
->irq_num
);
48 trace_loongarch_pch_pic_irq_handler(irq
, level
);
50 if (s
->intedge
& mask
) {
53 if ((s
->last_intirr
& mask
) == 0) {
56 s
->last_intirr
|= mask
;
58 s
->last_intirr
&= ~mask
;
64 s
->last_intirr
|= mask
;
67 s
->last_intirr
&= ~mask
;
70 pch_pic_update_irq(s
, mask
, level
);
73 static uint64_t loongarch_pch_pic_low_readw(void *opaque
, hwaddr addr
,
76 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
78 uint32_t offset
= addr
& 0xfff;
81 case PCH_PIC_INT_ID_LO
:
82 val
= PCH_PIC_INT_ID_VAL
;
84 case PCH_PIC_INT_ID_HI
:
87 * bit 0-15 pch irqchip version
88 * bit 16-31 irq number supported with pch irqchip
90 val
= deposit32(PCH_PIC_INT_ID_VER
, 16, 16, s
->irq_num
- 1);
92 case PCH_PIC_INT_MASK_LO
:
93 val
= (uint32_t)s
->int_mask
;
95 case PCH_PIC_INT_MASK_HI
:
96 val
= s
->int_mask
>> 32;
98 case PCH_PIC_INT_EDGE_LO
:
99 val
= (uint32_t)s
->intedge
;
101 case PCH_PIC_INT_EDGE_HI
:
102 val
= s
->intedge
>> 32;
104 case PCH_PIC_HTMSI_EN_LO
:
105 val
= (uint32_t)s
->htmsi_en
;
107 case PCH_PIC_HTMSI_EN_HI
:
108 val
= s
->htmsi_en
>> 32;
110 case PCH_PIC_AUTO_CTRL0_LO
:
111 case PCH_PIC_AUTO_CTRL0_HI
:
112 case PCH_PIC_AUTO_CTRL1_LO
:
113 case PCH_PIC_AUTO_CTRL1_HI
:
119 trace_loongarch_pch_pic_low_readw(size
, addr
, val
);
123 static uint64_t get_writew_val(uint64_t value
, uint32_t target
, bool hi
)
125 uint64_t mask
= 0xffffffff00000000;
126 uint64_t data
= target
;
128 return hi
? (value
& ~mask
) | (data
<< 32) : (value
& mask
) | data
;
131 static void loongarch_pch_pic_low_writew(void *opaque
, hwaddr addr
,
132 uint64_t value
, unsigned size
)
134 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
135 uint32_t offset
, old_valid
, data
= (uint32_t)value
;
136 uint64_t old
, int_mask
;
137 offset
= addr
& 0xfff;
139 trace_loongarch_pch_pic_low_writew(size
, addr
, data
);
142 case PCH_PIC_INT_MASK_LO
:
144 s
->int_mask
= get_writew_val(old
, data
, 0);
145 old_valid
= (uint32_t)old
;
146 if (old_valid
& ~data
) {
147 pch_pic_update_irq(s
, (old_valid
& ~data
), 1);
149 if (~old_valid
& data
) {
150 pch_pic_update_irq(s
, (~old_valid
& data
), 0);
153 case PCH_PIC_INT_MASK_HI
:
155 s
->int_mask
= get_writew_val(old
, data
, 1);
156 old_valid
= (uint32_t)(old
>> 32);
157 int_mask
= old_valid
& ~data
;
159 pch_pic_update_irq(s
, int_mask
<< 32, 1);
161 int_mask
= ~old_valid
& data
;
163 pch_pic_update_irq(s
, int_mask
<< 32, 0);
166 case PCH_PIC_INT_EDGE_LO
:
167 s
->intedge
= get_writew_val(s
->intedge
, data
, 0);
169 case PCH_PIC_INT_EDGE_HI
:
170 s
->intedge
= get_writew_val(s
->intedge
, data
, 1);
172 case PCH_PIC_INT_CLEAR_LO
:
173 if (s
->intedge
& data
) {
174 s
->intirr
&= (~data
);
175 pch_pic_update_irq(s
, data
, 0);
176 s
->intisr
&= (~data
);
179 case PCH_PIC_INT_CLEAR_HI
:
181 if (s
->intedge
& value
) {
182 s
->intirr
&= (~value
);
183 pch_pic_update_irq(s
, value
, 0);
184 s
->intisr
&= (~value
);
187 case PCH_PIC_HTMSI_EN_LO
:
188 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 0);
190 case PCH_PIC_HTMSI_EN_HI
:
191 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 1);
193 case PCH_PIC_AUTO_CTRL0_LO
:
194 case PCH_PIC_AUTO_CTRL0_HI
:
195 case PCH_PIC_AUTO_CTRL1_LO
:
196 case PCH_PIC_AUTO_CTRL1_HI
:
203 static uint64_t loongarch_pch_pic_high_readw(void *opaque
, hwaddr addr
,
206 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
208 uint32_t offset
= addr
& 0xfff;
211 case STATUS_LO_START
:
212 val
= (uint32_t)(s
->intisr
& (~s
->int_mask
));
214 case STATUS_HI_START
:
215 val
= (s
->intisr
& (~s
->int_mask
)) >> 32;
218 val
= (uint32_t)s
->int_polarity
;
221 val
= s
->int_polarity
>> 32;
227 trace_loongarch_pch_pic_high_readw(size
, addr
, val
);
231 static void loongarch_pch_pic_high_writew(void *opaque
, hwaddr addr
,
232 uint64_t value
, unsigned size
)
234 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
235 uint32_t offset
, data
= (uint32_t)value
;
236 offset
= addr
& 0xfff;
238 trace_loongarch_pch_pic_high_writew(size
, addr
, data
);
241 case STATUS_LO_START
:
242 s
->intisr
= get_writew_val(s
->intisr
, data
, 0);
244 case STATUS_HI_START
:
245 s
->intisr
= get_writew_val(s
->intisr
, data
, 1);
248 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 0);
251 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 1);
258 static uint64_t loongarch_pch_pic_readb(void *opaque
, hwaddr addr
,
261 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
263 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET
;
267 case PCH_PIC_HTMSI_VEC_OFFSET
... PCH_PIC_HTMSI_VEC_END
:
268 offset_tmp
= offset
- PCH_PIC_HTMSI_VEC_OFFSET
;
269 if (offset_tmp
>= 0 && offset_tmp
< 64) {
270 val
= s
->htmsi_vector
[offset_tmp
];
273 case PCH_PIC_ROUTE_ENTRY_OFFSET
... PCH_PIC_ROUTE_ENTRY_END
:
274 offset_tmp
= offset
- PCH_PIC_ROUTE_ENTRY_OFFSET
;
275 if (offset_tmp
>= 0 && offset_tmp
< 64) {
276 val
= s
->route_entry
[offset_tmp
];
283 trace_loongarch_pch_pic_readb(size
, addr
, val
);
287 static void loongarch_pch_pic_writeb(void *opaque
, hwaddr addr
,
288 uint64_t data
, unsigned size
)
290 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
292 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET
;
294 trace_loongarch_pch_pic_writeb(size
, addr
, data
);
297 case PCH_PIC_HTMSI_VEC_OFFSET
... PCH_PIC_HTMSI_VEC_END
:
298 offset_tmp
= offset
- PCH_PIC_HTMSI_VEC_OFFSET
;
299 if (offset_tmp
>= 0 && offset_tmp
< 64) {
300 s
->htmsi_vector
[offset_tmp
] = (uint8_t)(data
& 0xff);
303 case PCH_PIC_ROUTE_ENTRY_OFFSET
... PCH_PIC_ROUTE_ENTRY_END
:
304 offset_tmp
= offset
- PCH_PIC_ROUTE_ENTRY_OFFSET
;
305 if (offset_tmp
>= 0 && offset_tmp
< 64) {
306 s
->route_entry
[offset_tmp
] = (uint8_t)(data
& 0xff);
314 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops
= {
315 .read
= loongarch_pch_pic_low_readw
,
316 .write
= loongarch_pch_pic_low_writew
,
318 .min_access_size
= 4,
319 .max_access_size
= 8,
322 .min_access_size
= 4,
323 .max_access_size
= 4,
325 .endianness
= DEVICE_LITTLE_ENDIAN
,
328 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops
= {
329 .read
= loongarch_pch_pic_high_readw
,
330 .write
= loongarch_pch_pic_high_writew
,
332 .min_access_size
= 4,
333 .max_access_size
= 8,
336 .min_access_size
= 4,
337 .max_access_size
= 4,
339 .endianness
= DEVICE_LITTLE_ENDIAN
,
342 static const MemoryRegionOps loongarch_pch_pic_reg8_ops
= {
343 .read
= loongarch_pch_pic_readb
,
344 .write
= loongarch_pch_pic_writeb
,
346 .min_access_size
= 1,
347 .max_access_size
= 1,
350 .min_access_size
= 1,
351 .max_access_size
= 1,
353 .endianness
= DEVICE_LITTLE_ENDIAN
,
356 static void loongarch_pch_pic_reset(DeviceState
*d
)
358 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(d
);
367 for (i
= 0; i
< 64; i
++) {
368 s
->route_entry
[i
] = 0x1;
369 s
->htmsi_vector
[i
] = 0x0;
373 s
->last_intirr
= 0x0;
374 s
->int_polarity
= 0x0;
377 static void loongarch_pch_pic_realize(DeviceState
*dev
, Error
**errp
)
379 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(dev
);
381 if (!s
->irq_num
|| s
->irq_num
> VIRT_PCH_PIC_IRQ_NUM
) {
382 error_setg(errp
, "Invalid 'pic_irq_num'");
386 qdev_init_gpio_out(dev
, s
->parent_irq
, s
->irq_num
);
387 qdev_init_gpio_in(dev
, pch_pic_irq_handler
, s
->irq_num
);
390 static void loongarch_pch_pic_init(Object
*obj
)
392 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(obj
);
393 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
395 memory_region_init_io(&s
->iomem32_low
, obj
,
396 &loongarch_pch_pic_reg32_low_ops
,
397 s
, PCH_PIC_NAME(.reg32_part1
), 0x100);
398 memory_region_init_io(&s
->iomem8
, obj
, &loongarch_pch_pic_reg8_ops
,
399 s
, PCH_PIC_NAME(.reg8
), 0x2a0);
400 memory_region_init_io(&s
->iomem32_high
, obj
,
401 &loongarch_pch_pic_reg32_high_ops
,
402 s
, PCH_PIC_NAME(.reg32_part2
), 0xc60);
403 sysbus_init_mmio(sbd
, &s
->iomem32_low
);
404 sysbus_init_mmio(sbd
, &s
->iomem8
);
405 sysbus_init_mmio(sbd
, &s
->iomem32_high
);
409 static Property loongarch_pch_pic_properties
[] = {
410 DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPCHPIC
, irq_num
, 0),
411 DEFINE_PROP_END_OF_LIST(),
414 static const VMStateDescription vmstate_loongarch_pch_pic
= {
415 .name
= TYPE_LOONGARCH_PCH_PIC
,
417 .minimum_version_id
= 1,
418 .fields
= (VMStateField
[]) {
419 VMSTATE_UINT64(int_mask
, LoongArchPCHPIC
),
420 VMSTATE_UINT64(htmsi_en
, LoongArchPCHPIC
),
421 VMSTATE_UINT64(intedge
, LoongArchPCHPIC
),
422 VMSTATE_UINT64(intclr
, LoongArchPCHPIC
),
423 VMSTATE_UINT64(auto_crtl0
, LoongArchPCHPIC
),
424 VMSTATE_UINT64(auto_crtl1
, LoongArchPCHPIC
),
425 VMSTATE_UINT8_ARRAY(route_entry
, LoongArchPCHPIC
, 64),
426 VMSTATE_UINT8_ARRAY(htmsi_vector
, LoongArchPCHPIC
, 64),
427 VMSTATE_UINT64(last_intirr
, LoongArchPCHPIC
),
428 VMSTATE_UINT64(intirr
, LoongArchPCHPIC
),
429 VMSTATE_UINT64(intisr
, LoongArchPCHPIC
),
430 VMSTATE_UINT64(int_polarity
, LoongArchPCHPIC
),
431 VMSTATE_END_OF_LIST()
435 static void loongarch_pch_pic_class_init(ObjectClass
*klass
, void *data
)
437 DeviceClass
*dc
= DEVICE_CLASS(klass
);
439 dc
->realize
= loongarch_pch_pic_realize
;
440 dc
->reset
= loongarch_pch_pic_reset
;
441 dc
->vmsd
= &vmstate_loongarch_pch_pic
;
442 device_class_set_props(dc
, loongarch_pch_pic_properties
);
445 static const TypeInfo loongarch_pch_pic_info
= {
446 .name
= TYPE_LOONGARCH_PCH_PIC
,
447 .parent
= TYPE_SYS_BUS_DEVICE
,
448 .instance_size
= sizeof(LoongArchPCHPIC
),
449 .instance_init
= loongarch_pch_pic_init
,
450 .class_init
= loongarch_pch_pic_class_init
,
453 static void loongarch_pch_pic_register_types(void)
455 type_register_static(&loongarch_pch_pic_info
);
458 type_init(loongarch_pch_pic_register_types
)