1 /* 2 * QEMU GRLIB IRQMP Emulator 3 * 4 * (Multiprocessor and extended interrupt not supported) 5 * 6 * SPDX-License-Identifier: MIT 7 * 8 * Copyright (c) 2010-2024 AdaCore 9 * 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 * of this software and associated documentation files (the "Software"), to deal 12 * in the Software without restriction, including without limitation the rights 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 * copies of the Software, and to permit persons to whom the Software is 15 * furnished to do so, subject to the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be included in 18 * all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 * THE SOFTWARE. 27 */ 28 29 #include "qemu/osdep.h" 30 #include "hw/irq.h" 31 #include "hw/sysbus.h" 32 33 #include "hw/qdev-properties.h" 34 #include "hw/intc/grlib_irqmp.h" 35 36 #include "trace.h" 37 #include "qapi/error.h" 38 #include "qemu/module.h" 39 #include "qom/object.h" 40 41 #define IRQMP_MAX_CPU 16 42 #define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ 43 44 /* Memory mapped register offsets */ 45 #define LEVEL_OFFSET 0x00 46 #define PENDING_OFFSET 0x04 47 #define FORCE0_OFFSET 0x08 48 #define CLEAR_OFFSET 0x0C 49 #define MP_STATUS_OFFSET 0x10 50 #define BROADCAST_OFFSET 0x14 51 #define MASK_OFFSET 0x40 52 #define FORCE_OFFSET 0x80 53 #define EXTENDED_OFFSET 0xC0 54 55 #define MAX_PILS 16 56 57 OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP) 58 59 typedef struct IRQMPState IRQMPState; 60 61 struct IRQMP { 62 SysBusDevice parent_obj; 63 64 MemoryRegion iomem; 65 66 IRQMPState *state; 67 qemu_irq irq; 68 }; 69 70 struct IRQMPState { 71 uint32_t level; 72 uint32_t pending; 73 uint32_t clear; 74 uint32_t broadcast; 75 76 uint32_t mask[IRQMP_MAX_CPU]; 77 uint32_t force[IRQMP_MAX_CPU]; 78 uint32_t extended[IRQMP_MAX_CPU]; 79 80 IRQMP *parent; 81 }; 82 83 static void grlib_irqmp_check_irqs(IRQMPState *state) 84 { 85 uint32_t pend = 0; 86 uint32_t level0 = 0; 87 uint32_t level1 = 0; 88 89 assert(state != NULL); 90 assert(state->parent != NULL); 91 92 /* IRQ for CPU 0 (no SMP support) */ 93 pend = (state->pending | state->force[0]) 94 & state->mask[0]; 95 96 level0 = pend & ~state->level; 97 level1 = pend & state->level; 98 99 trace_grlib_irqmp_check_irqs(state->pending, state->force[0], 100 state->mask[0], level1, level0); 101 102 /* Trigger level1 interrupt first and level0 if there is no level1 */ 103 qemu_set_irq(state->parent->irq, level1 ?: level0); 104 } 105 106 static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask) 107 { 108 /* Clear registers */ 109 state->pending &= ~mask; 110 state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */ 111 112 grlib_irqmp_check_irqs(state); 113 } 114 115 void grlib_irqmp_ack(DeviceState *dev, int intno) 116 { 117 IRQMP *irqmp = GRLIB_IRQMP(dev); 118 IRQMPState *state; 119 uint32_t mask; 120 121 state = irqmp->state; 122 assert(state != NULL); 123 124 intno &= 15; 125 mask = 1 << intno; 126 127 trace_grlib_irqmp_ack(intno); 128 129 grlib_irqmp_ack_mask(state, mask); 130 } 131 132 static void grlib_irqmp_set_irq(void *opaque, int irq, int level) 133 { 134 IRQMP *irqmp = GRLIB_IRQMP(opaque); 135 IRQMPState *s; 136 int i = 0; 137 138 s = irqmp->state; 139 assert(s != NULL); 140 assert(s->parent != NULL); 141 142 143 if (level) { 144 trace_grlib_irqmp_set_irq(irq); 145 146 if (s->broadcast & 1 << irq) { 147 /* Broadcasted IRQ */ 148 for (i = 0; i < IRQMP_MAX_CPU; i++) { 149 s->force[i] |= 1 << irq; 150 } 151 } else { 152 s->pending |= 1 << irq; 153 } 154 grlib_irqmp_check_irqs(s); 155 156 } 157 } 158 159 static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr, 160 unsigned size) 161 { 162 IRQMP *irqmp = opaque; 163 IRQMPState *state; 164 165 assert(irqmp != NULL); 166 state = irqmp->state; 167 assert(state != NULL); 168 169 addr &= 0xff; 170 171 /* global registers */ 172 switch (addr) { 173 case LEVEL_OFFSET: 174 return state->level; 175 176 case PENDING_OFFSET: 177 return state->pending; 178 179 case FORCE0_OFFSET: 180 /* This register is an "alias" for the force register of CPU 0 */ 181 return state->force[0]; 182 183 case CLEAR_OFFSET: 184 case MP_STATUS_OFFSET: 185 /* Always read as 0 */ 186 return 0; 187 188 case BROADCAST_OFFSET: 189 return state->broadcast; 190 191 default: 192 break; 193 } 194 195 /* mask registers */ 196 if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { 197 int cpu = (addr - MASK_OFFSET) / 4; 198 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 199 200 return state->mask[cpu]; 201 } 202 203 /* force registers */ 204 if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { 205 int cpu = (addr - FORCE_OFFSET) / 4; 206 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 207 208 return state->force[cpu]; 209 } 210 211 /* extended (not supported) */ 212 if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { 213 int cpu = (addr - EXTENDED_OFFSET) / 4; 214 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 215 216 return state->extended[cpu]; 217 } 218 219 trace_grlib_irqmp_readl_unknown(addr); 220 return 0; 221 } 222 223 static void grlib_irqmp_write(void *opaque, hwaddr addr, 224 uint64_t value, unsigned size) 225 { 226 IRQMP *irqmp = opaque; 227 IRQMPState *state; 228 229 assert(irqmp != NULL); 230 state = irqmp->state; 231 assert(state != NULL); 232 233 addr &= 0xff; 234 235 /* global registers */ 236 switch (addr) { 237 case LEVEL_OFFSET: 238 value &= 0xFFFF << 1; /* clean up the value */ 239 state->level = value; 240 return; 241 242 case PENDING_OFFSET: 243 /* Read Only */ 244 return; 245 246 case FORCE0_OFFSET: 247 /* This register is an "alias" for the force register of CPU 0 */ 248 249 value &= 0xFFFE; /* clean up the value */ 250 state->force[0] = value; 251 grlib_irqmp_check_irqs(irqmp->state); 252 return; 253 254 case CLEAR_OFFSET: 255 value &= ~1; /* clean up the value */ 256 grlib_irqmp_ack_mask(state, value); 257 return; 258 259 case MP_STATUS_OFFSET: 260 /* Read Only (no SMP support) */ 261 return; 262 263 case BROADCAST_OFFSET: 264 value &= 0xFFFE; /* clean up the value */ 265 state->broadcast = value; 266 return; 267 268 default: 269 break; 270 } 271 272 /* mask registers */ 273 if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { 274 int cpu = (addr - MASK_OFFSET) / 4; 275 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 276 277 value &= ~1; /* clean up the value */ 278 state->mask[cpu] = value; 279 grlib_irqmp_check_irqs(irqmp->state); 280 return; 281 } 282 283 /* force registers */ 284 if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { 285 int cpu = (addr - FORCE_OFFSET) / 4; 286 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 287 288 uint32_t force = value & 0xFFFE; 289 uint32_t clear = (value >> 16) & 0xFFFE; 290 uint32_t old = state->force[cpu]; 291 292 state->force[cpu] = (old | force) & ~clear; 293 grlib_irqmp_check_irqs(irqmp->state); 294 return; 295 } 296 297 /* extended (not supported) */ 298 if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { 299 int cpu = (addr - EXTENDED_OFFSET) / 4; 300 assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); 301 302 value &= 0xF; /* clean up the value */ 303 state->extended[cpu] = value; 304 return; 305 } 306 307 trace_grlib_irqmp_writel_unknown(addr, value); 308 } 309 310 static const MemoryRegionOps grlib_irqmp_ops = { 311 .read = grlib_irqmp_read, 312 .write = grlib_irqmp_write, 313 .endianness = DEVICE_NATIVE_ENDIAN, 314 .valid = { 315 .min_access_size = 4, 316 .max_access_size = 4, 317 }, 318 }; 319 320 static void grlib_irqmp_reset(DeviceState *d) 321 { 322 IRQMP *irqmp = GRLIB_IRQMP(d); 323 assert(irqmp->state != NULL); 324 325 memset(irqmp->state, 0, sizeof *irqmp->state); 326 irqmp->state->parent = irqmp; 327 } 328 329 static void grlib_irqmp_init(Object *obj) 330 { 331 IRQMP *irqmp = GRLIB_IRQMP(obj); 332 SysBusDevice *dev = SYS_BUS_DEVICE(obj); 333 334 qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS); 335 qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1); 336 memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp, 337 "irqmp", IRQMP_REG_SIZE); 338 339 irqmp->state = g_malloc0(sizeof *irqmp->state); 340 341 sysbus_init_mmio(dev, &irqmp->iomem); 342 } 343 344 static void grlib_irqmp_class_init(ObjectClass *klass, void *data) 345 { 346 DeviceClass *dc = DEVICE_CLASS(klass); 347 348 dc->reset = grlib_irqmp_reset; 349 } 350 351 static const TypeInfo grlib_irqmp_info = { 352 .name = TYPE_GRLIB_IRQMP, 353 .parent = TYPE_SYS_BUS_DEVICE, 354 .instance_size = sizeof(IRQMP), 355 .instance_init = grlib_irqmp_init, 356 .class_init = grlib_irqmp_class_init, 357 }; 358 359 static void grlib_irqmp_register_types(void) 360 { 361 type_register_static(&grlib_irqmp_info); 362 } 363 364 type_init(grlib_irqmp_register_types) 365