gic.c 6.68 KB
Newer Older
Russell King's avatar
Russell King committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *  linux/arch/arm/common/gic.c
 *
 *  Copyright (C) 2002 ARM Limited, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Interrupt architecture for the GIC:
 *
 * o There is one Interrupt Distributor, which receives interrupts
 *   from system devices and sends them to the Interrupt Controllers.
 *
 * o There is one CPU Interface per CPU, which sends interrupts sent
 *   by the Distributor, and interrupts generated locally, to the
17 18 19
 *   associated CPU. The base address of the CPU interface is usually
 *   aliased so that the same address points to different chips depending
 *   on the CPU it is accessed from.
Russell King's avatar
Russell King committed
20 21 22 23 24 25 26 27 28
 *
 * Note that IRQs 0-31 are special - they are local to each CPU.
 * As such, the enable set/clear, pending set/clear and active bit
 * registers are banked per-cpu for these sources.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/smp.h>
29
#include <linux/cpumask.h>
Russell King's avatar
Russell King committed
30 31 32 33 34 35

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/mach/irq.h>
#include <asm/hardware/gic.h>

36
static DEFINE_SPINLOCK(irq_controller_lock);
Russell King's avatar
Russell King committed
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
struct gic_chip_data {
	unsigned int irq_offset;
	void __iomem *dist_base;
	void __iomem *cpu_base;
};

#ifndef MAX_GIC_NR
#define MAX_GIC_NR	1
#endif

static struct gic_chip_data gic_data[MAX_GIC_NR];

static inline void __iomem *gic_dist_base(unsigned int irq)
{
	struct gic_chip_data *gic_data = get_irq_chip_data(irq);
	return gic_data->dist_base;
}

static inline void __iomem *gic_cpu_base(unsigned int irq)
{
	struct gic_chip_data *gic_data = get_irq_chip_data(irq);
	return gic_data->cpu_base;
}

static inline unsigned int gic_irq(unsigned int irq)
{
	struct gic_chip_data *gic_data = get_irq_chip_data(irq);
	return irq - gic_data->irq_offset;
}

Russell King's avatar
Russell King committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
/*
 * Routines to acknowledge, disable and enable interrupts
 *
 * Linux assumes that when we're done with an interrupt we need to
 * unmask it, in the same way we need to unmask an interrupt when
 * we first enable it.
 *
 * The GIC has a seperate notion of "end of interrupt" to re-enable
 * an interrupt after handling, in order to support hardware
 * prioritisation.
 *
 * We can make the GIC behave in the way that Linux expects by making
 * our "acknowledge" routine disable the interrupt, then mark it as
 * complete.
 */
static void gic_ack_irq(unsigned int irq)
{
	u32 mask = 1 << (irq % 32);
86 87

	spin_lock(&irq_controller_lock);
88 89
	writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_CLEAR + (gic_irq(irq) / 32) * 4);
	writel(gic_irq(irq), gic_cpu_base(irq) + GIC_CPU_EOI);
90
	spin_unlock(&irq_controller_lock);
Russell King's avatar
Russell King committed
91 92 93 94 95
}

static void gic_mask_irq(unsigned int irq)
{
	u32 mask = 1 << (irq % 32);
96 97

	spin_lock(&irq_controller_lock);
98
	writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_CLEAR + (gic_irq(irq) / 32) * 4);
99
	spin_unlock(&irq_controller_lock);
Russell King's avatar
Russell King committed
100 101 102 103 104
}

static void gic_unmask_irq(unsigned int irq)
{
	u32 mask = 1 << (irq % 32);
105 106

	spin_lock(&irq_controller_lock);
107
	writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_SET + (gic_irq(irq) / 32) * 4);
108
	spin_unlock(&irq_controller_lock);
Russell King's avatar
Russell King committed
109 110
}

111
#ifdef CONFIG_SMP
112
static void gic_set_cpu(unsigned int irq, cpumask_t mask_val)
Russell King's avatar
Russell King committed
113
{
114
	void __iomem *reg = gic_dist_base(irq) + GIC_DIST_TARGET + (gic_irq(irq) & ~3);
Russell King's avatar
Russell King committed
115
	unsigned int shift = (irq % 4) * 8;
116
	unsigned int cpu = first_cpu(mask_val);
Russell King's avatar
Russell King committed
117 118
	u32 val;

119 120
	spin_lock(&irq_controller_lock);
	irq_desc[irq].cpu = cpu;
Russell King's avatar
Russell King committed
121 122 123
	val = readl(reg) & ~(0xff << shift);
	val |= 1 << (cpu + shift);
	writel(val, reg);
124
	spin_unlock(&irq_controller_lock);
Russell King's avatar
Russell King committed
125
}
126
#endif
Russell King's avatar
Russell King committed
127

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
static void fastcall gic_handle_cascade_irq(unsigned int irq,
					    struct irq_desc *desc)
{
	struct gic_chip_data *chip_data = get_irq_data(irq);
	struct irq_chip *chip = get_irq_chip(irq);
	unsigned int cascade_irq;
	unsigned long status;

