aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 14:22:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 14:22:14 -0400
commit1bf25e78af317e6d5d9b5594dfeb0036e0d589d6 (patch)
tree49dbd2d7bd6856b8ae1864c2dd0c0eb5e36d5398 /drivers/irqchip
parent38f56f33ca381751f9b8910f67e7a805ec0b68cb (diff)
parent0592c2189ece16f3e0c5a56245634aba93d0c9dd (diff)
Merge tag 'cleanup-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC late cleanups from Arnd Bergmann: "These are cleanups and smaller changes that either depend on earlier feature branches or came in late during the development cycle. We normally try to get all cleanups early, so these are the exceptions: - A follow-up on the clocksource reworks, hopefully the last time we need to merge clocksource subsystem changes through arm-soc. A first set of patches was part of the original 3.10 arm-soc cleanup series because of interdependencies with timer drivers now moved out of arch/arm. - Migrating the SPEAr13xx platform away from using auxdata for DMA channel descriptions towards using information in device tree, based on the earlier SPEAr multiplatform series - A few follow-ups on the Atmel SAMA5 support and other changes for Atmel at91 based on the larger at91 reworks. - Moving the armada irqchip implementation to drivers/irqchip - Several OMAP cleanups following up on the larger series already merged in 3.10." * tag 'cleanup-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (50 commits) ARM: OMAP4: change the device names in usb_bind_phy ARM: OMAP2+: Fix mismerge for timer.c between ff931c82 and da4a686a ARM: SPEAr: conditionalize SMP code ARM: arch_timer: Silence debug preempt warnings ARM: OMAP: remove unused variable serial: amba-pl011: fix !CONFIG_DMA_ENGINE case ata: arasan: remove the need for platform_data ARM: at91/sama5d34ek.dts: remove not needed compatibility string ARM: at91: dts: add MCI DMA support ARM: at91: dts: add i2c dma support ARM: at91: dts: set #dma-cells to the correct value ARM: at91: suspend both memory controllers on at91sam9263 irqchip: armada-370-xp: slightly cleanup irq controller driver irqchip: armada-370-xp: move IRQ handler to avoid forward declaration irqchip: move IRQ driver for Armada 370/XP ARM: mvebu: move L2 cache initialization in init_early() devtree: add binding documentation for sp804 ARM: integrator-cp: convert use CLKSRC_OF for timer init ARM: versatile: use OF init for sp804 timer ARM: versatile: add versatile dtbs to dtbs target ...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c288
2 files changed, 289 insertions, 0 deletions
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c28fcccf4a0d..cda4cb5f7327 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
2 2
3obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o 3obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
4obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o 4obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
5obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
5obj-$(CONFIG_ARCH_MXS) += irq-mxs.o 6obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
6obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o 7obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
7obj-$(CONFIG_METAG) += irq-metag-ext.o 8obj-$(CONFIG_METAG) += irq-metag-ext.o
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
new file mode 100644
index 000000000000..bb328a366122
--- /dev/null
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -0,0 +1,288 @@
1/*
2 * Marvell Armada 370 and Armada XP SoC IRQ handling
3 *
4 * Copyright (C) 2012 Marvell
5 *
6 * Lior Amsalem <alior@marvell.com>
7 * Gregory CLEMENT <gregory.clement@free-electrons.com>
8 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
9 * Ben Dooks <ben.dooks@codethink.co.uk>
10 *
11 * This file is licensed under the terms of the GNU General Public
12 * License version 2. This program is licensed "as is" without any
13 * warranty of any kind, whether express or implied.
14 */
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/irq.h>
20#include <linux/interrupt.h>
21#include <linux/io.h>
22#include <linux/of_address.h>
23#include <linux/of_irq.h>
24#include <linux/irqdomain.h>
25#include <asm/mach/arch.h>
26#include <asm/exception.h>
27#include <asm/smp_plat.h>
28#include <asm/mach/irq.h>
29
30#include "irqchip.h"
31
32/* Interrupt Controller Registers Map */
33#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
34#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
35
36#define ARMADA_370_XP_INT_CONTROL (0x00)
37#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
38#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
39#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
40
41#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
42
43#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
44#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
45#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
46
47#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
48
49#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
50
51#define IPI_DOORBELL_START (0)
52#define IPI_DOORBELL_END (8)
53#define IPI_DOORBELL_MASK 0xFF
54
55static DEFINE_RAW_SPINLOCK(irq_controller_lock);
56
57static void __iomem *per_cpu_int_base;
58static void __iomem *main_int_base;
59static struct irq_domain *armada_370_xp_mpic_domain;
60
61/*
62 * In SMP mode:
63 * For shared global interrupts, mask/unmask global enable bit
64 * For CPU interrupts, mask/unmask the calling CPU's bit
65 */
66static void armada_370_xp_irq_mask(struct irq_data *d)
67{
68 irq_hw_number_t hwirq = irqd_to_hwirq(d);
69
70 if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
71 writel(hwirq, main_int_base +
72 ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
73 else
74 writel(hwirq, per_cpu_int_base +
75 ARMADA_370_XP_INT_SET_MASK_OFFS);
76}
77
78static void armada_370_xp_irq_unmask(struct irq_data *d)
79{
80 irq_hw_number_t hwirq = irqd_to_hwirq(d);
81
82 if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
83 writel(hwirq, main_int_base +
84 ARMADA_370_XP_INT_SET_ENABLE_OFFS);
85 else
86 writel(hwirq, per_cpu_int_base +
87 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
88}
89
90#ifdef CONFIG_SMP
91static int armada_xp_set_affinity(struct irq_data *d,
92 const struct cpumask *mask_val, bool force)
93{
94 unsigned long reg;
95 unsigned long new_mask = 0;
96 unsigned long online_mask = 0;
97 unsigned long count = 0;
98 irq_hw_number_t hwirq = irqd_to_hwirq(d);
99 int cpu;
100
101 for_each_cpu(cpu, mask_val) {
102 new_mask |= 1 << cpu_logical_map(cpu);
103 count++;
104 }
105
106 /*
107 * Forbid mutlicore interrupt affinity
108 * This is required since the MPIC HW doesn't limit
109 * several CPUs from acknowledging the same interrupt.
110 */
111 if (count > 1)
112 return -EINVAL;
113
114 for_each_cpu(cpu, cpu_online_mask)
115 online_mask |= 1 << cpu_logical_map(cpu);
116
117 raw_spin_lock(&irq_controller_lock);
118
119 reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
120 reg = (reg & (~online_mask)) | new_mask;
121 writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
122
123 raw_spin_unlock(&irq_controller_lock);
124
125 return 0;
126}
127#endif
128
129static struct irq_chip armada_370_xp_irq_chip = {
130 .name = "armada_370_xp_irq",
131 .irq_mask = armada_370_xp_irq_mask,
132 .irq_mask_ack = armada_370_xp_irq_mask,
133 .irq_unmask = armada_370_xp_irq_unmask,
134#ifdef CONFIG_SMP
135 .irq_set_affinity = armada_xp_set_affinity,
136#endif
137};
138
139static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
140 unsigned int virq, irq_hw_number_t hw)
141{
142 armada_370_xp_irq_mask(irq_get_irq_data(virq));
143 if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
144 writel(hw, per_cpu_int_base +
145 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
146 else
147 writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
148 irq_set_status_flags(virq, IRQ_LEVEL);
149
150 if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) {
151 irq_set_percpu_devid(virq);
152 irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
153 handle_percpu_devid_irq);
154
155 } else {
156 irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
157 handle_level_irq);
158 }
159 set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
160
161 return 0;
162}
163
164#ifdef CONFIG_SMP
165void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
166{
167 int cpu;
168 unsigned long map = 0;
169
170 /* Convert our logical CPU mask into a physical one. */
171 for_each_cpu(cpu, mask)
172 map |= 1 << cpu_logical_map(cpu);
173
174 /*
175 * Ensure that stores to Normal memory are visible to the
176 * other CPUs before issuing the IPI.
177 */
178 dsb();
179
180 /* submit softirq */
181 writel((map << 8) | irq, main_int_base +
182 ARMADA_370_XP_SW_TRIG_INT_OFFS);
183}
184
185void armada_xp_mpic_smp_cpu_init(void)
186{
187 /* Clear pending IPIs */
188 writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
189
190 /* Enable first 8 IPIs */
191 writel(IPI_DOORBELL_MASK, per_cpu_int_base +
192 ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
193
194 /* Unmask IPI interrupt */
195 writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
196}
197#endif /* CONFIG_SMP */
198
199static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
200 .map = armada_370_xp_mpic_irq_map,
201 .xlate = irq_domain_xlate_onecell,
202};
203
204static asmlinkage void __exception_irq_entry
205armada_370_xp_handle_irq(struct pt_regs *regs)
206{
207 u32 irqstat, irqnr;
208
209 do {
210 irqstat = readl_relaxed(per_cpu_int_base +
211 ARMADA_370_XP_CPU_INTACK_OFFS);
212 irqnr = irqstat & 0x3FF;
213
214 if (irqnr > 1022)
215 break;
216
217 if (irqnr > 0) {
218 irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
219 irqnr);
220 handle_IRQ(irqnr, regs);
221 continue;
222 }
223#ifdef CONFIG_SMP
224 /* IPI Handling */
225 if (irqnr == 0) {
226 u32 ipimask, ipinr;
227
228 ipimask = readl_relaxed(per_cpu_int_base +
229 ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
230 & IPI_DOORBELL_MASK;
231
232 writel(~IPI_DOORBELL_MASK, per_cpu_int_base +
233 ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
234
235 /* Handle all pending doorbells */
236 for (ipinr = IPI_DOORBELL_START;
237 ipinr < IPI_DOORBELL_END; ipinr++) {
238 if (ipimask & (0x1 << ipinr))
239 handle_IPI(ipinr, regs);
240 }
241 continue;
242 }
243#endif
244
245 } while (1);
246}
247
248static int __init armada_370_xp_mpic_of_init(struct device_node *node,
249 struct device_node *parent)
250{
251 u32 control;
252
253 main_int_base = of_iomap(node, 0);
254 per_cpu_int_base = of_iomap(node, 1);
255
256 BUG_ON(!main_int_base);
257 BUG_ON(!per_cpu_int_base);
258
259 control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
260
261 armada_370_xp_mpic_domain =
262 irq_domain_add_linear(node, (control >> 2) & 0x3ff,
263 &armada_370_xp_mpic_irq_ops, NULL);
264
265 if (!armada_370_xp_mpic_domain)
266 panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
267
268 irq_set_default_host(armada_370_xp_mpic_domain);
269
270#ifdef CONFIG_SMP
271 armada_xp_mpic_smp_cpu_init();
272
273 /*
274 * Set the default affinity from all CPUs to the boot cpu.
275 * This is required since the MPIC doesn't limit several CPUs
276 * from acknowledging the same interrupt.
277 */
278 cpumask_clear(irq_default_affinity);
279 cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
280
281#endif
282
283 set_handle_irq(armada_370_xp_handle_irq);
284
285 return 0;
286}
287
288IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init);