/** * \file * * Implementation of Ark Interrupt Controller (AIC) controller. * */ /*---------------------------------------------------------------------------- * Headers *----------------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "chip.h" #include #include #include #define GICD_DIST_BASE (REGS_GIC_BASE+0x1000) #define GICC_CPU_BASE (REGS_GIC_BASE+0x2000) //#define GICD_CTLR *((volatile unsigned int *)(GICD_DIST_BASE+ 0x00)) //#define GICD_TYPER *((volatile unsigned int *)(GICD_DIST_BASE+ 0x04)) //#define GICD_IIDR *((volatile unsigned int *)(GICD_DIST_BASE+ 0x08)) //#define GICD_IGROUPR *((volatile unsigned int *)(GICD_DIST_BASE+ 0x80)) #define GICD_ISENABLER0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x100)) #define GICD_ISENABLER1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x104)) #define GICD_ISENABLER2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x108)) //#define GICD_ISENABLER3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x10C)) #define GICD_ICENABLER0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x180)) #define GICD_ICENABLER1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x184)) #define GICD_ICENABLER2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x188)) //#define GICD_ICENABLER3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x18c)) #define GICD_ISPENDR0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x200)) #define GICD_ISPENDR1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x204)) #define GICD_ISPENDR2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x208)) //#define GICD_ISPENDR3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x20C)) #define GICD_ICPENDR0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x280)) #define GICD_ICPENDR1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x284)) #define GICD_ICPENDR2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x288)) //#define GICD_ICPENDR3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x28C)) #define GICD_ISACTIVER0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x300)) #define GICD_ISACTIVER1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x304)) #define GICD_ISACTIVER2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x308)) //#define GICD_ISACTIVER3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x30C)) #define GICD_ICACTIVER0 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x380)) #define GICD_ICACTIVER1 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x384)) #define GICD_ICACTIVER2 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x388)) //#define GICD_ICACTIVER3 *((volatile unsigned int *)(GICD_DIST_BASE+ 0x38C)) #define GIC_CPU_CTRL 0x00 #define GIC_CPU_PRIMASK 0x04 #define GIC_CPU_BINPOINT 0x08 #define GIC_CPU_INTACK 0x0c #define GIC_CPU_EOI 0x10 #define GIC_CPU_RUNNINGPRI 0x14 #define GIC_CPU_HIGHPRI 0x18 #define GIC_CPU_ALIAS_BINPOINT 0x1c #define GIC_CPU_ACTIVEPRIO 0xd0 #define GIC_CPU_IDENT 0xfc #define GICC_ENABLE 0x1 #define GICC_INT_PRI_THRESHOLD 0xf0 #define GICC_IAR_INT_ID_MASK 0x3ff #define GICC_INT_SPURIOUS 1023 #define GICC_DIS_BYPASS_MASK 0x1e0 #define GIC_DIST_CTRL 0x000 #define GIC_DIST_TYPER 0x004 #define GIC_DIST_IGROUP 0x080 #define GIC_DIST_ENABLE_SET 0x100 #define GIC_DIST_ENABLE_CLEAR 0x180 #define GIC_DIST_PENDING_SET 0x200 #define GIC_DIST_PENDING_CLEAR 0x280 #define GIC_DIST_ACTIVE_SET 0x300 #define GIC_DIST_ACTIVE_CLEAR 0x380 #define GIC_DIST_PRI 0x400 #define GIC_DIST_TARGET 0x800 #define GIC_DIST_CONFIG 0xc00 #define GIC_DIST_SOFTINT 0xf00 #define GIC_DIST_SGI_PENDING_CLEAR 0xf10 #define GIC_DIST_SGI_PENDING_SET 0xf20 #define GICD_ENABLE 0x1 #define GICD_DISABLE 0x0 #define GICD_INT_ACTLOW_LVLTRIG 0x0 #define GICD_INT_EN_CLR_X32 0xffffffff #define GICD_INT_EN_SET_SGI 0x0000ffff #define GICD_INT_EN_CLR_PPI 0xffff0000 #define GICD_INT_DEF_PRI 0xa0 #define GICD_INT_DEF_PRI_X4 ((GICD_INT_DEF_PRI << 24) |\ (GICD_INT_DEF_PRI << 16) |\ (GICD_INT_DEF_PRI << 8) |\ GICD_INT_DEF_PRI) #define GICH_HCR 0x0 #define GICH_VTR 0x4 #define GICH_VMCR 0x8 #define GICH_MISR 0x10 #define GICH_EISR0 0x20 #define GICH_EISR1 0x24 #define GICH_ELRSR0 0x30 #define GICH_ELRSR1 0x34 #define GICH_APR 0xf0 #define GICH_LR0 0x100 #define GICH_HCR_EN (1 << 0) #define GICH_HCR_UIE (1 << 1) #define GICH_LR_VIRTUALID (0x3ff << 0) #define GICH_LR_PHYSID_CPUID_SHIFT (10) #define GICH_LR_PHYSID_CPUID (7 << GICH_LR_PHYSID_CPUID_SHIFT) #define GICH_LR_STATE (3 << 28) #define GICH_LR_PENDING_BIT (1 << 28) #define GICH_LR_ACTIVE_BIT (1 << 29) #define GICH_LR_EOI (1 << 19) #define GICH_VMCR_CTRL_SHIFT 0 #define GICH_VMCR_CTRL_MASK (0x21f << GICH_VMCR_CTRL_SHIFT) #define GICH_VMCR_PRIMASK_SHIFT 27 #define GICH_VMCR_PRIMASK_MASK (0x1f << GICH_VMCR_PRIMASK_SHIFT) #define GICH_VMCR_BINPOINT_SHIFT 21 #define GICH_VMCR_BINPOINT_MASK (0x7 << GICH_VMCR_BINPOINT_SHIFT) #define GICH_VMCR_ALIAS_BINPOINT_SHIFT 18 #define GICH_VMCR_ALIAS_BINPOINT_MASK (0x7 << GICH_VMCR_ALIAS_BINPOINT_SHIFT) #define IRQ_TYPE_LEVEL_HIGH (0x4) #define IRQ_TYPE_LEVEL_LOW (0x8) #define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) #define IRQ_TYPE_LEVEL_MASK (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH) #define ICSET 0x00 #define ICPEND 0x04 #define ICMODE 0x08 #define ICMASK 0x0C #define ICLEVEL 0x10 #define IRQISPR 0x3C #define IRQISPC 0x40 #define IVEC_ADDR 0x78 #define NR_GIC_CPU_IF 1 #define MAX_IRQ_NUM 160 typedef struct { ISRFunction_t handler; void *handler_param; }IrqDesc_t; static int gic_set_type(unsigned int irq, unsigned int type); static IrqDesc_t irq_descs[MAX_IRQ_NUM]; static unsigned char gic_cpu_map[NR_GIC_CPU_IF]; static volatile uint8_t interrupt_nest = 0; void gicd_Interrupt_disable(unsigned int irq) { if(irq<16) return; if(irq<31) { GICD_ICENABLER0|=(1<= 32) { portENTER_CRITICAL(); gic_set_pri(irq_source,priority); if((type == IRQ_TYPE_LEVEL_HIGH )|| (type ==IRQ_TYPE_EDGE_RISING) ) gic_set_type(irq_source,type); gicd_Interrupt_enable(irq_source); irq_descs[irq_source].handler = func; irq_descs[irq_source].handler_param = param; portEXIT_CRITICAL(); } return 0; } int32_t free_irq(uint32_t irq_source) { if (irq_source > MAX_IRQ_NUM ) { return -1; } portENTER_CRITICAL(); irq_descs[irq_source].handler = NULL; irq_descs[irq_source].handler_param = NULL; gicd_Active_disable(irq_source); portEXIT_CRITICAL(); return 0; } void GIC_IrqHandler(void) { unsigned int irqstat, irqnr; interrupt_nest++; do { irqstat = readl(GICC_CPU_BASE+ GIC_CPU_INTACK); irqnr = irqstat & GICC_IAR_INT_ID_MASK; if (irqnr > 15 && irqnr < 1021) { if (irq_descs[irqnr].handler) irq_descs[irqnr].handler(irq_descs[irqnr].handler_param); writel(irqstat, GICC_CPU_BASE+ GIC_CPU_EOI); gicd_Active_disable(irqnr); continue; } if (irqnr < 16) { writel(irqstat, GICC_CPU_BASE+ GIC_CPU_EOI); continue; } break; } while (0); interrupt_nest--; } int gic_configure_irq(unsigned int irq, unsigned int type) { unsigned int enablemask = 1 << (irq % 32); unsigned int enableoff = (irq / 32) * 4; unsigned int confmask = 0x2 << ((irq % 16) * 2); unsigned int confoff = (irq / 16) * 4; unsigned char enabled = 0; unsigned int val, oldval; int ret = 0; /* * Read current configuration register, and insert the config * for "irq", depending on "type". */ val = oldval = readl(GICD_DIST_BASE+ GIC_DIST_CONFIG + confoff); if (type & IRQ_TYPE_LEVEL_MASK) val &= ~confmask; else if (type & IRQ_TYPE_EDGE_BOTH) val |= confmask; /* * As recommended by the spec, disable the interrupt before changing * the configuration */ if (readl(GICD_DIST_BASE+ GIC_DIST_ENABLE_SET + enableoff) & enablemask) { writel(enablemask, GICD_DIST_BASE+ GIC_DIST_ENABLE_CLEAR + enableoff); enabled = 1; } /* * Write back the new configuration, and possibly re-enable * the interrupt. If we tried to write a new configuration and failed, * return an error. */ writel(val, GICD_DIST_BASE+ GIC_DIST_CONFIG + confoff); if (readl(GICD_DIST_BASE+ GIC_DIST_CONFIG + confoff) != val && val != oldval) ret = -1; if (enabled) writel(enablemask, GICD_DIST_BASE+ GIC_DIST_ENABLE_SET + enableoff); return ret; } #if 0 static void gic_mask_irq(unsigned int irq) { unsigned int mask = 1 << (irq % 32); //unsigned long flags; writel(mask, GICD_DIST_BASE+ GIC_DIST_ENABLE_CLEAR + (irq / 32) * 4); } static void gic_unmask_irq(unsigned int irq) { unsigned int mask = 1 << (irq % 32); //unsigned long flags; writel(mask, GICD_DIST_BASE+ GIC_DIST_ENABLE_SET + (irq / 32) * 4); } static void gic_eoi_irq(unsigned int irq ) { writel(irq, GICC_CPU_BASE+ GIC_CPU_EOI); } #endif static int gic_set_type(unsigned int irq, unsigned int type) { unsigned int gicirq = irq; // unsigned long flags; int ret; /* Interrupt configuration for SGIs can't be changed */ if (gicirq < 16) return -1; /* SPIs have restrictions on the supported types */ if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) return -1; // if (gic_arch_extn.irq_set_type) // gic_arch_extn.irq_set_type(d, type); ret = gic_configure_irq(gicirq, type); return ret; } void gic_dist_config(int gic_irqs) { unsigned int i; /* * Set all global interrupts to be level triggered, active low. */ for (i = 32; i < gic_irqs; i += 16) writel(GICD_INT_ACTLOW_LVLTRIG, GICD_DIST_BASE+ GIC_DIST_CONFIG + i / 4); /* * Set priority on all global interrupts. */ for (i = 32; i < gic_irqs; i += 4) writel(GICD_INT_DEF_PRI_X4, GICD_DIST_BASE+ GIC_DIST_PRI + i); /* * Disable all interrupts. Leave the PPI and SGIs alone * as they are enabled by redistributor registers. */ for (i = 32; i < gic_irqs; i += 32) writel(GICD_INT_EN_CLR_X32, GICD_DIST_BASE+ GIC_DIST_ENABLE_CLEAR + i / 8); } static unsigned char gic_get_cpumask(void) { unsigned int mask, i; for (i = mask = 0; i < 32; i += 4) { mask = readl(GICD_DIST_BASE+ GIC_DIST_TARGET + i); mask |= mask >> 16; mask |= mask >> 8; if (mask) break; } // if (!mask) // printf("GIC CPU mask not found - kernel will fail to boot.\n"); return mask; } static void gic_dist_init(void) { unsigned int i; unsigned int cpumask; unsigned int gic_irqs = 96; writel(GICD_DISABLE, GICD_DIST_BASE+ GIC_DIST_CTRL); /* * Set all global interrupts to this CPU only. */ cpumask = gic_get_cpumask(); cpumask |= cpumask << 8; cpumask |= cpumask << 16; for (i = 32; i < gic_irqs; i += 4) writel(cpumask, GICD_DIST_BASE+ GIC_DIST_TARGET + i * 4 / 4); gic_dist_config(gic_irqs); writel(GICD_ENABLE, GICD_DIST_BASE+ GIC_DIST_CTRL); } void gic_cpu_config(void) { int i; /* * Deal with the banked PPI and SGI interrupts - disable all * PPI interrupts, ensure all SGI interrupts are enabled. */ writel(GICD_INT_EN_CLR_PPI, GICD_DIST_BASE+ GIC_DIST_ENABLE_CLEAR); writel(GICD_INT_EN_SET_SGI, GICD_DIST_BASE+ GIC_DIST_ENABLE_SET); /* * Set priority on PPI and SGI interrupts */ for (i = 0; i < 32; i += 4) writel(GICD_INT_DEF_PRI_X4, GICD_DIST_BASE+ GIC_DIST_PRI + i * 4 / 4); } static void gic_cpu_if_up(void) { unsigned int bypass = 0; /* * Preserve bypass disable bits to be written back later */ bypass = readl(GICC_CPU_BASE+ GIC_CPU_CTRL); bypass &= GICC_DIS_BYPASS_MASK; writel(bypass | GICC_ENABLE, GICC_CPU_BASE+ GIC_CPU_CTRL); } void gic_cpu_if_down(void) { unsigned int val = 0; val = readl(GICC_CPU_BASE+ GIC_CPU_CTRL); val &= ~GICC_ENABLE; writel(val, GICC_CPU_BASE+ GIC_CPU_CTRL); } static void gic_cpu_init(void) { unsigned int cpu_mask, cpu = 0; int i; /* * Get what the GIC says our CPU mask is. */ // if(cpu >= NR_GIC_CPU_IF); // return; cpu_mask = gic_get_cpumask(); gic_cpu_map[cpu] = cpu_mask; /* * Clear our mask from the other map entries in case they're * still undefined. */ for (i = 0; i < NR_GIC_CPU_IF; i++) if (i != cpu) gic_cpu_map[i] &= ~cpu_mask; gic_cpu_config(); writel(GICC_INT_PRI_THRESHOLD, GICC_CPU_BASE+ GIC_CPU_PRIMASK); gic_cpu_if_up(); } void GIC_Initialize(void) { int gic_irqs, i; /* * Initialize the CPU interface map to all CPUs. * It will be refined as each CPU probes its ID. */ for (i = 0; i < NR_GIC_CPU_IF; i++) gic_cpu_map[i] = 0xff; /* * Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources. */ gic_irqs = readl(GICD_DIST_BASE+ GIC_DIST_TYPER) & 0x1f; gic_irqs = (gic_irqs + 1) * 32; if (gic_irqs > 1020) gic_irqs = 1020; for(i=0;i