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