gic.c 6.7 KB
Newer Older
R
Russell King 已提交
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.
R
Russell King 已提交
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>
R
Russell King 已提交
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);
R
Russell King 已提交
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;
}

R
Russell King 已提交
68 69 70 71 72 73 74
/*
 * 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.
 *
S
Simon Arlott 已提交
75
 * The GIC has a separate notion of "end of interrupt" to re-enable
R
Russell King 已提交
76 77 78 79 80 81 82 83 84 85
 * 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);
R
Russell King 已提交
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);
R
Russell King 已提交
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);
R
Russell King 已提交
109 110
}

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

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

128
static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
129 130 131
{
	struct gic_chip_data *chip_data = get_irq_data(irq);
	struct irq_chip *chip = get_irq_chip(irq);
132
	unsigned int cascade_irq, gic_irq;
133 134 135 136 137 138 139 140 141
	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);

142 143
	gic_irq = (status & 0x3ff);
	if (gic_irq == 1023)
144 145
		goto out;

146 147 148 149 150
	cascade_irq = gic_irq + chip_data->irq_offset;
	if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS))
		do_bad_IRQ(cascade_irq, desc);
	else
		generic_handle_irq(cascade_irq);
151 152 153 154 155 156

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

157 158
static struct irq_chip gic_chip = {
	.name		= "GIC",
R
Russell King 已提交
159 160 161 162
	.ack		= gic_ack_irq,
	.mask		= gic_mask_irq,
	.unmask		= gic_unmask_irq,
#ifdef CONFIG_SMP
163
	.set_affinity	= gic_set_cpu,
R
Russell King 已提交
164 165 166
#endif
};

167 168 169 170 171 172 173 174 175 176 177
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)
R
Russell King 已提交
178 179 180 181
{
	unsigned int max_irq, i;
	u32 cpumask = 1 << smp_processor_id();

182 183 184
	if (gic_nr >= MAX_GIC_NR)
		BUG();

R
Russell King 已提交
185 186 187
	cpumask |= cpumask << 8;
	cpumask |= cpumask << 16;

188 189
	gic_data[gic_nr].dist_base = base;
	gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
R
Russell King 已提交
190 191 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

	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.
	 */
234
	for (i = irq_start; i < gic_data[gic_nr].irq_offset + max_irq; i++) {
R
Russell King 已提交
235
		set_irq_chip(i, &gic_chip);
236
		set_irq_chip_data(i, &gic_data[gic_nr]);
237
		set_irq_handler(i, handle_level_irq);
R
Russell King 已提交
238 239 240 241 242 243
		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
	}

	writel(1, base + GIC_DIST_CTRL);
}

244
void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
R
Russell King 已提交
245
{
246 247 248 249 250
	if (gic_nr >= MAX_GIC_NR)
		BUG();

	gic_data[gic_nr].cpu_base = base;

R
Russell King 已提交
251 252 253 254 255 256 257 258 259
	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);

260 261
	/* this always happens on GIC0 */
	writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
R
Russell King 已提交
262 263
}
#endif