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"
10 #include "hw/loongarch/virt.h"
12 #include "hw/intc/loongarch_pch_pic.h"
13 #include "migration/vmstate.h"
16 static void pch_pic_update_irq(LoongArchPCHPIC
*s
, uint64_t mask
, int level
)
22 val
= mask
& s
->intirr
& ~s
->int_mask
;
25 s
->intisr
|= MAKE_64BIT_MASK(irq
, 1);
26 qemu_set_irq(s
->parent_irq
[s
->htmsi_vector
[irq
]], 1);
29 val
= mask
& s
->intisr
;
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
) {
49 if ((s
->last_intirr
& mask
) == 0) {
52 s
->last_intirr
|= mask
;
54 s
->last_intirr
&= ~mask
;
60 s
->last_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
,
72 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
74 uint32_t offset
= addr
& 0xfff;
77 case PCH_PIC_INT_ID_LO
:
78 val
= PCH_PIC_INT_ID_VAL
;
80 case PCH_PIC_INT_ID_HI
:
81 val
= PCH_PIC_INT_ID_NUM
;
83 case PCH_PIC_INT_MASK_LO
:
84 val
= (uint32_t)s
->int_mask
;
86 case PCH_PIC_INT_MASK_HI
:
87 val
= s
->int_mask
>> 32;
89 case PCH_PIC_INT_EDGE_LO
:
90 val
= (uint32_t)s
->intedge
;
92 case PCH_PIC_INT_EDGE_HI
:
93 val
= s
->intedge
>> 32;
95 case PCH_PIC_HTMSI_EN_LO
:
96 val
= (uint32_t)s
->htmsi_en
;
98 case PCH_PIC_HTMSI_EN_HI
:
99 val
= s
->htmsi_en
>> 32;
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
:
110 trace_loongarch_pch_pic_low_readw(size
, addr
, 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
);
133 case PCH_PIC_INT_MASK_LO
:
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);
144 case PCH_PIC_INT_MASK_HI
:
146 s
->int_mask
= get_writew_val(old
, data
, 1);
147 old_valid
= (uint32_t)(old
>> 32);
148 int_mask
= old_valid
& ~data
;
150 pch_pic_update_irq(s
, int_mask
<< 32, 1);
152 int_mask
= ~old_valid
& data
;
154 pch_pic_update_irq(s
, int_mask
<< 32, 0);
157 case PCH_PIC_INT_EDGE_LO
:
158 s
->intedge
= get_writew_val(s
->intedge
, data
, 0);
160 case PCH_PIC_INT_EDGE_HI
:
161 s
->intedge
= get_writew_val(s
->intedge
, data
, 1);
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
);
170 case PCH_PIC_INT_CLEAR_HI
:
172 if (s
->intedge
& value
) {
173 s
->intirr
&= (~value
);
174 pch_pic_update_irq(s
, value
, 0);
175 s
->intisr
&= (~value
);
178 case PCH_PIC_HTMSI_EN_LO
:
179 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 0);
181 case PCH_PIC_HTMSI_EN_HI
:
182 s
->htmsi_en
= get_writew_val(s
->htmsi_en
, data
, 1);
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
:
194 static uint64_t loongarch_pch_pic_high_readw(void *opaque
, hwaddr addr
,
197 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
199 uint32_t offset
= addr
& 0xfff;
202 case STATUS_LO_START
:
203 val
= (uint32_t)(s
->intisr
& (~s
->int_mask
));
205 case STATUS_HI_START
:
206 val
= (s
->intisr
& (~s
->int_mask
)) >> 32;
209 val
= (uint32_t)s
->int_polarity
;
212 val
= s
->int_polarity
>> 32;
218 trace_loongarch_pch_pic_high_readw(size
, addr
, 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
);
232 case STATUS_LO_START
:
233 s
->intisr
= get_writew_val(s
->intisr
, data
, 0);
235 case STATUS_HI_START
:
236 s
->intisr
= get_writew_val(s
->intisr
, data
, 1);
239 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 0);
242 s
->int_polarity
= get_writew_val(s
->int_polarity
, data
, 1);
249 static uint64_t loongarch_pch_pic_readb(void *opaque
, hwaddr addr
,
252 LoongArchPCHPIC
*s
= LOONGARCH_PCH_PIC(opaque
);
254 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_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
];
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
];
274 trace_loongarch_pch_pic_readb(size
, addr
, 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
);
283 uint32_t offset
= (addr
& 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET
;
285 trace_loongarch_pch_pic_writeb(size
, addr
, data
);
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);
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);
305 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops
= {
306 .read
= loongarch_pch_pic_low_readw
,
307 .write
= loongarch_pch_pic_low_writew
,
309 .min_access_size
= 4,
310 .max_access_size
= 8,
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
,
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_reg8_ops
= {
334 .read
= loongarch_pch_pic_readb
,
335 .write
= loongarch_pch_pic_writeb
,
337 .min_access_size
= 1,
338 .max_access_size
= 1,
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
);
358 for (i
= 0; i
< 64; i
++) {
359 s
->route_entry
[i
] = 0x1;
360 s
->htmsi_vector
[i
] = 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
,
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
)