xref: /openbmc/qemu/hw/intc/loongson_liointc.c (revision c012e0b1)
1*c012e0b1SHuacai Chen /*
2*c012e0b1SHuacai Chen  * QEMU Loongson Local I/O interrupt controler.
3*c012e0b1SHuacai Chen  *
4*c012e0b1SHuacai Chen  * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
5*c012e0b1SHuacai Chen  *
6*c012e0b1SHuacai Chen  * This program is free software: you can redistribute it and/or modify
7*c012e0b1SHuacai Chen  * it under the terms of the GNU General Public License as published by
8*c012e0b1SHuacai Chen  * the Free Software Foundation, either version 2 of the License, or
9*c012e0b1SHuacai Chen  * (at your option) any later version.
10*c012e0b1SHuacai Chen  *
11*c012e0b1SHuacai Chen  * This program is distributed in the hope that it will be useful,
12*c012e0b1SHuacai Chen  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*c012e0b1SHuacai Chen  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*c012e0b1SHuacai Chen  * GNU General Public License for more details.
15*c012e0b1SHuacai Chen  *
16*c012e0b1SHuacai Chen  * You should have received a copy of the GNU General Public License
17*c012e0b1SHuacai Chen  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18*c012e0b1SHuacai Chen  *
19*c012e0b1SHuacai Chen  */
20*c012e0b1SHuacai Chen 
21*c012e0b1SHuacai Chen #include "qemu/osdep.h"
22*c012e0b1SHuacai Chen #include "hw/sysbus.h"
23*c012e0b1SHuacai Chen #include "qemu/module.h"
24*c012e0b1SHuacai Chen #include "hw/irq.h"
25*c012e0b1SHuacai Chen #include "hw/qdev-properties.h"
26*c012e0b1SHuacai Chen 
27*c012e0b1SHuacai Chen #define D(x)
28*c012e0b1SHuacai Chen 
29*c012e0b1SHuacai Chen #define NUM_IRQS                32
30*c012e0b1SHuacai Chen 
31*c012e0b1SHuacai Chen #define NUM_CORES               4
32*c012e0b1SHuacai Chen #define NUM_IPS                 4
33*c012e0b1SHuacai Chen #define NUM_PARENTS             (NUM_CORES * NUM_IPS)
34*c012e0b1SHuacai Chen #define PARENT_COREx_IPy(x, y)  (NUM_IPS * x + y)
35*c012e0b1SHuacai Chen 
36*c012e0b1SHuacai Chen #define R_MAPPER_START          0x0
37*c012e0b1SHuacai Chen #define R_MAPPER_END            0x20
38*c012e0b1SHuacai Chen #define R_ISR                   R_MAPPER_END
39*c012e0b1SHuacai Chen #define R_IEN                   0x24
40*c012e0b1SHuacai Chen #define R_IEN_SET               0x28
41*c012e0b1SHuacai Chen #define R_IEN_CLR               0x2c
42*c012e0b1SHuacai Chen #define R_PERCORE_ISR(x)        (0x40 + 0x8 * x)
43*c012e0b1SHuacai Chen #define R_END                   0x64
44*c012e0b1SHuacai Chen 
45*c012e0b1SHuacai Chen #define TYPE_LOONGSON_LIOINTC "loongson.liointc"
46*c012e0b1SHuacai Chen #define LOONGSON_LIOINTC(obj) \
47*c012e0b1SHuacai Chen         OBJECT_CHECK(struct loongson_liointc, (obj), TYPE_LOONGSON_LIOINTC)
48*c012e0b1SHuacai Chen 
49*c012e0b1SHuacai Chen struct loongson_liointc {
50*c012e0b1SHuacai Chen     SysBusDevice parent_obj;
51*c012e0b1SHuacai Chen 
52*c012e0b1SHuacai Chen     MemoryRegion mmio;
53*c012e0b1SHuacai Chen     qemu_irq parent_irq[NUM_PARENTS];
54*c012e0b1SHuacai Chen 
55*c012e0b1SHuacai Chen     uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
56*c012e0b1SHuacai Chen     uint32_t isr;
57*c012e0b1SHuacai Chen     uint32_t ien;
58*c012e0b1SHuacai Chen     uint32_t per_core_isr[NUM_CORES];
59*c012e0b1SHuacai Chen 
60*c012e0b1SHuacai Chen     /* state of the interrupt input pins */
61*c012e0b1SHuacai Chen     uint32_t pin_state;
62*c012e0b1SHuacai Chen     bool parent_state[NUM_PARENTS];
63*c012e0b1SHuacai Chen };
64*c012e0b1SHuacai Chen 
65*c012e0b1SHuacai Chen static void update_irq(struct loongson_liointc *p)
66*c012e0b1SHuacai Chen {
67*c012e0b1SHuacai Chen     uint32_t irq, core, ip;
68*c012e0b1SHuacai Chen     uint32_t per_ip_isr[NUM_IPS] = {0};
69*c012e0b1SHuacai Chen 
70*c012e0b1SHuacai Chen     /* level triggered interrupt */
71*c012e0b1SHuacai Chen     p->isr = p->pin_state;
72*c012e0b1SHuacai Chen 
73*c012e0b1SHuacai Chen     /* Clear disabled IRQs */
74*c012e0b1SHuacai Chen     p->isr &= p->ien;
75*c012e0b1SHuacai Chen 
76*c012e0b1SHuacai Chen     /* Clear per_core_isr */
77*c012e0b1SHuacai Chen     for (core = 0; core < NUM_CORES; core++) {
78*c012e0b1SHuacai Chen         p->per_core_isr[core] = 0;
79*c012e0b1SHuacai Chen     }
80*c012e0b1SHuacai Chen 
81*c012e0b1SHuacai Chen     /* Update per_core_isr and per_ip_isr */
82*c012e0b1SHuacai Chen     for (irq = 0; irq < NUM_IRQS; irq++) {
83*c012e0b1SHuacai Chen         if (!(p->isr & (1 << irq))) {
84*c012e0b1SHuacai Chen             continue;
85*c012e0b1SHuacai Chen         }
86*c012e0b1SHuacai Chen 
87*c012e0b1SHuacai Chen         for (core = 0; core < NUM_CORES; core++) {
88*c012e0b1SHuacai Chen             if ((p->mapper[irq] & (1 << core))) {
89*c012e0b1SHuacai Chen                 p->per_core_isr[core] |= (1 << irq);
90*c012e0b1SHuacai Chen             }
91*c012e0b1SHuacai Chen         }
92*c012e0b1SHuacai Chen 
93*c012e0b1SHuacai Chen         for (ip = 0; ip < NUM_IPS; ip++) {
94*c012e0b1SHuacai Chen             if ((p->mapper[irq] & (1 << (ip + 4)))) {
95*c012e0b1SHuacai Chen                 per_ip_isr[ip] |= (1 << irq);
96*c012e0b1SHuacai Chen             }
97*c012e0b1SHuacai Chen         }
98*c012e0b1SHuacai Chen     }
99*c012e0b1SHuacai Chen 
100*c012e0b1SHuacai Chen     /* Emit IRQ to parent! */
101*c012e0b1SHuacai Chen     for (core = 0; core < NUM_CORES; core++) {
102*c012e0b1SHuacai Chen         for (ip = 0; ip < NUM_IPS; ip++) {
103*c012e0b1SHuacai Chen             int parent = PARENT_COREx_IPy(core, ip);
104*c012e0b1SHuacai Chen             if (p->parent_state[parent] !=
105*c012e0b1SHuacai Chen                 (!!p->per_core_isr[core] && !!per_ip_isr[ip])) {
106*c012e0b1SHuacai Chen                 p->parent_state[parent] = !p->parent_state[parent];
107*c012e0b1SHuacai Chen                 qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]);
108*c012e0b1SHuacai Chen             }
109*c012e0b1SHuacai Chen         }
110*c012e0b1SHuacai Chen     }
111*c012e0b1SHuacai Chen }
112*c012e0b1SHuacai Chen 
113*c012e0b1SHuacai Chen static uint64_t
114*c012e0b1SHuacai Chen liointc_read(void *opaque, hwaddr addr, unsigned int size)
115*c012e0b1SHuacai Chen {
116*c012e0b1SHuacai Chen     struct loongson_liointc *p = opaque;
117*c012e0b1SHuacai Chen     uint32_t r = 0;
118*c012e0b1SHuacai Chen 
119*c012e0b1SHuacai Chen     /* Mapper is 1 byte */
120*c012e0b1SHuacai Chen     if (size == 1 && addr < R_MAPPER_END) {
121*c012e0b1SHuacai Chen         r = p->mapper[addr];
122*c012e0b1SHuacai Chen         goto out;
123*c012e0b1SHuacai Chen     }
124*c012e0b1SHuacai Chen 
125*c012e0b1SHuacai Chen     /* Rest is 4 byte */
126*c012e0b1SHuacai Chen     if (size != 4 || (addr % 4)) {
127*c012e0b1SHuacai Chen         goto out;
128*c012e0b1SHuacai Chen     }
129*c012e0b1SHuacai Chen 
130*c012e0b1SHuacai Chen     if (addr >= R_PERCORE_ISR(0) &&
131*c012e0b1SHuacai Chen         addr < R_PERCORE_ISR(NUM_CORES)) {
132*c012e0b1SHuacai Chen         int core = (addr - R_PERCORE_ISR(0)) / 4;
133*c012e0b1SHuacai Chen         r = p->per_core_isr[core];
134*c012e0b1SHuacai Chen         goto out;
135*c012e0b1SHuacai Chen     }
136*c012e0b1SHuacai Chen 
137*c012e0b1SHuacai Chen     switch (addr) {
138*c012e0b1SHuacai Chen     case R_ISR:
139*c012e0b1SHuacai Chen         r = p->isr;
140*c012e0b1SHuacai Chen         break;
141*c012e0b1SHuacai Chen     case R_IEN:
142*c012e0b1SHuacai Chen         r = p->ien;
143*c012e0b1SHuacai Chen         break;
144*c012e0b1SHuacai Chen     default:
145*c012e0b1SHuacai Chen         break;
146*c012e0b1SHuacai Chen     }
147*c012e0b1SHuacai Chen 
148*c012e0b1SHuacai Chen out:
149*c012e0b1SHuacai Chen     D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r));
150*c012e0b1SHuacai Chen     return r;
151*c012e0b1SHuacai Chen }
152*c012e0b1SHuacai Chen 
153*c012e0b1SHuacai Chen static void
154*c012e0b1SHuacai Chen liointc_write(void *opaque, hwaddr addr,
155*c012e0b1SHuacai Chen           uint64_t val64, unsigned int size)
156*c012e0b1SHuacai Chen {
157*c012e0b1SHuacai Chen     struct loongson_liointc *p = opaque;
158*c012e0b1SHuacai Chen     uint32_t value = val64;
159*c012e0b1SHuacai Chen 
160*c012e0b1SHuacai Chen     D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value));
161*c012e0b1SHuacai Chen 
162*c012e0b1SHuacai Chen     /* Mapper is 1 byte */
163*c012e0b1SHuacai Chen     if (size == 1 && addr < R_MAPPER_END) {
164*c012e0b1SHuacai Chen         p->mapper[addr] = value;
165*c012e0b1SHuacai Chen         goto out;
166*c012e0b1SHuacai Chen     }
167*c012e0b1SHuacai Chen 
168*c012e0b1SHuacai Chen     /* Rest is 4 byte */
169*c012e0b1SHuacai Chen     if (size != 4 || (addr % 4)) {
170*c012e0b1SHuacai Chen         goto out;
171*c012e0b1SHuacai Chen     }
172*c012e0b1SHuacai Chen 
173*c012e0b1SHuacai Chen     if (addr >= R_PERCORE_ISR(0) &&
174*c012e0b1SHuacai Chen         addr < R_PERCORE_ISR(NUM_CORES)) {
175*c012e0b1SHuacai Chen         int core = (addr - R_PERCORE_ISR(0)) / 4;
176*c012e0b1SHuacai Chen         p->per_core_isr[core] = value;
177*c012e0b1SHuacai Chen         goto out;
178*c012e0b1SHuacai Chen     }
179*c012e0b1SHuacai Chen 
180*c012e0b1SHuacai Chen     switch (addr) {
181*c012e0b1SHuacai Chen     case R_IEN_SET:
182*c012e0b1SHuacai Chen         p->ien |= value;
183*c012e0b1SHuacai Chen         break;
184*c012e0b1SHuacai Chen     case R_IEN_CLR:
185*c012e0b1SHuacai Chen         p->ien &= ~value;
186*c012e0b1SHuacai Chen         break;
187*c012e0b1SHuacai Chen     default:
188*c012e0b1SHuacai Chen         break;
189*c012e0b1SHuacai Chen     }
190*c012e0b1SHuacai Chen 
191*c012e0b1SHuacai Chen out:
192*c012e0b1SHuacai Chen     update_irq(p);
193*c012e0b1SHuacai Chen }
194*c012e0b1SHuacai Chen 
195*c012e0b1SHuacai Chen static const MemoryRegionOps pic_ops = {
196*c012e0b1SHuacai Chen     .read = liointc_read,
197*c012e0b1SHuacai Chen     .write = liointc_write,
198*c012e0b1SHuacai Chen     .endianness = DEVICE_NATIVE_ENDIAN,
199*c012e0b1SHuacai Chen     .valid = {
200*c012e0b1SHuacai Chen         .min_access_size = 1,
201*c012e0b1SHuacai Chen         .max_access_size = 4
202*c012e0b1SHuacai Chen     }
203*c012e0b1SHuacai Chen };
204*c012e0b1SHuacai Chen 
205*c012e0b1SHuacai Chen static void irq_handler(void *opaque, int irq, int level)
206*c012e0b1SHuacai Chen {
207*c012e0b1SHuacai Chen     struct loongson_liointc *p = opaque;
208*c012e0b1SHuacai Chen 
209*c012e0b1SHuacai Chen     p->pin_state &= ~(1 << irq);
210*c012e0b1SHuacai Chen     p->pin_state |= level << irq;
211*c012e0b1SHuacai Chen     update_irq(p);
212*c012e0b1SHuacai Chen }
213*c012e0b1SHuacai Chen 
214*c012e0b1SHuacai Chen static void loongson_liointc_init(Object *obj)
215*c012e0b1SHuacai Chen {
216*c012e0b1SHuacai Chen     struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
217*c012e0b1SHuacai Chen     int i;
218*c012e0b1SHuacai Chen 
219*c012e0b1SHuacai Chen     qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
220*c012e0b1SHuacai Chen 
221*c012e0b1SHuacai Chen     for (i = 0; i < NUM_PARENTS; i++) {
222*c012e0b1SHuacai Chen         sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
223*c012e0b1SHuacai Chen     }
224*c012e0b1SHuacai Chen 
225*c012e0b1SHuacai Chen     memory_region_init_io(&p->mmio, obj, &pic_ops, p,
226*c012e0b1SHuacai Chen                          "loongson.liointc", R_END);
227*c012e0b1SHuacai Chen     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
228*c012e0b1SHuacai Chen }
229*c012e0b1SHuacai Chen 
230*c012e0b1SHuacai Chen static const TypeInfo loongson_liointc_info = {
231*c012e0b1SHuacai Chen     .name          = TYPE_LOONGSON_LIOINTC,
232*c012e0b1SHuacai Chen     .parent        = TYPE_SYS_BUS_DEVICE,
233*c012e0b1SHuacai Chen     .instance_size = sizeof(struct loongson_liointc),
234*c012e0b1SHuacai Chen     .instance_init = loongson_liointc_init,
235*c012e0b1SHuacai Chen };
236*c012e0b1SHuacai Chen 
237*c012e0b1SHuacai Chen static void loongson_liointc_register_types(void)
238*c012e0b1SHuacai Chen {
239*c012e0b1SHuacai Chen     type_register_static(&loongson_liointc_info);
240*c012e0b1SHuacai Chen }
241*c012e0b1SHuacai Chen 
242*c012e0b1SHuacai Chen type_init(loongson_liointc_register_types)
243