ref: f1a32c955360268a7bc901de057e6e451c979853
dir: /sys/src/9/bcm64/gic.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/pci.h" #include "ureg.h" #include "sysreg.h" #include "../port/error.h" enum { GICD_CTLR = 0x000/4, /* RW, Distributor Control Register */ GICD_TYPER = 0x004/4, /* RO, Interrupt Controller Type */ GICD_IIDR = 0x008/4, /* RO, Distributor Implementer Identification Register */ GICD_IGROUPR0 = 0x080/4, /* RW, Interrupt Group Registers (0x80-0xBC) */ GICD_ISENABLER0 = 0x100/4, /* RW, Interrupt Set-Enable Registers (0x100-0x13C) */ GICD_ICENABLER0 = 0x180/4, /* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */ GICD_ISPENDR0 = 0x200/4, /* RW, Interrupt Set-Pending Registers (0x200-0x23C) */ GICD_ICPENDR0 = 0x280/4, /* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */ GICD_ISACTIVER0 = 0x300/4, /* RW, Interrupt Set-Active Registers (0x300-0x33C) */ GICD_ICACTIVER0 = 0x380/4, /* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */ GICD_IPRIORITYR0= 0x400/4, /* RW, Interrupt Priority Registers (0x400-0x5FC) */ GICD_TARGETSR0 = 0x800/4, /* RW, Interrupt Target Registers (0x800-0x9FC) */ GICD_ICFGR0 = 0xC00/4, /* RW, Interrupt Configuration Registers (0xC00-0xC7C) */ GICD_ISR0 = 0xD00/4, GICD_PPISR = GICD_ISR0, /* RO, Private Peripheral Interrupt Status Register */ GICD_SPISR0 = GICD_ISR0+1, /* RO, Shared Peripheral Interrupt Status Register */ GICD_SGIR = 0xF00/4, /* WO, Software Generated Interrupt Register */ GICD_CPENDSGIR0 = 0xF10/4, /* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */ GICD_SPENDSGIR0 = 0xF20/4, /* RW, SGI Set-Pending Registers (0xF20-0xF2C) */ GICD_PIDR4 = 0xFD0/4, /* RO, Perpheral ID Registers */ GICD_PIDR5 = 0xFD4/4, GICD_PIDR6 = 0xFD8/4, GICD_PIDR7 = 0xFDC/4, GICD_PIDR0 = 0xFE0/4, GICD_PIDR1 = 0xFE4/4, GICD_PIDR2 = 0xFE8/4, GICD_PIDR3 = 0xFEC/4, GICD_CIDR0 = 0xFF0/4, /* RO, Component ID Registers */ GICD_CIDR1 = 0xFF4/4, GICD_CIDR2 = 0xFF8/4, GICD_CIDR3 = 0xFFC/4, GICC_CTLR = 0x000/4, /* RW, CPU Interace Control Register */ GICC_PMR = 0x004/4, /* RW, Interrupt Priority Mask Register */ GICC_BPR = 0x008/4, /* RW, Binary Point Register */ GICC_IAR = 0x00C/4, /* RO, Interrupt Acknowledge Register */ GICC_EOIR = 0x010/4, /* WO, End of Interrupt Register */ GICC_RPR = 0x014/4, /* RO, Running Priority Register */ GICC_HPPIR = 0x018/4, /* RO, Highest Priority Pending Interrupt Register */ GICC_ABPR = 0x01C/4, /* RW, Aliased Binary Point Register */ GICC_AIAR = 0x020/4, /* RO, Aliased Interrupt Acknowledge Register */ GICC_AEOIR = 0x024/4, /* WO, Aliased End of Interrupt Register */ GICC_AHPPIR = 0x028/4, /* RO, Aliased Highest Priority Pending Interrupt Register */ GICC_APR0 = 0x0D0/4, /* RW, Active Priority Register */ GICC_NSAPR0 = 0x0E0/4, /* RW, Non-Secure Active Priority Register */ GICC_IIDR = 0x0FC/4, /* RO, CPU Interface Identification Register */ GICC_DIR = 0x1000/4, /* WO, Deactivate Interrupt Register */ GICH_HCR = 0x000/4, /* RW, Hypervisor Control Register */ GICH_VTR = 0x004/4, /* RO, VGIC Type Register */ GICH_VMCR = 0x008/4, /* RW, Virtual Machine Control Register */ GICH_MISR = 0x010/4, /* RO, Maintenance Interrupt Status Register */ GICH_EISR0 = 0x020/4, /* RO, End of Interrupt Status Register */ GICH_ELSR0 = 0x030/4, /* RO, Empty List Register Status Register */ GICH_APR0 = 0x0F0/4, /* RW, Active Priority Register */ GICH_LR0 = 0x100/4, /* RW, List Registers (0x100-0x10C) */ GICV_CTLR = 0x000/4, /* RW, Virtual Machine Control Register */ GICV_PMR = 0x004/4, /* RW, VM Priority Mask Register */ GICV_BPR = 0x008/4, /* RW, VM Binary Point Register */ GICV_IAR = 0x00C/4, /* RO, VM Interrupt Acknowledge Register */ GICV_EOIR = 0x010/4, /* WO, VM End of Interrupt Register */ GICV_RPR = 0x014/4, /* RO, VM Running Priority Register */ GICV_HPPIR = 0x018/4, /* RO, VM Highest Piority Pending Interrupt Register */ GICV_ABPR = 0x01C/4, /* RW, VM Aliased Binary Point Register */ GICV_AIAR = 0x020/4, /* RO, VM Aliased Interrupt Acknowledge Register */ GICV_AEOIR = 0x024/4, /* WO, VM Aliased End of Interrupt Register */ GICV_AHPPIR = 0x028/4, /* RO, VM Aliaed Highest Piority Pending Interrupt Register */ GICV_APR0 = 0x0D0/4, /* RW, VM Active Priority Register */ GICV_IIDR = 0x0FC/4, /* RO, VM CPU Interface Identification Register */ GICV_DIR = 0x1000/4, /* WO, VM Deactivate Interrupt Register */ }; typedef struct Vctl Vctl; struct Vctl { Vctl *next; void (*f)(Ureg*, void*); void *a; int irq; u32int intid; }; static Lock vctllock; static Vctl *vctl[MAXMACH][32], *vfiq; static u32int *cregs, *dregs; void intrcpushutdown(void) { if(cregs == nil || dregs == nil){ uintptr va, pa; pa = sysrd(CBAR_EL1); va = ARMLOCAL + (pa - soc.armlocal); dregs = (u32int*)(va + 0x1000); cregs = (u32int*)(va + 0x2000); } /* disable cpu interface */ cregs[GICC_CTLR] &= ~1; coherence(); } void intrsoff(void) { int i, n; intrcpushutdown(); /* disable distributor */ dregs[GICD_CTLR] &= ~1; coherence(); /* clear all interrupts */ n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5; for(i = 0; i < n; i += 32){ dregs[GICD_ISENABLER0 + (i/32)] = -1; coherence(); dregs[GICD_ICENABLER0 + (i/32)] = -1; coherence(); } for(i = 0; i < n; i += 4){ dregs[GICD_IPRIORITYR0 + (i/4)] = 0; dregs[GICD_TARGETSR0 + (i/4)] = 0; } for(i = 32; i < n; i += 16) dregs[GICD_ICFGR0 + (i/16)] = 0; coherence(); } /* * called by trap to handle irq interrupts. * returns true iff a clock interrupt, thus maybe reschedule. */ int irq(Ureg* ureg) { Vctl *v; int clockintr; u32int intid; m->intr++; intid = cregs[GICC_IAR] & 0xFFFFFF; if((intid & ~3) == 1020) return 0; // spurious clockintr = 0; for(v = vctl[m->machno][intid%32]; v != nil; v = v->next) if(v->intid == intid){ coherence(); v->f(ureg, v->a); coherence(); if(v->irq == IRQclock || v->irq == IRQcntps || v->irq == IRQcntpns) clockintr = 1; } coherence(); cregs[GICC_EOIR] = intid; return clockintr; } /* * called direct from lexception.s to handle fiq interrupt. */ void fiq(Ureg *ureg) { Vctl *v; u32int intid; m->intr++; intid = cregs[GICC_IAR] & 0xFFFFFF; if((intid & ~3) == 1020) return; // spurious v = vfiq; if(v != nil && v->intid == intid && m->machno == 0){ coherence(); v->f(ureg, v->a); coherence(); } cregs[GICC_EOIR] = intid; } void intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char*) { Vctl *v; u32int intid; int cpu, prio; if(BUSTYPE(tbdf) == BusPCI){ pciintrenable(tbdf, f, a); return; } if(tbdf != BUSUNKNOWN) return; cpu = 0; prio = 0x80; intid = irq; switch(irq){ case IRQcntps: intid = 16 + 13; break; case IRQcntpns: intid = 16 + 14; break; case IRQmbox0: case IRQmbox1: case IRQmbox2: case IRQmbox3: case IRQlocaltmr: print("irqenable: missing documentation for local irq %d\n", irq); return; default: if(irq < IRQgic){ if(irq < 64) intid += IRQgic-64; else if(irq >= IRQbasic) intid += IRQgic-64-32-8-IRQbasic; } } if(intid < 32) cpu = m->machno; if((v = xalloc(sizeof(Vctl))) == nil) panic("irqenable: no mem"); v->irq = irq; v->intid = intid; v->f = f; v->a = a; lock(&vctllock); if(irq == IRQfiq){ vfiq = v; prio = 0; }else{ v->next = vctl[cpu][intid%32]; vctl[cpu][intid%32] = v; } /* enable cpu interface */ cregs[GICC_PMR] = 0xFF; coherence(); cregs[GICC_CTLR] |= 1; coherence(); cregs[GICC_EOIR] = intid; /* enable distributor */ dregs[GICD_CTLR] |= 1; coherence(); /* setup */ dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3); dregs[GICD_TARGETSR0 + (intid/4)] |= (1<<cpu) << ((intid%4) << 3); coherence(); /* turn on */ dregs[GICD_ISENABLER0 + (intid/32)] = 1 << (intid%32); coherence(); unlock(&vctllock); } void intrdisable(int, void (*f)(Ureg*, void*), void *a, int tbdf, char*) { if(BUSTYPE(tbdf) == BusPCI){ pciintrdisable(tbdf, f, a); return; } }