blob: 11f8942b82dcdd1e9172126bcab0280ca76ffdb5 [file] [log] [blame]
Maxime Bizone7300d02009-08-18 13:23:37 +01001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
9
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
David Howellsca4d3e672010-10-07 14:08:54 +010014#include <linux/irq.h>
Maxime Bizone7300d02009-08-18 13:23:37 +010015#include <asm/irq_cpu.h>
16#include <asm/mipsregs.h>
17#include <bcm63xx_cpu.h>
18#include <bcm63xx_regs.h>
19#include <bcm63xx_io.h>
20#include <bcm63xx_irq.h>
21
Maxime Bizonf61cced2011-11-04 19:09:31 +010022static void __dispatch_internal(void) __maybe_unused;
Maxime Bizon71a43922011-11-04 19:09:33 +010023static void __dispatch_internal_64(void) __maybe_unused;
24static void __internal_irq_mask_32(unsigned int irq) __maybe_unused;
25static void __internal_irq_mask_64(unsigned int irq) __maybe_unused;
26static void __internal_irq_unmask_32(unsigned int irq) __maybe_unused;
27static void __internal_irq_unmask_64(unsigned int irq) __maybe_unused;
Maxime Bizonf61cced2011-11-04 19:09:31 +010028
29#ifndef BCMCPU_RUNTIME_DETECT
30#ifdef CONFIG_BCM63XX_CPU_6338
31#define irq_stat_reg PERF_IRQSTAT_6338_REG
32#define irq_mask_reg PERF_IRQMASK_6338_REG
Maxime Bizon71a43922011-11-04 19:09:33 +010033#define irq_bits 32
Maxime Bizon37c42a72011-11-04 19:09:32 +010034#define is_ext_irq_cascaded 0
35#define ext_irq_start 0
36#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010037#endif
38#ifdef CONFIG_BCM63XX_CPU_6345
39#define irq_stat_reg PERF_IRQSTAT_6345_REG
40#define irq_mask_reg PERF_IRQMASK_6345_REG
Maxime Bizon71a43922011-11-04 19:09:33 +010041#define irq_bits 32
Maxime Bizon37c42a72011-11-04 19:09:32 +010042#define is_ext_irq_cascaded 0
43#define ext_irq_start 0
44#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010045#endif
46#ifdef CONFIG_BCM63XX_CPU_6348
47#define irq_stat_reg PERF_IRQSTAT_6348_REG
48#define irq_mask_reg PERF_IRQMASK_6348_REG
Maxime Bizon71a43922011-11-04 19:09:33 +010049#define irq_bits 32
Maxime Bizon37c42a72011-11-04 19:09:32 +010050#define is_ext_irq_cascaded 0
51#define ext_irq_start 0
52#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010053#endif
54#ifdef CONFIG_BCM63XX_CPU_6358
55#define irq_stat_reg PERF_IRQSTAT_6358_REG
56#define irq_mask_reg PERF_IRQMASK_6358_REG
Maxime Bizon71a43922011-11-04 19:09:33 +010057#define irq_bits 32
Maxime Bizon37c42a72011-11-04 19:09:32 +010058#define is_ext_irq_cascaded 1
59#define ext_irq_start (BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE)
60#define ext_irq_end (BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE)
Maxime Bizonf61cced2011-11-04 19:09:31 +010061#endif
62
Maxime Bizon71a43922011-11-04 19:09:33 +010063#if irq_bits == 32
64#define dispatch_internal __dispatch_internal
65#define internal_irq_mask __internal_irq_mask_32
66#define internal_irq_unmask __internal_irq_unmask_32
67#else
68#define dispatch_internal __dispatch_internal_64
69#define internal_irq_mask __internal_irq_mask_64
70#define internal_irq_unmask __internal_irq_unmask_64
71#endif
Maxime Bizonf61cced2011-11-04 19:09:31 +010072
73#define irq_stat_addr (bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
74#define irq_mask_addr (bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)
75
76static inline void bcm63xx_init_irq(void)
77{
78}
79#else /* ! BCMCPU_RUNTIME_DETECT */
80
81static u32 irq_stat_addr, irq_mask_addr;
82static void (*dispatch_internal)(void);
Maxime Bizon37c42a72011-11-04 19:09:32 +010083static int is_ext_irq_cascaded;
84static unsigned int ext_irq_start, ext_irq_end;
Maxime Bizon71a43922011-11-04 19:09:33 +010085static void (*internal_irq_mask)(unsigned int irq);
86static void (*internal_irq_unmask)(unsigned int irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +010087
88static void bcm63xx_init_irq(void)
89{
Maxime Bizon71a43922011-11-04 19:09:33 +010090 int irq_bits;
91
Maxime Bizonf61cced2011-11-04 19:09:31 +010092 irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
93 irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
94
95 switch (bcm63xx_get_cpu_id()) {
96 case BCM6338_CPU_ID:
97 irq_stat_addr += PERF_IRQSTAT_6338_REG;
98 irq_mask_addr += PERF_IRQMASK_6338_REG;
Maxime Bizon71a43922011-11-04 19:09:33 +010099 irq_bits = 32;
Maxime Bizonf61cced2011-11-04 19:09:31 +0100100 break;
101 case BCM6345_CPU_ID:
102 irq_stat_addr += PERF_IRQSTAT_6345_REG;
103 irq_mask_addr += PERF_IRQMASK_6345_REG;
Maxime Bizon71a43922011-11-04 19:09:33 +0100104 irq_bits = 32;
Maxime Bizonf61cced2011-11-04 19:09:31 +0100105 break;
106 case BCM6348_CPU_ID:
107 irq_stat_addr += PERF_IRQSTAT_6348_REG;
108 irq_mask_addr += PERF_IRQMASK_6348_REG;
Maxime Bizon71a43922011-11-04 19:09:33 +0100109 irq_bits = 32;
Maxime Bizonf61cced2011-11-04 19:09:31 +0100110 break;
111 case BCM6358_CPU_ID:
112 irq_stat_addr += PERF_IRQSTAT_6358_REG;
113 irq_mask_addr += PERF_IRQMASK_6358_REG;
Maxime Bizon71a43922011-11-04 19:09:33 +0100114 irq_bits = 32;
Maxime Bizon37c42a72011-11-04 19:09:32 +0100115 is_ext_irq_cascaded = 1;
116 ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
117 ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
Maxime Bizonf61cced2011-11-04 19:09:31 +0100118 break;
119 default:
120 BUG();
121 }
122
Maxime Bizon71a43922011-11-04 19:09:33 +0100123 if (irq_bits == 32) {
124 dispatch_internal = __dispatch_internal;
125 internal_irq_mask = __internal_irq_mask_32;
126 internal_irq_unmask = __internal_irq_unmask_32;
127 } else {
128 dispatch_internal = __dispatch_internal_64;
129 internal_irq_mask = __internal_irq_mask_64;
130 internal_irq_unmask = __internal_irq_unmask_64;
131 }
Maxime Bizonf61cced2011-11-04 19:09:31 +0100132}
133#endif /* ! BCMCPU_RUNTIME_DETECT */
134
135static inline void handle_internal(int intbit)
136{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100137 if (is_ext_irq_cascaded &&
138 intbit >= ext_irq_start && intbit <= ext_irq_end)
139 do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
140 else
141 do_IRQ(intbit + IRQ_INTERNAL_BASE);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100142}
143
Maxime Bizone7300d02009-08-18 13:23:37 +0100144/*
145 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
146 * prioritize any interrupt relatively to another. the static counter
147 * will resume the loop where it ended the last time we left this
148 * function.
149 */
Maxime Bizonf61cced2011-11-04 19:09:31 +0100150static void __dispatch_internal(void)
Maxime Bizone7300d02009-08-18 13:23:37 +0100151{
152 u32 pending;
153 static int i;
154
Maxime Bizonf61cced2011-11-04 19:09:31 +0100155 pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100156
157 if (!pending)
158 return ;
159
160 while (1) {
161 int to_call = i;
162
163 i = (i + 1) & 0x1f;
164 if (pending & (1 << to_call)) {
Maxime Bizonf61cced2011-11-04 19:09:31 +0100165 handle_internal(to_call);
Maxime Bizone7300d02009-08-18 13:23:37 +0100166 break;
167 }
168 }
169}
170
Maxime Bizon71a43922011-11-04 19:09:33 +0100171static void __dispatch_internal_64(void)
172{
173 u64 pending;
174 static int i;
175
176 pending = bcm_readq(irq_stat_addr) & bcm_readq(irq_mask_addr);
177
178 if (!pending)
179 return ;
180
181 while (1) {
182 int to_call = i;
183
184 i = (i + 1) & 0x3f;
185 if (pending & (1ull << to_call)) {
186 handle_internal(to_call);
187 break;
188 }
189 }
190}
191
Maxime Bizone7300d02009-08-18 13:23:37 +0100192asmlinkage void plat_irq_dispatch(void)
193{
194 u32 cause;
195
196 do {
197 cause = read_c0_cause() & read_c0_status() & ST0_IM;
198
199 if (!cause)
200 break;
201
202 if (cause & CAUSEF_IP7)
203 do_IRQ(7);
204 if (cause & CAUSEF_IP2)
Maxime Bizonf61cced2011-11-04 19:09:31 +0100205 dispatch_internal();
Maxime Bizon37c42a72011-11-04 19:09:32 +0100206 if (!is_ext_irq_cascaded) {
207 if (cause & CAUSEF_IP3)
208 do_IRQ(IRQ_EXT_0);
209 if (cause & CAUSEF_IP4)
210 do_IRQ(IRQ_EXT_1);
211 if (cause & CAUSEF_IP5)
212 do_IRQ(IRQ_EXT_2);
213 if (cause & CAUSEF_IP6)
214 do_IRQ(IRQ_EXT_3);
215 }
Maxime Bizone7300d02009-08-18 13:23:37 +0100216 } while (1);
217}
218
219/*
220 * internal IRQs operations: only mask/unmask on PERF irq mask
221 * register.
222 */
Maxime Bizon71a43922011-11-04 19:09:33 +0100223static void __internal_irq_mask_32(unsigned int irq)
Maxime Bizone7300d02009-08-18 13:23:37 +0100224{
225 u32 mask;
226
Maxime Bizonf61cced2011-11-04 19:09:31 +0100227 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100228 mask &= ~(1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100229 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100230}
231
Maxime Bizon71a43922011-11-04 19:09:33 +0100232static void __internal_irq_mask_64(unsigned int irq)
233{
234 u64 mask;
235
236 mask = bcm_readq(irq_mask_addr);
237 mask &= ~(1ull << irq);
238 bcm_writeq(mask, irq_mask_addr);
239}
240
241static void __internal_irq_unmask_32(unsigned int irq)
Maxime Bizone7300d02009-08-18 13:23:37 +0100242{
243 u32 mask;
244
Maxime Bizonf61cced2011-11-04 19:09:31 +0100245 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100246 mask |= (1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100247 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100248}
249
Maxime Bizon71a43922011-11-04 19:09:33 +0100250static void __internal_irq_unmask_64(unsigned int irq)
251{
252 u64 mask;
253
254 mask = bcm_readq(irq_mask_addr);
255 mask |= (1ull << irq);
256 bcm_writeq(mask, irq_mask_addr);
257}
258
Maxime Bizon37c42a72011-11-04 19:09:32 +0100259static void bcm63xx_internal_irq_mask(struct irq_data *d)
260{
261 internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
262}
263
264static void bcm63xx_internal_irq_unmask(struct irq_data *d)
265{
266 internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
267}
268
Maxime Bizone7300d02009-08-18 13:23:37 +0100269/*
270 * external IRQs operations: mask/unmask and clear on PERF external
271 * irq control register.
272 */
Thomas Gleixner93f29362011-03-23 21:08:47 +0000273static void bcm63xx_external_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100274{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100275 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100276 u32 reg;
277
Maxime Bizone7300d02009-08-18 13:23:37 +0100278 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
279 reg &= ~EXTIRQ_CFG_MASK(irq);
280 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
Maxime Bizon37c42a72011-11-04 19:09:32 +0100281 if (is_ext_irq_cascaded)
282 internal_irq_mask(irq + ext_irq_start);
Maxime Bizone7300d02009-08-18 13:23:37 +0100283}
284
Thomas Gleixner93f29362011-03-23 21:08:47 +0000285static void bcm63xx_external_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100286{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100287 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100288 u32 reg;
289
Maxime Bizone7300d02009-08-18 13:23:37 +0100290 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
291 reg |= EXTIRQ_CFG_MASK(irq);
292 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
Maxime Bizon37c42a72011-11-04 19:09:32 +0100293 if (is_ext_irq_cascaded)
294 internal_irq_unmask(irq + ext_irq_start);
Maxime Bizone7300d02009-08-18 13:23:37 +0100295}
296
Thomas Gleixner93f29362011-03-23 21:08:47 +0000297static void bcm63xx_external_irq_clear(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100298{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100299 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100300 u32 reg;
301
Maxime Bizone7300d02009-08-18 13:23:37 +0100302 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
303 reg |= EXTIRQ_CFG_CLEAR(irq);
304 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
305}
306
Thomas Gleixner93f29362011-03-23 21:08:47 +0000307static int bcm63xx_external_irq_set_type(struct irq_data *d,
Maxime Bizone7300d02009-08-18 13:23:37 +0100308 unsigned int flow_type)
309{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100310 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100311 u32 reg;
Maxime Bizone7300d02009-08-18 13:23:37 +0100312
313 flow_type &= IRQ_TYPE_SENSE_MASK;
314
315 if (flow_type == IRQ_TYPE_NONE)
316 flow_type = IRQ_TYPE_LEVEL_LOW;
317
318 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
319 switch (flow_type) {
320 case IRQ_TYPE_EDGE_BOTH:
321 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
322 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
323 break;
324
325 case IRQ_TYPE_EDGE_RISING:
326 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
327 reg |= EXTIRQ_CFG_SENSE(irq);
328 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
329 break;
330
331 case IRQ_TYPE_EDGE_FALLING:
332 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
333 reg &= ~EXTIRQ_CFG_SENSE(irq);
334 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
335 break;
336
337 case IRQ_TYPE_LEVEL_HIGH:
338 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
339 reg |= EXTIRQ_CFG_SENSE(irq);
340 break;
341
342 case IRQ_TYPE_LEVEL_LOW:
343 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
344 reg &= ~EXTIRQ_CFG_SENSE(irq);
345 break;
346
347 default:
348 printk(KERN_ERR "bogus flow type combination given !\n");
349 return -EINVAL;
350 }
351 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
352
Thomas Gleixner93f29362011-03-23 21:08:47 +0000353 irqd_set_trigger_type(d, flow_type);
354 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
355 __irq_set_handler_locked(d->irq, handle_level_irq);
356 else
357 __irq_set_handler_locked(d->irq, handle_edge_irq);
Maxime Bizone7300d02009-08-18 13:23:37 +0100358
Thomas Gleixner93f29362011-03-23 21:08:47 +0000359 return IRQ_SET_MASK_OK_NOCOPY;
Maxime Bizone7300d02009-08-18 13:23:37 +0100360}
361
362static struct irq_chip bcm63xx_internal_irq_chip = {
363 .name = "bcm63xx_ipic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000364 .irq_mask = bcm63xx_internal_irq_mask,
365 .irq_unmask = bcm63xx_internal_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100366};
367
368static struct irq_chip bcm63xx_external_irq_chip = {
369 .name = "bcm63xx_epic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000370 .irq_ack = bcm63xx_external_irq_clear,
Maxime Bizone7300d02009-08-18 13:23:37 +0100371
Thomas Gleixner93f29362011-03-23 21:08:47 +0000372 .irq_mask = bcm63xx_external_irq_mask,
373 .irq_unmask = bcm63xx_external_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100374
Thomas Gleixner93f29362011-03-23 21:08:47 +0000375 .irq_set_type = bcm63xx_external_irq_set_type,
Maxime Bizone7300d02009-08-18 13:23:37 +0100376};
377
378static struct irqaction cpu_ip2_cascade_action = {
379 .handler = no_action,
380 .name = "cascade_ip2",
Wu Zhangjin5a4a4ad2011-07-23 12:41:24 +0000381 .flags = IRQF_NO_THREAD,
Maxime Bizone7300d02009-08-18 13:23:37 +0100382};
383
Maxime Bizon37c42a72011-11-04 19:09:32 +0100384static struct irqaction cpu_ext_cascade_action = {
385 .handler = no_action,
386 .name = "cascade_extirq",
387 .flags = IRQF_NO_THREAD,
388};
389
Maxime Bizone7300d02009-08-18 13:23:37 +0100390void __init arch_init_irq(void)
391{
392 int i;
393
Maxime Bizonf61cced2011-11-04 19:09:31 +0100394 bcm63xx_init_irq();
Maxime Bizone7300d02009-08-18 13:23:37 +0100395 mips_cpu_irq_init();
396 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200397 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100398 handle_level_irq);
399
Maxime Bizon37c42a72011-11-04 19:09:32 +0100400 for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + 4; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200401 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100402 handle_edge_irq);
403
Maxime Bizon37c42a72011-11-04 19:09:32 +0100404 if (!is_ext_irq_cascaded) {
405 for (i = 3; i < 7; ++i)
406 setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
407 }
408
409 setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
Maxime Bizone7300d02009-08-18 13:23:37 +0100410}