[ARM] Add support for ARM GIC

Add support for the ARM Generic Interrupt Controller.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Russell King and committed by
Russell King
f27ecacc 099d44e8

+211
+3
arch/arm/common/Kconfig
··· 1 config ICST525 2 bool 3 4 config ICST307 5 bool 6
··· 1 config ICST525 2 bool 3 4 + config ARM_GIC 5 + bool 6 + 7 config ICST307 8 bool 9
+1
arch/arm/common/Makefile
··· 4 5 obj-y += rtctime.o 6 obj-$(CONFIG_ARM_AMBA) += amba.o 7 obj-$(CONFIG_ICST525) += icst525.o 8 obj-$(CONFIG_ICST307) += icst307.o 9 obj-$(CONFIG_SA1111) += sa1111.o
··· 4 5 obj-y += rtctime.o 6 obj-$(CONFIG_ARM_AMBA) += amba.o 7 + obj-$(CONFIG_ARM_GIC) += gic.o 8 obj-$(CONFIG_ICST525) += icst525.o 9 obj-$(CONFIG_ICST307) += icst307.o 10 obj-$(CONFIG_SA1111) += sa1111.o
+166
arch/arm/common/gic.c
···
··· 1 + /* 2 + * linux/arch/arm/common/gic.c 3 + * 4 + * Copyright (C) 2002 ARM Limited, All Rights Reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + * Interrupt architecture for the GIC: 11 + * 12 + * o There is one Interrupt Distributor, which receives interrupts 13 + * from system devices and sends them to the Interrupt Controllers. 14 + * 15 + * o There is one CPU Interface per CPU, which sends interrupts sent 16 + * by the Distributor, and interrupts generated locally, to the 17 + * associated CPU. 18 + * 19 + * Note that IRQs 0-31 are special - they are local to each CPU. 20 + * As such, the enable set/clear, pending set/clear and active bit 21 + * registers are banked per-cpu for these sources. 22 + */ 23 + #include <linux/init.h> 24 + #include <linux/kernel.h> 25 + #include <linux/list.h> 26 + #include <linux/smp.h> 27 + 28 + #include <asm/irq.h> 29 + #include <asm/io.h> 30 + #include <asm/mach/irq.h> 31 + #include <asm/hardware/gic.h> 32 + 33 + static void __iomem *gic_dist_base; 34 + static void __iomem *gic_cpu_base; 35 + 36 + /* 37 + * Routines to acknowledge, disable and enable interrupts 38 + * 39 + * Linux assumes that when we're done with an interrupt we need to 40 + * unmask it, in the same way we need to unmask an interrupt when 41 + * we first enable it. 42 + * 43 + * The GIC has a seperate notion of "end of interrupt" to re-enable 44 + * an interrupt after handling, in order to support hardware 45 + * prioritisation. 46 + * 47 + * We can make the GIC behave in the way that Linux expects by making 48 + * our "acknowledge" routine disable the interrupt, then mark it as 49 + * complete. 50 + */ 51 + static void gic_ack_irq(unsigned int irq) 52 + { 53 + u32 mask = 1 << (irq % 32); 54 + writel(mask, gic_dist_base + GIC_DIST_ENABLE_CLEAR + (irq / 32) * 4); 55 + writel(irq, gic_cpu_base + GIC_CPU_EOI); 56 + } 57 + 58 + static void gic_mask_irq(unsigned int irq) 59 + { 60 + u32 mask = 1 << (irq % 32); 61 + writel(mask, gic_dist_base + GIC_DIST_ENABLE_CLEAR + (irq / 32) * 4); 62 + } 63 + 64 + static void gic_unmask_irq(unsigned int irq) 65 + { 66 + u32 mask = 1 << (irq % 32); 67 + writel(mask, gic_dist_base + GIC_DIST_ENABLE_SET + (irq / 32) * 4); 68 + } 69 + 70 + static void gic_set_cpu(struct irqdesc *desc, unsigned int irq, unsigned int cpu) 71 + { 72 + void __iomem *reg = gic_dist_base + GIC_DIST_TARGET + (irq & ~3); 73 + unsigned int shift = (irq % 4) * 8; 74 + u32 val; 75 + 76 + val = readl(reg) & ~(0xff << shift); 77 + val |= 1 << (cpu + shift); 78 + writel(val, reg); 79 + } 80 + 81 + static struct irqchip gic_chip = { 82 + .ack = gic_ack_irq, 83 + .mask = gic_mask_irq, 84 + .unmask = gic_unmask_irq, 85 + #ifdef CONFIG_SMP 86 + .set_cpu = gic_set_cpu, 87 + #endif 88 + }; 89 + 90 + void __init gic_dist_init(void __iomem *base) 91 + { 92 + unsigned int max_irq, i; 93 + u32 cpumask = 1 << smp_processor_id(); 94 + 95 + cpumask |= cpumask << 8; 96 + cpumask |= cpumask << 16; 97 + 98 + gic_dist_base = base; 99 + 100 + writel(0, base + GIC_DIST_CTRL); 101 + 102 + /* 103 + * Find out how many interrupts are supported. 104 + */ 105 + max_irq = readl(base + GIC_DIST_CTR) & 0x1f; 106 + max_irq = (max_irq + 1) * 32; 107 + 108 + /* 109 + * The GIC only supports up to 1020 interrupt sources. 110 + * Limit this to either the architected maximum, or the 111 + * platform maximum. 112 + */ 113 + if (max_irq > max(1020, NR_IRQS)) 114 + max_irq = max(1020, NR_IRQS); 115 + 116 + /* 117 + * Set all global interrupts to be level triggered, active low. 118 + */ 119 + for (i = 32; i < max_irq; i += 16) 120 + writel(0, base + GIC_DIST_CONFIG + i * 4 / 16); 121 + 122 + /* 123 + * Set all global interrupts to this CPU only. 124 + */ 125 + for (i = 32; i < max_irq; i += 4) 126 + writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); 127 + 128 + /* 129 + * Set priority on all interrupts. 130 + */ 131 + for (i = 0; i < max_irq; i += 4) 132 + writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4); 133 + 134 + /* 135 + * Disable all interrupts. 136 + */ 137 + for (i = 0; i < max_irq; i += 32) 138 + writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); 139 + 140 + /* 141 + * Setup the Linux IRQ subsystem. 142 + */ 143 + for (i = 29; i < max_irq; i++) { 144 + set_irq_chip(i, &gic_chip); 145 + set_irq_handler(i, do_level_IRQ); 146 + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); 147 + } 148 + 149 + writel(1, base + GIC_DIST_CTRL); 150 + } 151 + 152 + void __cpuinit gic_cpu_init(void __iomem *base) 153 + { 154 + gic_cpu_base = base; 155 + writel(0xf0, base + GIC_CPU_PRIMASK); 156 + writel(1, base + GIC_CPU_CTRL); 157 + } 158 + 159 + #ifdef CONFIG_SMP 160 + void gic_raise_softirq(cpumask_t cpumask, unsigned int irq) 161 + { 162 + unsigned long map = *cpus_addr(cpumask); 163 + 164 + writel(map << 16 | irq, gic_dist_base + GIC_DIST_SOFTINT); 165 + } 166 + #endif
+41
include/asm-arm/hardware/gic.h
···
··· 1 + /* 2 + * linux/include/asm-arm/hardware/gic.h 3 + * 4 + * Copyright (C) 2002 ARM Limited, All Rights Reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + #ifndef __ASM_ARM_HARDWARE_GIC_H 11 + #define __ASM_ARM_HARDWARE_GIC_H 12 + 13 + #include <linux/compiler.h> 14 + 15 + #define GIC_CPU_CTRL 0x00 16 + #define GIC_CPU_PRIMASK 0x04 17 + #define GIC_CPU_BINPOINT 0x08 18 + #define GIC_CPU_INTACK 0x0c 19 + #define GIC_CPU_EOI 0x10 20 + #define GIC_CPU_RUNNINGPRI 0x14 21 + #define GIC_CPU_HIGHPRI 0x18 22 + 23 + #define GIC_DIST_CTRL 0x000 24 + #define GIC_DIST_CTR 0x004 25 + #define GIC_DIST_ENABLE_SET 0x100 26 + #define GIC_DIST_ENABLE_CLEAR 0x180 27 + #define GIC_DIST_PENDING_SET 0x200 28 + #define GIC_DIST_PENDING_CLEAR 0x280 29 + #define GIC_DIST_ACTIVE_BIT 0x300 30 + #define GIC_DIST_PRI 0x400 31 + #define GIC_DIST_TARGET 0x800 32 + #define GIC_DIST_CONFIG 0xc00 33 + #define GIC_DIST_SOFTINT 0xf00 34 + 35 + #ifndef __ASSEMBLY__ 36 + void gic_dist_init(void __iomem *base); 37 + void gic_cpu_init(void __iomem *base); 38 + void gic_raise_softirq(cpumask_t cpumask, unsigned int irq); 39 + #endif 40 + 41 + #endif