Skip to content

Commit cbff2db

Browse files
yangxiaojuan-loongsonrth7680
authored andcommitted
hw/intc: Add LoongArch extioi interrupt controller(EIOINTC)
This patch realize the EIOINTC interrupt controller. Signed-off-by: Xiaojuan Yang <[email protected]> Signed-off-by: Song Gao <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Richard Henderson <[email protected]>
1 parent 249ad85 commit cbff2db

File tree

6 files changed

+385
-0
lines changed

6 files changed

+385
-0
lines changed

hw/intc/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,6 @@ config LOONGARCH_PCH_MSI
9999
select MSI_NONBROKEN
100100
bool
101101
select UNIMP
102+
103+
config LOONGARCH_EXTIOI
104+
bool

hw/intc/loongarch_extioi.c

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
/*
3+
* Loongson 3A5000 ext interrupt controller emulation
4+
*
5+
* Copyright (C) 2021 Loongson Technology Corporation Limited
6+
*/
7+
8+
#include "qemu/osdep.h"
9+
#include "qemu/module.h"
10+
#include "qemu/log.h"
11+
#include "hw/irq.h"
12+
#include "hw/sysbus.h"
13+
#include "hw/loongarch/virt.h"
14+
#include "hw/qdev-properties.h"
15+
#include "exec/address-spaces.h"
16+
#include "hw/intc/loongarch_extioi.h"
17+
#include "migration/vmstate.h"
18+
#include "trace.h"
19+
20+
21+
static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level)
22+
{
23+
int ipnum, cpu, found, irq_index, irq_mask;
24+
25+
ipnum = s->sw_ipmap[irq / 32];
26+
cpu = s->sw_coremap[irq];
27+
irq_index = irq / 32;
28+
irq_mask = 1 << (irq & 0x1f);
29+
30+
if (level) {
31+
/* if not enable return false */
32+
if (((s->enable[irq_index]) & irq_mask) == 0) {
33+
return;
34+
}
35+
s->coreisr[cpu][irq_index] |= irq_mask;
36+
found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
37+
set_bit(irq, s->sw_isr[cpu][ipnum]);
38+
if (found < EXTIOI_IRQS) {
39+
/* other irq is handling, need not update parent irq level */
40+
return;
41+
}
42+
} else {
43+
s->coreisr[cpu][irq_index] &= ~irq_mask;
44+
clear_bit(irq, s->sw_isr[cpu][ipnum]);
45+
found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
46+
if (found < EXTIOI_IRQS) {
47+
/* other irq is handling, need not update parent irq level */
48+
return;
49+
}
50+
}
51+
qemu_set_irq(s->parent_irq[cpu][ipnum], level);
52+
}
53+
54+
static void extioi_setirq(void *opaque, int irq, int level)
55+
{
56+
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
57+
trace_loongarch_extioi_setirq(irq, level);
58+
if (level) {
59+
/*
60+
* s->isr should be used in vmstate structure,
61+
* but it not support 'unsigned long',
62+
* so we have to switch it.
63+
*/
64+
set_bit(irq, (unsigned long *)s->isr);
65+
} else {
66+
clear_bit(irq, (unsigned long *)s->isr);
67+
}
68+
extioi_update_irq(s, irq, level);
69+
}
70+
71+
static uint64_t extioi_readw(void *opaque, hwaddr addr, unsigned size)
72+
{
73+
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
74+
unsigned long offset = addr & 0xffff;
75+
uint32_t index, cpu, ret = 0;
76+
77+
switch (offset) {
78+
case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
79+
index = (offset - EXTIOI_NODETYPE_START) >> 2;
80+
ret = s->nodetype[index];
81+
break;
82+
case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
83+
index = (offset - EXTIOI_IPMAP_START) >> 2;
84+
ret = s->ipmap[index];
85+
break;
86+
case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
87+
index = (offset - EXTIOI_ENABLE_START) >> 2;
88+
ret = s->enable[index];
89+
break;
90+
case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
91+
index = (offset - EXTIOI_BOUNCE_START) >> 2;
92+
ret = s->bounce[index];
93+
break;
94+
case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
95+
index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2;
96+
cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
97+
ret = s->coreisr[cpu][index];
98+
break;
99+
case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
100+
index = (offset - EXTIOI_COREMAP_START) >> 2;
101+
ret = s->coremap[index];
102+
break;
103+
default:
104+
break;
105+
}
106+
107+
trace_loongarch_extioi_readw(addr, ret);
108+
return ret;
109+
}
110+
111+
static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\
112+
uint32_t mask, int level)
113+
{
114+
uint32_t val;
115+
int irq;
116+
117+
val = mask & s->isr[index];
118+
irq = ctz32(val);
119+
while (irq != 32) {
120+
/*
121+
* enable bit change from 0 to 1,
122+
* need to update irq by pending bits
123+
*/
124+
extioi_update_irq(s, irq + index * 32, level);
125+
val &= ~(1 << irq);
126+
irq = ctz32(val);
127+
}
128+
}
129+
130+
static void extioi_writew(void *opaque, hwaddr addr,
131+
uint64_t val, unsigned size)
132+
{
133+
LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
134+
int i, cpu, index, old_data, irq;
135+
uint32_t offset;
136+
137+
trace_loongarch_extioi_writew(addr, val);
138+
offset = addr & 0xffff;
139+
140+
switch (offset) {
141+
case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
142+
index = (offset - EXTIOI_NODETYPE_START) >> 2;
143+
s->nodetype[index] = val;
144+
break;
145+
case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
146+
/*
147+
* ipmap cannot be set at runtime, can be set only at the beginning
148+
* of intr driver, need not update upper irq level
149+
*/
150+
index = (offset - EXTIOI_IPMAP_START) >> 2;
151+
s->ipmap[index] = val;
152+
/*
153+
* loongarch only support little endian,
154+
* so we paresd the value with little endian.
155+
*/
156+
val = cpu_to_le64(val);
157+
for (i = 0; i < 4; i++) {
158+
uint8_t ipnum;
159+
ipnum = val & 0xff;
160+
ipnum = ctz32(ipnum);
161+
ipnum = (ipnum >= 4) ? 0 : ipnum;
162+
s->sw_ipmap[index * 4 + i] = ipnum;
163+
val = val >> 8;
164+
}
165+
166+
break;
167+
case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
168+
index = (offset - EXTIOI_ENABLE_START) >> 2;
169+
old_data = s->enable[index];
170+
s->enable[index] = val;
171+
172+
/* unmask irq */
173+
val = s->enable[index] & ~old_data;
174+
extioi_enable_irq(s, index, val, 1);
175+
176+
/* mask irq */
177+
val = ~s->enable[index] & old_data;
178+
extioi_enable_irq(s, index, val, 0);
179+
break;
180+
case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
181+
/* do not emulate hw bounced irq routing */
182+
index = (offset - EXTIOI_BOUNCE_START) >> 2;
183+
s->bounce[index] = val;
184+
break;
185+
case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
186+
index = ((offset - EXTIOI_COREISR_START) & 0x1f) >> 2;
187+
cpu = ((offset - EXTIOI_COREISR_START) >> 8) & 0x3;
188+
old_data = s->coreisr[cpu][index];
189+
s->coreisr[cpu][index] = old_data & ~val;
190+
/* write 1 to clear interrrupt */
191+
old_data &= val;
192+
irq = ctz32(old_data);
193+
while (irq != 32) {
194+
extioi_update_irq(s, irq + index * 32, 0);
195+
old_data &= ~(1 << irq);
196+
irq = ctz32(old_data);
197+
}
198+
break;
199+
case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
200+
irq = offset - EXTIOI_COREMAP_START;
201+
index = irq / 4;
202+
s->coremap[index] = val;
203+
/*
204+
* loongarch only support little endian,
205+
* so we paresd the value with little endian.
206+
*/
207+
val = cpu_to_le64(val);
208+
209+
for (i = 0; i < 4; i++) {
210+
cpu = val & 0xff;
211+
cpu = ctz32(cpu);
212+
cpu = (cpu >= 4) ? 0 : cpu;
213+
val = val >> 8;
214+
215+
if (s->sw_coremap[irq + i] == cpu) {
216+
continue;
217+
}
218+
219+
if (test_bit(irq, (unsigned long *)s->isr)) {
220+
/*
221+
* lower irq at old cpu and raise irq at new cpu
222+
*/
223+
extioi_update_irq(s, irq + i, 0);
224+
s->sw_coremap[irq + i] = cpu;
225+
extioi_update_irq(s, irq + i, 1);
226+
} else {
227+
s->sw_coremap[irq + i] = cpu;
228+
}
229+
}
230+
break;
231+
default:
232+
break;
233+
}
234+
}
235+
236+
static const MemoryRegionOps extioi_ops = {
237+
.read = extioi_readw,
238+
.write = extioi_writew,
239+
.impl.min_access_size = 4,
240+
.impl.max_access_size = 4,
241+
.valid.min_access_size = 4,
242+
.valid.max_access_size = 8,
243+
.endianness = DEVICE_LITTLE_ENDIAN,
244+
};
245+
246+
static const VMStateDescription vmstate_loongarch_extioi = {
247+
.name = TYPE_LOONGARCH_EXTIOI,
248+
.version_id = 1,
249+
.minimum_version_id = 1,
250+
.fields = (VMStateField[]) {
251+
VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT),
252+
VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, LOONGARCH_MAX_VCPUS,
253+
EXTIOI_IRQS_GROUP_COUNT),
254+
VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI,
255+
EXTIOI_IRQS_NODETYPE_COUNT / 2),
256+
VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32),
257+
VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32),
258+
VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4),
259+
VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4),
260+
VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE),
261+
VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS),
262+
263+
VMSTATE_END_OF_LIST()
264+
}
265+
};
266+
267+
static void loongarch_extioi_instance_init(Object *obj)
268+
{
269+
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
270+
LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj);
271+
int i, cpu, pin;
272+
273+
for (i = 0; i < EXTIOI_IRQS; i++) {
274+
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
275+
}
276+
277+
qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS);
278+
279+
for (cpu = 0; cpu < LOONGARCH_MAX_VCPUS; cpu++) {
280+
memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops,
281+
s, "extioi_iocsr", 0x900);
282+
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_iocsr_mem[cpu]);
283+
for (pin = 0; pin < LS3A_INTC_IP; pin++) {
284+
qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1);
285+
}
286+
}
287+
memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops,
288+
s, "extioi_system_mem", 0x900);
289+
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_system_mem);
290+
}
291+
292+
static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
293+
{
294+
DeviceClass *dc = DEVICE_CLASS(klass);
295+
296+
dc->vmsd = &vmstate_loongarch_extioi;
297+
}
298+
299+
static const TypeInfo loongarch_extioi_info = {
300+
.name = TYPE_LOONGARCH_EXTIOI,
301+
.parent = TYPE_SYS_BUS_DEVICE,
302+
.instance_init = loongarch_extioi_instance_init,
303+
.instance_size = sizeof(struct LoongArchExtIOI),
304+
.class_init = loongarch_extioi_class_init,
305+
};
306+
307+
static void loongarch_extioi_register_types(void)
308+
{
309+
type_register_static(&loongarch_extioi_info);
310+
}
311+
312+
type_init(loongarch_extioi_register_types)

hw/intc/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c'))
6666
specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c'))
6767
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c'))
6868
specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c'))
69+
specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c'))

hw/intc/trace-events

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,9 @@ loongarch_pch_pic_writeb(unsigned size, uint64_t addr, uint64_t val) "size: %u a
303303

304304
# loongarch_pch_msi.c
305305
loongarch_msi_set_irq(int irq_num) "set msi irq %d"
306+
307+
# loongarch_extioi.c
308+
loongarch_extioi_setirq(int irq, int level) "set extirq irq %d level %d"
309+
loongarch_extioi_readw(uint64_t addr, uint32_t val) "addr: 0x%"PRIx64 "val: 0x%x"
310+
loongarch_extioi_writew(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64 "val: 0x%" PRIx64
311+

hw/loongarch/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ config LOONGARCH_VIRT
55
select LOONGARCH_IPI
66
select LOONGARCH_PCH_PIC
77
select LOONGARCH_PCH_MSI
8+
select LOONGARCH_EXTIOI

0 commit comments

Comments
 (0)