|
|
1.1 ! root 1: /* ! 2: * ARM AMBA Generic/Distributed Interrupt Controller ! 3: * ! 4: * Copyright (c) 2006 CodeSourcery. ! 5: * Written by Paul Brook ! 6: * ! 7: * This code is licenced under the GPL. ! 8: */ ! 9: ! 10: /* TODO: Some variants of this controller can handle multiple CPUs. ! 11: Currently only single CPU operation is implemented. */ ! 12: ! 13: #include "vl.h" ! 14: #include "arm_pic.h" ! 15: ! 16: //#define DEBUG_GIC ! 17: ! 18: #ifdef DEBUG_GIC ! 19: #define DPRINTF(fmt, args...) \ ! 20: do { printf("arm_gic: " fmt , ##args); } while (0) ! 21: #else ! 22: #define DPRINTF(fmt, args...) do {} while(0) ! 23: #endif ! 24: ! 25: /* Distributed interrupt controller. */ ! 26: ! 27: static const uint8_t gic_id[] = ! 28: { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; ! 29: ! 30: #define GIC_NIRQ 96 ! 31: ! 32: typedef struct gic_irq_state ! 33: { ! 34: unsigned enabled:1; ! 35: unsigned pending:1; ! 36: unsigned active:1; ! 37: unsigned level:1; ! 38: unsigned model:1; /* 0 = 1:N, 1 = N:N */ ! 39: unsigned trigger:1; /* nonzero = edge triggered. */ ! 40: } gic_irq_state; ! 41: ! 42: #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1 ! 43: #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0 ! 44: #define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled ! 45: #define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1 ! 46: #define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0 ! 47: #define GIC_TEST_PENDING(irq) s->irq_state[irq].pending ! 48: #define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1 ! 49: #define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0 ! 50: #define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active ! 51: #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 ! 52: #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 ! 53: #define GIC_TEST_MODEL(irq) s->irq_state[irq].model ! 54: #define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1 ! 55: #define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0 ! 56: #define GIC_TEST_LEVEL(irq) s->irq_state[irq].level ! 57: #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 ! 58: #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 ! 59: #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger ! 60: ! 61: typedef struct gic_state ! 62: { ! 63: arm_pic_handler handler; ! 64: uint32_t base; ! 65: void *parent; ! 66: int parent_irq; ! 67: int enabled; ! 68: int cpu_enabled; ! 69: ! 70: gic_irq_state irq_state[GIC_NIRQ]; ! 71: int irq_target[GIC_NIRQ]; ! 72: int priority[GIC_NIRQ]; ! 73: int last_active[GIC_NIRQ]; ! 74: ! 75: int priority_mask; ! 76: int running_irq; ! 77: int running_priority; ! 78: int current_pending; ! 79: } gic_state; ! 80: ! 81: /* TODO: Many places that call this routine could be optimized. */ ! 82: /* Update interrupt status after enabled or pending bits have been changed. */ ! 83: static void gic_update(gic_state *s) ! 84: { ! 85: int best_irq; ! 86: int best_prio; ! 87: int irq; ! 88: ! 89: s->current_pending = 1023; ! 90: if (!s->enabled || !s->cpu_enabled) { ! 91: pic_set_irq_new(s->parent, s->parent_irq, 0); ! 92: return; ! 93: } ! 94: best_prio = 0x100; ! 95: best_irq = 1023; ! 96: for (irq = 0; irq < 96; irq++) { ! 97: if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) { ! 98: if (s->priority[irq] < best_prio) { ! 99: best_prio = s->priority[irq]; ! 100: best_irq = irq; ! 101: } ! 102: } ! 103: } ! 104: if (best_prio > s->priority_mask) { ! 105: pic_set_irq_new(s->parent, s->parent_irq, 0); ! 106: } else { ! 107: s->current_pending = best_irq; ! 108: if (best_prio < s->running_priority) { ! 109: DPRINTF("Raised pending IRQ %d\n", best_irq); ! 110: pic_set_irq_new(s->parent, s->parent_irq, 1); ! 111: } ! 112: } ! 113: } ! 114: ! 115: static void gic_set_irq(void *opaque, int irq, int level) ! 116: { ! 117: gic_state *s = (gic_state *)opaque; ! 118: /* The first external input line is internal interrupt 32. */ ! 119: irq += 32; ! 120: if (level == GIC_TEST_LEVEL(irq)) ! 121: return; ! 122: ! 123: if (level) { ! 124: GIC_SET_LEVEL(irq); ! 125: if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) { ! 126: DPRINTF("Set %d pending\n", irq); ! 127: GIC_SET_PENDING(irq); ! 128: } ! 129: } else { ! 130: GIC_CLEAR_LEVEL(irq); ! 131: } ! 132: gic_update(s); ! 133: } ! 134: ! 135: static void gic_set_running_irq(gic_state *s, int irq) ! 136: { ! 137: s->running_irq = irq; ! 138: if (irq == 1023) ! 139: s->running_priority = 0x100; ! 140: else ! 141: s->running_priority = s->priority[irq]; ! 142: gic_update(s); ! 143: } ! 144: ! 145: static uint32_t gic_acknowledge_irq(gic_state *s) ! 146: { ! 147: int new_irq; ! 148: new_irq = s->current_pending; ! 149: if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) { ! 150: DPRINTF("ACK no pending IRQ\n"); ! 151: return 1023; ! 152: } ! 153: pic_set_irq_new(s->parent, s->parent_irq, 0); ! 154: s->last_active[new_irq] = s->running_irq; ! 155: /* For level triggered interrupts we clear the pending bit while ! 156: the interrupt is active. */ ! 157: GIC_CLEAR_PENDING(new_irq); ! 158: gic_set_running_irq(s, new_irq); ! 159: DPRINTF("ACK %d\n", new_irq); ! 160: return new_irq; ! 161: } ! 162: ! 163: static void gic_complete_irq(gic_state * s, int irq) ! 164: { ! 165: int update = 0; ! 166: DPRINTF("EOI %d\n", irq); ! 167: if (s->running_irq == 1023) ! 168: return; /* No active IRQ. */ ! 169: if (irq != 1023) { ! 170: /* Mark level triggered interrupts as pending if they are still ! 171: raised. */ ! 172: if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq) ! 173: && GIC_TEST_LEVEL(irq)) { ! 174: GIC_SET_PENDING(irq); ! 175: update = 1; ! 176: } ! 177: } ! 178: if (irq != s->running_irq) { ! 179: /* Complete an IRQ that is not currently running. */ ! 180: int tmp = s->running_irq; ! 181: while (s->last_active[tmp] != 1023) { ! 182: if (s->last_active[tmp] == irq) { ! 183: s->last_active[tmp] = s->last_active[irq]; ! 184: break; ! 185: } ! 186: tmp = s->last_active[tmp]; ! 187: } ! 188: if (update) { ! 189: gic_update(s); ! 190: } ! 191: } else { ! 192: /* Complete the current running IRQ. */ ! 193: gic_set_running_irq(s, s->last_active[s->running_irq]); ! 194: } ! 195: } ! 196: ! 197: static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) ! 198: { ! 199: gic_state *s = (gic_state *)opaque; ! 200: uint32_t res; ! 201: int irq; ! 202: int i; ! 203: ! 204: offset -= s->base + 0x1000; ! 205: if (offset < 0x100) { ! 206: if (offset == 0) ! 207: return s->enabled; ! 208: if (offset == 4) ! 209: return (GIC_NIRQ / 32) - 1; ! 210: if (offset < 0x08) ! 211: return 0; ! 212: goto bad_reg; ! 213: } else if (offset < 0x200) { ! 214: /* Interrupt Set/Clear Enable. */ ! 215: if (offset < 0x180) ! 216: irq = (offset - 0x100) * 8; ! 217: else ! 218: irq = (offset - 0x180) * 8; ! 219: if (irq >= GIC_NIRQ) ! 220: goto bad_reg; ! 221: res = 0; ! 222: for (i = 0; i < 8; i++) { ! 223: if (GIC_TEST_ENABLED(irq + i)) { ! 224: res |= (1 << i); ! 225: } ! 226: } ! 227: } else if (offset < 0x300) { ! 228: /* Interrupt Set/Clear Pending. */ ! 229: if (offset < 0x280) ! 230: irq = (offset - 0x200) * 8; ! 231: else ! 232: irq = (offset - 0x280) * 8; ! 233: if (irq >= GIC_NIRQ) ! 234: goto bad_reg; ! 235: res = 0; ! 236: for (i = 0; i < 8; i++) { ! 237: if (GIC_TEST_PENDING(irq + i)) { ! 238: res |= (1 << i); ! 239: } ! 240: } ! 241: } else if (offset < 0x400) { ! 242: /* Interrupt Active. */ ! 243: irq = (offset - 0x300) * 8; ! 244: if (irq >= GIC_NIRQ) ! 245: goto bad_reg; ! 246: res = 0; ! 247: for (i = 0; i < 8; i++) { ! 248: if (GIC_TEST_ACTIVE(irq + i)) { ! 249: res |= (1 << i); ! 250: } ! 251: } ! 252: } else if (offset < 0x800) { ! 253: /* Interrupt Priority. */ ! 254: irq = offset - 0x400; ! 255: if (irq >= GIC_NIRQ) ! 256: goto bad_reg; ! 257: res = s->priority[irq]; ! 258: } else if (offset < 0xc00) { ! 259: /* Interrupt CPU Target. */ ! 260: irq = offset - 0x800; ! 261: if (irq >= GIC_NIRQ) ! 262: goto bad_reg; ! 263: res = s->irq_target[irq]; ! 264: } else if (offset < 0xf00) { ! 265: /* Interrupt Configuration. */ ! 266: irq = (offset - 0xc00) * 2; ! 267: if (irq >= GIC_NIRQ) ! 268: goto bad_reg; ! 269: res = 0; ! 270: for (i = 0; i < 4; i++) { ! 271: if (GIC_TEST_MODEL(irq + i)) ! 272: res |= (1 << (i * 2)); ! 273: if (GIC_TEST_TRIGGER(irq + i)) ! 274: res |= (2 << (i * 2)); ! 275: } ! 276: } else if (offset < 0xfe0) { ! 277: goto bad_reg; ! 278: } else /* offset >= 0xfe0 */ { ! 279: if (offset & 3) { ! 280: res = 0; ! 281: } else { ! 282: res = gic_id[(offset - 0xfe0) >> 2]; ! 283: } ! 284: } ! 285: return res; ! 286: bad_reg: ! 287: cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset); ! 288: return 0; ! 289: } ! 290: ! 291: static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) ! 292: { ! 293: uint32_t val; ! 294: val = gic_dist_readb(opaque, offset); ! 295: val |= gic_dist_readb(opaque, offset + 1) << 8; ! 296: return val; ! 297: } ! 298: ! 299: static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) ! 300: { ! 301: uint32_t val; ! 302: val = gic_dist_readw(opaque, offset); ! 303: val |= gic_dist_readw(opaque, offset + 2) << 16; ! 304: return val; ! 305: } ! 306: ! 307: static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, ! 308: uint32_t value) ! 309: { ! 310: gic_state *s = (gic_state *)opaque; ! 311: int irq; ! 312: int i; ! 313: ! 314: offset -= s->base + 0x1000; ! 315: if (offset < 0x100) { ! 316: if (offset == 0) { ! 317: s->enabled = (value & 1); ! 318: DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); ! 319: } else if (offset < 4) { ! 320: /* ignored. */ ! 321: } else { ! 322: goto bad_reg; ! 323: } ! 324: } else if (offset < 0x180) { ! 325: /* Interrupt Set Enable. */ ! 326: irq = (offset - 0x100) * 8; ! 327: if (irq >= GIC_NIRQ) ! 328: goto bad_reg; ! 329: for (i = 0; i < 8; i++) { ! 330: if (value & (1 << i)) { ! 331: if (!GIC_TEST_ENABLED(irq + i)) ! 332: DPRINTF("Enabled IRQ %d\n", irq + i); ! 333: GIC_SET_ENABLED(irq + i); ! 334: /* If a raised level triggered IRQ enabled then mark ! 335: is as pending. */ ! 336: if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i)) ! 337: GIC_SET_PENDING(irq + i); ! 338: } ! 339: } ! 340: } else if (offset < 0x200) { ! 341: /* Interrupt Clear Enable. */ ! 342: irq = (offset - 0x180) * 8; ! 343: if (irq >= GIC_NIRQ) ! 344: goto bad_reg; ! 345: for (i = 0; i < 8; i++) { ! 346: if (value & (1 << i)) { ! 347: if (GIC_TEST_ENABLED(irq + i)) ! 348: DPRINTF("Disabled IRQ %d\n", irq + i); ! 349: GIC_CLEAR_ENABLED(irq + i); ! 350: } ! 351: } ! 352: } else if (offset < 0x280) { ! 353: /* Interrupt Set Pending. */ ! 354: irq = (offset - 0x200) * 8; ! 355: if (irq >= GIC_NIRQ) ! 356: goto bad_reg; ! 357: for (i = 0; i < 8; i++) { ! 358: if (value & (1 << i)) { ! 359: GIC_SET_PENDING(irq + i); ! 360: } ! 361: } ! 362: } else if (offset < 0x300) { ! 363: /* Interrupt Clear Pending. */ ! 364: irq = (offset - 0x280) * 8; ! 365: if (irq >= GIC_NIRQ) ! 366: goto bad_reg; ! 367: for (i = 0; i < 8; i++) { ! 368: if (value & (1 << i)) { ! 369: GIC_CLEAR_PENDING(irq + i); ! 370: } ! 371: } ! 372: } else if (offset < 0x400) { ! 373: /* Interrupt Active. */ ! 374: goto bad_reg; ! 375: } else if (offset < 0x800) { ! 376: /* Interrupt Priority. */ ! 377: irq = offset - 0x400; ! 378: if (irq >= GIC_NIRQ) ! 379: goto bad_reg; ! 380: s->priority[irq] = value; ! 381: } else if (offset < 0xc00) { ! 382: /* Interrupt CPU Target. */ ! 383: irq = offset - 0x800; ! 384: if (irq >= GIC_NIRQ) ! 385: goto bad_reg; ! 386: s->irq_target[irq] = value; ! 387: } else if (offset < 0xf00) { ! 388: /* Interrupt Configuration. */ ! 389: irq = (offset - 0xc00) * 4; ! 390: if (irq >= GIC_NIRQ) ! 391: goto bad_reg; ! 392: for (i = 0; i < 4; i++) { ! 393: if (value & (1 << (i * 2))) { ! 394: GIC_SET_MODEL(irq + i); ! 395: } else { ! 396: GIC_CLEAR_MODEL(irq + i); ! 397: } ! 398: if (value & (2 << (i * 2))) { ! 399: GIC_SET_TRIGGER(irq + i); ! 400: } else { ! 401: GIC_CLEAR_TRIGGER(irq + i); ! 402: } ! 403: } ! 404: } else { ! 405: /* 0xf00 is only handled for word writes. */ ! 406: goto bad_reg; ! 407: } ! 408: gic_update(s); ! 409: return; ! 410: bad_reg: ! 411: cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset); ! 412: } ! 413: ! 414: static void gic_dist_writew(void *opaque, target_phys_addr_t offset, ! 415: uint32_t value) ! 416: { ! 417: gic_state *s = (gic_state *)opaque; ! 418: if (offset - s->base == 0xf00) { ! 419: GIC_SET_PENDING(value & 0x3ff); ! 420: gic_update(s); ! 421: return; ! 422: } ! 423: gic_dist_writeb(opaque, offset, value & 0xff); ! 424: gic_dist_writeb(opaque, offset + 1, value >> 8); ! 425: } ! 426: ! 427: static void gic_dist_writel(void *opaque, target_phys_addr_t offset, ! 428: uint32_t value) ! 429: { ! 430: gic_dist_writew(opaque, offset, value & 0xffff); ! 431: gic_dist_writew(opaque, offset + 2, value >> 16); ! 432: } ! 433: ! 434: static CPUReadMemoryFunc *gic_dist_readfn[] = { ! 435: gic_dist_readb, ! 436: gic_dist_readw, ! 437: gic_dist_readl ! 438: }; ! 439: ! 440: static CPUWriteMemoryFunc *gic_dist_writefn[] = { ! 441: gic_dist_writeb, ! 442: gic_dist_writew, ! 443: gic_dist_writel ! 444: }; ! 445: ! 446: static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset) ! 447: { ! 448: gic_state *s = (gic_state *)opaque; ! 449: offset -= s->base; ! 450: switch (offset) { ! 451: case 0x00: /* Control */ ! 452: return s->cpu_enabled; ! 453: case 0x04: /* Priority mask */ ! 454: return s->priority_mask; ! 455: case 0x08: /* Binary Point */ ! 456: /* ??? Not implemented. */ ! 457: return 0; ! 458: case 0x0c: /* Acknowledge */ ! 459: return gic_acknowledge_irq(s); ! 460: case 0x14: /* Runing Priority */ ! 461: return s->running_priority; ! 462: case 0x18: /* Highest Pending Interrupt */ ! 463: return s->current_pending; ! 464: default: ! 465: cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset); ! 466: return 0; ! 467: } ! 468: } ! 469: ! 470: static void gic_cpu_write(void *opaque, target_phys_addr_t offset, ! 471: uint32_t value) ! 472: { ! 473: gic_state *s = (gic_state *)opaque; ! 474: offset -= s->base; ! 475: switch (offset) { ! 476: case 0x00: /* Control */ ! 477: s->cpu_enabled = (value & 1); ! 478: DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis"); ! 479: break; ! 480: case 0x04: /* Priority mask */ ! 481: s->priority_mask = (value & 0x3ff); ! 482: break; ! 483: case 0x08: /* Binary Point */ ! 484: /* ??? Not implemented. */ ! 485: break; ! 486: case 0x10: /* End Of Interrupt */ ! 487: return gic_complete_irq(s, value & 0x3ff); ! 488: default: ! 489: cpu_abort (cpu_single_env, "gic_cpu_writeb: Bad offset %x\n", offset); ! 490: return; ! 491: } ! 492: gic_update(s); ! 493: } ! 494: ! 495: static CPUReadMemoryFunc *gic_cpu_readfn[] = { ! 496: gic_cpu_read, ! 497: gic_cpu_read, ! 498: gic_cpu_read ! 499: }; ! 500: ! 501: static CPUWriteMemoryFunc *gic_cpu_writefn[] = { ! 502: gic_cpu_write, ! 503: gic_cpu_write, ! 504: gic_cpu_write ! 505: }; ! 506: ! 507: static void gic_reset(gic_state *s) ! 508: { ! 509: int i; ! 510: memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state)); ! 511: s->priority_mask = 0xf0; ! 512: s->current_pending = 1023; ! 513: s->running_irq = 1023; ! 514: s->running_priority = 0x100; ! 515: for (i = 0; i < 15; i++) { ! 516: GIC_SET_ENABLED(i); ! 517: GIC_SET_TRIGGER(i); ! 518: } ! 519: s->enabled = 0; ! 520: s->cpu_enabled = 0; ! 521: } ! 522: ! 523: void *arm_gic_init(uint32_t base, void *parent, int parent_irq) ! 524: { ! 525: gic_state *s; ! 526: int iomemtype; ! 527: ! 528: s = (gic_state *)qemu_mallocz(sizeof(gic_state)); ! 529: if (!s) ! 530: return NULL; ! 531: s->handler = gic_set_irq; ! 532: s->parent = parent; ! 533: s->parent_irq = parent_irq; ! 534: if (base != 0xffffffff) { ! 535: iomemtype = cpu_register_io_memory(0, gic_cpu_readfn, ! 536: gic_cpu_writefn, s); ! 537: cpu_register_physical_memory(base, 0x00000fff, iomemtype); ! 538: iomemtype = cpu_register_io_memory(0, gic_dist_readfn, ! 539: gic_dist_writefn, s); ! 540: cpu_register_physical_memory(base + 0x1000, 0x00000fff, iomemtype); ! 541: s->base = base; ! 542: } else { ! 543: s->base = 0; ! 544: } ! 545: gic_reset(s); ! 546: return s; ! 547: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.