1.1new file mode 100644
1.2--- /dev/null
1.3+++ b/sys/src/9/bcm64/gic.c
1.4@@ -0,0 +1,295 @@
1.5+#include "u.h"
1.6+#include "../port/lib.h"
1.7+#include "mem.h"
1.8+#include "dat.h"
1.9+#include "fns.h"
1.10+#include "io.h"
1.11+#include "ureg.h"
1.12+#include "sysreg.h"
1.13+#include "../port/error.h"
1.14+
1.15+enum {
1.16+ GICD_CTLR = 0x000/4, /* RW, Distributor Control Register */
1.17+ GICD_TYPER = 0x004/4, /* RO, Interrupt Controller Type */
1.18+ GICD_IIDR = 0x008/4, /* RO, Distributor Implementer Identification Register */
1.19+
1.20+ GICD_IGROUPR0 = 0x080/4, /* RW, Interrupt Group Registers (0x80-0xBC) */
1.21+
1.22+ GICD_ISENABLER0 = 0x100/4, /* RW, Interrupt Set-Enable Registers (0x100-0x13C) */
1.23+ GICD_ICENABLER0 = 0x180/4, /* RW, Interrupt Clear-Enable Registers (0x180-0x1BC) */
1.24+
1.25+ GICD_ISPENDR0 = 0x200/4, /* RW, Interrupt Set-Pending Registers (0x200-0x23C) */
1.26+ GICD_ICPENDR0 = 0x280/4, /* RW, Interrupt Clear-Pending Registers (0x280-0x2BC) */
1.27+
1.28+ GICD_ISACTIVER0 = 0x300/4, /* RW, Interrupt Set-Active Registers (0x300-0x33C) */
1.29+ GICD_ICACTIVER0 = 0x380/4, /* RW, Interrupt Clear-Active Registers (0x380-0x3BC) */
1.30+
1.31+ GICD_IPRIORITYR0= 0x400/4, /* RW, Interrupt Priority Registers (0x400-0x5FC) */
1.32+ GICD_TARGETSR0 = 0x800/4, /* RW, Interrupt Target Registers (0x800-0x9FC) */
1.33+ GICD_ICFGR0 = 0xC00/4, /* RW, Interrupt Configuration Registers (0xC00-0xC7C) */
1.34+
1.35+ GICD_ISR0 = 0xD00/4,
1.36+ GICD_PPISR = GICD_ISR0, /* RO, Private Peripheral Interrupt Status Register */
1.37+ GICD_SPISR0 = GICD_ISR0+1, /* RO, Shared Peripheral Interrupt Status Register */
1.38+ GICD_SGIR = 0xF00/4, /* WO, Software Generated Interrupt Register */
1.39+
1.40+ GICD_CPENDSGIR0 = 0xF10/4, /* RW, SGI Clear-Pending Registers (0xF10-0xF1C) */
1.41+ GICD_SPENDSGIR0 = 0xF20/4, /* RW, SGI Set-Pending Registers (0xF20-0xF2C) */
1.42+
1.43+ GICD_PIDR4 = 0xFD0/4, /* RO, Perpheral ID Registers */
1.44+ GICD_PIDR5 = 0xFD4/4,
1.45+ GICD_PIDR6 = 0xFD8/4,
1.46+ GICD_PIDR7 = 0xFDC/4,
1.47+ GICD_PIDR0 = 0xFE0/4,
1.48+ GICD_PIDR1 = 0xFE4/4,
1.49+ GICD_PIDR2 = 0xFE8/4,
1.50+ GICD_PIDR3 = 0xFEC/4,
1.51+
1.52+ GICD_CIDR0 = 0xFF0/4, /* RO, Component ID Registers */
1.53+ GICD_CIDR1 = 0xFF4/4,
1.54+ GICD_CIDR2 = 0xFF8/4,
1.55+ GICD_CIDR3 = 0xFFC/4,
1.56+
1.57+ GICC_CTLR = 0x000/4, /* RW, CPU Interace Control Register */
1.58+ GICC_PMR = 0x004/4, /* RW, Interrupt Priority Mask Register */
1.59+ GICC_BPR = 0x008/4, /* RW, Binary Point Register */
1.60+ GICC_IAR = 0x00C/4, /* RO, Interrupt Acknowledge Register */
1.61+ GICC_EOIR = 0x010/4, /* WO, End of Interrupt Register */
1.62+ GICC_RPR = 0x014/4, /* RO, Running Priority Register */
1.63+ GICC_HPPIR = 0x018/4, /* RO, Highest Priority Pending Interrupt Register */
1.64+ GICC_ABPR = 0x01C/4, /* RW, Aliased Binary Point Register */
1.65+ GICC_AIAR = 0x020/4, /* RO, Aliased Interrupt Acknowledge Register */
1.66+ GICC_AEOIR = 0x024/4, /* WO, Aliased End of Interrupt Register */
1.67+ GICC_AHPPIR = 0x028/4, /* RO, Aliased Highest Priority Pending Interrupt Register */
1.68+ GICC_APR0 = 0x0D0/4, /* RW, Active Priority Register */
1.69+ GICC_NSAPR0 = 0x0E0/4, /* RW, Non-Secure Active Priority Register */
1.70+ GICC_IIDR = 0x0FC/4, /* RO, CPU Interface Identification Register */
1.71+ GICC_DIR = 0x1000/4, /* WO, Deactivate Interrupt Register */
1.72+
1.73+ GICH_HCR = 0x000/4, /* RW, Hypervisor Control Register */
1.74+ GICH_VTR = 0x004/4, /* RO, VGIC Type Register */
1.75+ GICH_VMCR = 0x008/4, /* RW, Virtual Machine Control Register */
1.76+ GICH_MISR = 0x010/4, /* RO, Maintenance Interrupt Status Register */
1.77+ GICH_EISR0 = 0x020/4, /* RO, End of Interrupt Status Register */
1.78+ GICH_ELSR0 = 0x030/4, /* RO, Empty List Register Status Register */
1.79+ GICH_APR0 = 0x0F0/4, /* RW, Active Priority Register */
1.80+ GICH_LR0 = 0x100/4, /* RW, List Registers (0x100-0x10C) */
1.81+
1.82+ GICV_CTLR = 0x000/4, /* RW, Virtual Machine Control Register */
1.83+ GICV_PMR = 0x004/4, /* RW, VM Priority Mask Register */
1.84+ GICV_BPR = 0x008/4, /* RW, VM Binary Point Register */
1.85+ GICV_IAR = 0x00C/4, /* RO, VM Interrupt Acknowledge Register */
1.86+ GICV_EOIR = 0x010/4, /* WO, VM End of Interrupt Register */
1.87+ GICV_RPR = 0x014/4, /* RO, VM Running Priority Register */
1.88+ GICV_HPPIR = 0x018/4, /* RO, VM Highest Piority Pending Interrupt Register */
1.89+ GICV_ABPR = 0x01C/4, /* RW, VM Aliased Binary Point Register */
1.90+ GICV_AIAR = 0x020/4, /* RO, VM Aliased Interrupt Acknowledge Register */
1.91+ GICV_AEOIR = 0x024/4, /* WO, VM Aliased End of Interrupt Register */
1.92+ GICV_AHPPIR = 0x028/4, /* RO, VM Aliaed Highest Piority Pending Interrupt Register */
1.93+ GICV_APR0 = 0x0D0/4, /* RW, VM Active Priority Register */
1.94+ GICV_IIDR = 0x0FC/4, /* RO, VM CPU Interface Identification Register */
1.95+ GICV_DIR = 0x1000/4, /* WO, VM Deactivate Interrupt Register */
1.96+};
1.97+
1.98+typedef struct Vctl Vctl;
1.99+struct Vctl {
1.100+ Vctl *next;
1.101+ void (*f)(Ureg*, void*);
1.102+ void *a;
1.103+ int irq;
1.104+ u32int intid;
1.105+};
1.106+
1.107+static Lock vctllock;
1.108+static Vctl *vctl[MAXMACH][32], *vfiq;
1.109+static u32int *cregs, *dregs;
1.110+
1.111+void
1.112+intrcpushutdown(void)
1.113+{
1.114+ if(cregs == nil || dregs == nil){
1.115+ uintptr va, pa;
1.116+
1.117+ pa = sysrd(CBAR_EL1);
1.118+ va = ARMLOCAL + (pa - soc.armlocal);
1.119+ dregs = (u32int*)(va + 0x1000);
1.120+ cregs = (u32int*)(va + 0x2000);
1.121+ }
1.122+
1.123+ /* disable cpu interface */
1.124+ cregs[GICC_CTLR] &= ~1;
1.125+ coherence();
1.126+}
1.127+
1.128+void
1.129+intrsoff(void)
1.130+{
1.131+ int i, n;
1.132+
1.133+ intrcpushutdown();
1.134+
1.135+ /* disable distributor */
1.136+ dregs[GICD_CTLR] &= ~1;
1.137+ coherence();
1.138+
1.139+ /* clear all interrupts */
1.140+ n = ((dregs[GICD_TYPER] & 0x1F)+1) << 5;
1.141+ for(i = 0; i < n; i += 32){
1.142+ dregs[GICD_ISENABLER0 + (i/32)] = -1;
1.143+ coherence();
1.144+ dregs[GICD_ICENABLER0 + (i/32)] = -1;
1.145+ coherence();
1.146+ }
1.147+ for(i = 0; i < n; i += 4){
1.148+ dregs[GICD_IPRIORITYR0 + (i/4)] = 0;
1.149+ dregs[GICD_TARGETSR0 + (i/4)] = 0;
1.150+ }
1.151+ for(i = 32; i < n; i += 16)
1.152+ dregs[GICD_ICFGR0 + (i/16)] = 0;
1.153+ coherence();
1.154+}
1.155+
1.156+/*
1.157+ * called by trap to handle irq interrupts.
1.158+ * returns true iff a clock interrupt, thus maybe reschedule.
1.159+ */
1.160+int
1.161+irq(Ureg* ureg)
1.162+{
1.163+ Vctl *v;
1.164+ int clockintr;
1.165+ u32int intid;
1.166+
1.167+ m->intr++;
1.168+ intid = cregs[GICC_IAR] & 0xFFFFFF;
1.169+ if((intid & ~3) == 1020)
1.170+ return 0; // spurious
1.171+ clockintr = 0;
1.172+ for(v = vctl[m->machno][intid%32]; v != nil; v = v->next)
1.173+ if(v->intid == intid){
1.174+ coherence();
1.175+ v->f(ureg, v->a);
1.176+ coherence();
1.177+ if(v->irq == IRQclock || v->irq == IRQcntps || v->irq == IRQcntpns)
1.178+ clockintr = 1;
1.179+ }
1.180+ coherence();
1.181+ cregs[GICC_EOIR] = intid;
1.182+ return clockintr;
1.183+}
1.184+
1.185+/*
1.186+ * called direct from lexception.s to handle fiq interrupt.
1.187+ */
1.188+void
1.189+fiq(Ureg *ureg)
1.190+{
1.191+ Vctl *v;
1.192+ u32int intid;
1.193+
1.194+ m->intr++;
1.195+ intid = cregs[GICC_IAR] & 0xFFFFFF;
1.196+ if((intid & ~3) == 1020)
1.197+ return; // spurious
1.198+ v = vfiq;
1.199+ if(v != nil && v->intid == intid && m->machno == 0){
1.200+ coherence();
1.201+ v->f(ureg, v->a);
1.202+ coherence();
1.203+ }
1.204+ cregs[GICC_EOIR] = intid;
1.205+}
1.206+
1.207+void
1.208+intrenable(int irq, void (*f)(Ureg*, void*), void *a, int tbdf, char*)
1.209+{
1.210+ Vctl *v;
1.211+ u32int intid;
1.212+ int cpu, prio;
1.213+
1.214+ if(BUSTYPE(tbdf) == BusPCI){
1.215+ pciintrenable(tbdf, f, a);
1.216+ return;
1.217+ }
1.218+ if(tbdf != BUSUNKNOWN)
1.219+ return;
1.220+
1.221+ cpu = 0;
1.222+ prio = 0x80;
1.223+ intid = irq;
1.224+ switch(irq){
1.225+ case IRQcntps:
1.226+ intid = 16 + 13;
1.227+ break;
1.228+ case IRQcntpns:
1.229+ intid = 16 + 14;
1.230+ break;
1.231+
1.232+ case IRQmbox0:
1.233+ case IRQmbox1:
1.234+ case IRQmbox2:
1.235+ case IRQmbox3:
1.236+ case IRQlocaltmr:
1.237+ print("irqenable: missing documentation for local irq %d\n", irq);
1.238+ return;
1.239+
1.240+ default:
1.241+ if(irq < IRQgic){
1.242+ if(irq < 64)
1.243+ intid += IRQgic-64;
1.244+ else if(irq >= IRQbasic)
1.245+ intid += IRQgic-64-32-8-IRQbasic;
1.246+ }
1.247+ }
1.248+ if(intid < 32)
1.249+ cpu = m->machno;
1.250+
1.251+ if((v = xalloc(sizeof(Vctl))) == nil)
1.252+ panic("irqenable: no mem");
1.253+ v->irq = irq;
1.254+ v->intid = intid;
1.255+ v->f = f;
1.256+ v->a = a;
1.257+
1.258+ lock(&vctllock);
1.259+ if(irq == IRQfiq){
1.260+ vfiq = v;
1.261+ prio = 0;
1.262+ }else{
1.263+ v->next = vctl[cpu][intid%32];
1.264+ vctl[cpu][intid%32] = v;
1.265+ }
1.266+
1.267+ /* enable cpu interface */
1.268+ cregs[GICC_PMR] = 0xFF;
1.269+ coherence();
1.270+
1.271+ cregs[GICC_CTLR] |= 1;
1.272+ coherence();
1.273+
1.274+ cregs[GICC_EOIR] = intid;
1.275+
1.276+ /* enable distributor */
1.277+ dregs[GICD_CTLR] |= 1;
1.278+ coherence();
1.279+
1.280+ /* setup */
1.281+ dregs[GICD_IPRIORITYR0 + (intid/4)] |= prio << ((intid%4) << 3);
1.282+ dregs[GICD_TARGETSR0 + (intid/4)] |= (1<<cpu) << ((intid%4) << 3);
1.283+ coherence();
1.284+
1.285+ /* turn on */
1.286+ dregs[GICD_ISENABLER0 + (intid/32)] = 1 << (intid%32);
1.287+ coherence();
1.288+
1.289+ unlock(&vctllock);
1.290+}
1.291+
1.292+void
1.293+intrdisable(int, void (*f)(Ureg*, void*), void *a, int tbdf, char*)
1.294+{
1.295+ if(BUSTYPE(tbdf) == BusPCI){
1.296+ pciintrdisable(tbdf, f, a);
1.297+ return;
1.298+ }
1.299+}