2 * QEMU Loongson Local I/O interrupt controler.
4 * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 #include "qemu/osdep.h"
22 #include "hw/sysbus.h"
23 #include "qemu/module.h"
25 #include "hw/qdev-properties.h"
33 #define NUM_PARENTS (NUM_CORES * NUM_IPS)
34 #define PARENT_COREx_IPy(x, y) (NUM_IPS * x + y)
36 #define R_MAPPER_START 0x0
37 #define R_MAPPER_END 0x20
38 #define R_ISR R_MAPPER_END
40 #define R_IEN_SET 0x28
41 #define R_IEN_CLR 0x2c
42 #define R_PERCORE_ISR(x) (0x40 + 0x8 * x)
45 #define TYPE_LOONGSON_LIOINTC "loongson.liointc"
46 #define LOONGSON_LIOINTC(obj) \
47 OBJECT_CHECK(struct loongson_liointc, (obj), TYPE_LOONGSON_LIOINTC)
49 struct loongson_liointc
{
50 SysBusDevice parent_obj
;
53 qemu_irq parent_irq
[NUM_PARENTS
];
55 uint8_t mapper
[NUM_IRQS
]; /* 0:3 for core, 4:7 for IP */
58 uint32_t per_core_isr
[NUM_CORES
];
60 /* state of the interrupt input pins */
62 bool parent_state
[NUM_PARENTS
];
65 static void update_irq(struct loongson_liointc
*p
)
67 uint32_t irq
, core
, ip
;
68 uint32_t per_ip_isr
[NUM_IPS
] = {0};
70 /* level triggered interrupt */
71 p
->isr
= p
->pin_state
;
73 /* Clear disabled IRQs */
76 /* Clear per_core_isr */
77 for (core
= 0; core
< NUM_CORES
; core
++) {
78 p
->per_core_isr
[core
] = 0;
81 /* Update per_core_isr and per_ip_isr */
82 for (irq
= 0; irq
< NUM_IRQS
; irq
++) {
83 if (!(p
->isr
& (1 << irq
))) {
87 for (core
= 0; core
< NUM_CORES
; core
++) {
88 if ((p
->mapper
[irq
] & (1 << core
))) {
89 p
->per_core_isr
[core
] |= (1 << irq
);
93 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
94 if ((p
->mapper
[irq
] & (1 << (ip
+ 4)))) {
95 per_ip_isr
[ip
] |= (1 << irq
);
100 /* Emit IRQ to parent! */
101 for (core
= 0; core
< NUM_CORES
; core
++) {
102 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
103 int parent
= PARENT_COREx_IPy(core
, ip
);
104 if (p
->parent_state
[parent
] !=
105 (!!p
->per_core_isr
[core
] && !!per_ip_isr
[ip
])) {
106 p
->parent_state
[parent
] = !p
->parent_state
[parent
];
107 qemu_set_irq(p
->parent_irq
[parent
], p
->parent_state
[parent
]);
114 liointc_read(void *opaque
, hwaddr addr
, unsigned int size
)
116 struct loongson_liointc
*p
= opaque
;
119 /* Mapper is 1 byte */
120 if (size
== 1 && addr
< R_MAPPER_END
) {
126 if (size
!= 4 || (addr
% 4)) {
130 if (addr
>= R_PERCORE_ISR(0) &&
131 addr
< R_PERCORE_ISR(NUM_CORES
)) {
132 int core
= (addr
- R_PERCORE_ISR(0)) / 4;
133 r
= p
->per_core_isr
[core
];
149 D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__
, size
, addr
, r
));
154 liointc_write(void *opaque
, hwaddr addr
,
155 uint64_t val64
, unsigned int size
)
157 struct loongson_liointc
*p
= opaque
;
158 uint32_t value
= val64
;
160 D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__
, size
, addr
, value
));
162 /* Mapper is 1 byte */
163 if (size
== 1 && addr
< R_MAPPER_END
) {
164 p
->mapper
[addr
] = value
;
169 if (size
!= 4 || (addr
% 4)) {
173 if (addr
>= R_PERCORE_ISR(0) &&
174 addr
< R_PERCORE_ISR(NUM_CORES
)) {
175 int core
= (addr
- R_PERCORE_ISR(0)) / 4;
176 p
->per_core_isr
[core
] = value
;
195 static const MemoryRegionOps pic_ops
= {
196 .read
= liointc_read
,
197 .write
= liointc_write
,
198 .endianness
= DEVICE_NATIVE_ENDIAN
,
200 .min_access_size
= 1,
205 static void irq_handler(void *opaque
, int irq
, int level
)
207 struct loongson_liointc
*p
= opaque
;
209 p
->pin_state
&= ~(1 << irq
);
210 p
->pin_state
|= level
<< irq
;
214 static void loongson_liointc_init(Object
*obj
)
216 struct loongson_liointc
*p
= LOONGSON_LIOINTC(obj
);
219 qdev_init_gpio_in(DEVICE(obj
), irq_handler
, 32);
221 for (i
= 0; i
< NUM_PARENTS
; i
++) {
222 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &p
->parent_irq
[i
]);
225 memory_region_init_io(&p
->mmio
, obj
, &pic_ops
, p
,
226 "loongson.liointc", R_END
);
227 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &p
->mmio
);
230 static const TypeInfo loongson_liointc_info
= {
231 .name
= TYPE_LOONGSON_LIOINTC
,
232 .parent
= TYPE_SYS_BUS_DEVICE
,
233 .instance_size
= sizeof(struct loongson_liointc
),
234 .instance_init
= loongson_liointc_init
,
237 static void loongson_liointc_register_types(void)
239 type_register_static(&loongson_liointc_info
);
242 type_init(loongson_liointc_register_types
)