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"
26 #include "qom/object.h"
34 #define NUM_PARENTS (NUM_CORES * NUM_IPS)
35 #define PARENT_COREx_IPy(x, y) (NUM_IPS * x + y)
37 #define R_MAPPER_START 0x0
38 #define R_MAPPER_END 0x20
39 #define R_ISR R_MAPPER_END
41 #define R_IEN_SET 0x28
42 #define R_IEN_CLR 0x2c
43 #define R_PERCORE_ISR(x) (0x40 + 0x8 * x)
46 #define TYPE_LOONGSON_LIOINTC "loongson.liointc"
47 DECLARE_INSTANCE_CHECKER(struct loongson_liointc
, LOONGSON_LIOINTC
,
48 TYPE_LOONGSON_LIOINTC
)
50 struct loongson_liointc
{
51 SysBusDevice parent_obj
;
54 qemu_irq parent_irq
[NUM_PARENTS
];
56 uint8_t mapper
[NUM_IRQS
]; /* 0:3 for core, 4:7 for IP */
59 uint32_t per_core_isr
[NUM_CORES
];
61 /* state of the interrupt input pins */
63 bool parent_state
[NUM_PARENTS
];
66 static void update_irq(struct loongson_liointc
*p
)
68 uint32_t irq
, core
, ip
;
69 uint32_t per_ip_isr
[NUM_IPS
] = {0};
71 /* level triggered interrupt */
72 p
->isr
= p
->pin_state
;
74 /* Clear disabled IRQs */
77 /* Clear per_core_isr */
78 for (core
= 0; core
< NUM_CORES
; core
++) {
79 p
->per_core_isr
[core
] = 0;
82 /* Update per_core_isr and per_ip_isr */
83 for (irq
= 0; irq
< NUM_IRQS
; irq
++) {
84 if (!(p
->isr
& (1 << irq
))) {
88 for (core
= 0; core
< NUM_CORES
; core
++) {
89 if ((p
->mapper
[irq
] & (1 << core
))) {
90 p
->per_core_isr
[core
] |= (1 << irq
);
94 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
95 if ((p
->mapper
[irq
] & (1 << (ip
+ 4)))) {
96 per_ip_isr
[ip
] |= (1 << irq
);
101 /* Emit IRQ to parent! */
102 for (core
= 0; core
< NUM_CORES
; core
++) {
103 for (ip
= 0; ip
< NUM_IPS
; ip
++) {
104 int parent
= PARENT_COREx_IPy(core
, ip
);
105 if (p
->parent_state
[parent
] !=
106 (!!p
->per_core_isr
[core
] && !!per_ip_isr
[ip
])) {
107 p
->parent_state
[parent
] = !p
->parent_state
[parent
];
108 qemu_set_irq(p
->parent_irq
[parent
], p
->parent_state
[parent
]);
115 liointc_read(void *opaque
, hwaddr addr
, unsigned int size
)
117 struct loongson_liointc
*p
= opaque
;
120 /* Mapper is 1 byte */
121 if (size
== 1 && addr
< R_MAPPER_END
) {
127 if (size
!= 4 || (addr
% 4)) {
131 if (addr
>= R_PERCORE_ISR(0) &&
132 addr
< R_PERCORE_ISR(NUM_CORES
)) {
133 int core
= (addr
- R_PERCORE_ISR(0)) / 8;
134 r
= p
->per_core_isr
[core
];
150 D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__
, size
, addr
, r
));
155 liointc_write(void *opaque
, hwaddr addr
,
156 uint64_t val64
, unsigned int size
)
158 struct loongson_liointc
*p
= opaque
;
159 uint32_t value
= val64
;
161 D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__
, size
, addr
, value
));
163 /* Mapper is 1 byte */
164 if (size
== 1 && addr
< R_MAPPER_END
) {
165 p
->mapper
[addr
] = value
;
170 if (size
!= 4 || (addr
% 4)) {
174 if (addr
>= R_PERCORE_ISR(0) &&
175 addr
< R_PERCORE_ISR(NUM_CORES
)) {
176 int core
= (addr
- R_PERCORE_ISR(0)) / 8;
177 p
->per_core_isr
[core
] = value
;
196 static const MemoryRegionOps pic_ops
= {
197 .read
= liointc_read
,
198 .write
= liointc_write
,
199 .endianness
= DEVICE_NATIVE_ENDIAN
,
201 .min_access_size
= 1,
206 static void irq_handler(void *opaque
, int irq
, int level
)
208 struct loongson_liointc
*p
= opaque
;
210 p
->pin_state
&= ~(1 << irq
);
211 p
->pin_state
|= level
<< irq
;
215 static void loongson_liointc_init(Object
*obj
)
217 struct loongson_liointc
*p
= LOONGSON_LIOINTC(obj
);
220 qdev_init_gpio_in(DEVICE(obj
), irq_handler
, 32);
222 for (i
= 0; i
< NUM_PARENTS
; i
++) {
223 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &p
->parent_irq
[i
]);
226 memory_region_init_io(&p
->mmio
, obj
, &pic_ops
, p
,
227 "loongson.liointc", R_END
);
228 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &p
->mmio
);
231 static const TypeInfo loongson_liointc_info
= {
232 .name
= TYPE_LOONGSON_LIOINTC
,
233 .parent
= TYPE_SYS_BUS_DEVICE
,
234 .instance_size
= sizeof(struct loongson_liointc
),
235 .instance_init
= loongson_liointc_init
,
238 static void loongson_liointc_register_types(void)
240 type_register_static(&loongson_liointc_info
);
243 type_init(loongson_liointc_register_types
)