xref: /openbmc/qemu/hw/intc/loongson_liointc.c (revision ad9e5aa2)
1 /*
2  * QEMU Loongson Local I/O interrupt controler.
3  *
4  * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  */
20 
21 #include "qemu/osdep.h"
22 #include "hw/sysbus.h"
23 #include "qemu/module.h"
24 #include "hw/irq.h"
25 #include "hw/qdev-properties.h"
26 
27 #define D(x)
28 
29 #define NUM_IRQS                32
30 
31 #define NUM_CORES               4
32 #define NUM_IPS                 4
33 #define NUM_PARENTS             (NUM_CORES * NUM_IPS)
34 #define PARENT_COREx_IPy(x, y)  (NUM_IPS * x + y)
35 
36 #define R_MAPPER_START          0x0
37 #define R_MAPPER_END            0x20
38 #define R_ISR                   R_MAPPER_END
39 #define R_IEN                   0x24
40 #define R_IEN_SET               0x28
41 #define R_IEN_CLR               0x2c
42 #define R_PERCORE_ISR(x)        (0x40 + 0x8 * x)
43 #define R_END                   0x64
44 
45 #define TYPE_LOONGSON_LIOINTC "loongson.liointc"
46 #define LOONGSON_LIOINTC(obj) \
47         OBJECT_CHECK(struct loongson_liointc, (obj), TYPE_LOONGSON_LIOINTC)
48 
49 struct loongson_liointc {
50     SysBusDevice parent_obj;
51 
52     MemoryRegion mmio;
53     qemu_irq parent_irq[NUM_PARENTS];
54 
55     uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
56     uint32_t isr;
57     uint32_t ien;
58     uint32_t per_core_isr[NUM_CORES];
59 
60     /* state of the interrupt input pins */
61     uint32_t pin_state;
62     bool parent_state[NUM_PARENTS];
63 };
64 
65 static void update_irq(struct loongson_liointc *p)
66 {
67     uint32_t irq, core, ip;
68     uint32_t per_ip_isr[NUM_IPS] = {0};
69 
70     /* level triggered interrupt */
71     p->isr = p->pin_state;
72 
73     /* Clear disabled IRQs */
74     p->isr &= p->ien;
75 
76     /* Clear per_core_isr */
77     for (core = 0; core < NUM_CORES; core++) {
78         p->per_core_isr[core] = 0;
79     }
80 
81     /* Update per_core_isr and per_ip_isr */
82     for (irq = 0; irq < NUM_IRQS; irq++) {
83         if (!(p->isr & (1 << irq))) {
84             continue;
85         }
86 
87         for (core = 0; core < NUM_CORES; core++) {
88             if ((p->mapper[irq] & (1 << core))) {
89                 p->per_core_isr[core] |= (1 << irq);
90             }
91         }
92 
93         for (ip = 0; ip < NUM_IPS; ip++) {
94             if ((p->mapper[irq] & (1 << (ip + 4)))) {
95                 per_ip_isr[ip] |= (1 << irq);
96             }
97         }
98     }
99 
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]);
108             }
109         }
110     }
111 }
112 
113 static uint64_t
114 liointc_read(void *opaque, hwaddr addr, unsigned int size)
115 {
116     struct loongson_liointc *p = opaque;
117     uint32_t r = 0;
118 
119     /* Mapper is 1 byte */
120     if (size == 1 && addr < R_MAPPER_END) {
121         r = p->mapper[addr];
122         goto out;
123     }
124 
125     /* Rest is 4 byte */
126     if (size != 4 || (addr % 4)) {
127         goto out;
128     }
129 
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];
134         goto out;
135     }
136 
137     switch (addr) {
138     case R_ISR:
139         r = p->isr;
140         break;
141     case R_IEN:
142         r = p->ien;
143         break;
144     default:
145         break;
146     }
147 
148 out:
149     D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r));
150     return r;
151 }
152 
153 static void
154 liointc_write(void *opaque, hwaddr addr,
155           uint64_t val64, unsigned int size)
156 {
157     struct loongson_liointc *p = opaque;
158     uint32_t value = val64;
159 
160     D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value));
161 
162     /* Mapper is 1 byte */
163     if (size == 1 && addr < R_MAPPER_END) {
164         p->mapper[addr] = value;
165         goto out;
166     }
167 
168     /* Rest is 4 byte */
169     if (size != 4 || (addr % 4)) {
170         goto out;
171     }
172 
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;
177         goto out;
178     }
179 
180     switch (addr) {
181     case R_IEN_SET:
182         p->ien |= value;
183         break;
184     case R_IEN_CLR:
185         p->ien &= ~value;
186         break;
187     default:
188         break;
189     }
190 
191 out:
192     update_irq(p);
193 }
194 
195 static const MemoryRegionOps pic_ops = {
196     .read = liointc_read,
197     .write = liointc_write,
198     .endianness = DEVICE_NATIVE_ENDIAN,
199     .valid = {
200         .min_access_size = 1,
201         .max_access_size = 4
202     }
203 };
204 
205 static void irq_handler(void *opaque, int irq, int level)
206 {
207     struct loongson_liointc *p = opaque;
208 
209     p->pin_state &= ~(1 << irq);
210     p->pin_state |= level << irq;
211     update_irq(p);
212 }
213 
214 static void loongson_liointc_init(Object *obj)
215 {
216     struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
217     int i;
218 
219     qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
220 
221     for (i = 0; i < NUM_PARENTS; i++) {
222         sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
223     }
224 
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);
228 }
229 
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,
235 };
236 
237 static void loongson_liointc_register_types(void)
238 {
239     type_register_static(&loongson_liointc_info);
240 }
241 
242 type_init(loongson_liointc_register_types)
243