2 * QEMU Loongson Local I/O interrupt controller.
4 * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
5 * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 #include "qemu/osdep.h"
23 #include "qemu/module.h"
26 #include "hw/qdev-properties.h"
27 #include "hw/intc/loongson_liointc.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_ISR_SIZE 0x8
44 #define R_END (R_START + R_ISR_SIZE * NUM_CORES)
46 struct loongson_liointc
{
47 SysBusDevice parent_obj
;
50 qemu_irq parent_irq
[NUM_PARENTS
];
52 uint8_t mapper
[NUM_IRQS
]; /* 0:3 for core, 4:7 for IP */
55 uint32_t per_core_isr
[NUM_CORES
];
57 /* state of the interrupt input pins */
59 bool parent_state
[NUM_PARENTS
];
62 static void update_irq(struct loongson_liointc
*p
)
64 uint32_t irq
, core
, ip
;
65 uint32_t per_ip_isr
[NUM_IPS
] = {0};
67 /* level triggered interrupt */
68 p
->isr
= p
->pin_state
;
70 /* Clear disabled IRQs */
73 /* Clear per_core_isr */
74 for (core
= 0; core
< NUM_CORES
; core
++) {
75 p
->per_core_isr
[core
] = 0;
78 /* Update per_core_isr and per_ip_isr */
79 for (irq
= 0; irq
< NUM_IRQS
; irq
++) {
80 if (!(p
->isr
& (1 << irq
))) {
84 for (core
= 0; core
< NUM_CORES
; core
++) {
85 if ((p
->mapper
[irq
] & (1 << core
))) {
86 p
->per_core_isr
[core
] |= (1 << irq
);
90 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
91 if ((p
->mapper
[irq
] & (1 << (ip
+ 4)))) {
92 per_ip_isr
[ip
] |= (1 << irq
);
97 /* Emit IRQ to parent! */
98 for (core
= 0; core
< NUM_CORES
; core
++) {
99 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
100 int parent
= PARENT_COREx_IPy(core
, ip
);
101 if (p
->parent_state
[parent
] !=
102 (!!p
->per_core_isr
[core
] && !!per_ip_isr
[ip
])) {
103 p
->parent_state
[parent
] = !p
->parent_state
[parent
];
104 qemu_set_irq(p
->parent_irq
[parent
], p
->parent_state
[parent
]);
111 liointc_read(void *opaque
, hwaddr addr
, unsigned int size
)
113 struct loongson_liointc
*p
= opaque
;
116 /* Mapper is 1 byte */
117 if (size
== 1 && addr
< R_MAPPER_END
) {
122 /* Rest are 4 bytes */
123 if (size
!= 4 || (addr
% 4)) {
127 if (addr
>= R_START
&& addr
< R_END
) {
128 hwaddr offset
= addr
- R_START
;
129 int core
= offset
/ R_ISR_SIZE
;
131 if (offset
% R_ISR_SIZE
) {
134 r
= p
->per_core_isr
[core
];
150 qemu_log_mask(CPU_LOG_INT
, "%s: size=%d, addr=%"HWADDR_PRIx
", val=%x\n",
151 __func__
, size
, addr
, r
);
156 liointc_write(void *opaque
, hwaddr addr
,
157 uint64_t val64
, unsigned int size
)
159 struct loongson_liointc
*p
= opaque
;
160 uint32_t value
= val64
;
162 qemu_log_mask(CPU_LOG_INT
, "%s: size=%d, addr=%"HWADDR_PRIx
", val=%x\n",
163 __func__
, size
, addr
, value
);
165 /* Mapper is 1 byte */
166 if (size
== 1 && addr
< R_MAPPER_END
) {
167 p
->mapper
[addr
] = value
;
171 /* Rest are 4 bytes */
172 if (size
!= 4 || (addr
% 4)) {
176 if (addr
>= R_START
&& addr
< R_END
) {
177 hwaddr offset
= addr
- R_START
;
178 int core
= offset
/ R_ISR_SIZE
;
180 if (offset
% R_ISR_SIZE
) {
183 p
->per_core_isr
[core
] = value
;
202 static const MemoryRegionOps pic_ops
= {
203 .read
= liointc_read
,
204 .write
= liointc_write
,
205 .endianness
= DEVICE_NATIVE_ENDIAN
,
207 .min_access_size
= 1,
212 static void irq_handler(void *opaque
, int irq
, int level
)
214 struct loongson_liointc
*p
= opaque
;
216 p
->pin_state
&= ~(1 << irq
);
217 p
->pin_state
|= level
<< irq
;
221 static void loongson_liointc_init(Object
*obj
)
223 struct loongson_liointc
*p
= LOONGSON_LIOINTC(obj
);
226 qdev_init_gpio_in(DEVICE(obj
), irq_handler
, 32);
228 for (i
= 0; i
< NUM_PARENTS
; i
++) {
229 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &p
->parent_irq
[i
]);
232 memory_region_init_io(&p
->mmio
, obj
, &pic_ops
, p
,
233 TYPE_LOONGSON_LIOINTC
, R_END
);
234 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &p
->mmio
);
237 static const TypeInfo loongson_liointc_info
= {
238 .name
= TYPE_LOONGSON_LIOINTC
,
239 .parent
= TYPE_SYS_BUS_DEVICE
,
240 .instance_size
= sizeof(struct loongson_liointc
),
241 .instance_init
= loongson_liointc_init
,
244 static void loongson_liointc_register_types(void)
246 type_register_static(&loongson_liointc_info
);
249 type_init(loongson_liointc_register_types
)