···1+/*2+ * linux/arch/arm/common/gic.c3+ *4+ * Copyright (C) 2002 ARM Limited, All Rights Reserved.5+ *6+ * This program is free software; you can redistribute it and/or modify7+ * it under the terms of the GNU General Public License version 2 as8+ * published by the Free Software Foundation.9+ *10+ * Interrupt architecture for the GIC:11+ *12+ * o There is one Interrupt Distributor, which receives interrupts13+ * from system devices and sends them to the Interrupt Controllers.14+ *15+ * o There is one CPU Interface per CPU, which sends interrupts sent16+ * by the Distributor, and interrupts generated locally, to the17+ * 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 bit21+ * 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 interrupts38+ *39+ * Linux assumes that when we're done with an interrupt we need to40+ * unmask it, in the same way we need to unmask an interrupt when41+ * we first enable it.42+ *43+ * The GIC has a seperate notion of "end of interrupt" to re-enable44+ * an interrupt after handling, in order to support hardware45+ * prioritisation.46+ *47+ * We can make the GIC behave in the way that Linux expects by making48+ * our "acknowledge" routine disable the interrupt, then mark it as49+ * 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_SMP86+ .set_cpu = gic_set_cpu,87+#endif88+};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 the111+ * 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_SMP160+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
···00000000000000000000000000000000000000000
···1+/*2+ * linux/include/asm-arm/hardware/gic.h3+ *4+ * Copyright (C) 2002 ARM Limited, All Rights Reserved.5+ *6+ * This program is free software; you can redistribute it and/or modify7+ * it under the terms of the GNU General Public License version 2 as8+ * published by the Free Software Foundation.9+ */10+#ifndef __ASM_ARM_HARDWARE_GIC_H11+#define __ASM_ARM_HARDWARE_GIC_H12+13+#include <linux/compiler.h>14+15+#define GIC_CPU_CTRL 0x0016+#define GIC_CPU_PRIMASK 0x0417+#define GIC_CPU_BINPOINT 0x0818+#define GIC_CPU_INTACK 0x0c19+#define GIC_CPU_EOI 0x1020+#define GIC_CPU_RUNNINGPRI 0x1421+#define GIC_CPU_HIGHPRI 0x1822+23+#define GIC_DIST_CTRL 0x00024+#define GIC_DIST_CTR 0x00425+#define GIC_DIST_ENABLE_SET 0x10026+#define GIC_DIST_ENABLE_CLEAR 0x18027+#define GIC_DIST_PENDING_SET 0x20028+#define GIC_DIST_PENDING_CLEAR 0x28029+#define GIC_DIST_ACTIVE_BIT 0x30030+#define GIC_DIST_PRI 0x40031+#define GIC_DIST_TARGET 0x80032+#define GIC_DIST_CONFIG 0xc0033+#define GIC_DIST_SOFTINT 0xf0034+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+#endif40+41+#endif