	/* primary controller ack'ing */
	chip->ack(irq);

	spin_lock(&irq_controller_lock);
	status = readl(chip_data->cpu_base + GIC_CPU_INTACK);
	spin_unlock(&irq_controller_lock);

	cascade_irq = (status & 0x3ff);
	if (cascade_irq > 1020)
		goto out;
	if (cascade_irq < 32 || cascade_irq >= NR_IRQS) {
		do_bad_IRQ(cascade_irq, desc);
		goto out;
	}

	cascade_irq += chip_data->irq_offset;
	generic_handle_irq(cascade_irq);

 out:
	/* primary controller unmasking */
	chip->unmask(irq);
}

159 160
static struct irq_chip gic_chip = {
	.name		= "GIC",
Russell King's avatar
Russell King committed
161 162 163 164
	.ack		= gic_ack_irq,
	.mask		= gic_mask_irq,
	.unmask		= gic_unmask_irq,
#ifdef CONFIG_SMP
165
	.set_affinity	= gic_set_cpu,
Russell King's avatar
Russell King committed
166 167 168
#endif
};

169 170 171 172 173 174 175 176 177 178 179
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
{
	if (gic_nr >= MAX_GIC_NR)
		BUG();
	if (set_irq_data(irq, &gic_data[gic_nr]) != 0)
		BUG();
	set_irq_chained_handler(irq, gic_handle_cascade_irq);
}

void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
			  unsigned int irq_start)
Russell King's avatar
Russell King committed
180 181 182 183
{
	unsigned int max_irq, i;
	u32 cpumask = 1 << smp_processor_id();

184 185 186
	if (gic_nr >= MAX_GIC_NR)
		BUG();

Russell King's avatar
Russell King committed
187 188 189
	cpumask |= cpumask << 8;
	cpumask |= cpumask << 16;

190 191
	gic_data[gic_nr].dist_base = base;
	gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
Russell King's avatar
Russell King committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

	writel(0, base + GIC_DIST_CTRL);

	/*
	 * Find out how many interrupts are supported.
	 */
	max_irq = readl(base + GIC_DIST_CTR) & 0x1f;
	max_irq = (max_irq + 1) * 32;

	/*
	 * The GIC only supports up to 1020 interrupt sources.
	 * Limit this to either the architected maximum, or the
	 * platform maximum.
	 */
	if (max_irq > max(1020, NR_IRQS))
		max_irq = max(1020, NR_IRQS);

	/*
	 * Set all global interrupts to be level triggered, active low.
	 */
	for (i = 32; i < max_irq; i += 16)
		writel(0, base + GIC_DIST_CONFIG + i * 4 / 16);

	/*
	 * Set all global interrupts to this CPU only.
	 */
	for (i = 32; i < max_irq; i += 4)
		writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);

	/*
	 * Set priority on all interrupts.
	 */
	for (i = 0; i < max_irq; i += 4)
		writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);

	/*
	 * Disable all interrupts.
	 */
	for (i = 0; i < max_irq; i += 32)
		writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);

	/*
	 * Setup the Linux IRQ subsystem.
	 */
236
	for (i = irq_start; i < gic_data[gic_nr].irq_offset + max_irq; i++) {
Russell King's avatar
Russell King committed
237
		set_irq_chip(i, &gic_chip);
238
		set_irq_chip_data(i, &gic_data[gic_nr]);
239
		set_irq_handler(i, handle_level_irq);
Russell King's avatar
Russell King committed
240 241 242 243 244 245
		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
	}

	writel(1, base + GIC_DIST_CTRL);
}

246
void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
Russell King's avatar
Russell King committed
247
{
248 249 250 251 252
	if (gic_nr >= MAX_GIC_NR)
		BUG();

	gic_data[gic_nr].cpu_base = base;

Russell King's avatar
Russell King committed
253 254 255 256 257 258 259 260 261
	writel(0xf0, base + GIC_CPU_PRIMASK);
	writel(1, base + GIC_CPU_CTRL);
}

#ifdef CONFIG_SMP
void gic_raise_softirq(cpumask_t cpumask, unsigned int irq)
{
	unsigned long map = *cpus_addr(cpumask);

262 263
	/* this always happens on GIC0 */
	writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
Russell King's avatar
Russell King committed
264 265
}
#endif