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