imx_avic.c (10911B)
1/* 2 * i.MX31 Vectored Interrupt Controller 3 * 4 * Note this is NOT the PL192 provided by ARM, but 5 * a custom implementation by Freescale. 6 * 7 * Copyright (c) 2008 OKL 8 * Copyright (c) 2011 NICTA Pty Ltd 9 * Originally written by Hans Jiang 10 * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> 11 * 12 * This code is licensed under the GPL version 2 or later. See 13 * the COPYING file in the top-level directory. 14 * 15 * TODO: implement vectors. 16 */ 17 18#include "qemu/osdep.h" 19#include "hw/intc/imx_avic.h" 20#include "hw/irq.h" 21#include "migration/vmstate.h" 22#include "qemu/log.h" 23#include "qemu/module.h" 24 25#ifndef DEBUG_IMX_AVIC 26#define DEBUG_IMX_AVIC 0 27#endif 28 29#define DPRINTF(fmt, args...) \ 30 do { \ 31 if (DEBUG_IMX_AVIC) { \ 32 fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \ 33 __func__, ##args); \ 34 } \ 35 } while (0) 36 37static const VMStateDescription vmstate_imx_avic = { 38 .name = TYPE_IMX_AVIC, 39 .version_id = 1, 40 .minimum_version_id = 1, 41 .fields = (VMStateField[]) { 42 VMSTATE_UINT64(pending, IMXAVICState), 43 VMSTATE_UINT64(enabled, IMXAVICState), 44 VMSTATE_UINT64(is_fiq, IMXAVICState), 45 VMSTATE_UINT32(intcntl, IMXAVICState), 46 VMSTATE_UINT32(intmask, IMXAVICState), 47 VMSTATE_UINT32_ARRAY(prio, IMXAVICState, PRIO_WORDS), 48 VMSTATE_END_OF_LIST() 49 }, 50}; 51 52static inline int imx_avic_prio(IMXAVICState *s, int irq) 53{ 54 uint32_t word = irq / PRIO_PER_WORD; 55 uint32_t part = 4 * (irq % PRIO_PER_WORD); 56 return 0xf & (s->prio[word] >> part); 57} 58 59/* Update interrupts. */ 60static void imx_avic_update(IMXAVICState *s) 61{ 62 int i; 63 uint64_t new = s->pending & s->enabled; 64 uint64_t flags; 65 66 flags = new & s->is_fiq; 67 qemu_set_irq(s->fiq, !!flags); 68 69 flags = new & ~s->is_fiq; 70 if (!flags || (s->intmask == 0x1f)) { 71 qemu_set_irq(s->irq, !!flags); 72 return; 73 } 74 75 /* 76 * Take interrupt if there's a pending interrupt with 77 * priority higher than the value of intmask 78 */ 79 for (i = 0; i < IMX_AVIC_NUM_IRQS; i++) { 80 if (flags & (1UL << i)) { 81 if (imx_avic_prio(s, i) > s->intmask) { 82 qemu_set_irq(s->irq, 1); 83 return; 84 } 85 } 86 } 87 qemu_set_irq(s->irq, 0); 88} 89 90static void imx_avic_set_irq(void *opaque, int irq, int level) 91{ 92 IMXAVICState *s = (IMXAVICState *)opaque; 93 94 if (level) { 95 DPRINTF("Raising IRQ %d, prio %d\n", 96 irq, imx_avic_prio(s, irq)); 97 s->pending |= (1ULL << irq); 98 } else { 99 DPRINTF("Clearing IRQ %d, prio %d\n", 100 irq, imx_avic_prio(s, irq)); 101 s->pending &= ~(1ULL << irq); 102 } 103 104 imx_avic_update(s); 105} 106 107 108static uint64_t imx_avic_read(void *opaque, 109 hwaddr offset, unsigned size) 110{ 111 IMXAVICState *s = (IMXAVICState *)opaque; 112 113 DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset); 114 115 switch (offset >> 2) { 116 case 0: /* INTCNTL */ 117 return s->intcntl; 118 119 case 1: /* Normal Interrupt Mask Register, NIMASK */ 120 return s->intmask; 121 122 case 2: /* Interrupt Enable Number Register, INTENNUM */ 123 case 3: /* Interrupt Disable Number Register, INTDISNUM */ 124 return 0; 125 126 case 4: /* Interrupt Enabled Number Register High */ 127 return s->enabled >> 32; 128 129 case 5: /* Interrupt Enabled Number Register Low */ 130 return s->enabled & 0xffffffffULL; 131 132 case 6: /* Interrupt Type Register High */ 133 return s->is_fiq >> 32; 134 135 case 7: /* Interrupt Type Register Low */ 136 return s->is_fiq & 0xffffffffULL; 137 138 case 8: /* Normal Interrupt Priority Register 7 */ 139 case 9: /* Normal Interrupt Priority Register 6 */ 140 case 10:/* Normal Interrupt Priority Register 5 */ 141 case 11:/* Normal Interrupt Priority Register 4 */ 142 case 12:/* Normal Interrupt Priority Register 3 */ 143 case 13:/* Normal Interrupt Priority Register 2 */ 144 case 14:/* Normal Interrupt Priority Register 1 */ 145 case 15:/* Normal Interrupt Priority Register 0 */ 146 return s->prio[15-(offset>>2)]; 147 148 case 16: /* Normal interrupt vector and status register */ 149 { 150 /* 151 * This returns the highest priority 152 * outstanding interrupt. Where there is more than 153 * one pending IRQ with the same priority, 154 * take the highest numbered one. 155 */ 156 uint64_t flags = s->pending & s->enabled & ~s->is_fiq; 157 int i; 158 int prio = -1; 159 int irq = -1; 160 for (i = 63; i >= 0; --i) { 161 if (flags & (1ULL<<i)) { 162 int irq_prio = imx_avic_prio(s, i); 163 if (irq_prio > prio) { 164 irq = i; 165 prio = irq_prio; 166 } 167 } 168 } 169 if (irq >= 0) { 170 imx_avic_set_irq(s, irq, 0); 171 return irq << 16 | prio; 172 } 173 return 0xffffffffULL; 174 } 175 case 17:/* Fast Interrupt vector and status register */ 176 { 177 uint64_t flags = s->pending & s->enabled & s->is_fiq; 178 int i = ctz64(flags); 179 if (i < 64) { 180 imx_avic_set_irq(opaque, i, 0); 181 return i; 182 } 183 return 0xffffffffULL; 184 } 185 case 18:/* Interrupt source register high */ 186 return s->pending >> 32; 187 188 case 19:/* Interrupt source register low */ 189 return s->pending & 0xffffffffULL; 190 191 case 20:/* Interrupt Force Register high */ 192 case 21:/* Interrupt Force Register low */ 193 return 0; 194 195 case 22:/* Normal Interrupt Pending Register High */ 196 return (s->pending & s->enabled & ~s->is_fiq) >> 32; 197 198 case 23:/* Normal Interrupt Pending Register Low */ 199 return (s->pending & s->enabled & ~s->is_fiq) & 0xffffffffULL; 200 201 case 24: /* Fast Interrupt Pending Register High */ 202 return (s->pending & s->enabled & s->is_fiq) >> 32; 203 204 case 25: /* Fast Interrupt Pending Register Low */ 205 return (s->pending & s->enabled & s->is_fiq) & 0xffffffffULL; 206 207 case 0x40: /* AVIC vector 0, use for WFI WAR */ 208 return 0x4; 209 210 default: 211 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 212 HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); 213 return 0; 214 } 215} 216 217static void imx_avic_write(void *opaque, hwaddr offset, 218 uint64_t val, unsigned size) 219{ 220 IMXAVICState *s = (IMXAVICState *)opaque; 221 222 /* Vector Registers not yet supported */ 223 if (offset >= 0x100 && offset <= 0x2fc) { 224 qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n", 225 TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2)); 226 return; 227 } 228 229 DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val); 230 231 switch (offset >> 2) { 232 case 0: /* Interrupt Control Register, INTCNTL */ 233 s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM); 234 if (s->intcntl & ABFEN) { 235 s->intcntl &= ~(val & ABFLAG); 236 } 237 break; 238 239 case 1: /* Normal Interrupt Mask Register, NIMASK */ 240 s->intmask = val & 0x1f; 241 break; 242 243 case 2: /* Interrupt Enable Number Register, INTENNUM */ 244 DPRINTF("enable(%d)\n", (int)val); 245 val &= 0x3f; 246 s->enabled |= (1ULL << val); 247 break; 248 249 case 3: /* Interrupt Disable Number Register, INTDISNUM */ 250 DPRINTF("disable(%d)\n", (int)val); 251 val &= 0x3f; 252 s->enabled &= ~(1ULL << val); 253 break; 254 255 case 4: /* Interrupt Enable Number Register High */ 256 s->enabled = (s->enabled & 0xffffffffULL) | (val << 32); 257 break; 258 259 case 5: /* Interrupt Enable Number Register Low */ 260 s->enabled = (s->enabled & 0xffffffff00000000ULL) | val; 261 break; 262 263 case 6: /* Interrupt Type Register High */ 264 s->is_fiq = (s->is_fiq & 0xffffffffULL) | (val << 32); 265 break; 266 267 case 7: /* Interrupt Type Register Low */ 268 s->is_fiq = (s->is_fiq & 0xffffffff00000000ULL) | val; 269 break; 270 271 case 8: /* Normal Interrupt Priority Register 7 */ 272 case 9: /* Normal Interrupt Priority Register 6 */ 273 case 10:/* Normal Interrupt Priority Register 5 */ 274 case 11:/* Normal Interrupt Priority Register 4 */ 275 case 12:/* Normal Interrupt Priority Register 3 */ 276 case 13:/* Normal Interrupt Priority Register 2 */ 277 case 14:/* Normal Interrupt Priority Register 1 */ 278 case 15:/* Normal Interrupt Priority Register 0 */ 279 s->prio[15-(offset>>2)] = val; 280 break; 281 282 /* Read-only registers, writes ignored */ 283 case 16:/* Normal Interrupt Vector and Status register */ 284 case 17:/* Fast Interrupt vector and status register */ 285 case 18:/* Interrupt source register high */ 286 case 19:/* Interrupt source register low */ 287 return; 288 289 case 20:/* Interrupt Force Register high */ 290 s->pending = (s->pending & 0xffffffffULL) | (val << 32); 291 break; 292 293 case 21:/* Interrupt Force Register low */ 294 s->pending = (s->pending & 0xffffffff00000000ULL) | val; 295 break; 296 297 case 22:/* Normal Interrupt Pending Register High */ 298 case 23:/* Normal Interrupt Pending Register Low */ 299 case 24: /* Fast Interrupt Pending Register High */ 300 case 25: /* Fast Interrupt Pending Register Low */ 301 return; 302 303 default: 304 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 305 HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset); 306 } 307 imx_avic_update(s); 308} 309 310static const MemoryRegionOps imx_avic_ops = { 311 .read = imx_avic_read, 312 .write = imx_avic_write, 313 .endianness = DEVICE_NATIVE_ENDIAN, 314}; 315 316static void imx_avic_reset(DeviceState *dev) 317{ 318 IMXAVICState *s = IMX_AVIC(dev); 319 320 s->pending = 0; 321 s->enabled = 0; 322 s->is_fiq = 0; 323 s->intmask = 0x1f; 324 s->intcntl = 0; 325 memset(s->prio, 0, sizeof s->prio); 326} 327 328static void imx_avic_init(Object *obj) 329{ 330 DeviceState *dev = DEVICE(obj); 331 IMXAVICState *s = IMX_AVIC(obj); 332 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 333 334 memory_region_init_io(&s->iomem, obj, &imx_avic_ops, s, 335 TYPE_IMX_AVIC, 0x1000); 336 sysbus_init_mmio(sbd, &s->iomem); 337 338 qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS); 339 sysbus_init_irq(sbd, &s->irq); 340 sysbus_init_irq(sbd, &s->fiq); 341} 342 343 344static void imx_avic_class_init(ObjectClass *klass, void *data) 345{ 346 DeviceClass *dc = DEVICE_CLASS(klass); 347 348 dc->vmsd = &vmstate_imx_avic; 349 dc->reset = imx_avic_reset; 350 dc->desc = "i.MX Advanced Vector Interrupt Controller"; 351} 352 353static const TypeInfo imx_avic_info = { 354 .name = TYPE_IMX_AVIC, 355 .parent = TYPE_SYS_BUS_DEVICE, 356 .instance_size = sizeof(IMXAVICState), 357 .instance_init = imx_avic_init, 358 .class_init = imx_avic_class_init, 359}; 360 361static void imx_avic_register_types(void) 362{ 363 type_register_static(&imx_avic_info); 364} 365 366type_init(imx_avic_register_types)