blob: cdf352b040a9ddbc9a40976460cb1a5a445ef22e [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;
23
24#ifndef BCMCPU_RUNTIME_DETECT
25#ifdef CONFIG_BCM63XX_CPU_6338
26#define irq_stat_reg PERF_IRQSTAT_6338_REG
27#define irq_mask_reg PERF_IRQMASK_6338_REG
Maxime Bizon37c42a72011-11-04 19:09:32 +010028#define is_ext_irq_cascaded 0
29#define ext_irq_start 0
30#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010031#endif
32#ifdef CONFIG_BCM63XX_CPU_6345
33#define irq_stat_reg PERF_IRQSTAT_6345_REG
34#define irq_mask_reg PERF_IRQMASK_6345_REG
Maxime Bizon37c42a72011-11-04 19:09:32 +010035#define is_ext_irq_cascaded 0
36#define ext_irq_start 0
37#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010038#endif
39#ifdef CONFIG_BCM63XX_CPU_6348
40#define irq_stat_reg PERF_IRQSTAT_6348_REG
41#define irq_mask_reg PERF_IRQMASK_6348_REG
Maxime Bizon37c42a72011-11-04 19:09:32 +010042#define dispatch_internal __dispatch_internal
43#define is_ext_irq_cascaded 0
44#define ext_irq_start 0
45#define ext_irq_end 0
Maxime Bizonf61cced2011-11-04 19:09:31 +010046#endif
47#ifdef CONFIG_BCM63XX_CPU_6358
48#define irq_stat_reg PERF_IRQSTAT_6358_REG
49#define irq_mask_reg PERF_IRQMASK_6358_REG
Maxime Bizon37c42a72011-11-04 19:09:32 +010050#define dispatch_internal __dispatch_internal
51#define is_ext_irq_cascaded 1
52#define ext_irq_start (BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE)
53#define ext_irq_end (BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE)
Maxime Bizonf61cced2011-11-04 19:09:31 +010054#endif
55
56#define dispatch_internal __dispatch_internal
57
58#define irq_stat_addr (bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
59#define irq_mask_addr (bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)
60
61static inline void bcm63xx_init_irq(void)
62{
63}
64#else /* ! BCMCPU_RUNTIME_DETECT */
65
66static u32 irq_stat_addr, irq_mask_addr;
67static void (*dispatch_internal)(void);
Maxime Bizon37c42a72011-11-04 19:09:32 +010068static int is_ext_irq_cascaded;
69static unsigned int ext_irq_start, ext_irq_end;
Maxime Bizonf61cced2011-11-04 19:09:31 +010070
71static void bcm63xx_init_irq(void)
72{
73 irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
74 irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
75
76 switch (bcm63xx_get_cpu_id()) {
77 case BCM6338_CPU_ID:
78 irq_stat_addr += PERF_IRQSTAT_6338_REG;
79 irq_mask_addr += PERF_IRQMASK_6338_REG;
80 break;
81 case BCM6345_CPU_ID:
82 irq_stat_addr += PERF_IRQSTAT_6345_REG;
83 irq_mask_addr += PERF_IRQMASK_6345_REG;
84 break;
85 case BCM6348_CPU_ID:
86 irq_stat_addr += PERF_IRQSTAT_6348_REG;
87 irq_mask_addr += PERF_IRQMASK_6348_REG;
88 break;
89 case BCM6358_CPU_ID:
90 irq_stat_addr += PERF_IRQSTAT_6358_REG;
91 irq_mask_addr += PERF_IRQMASK_6358_REG;
Maxime Bizon37c42a72011-11-04 19:09:32 +010092 is_ext_irq_cascaded = 1;
93 ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
94 ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
Maxime Bizonf61cced2011-11-04 19:09:31 +010095 break;
96 default:
97 BUG();
98 }
99
100 dispatch_internal = __dispatch_internal;
101}
102#endif /* ! BCMCPU_RUNTIME_DETECT */
103
104static inline void handle_internal(int intbit)
105{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100106 if (is_ext_irq_cascaded &&
107 intbit >= ext_irq_start && intbit <= ext_irq_end)
108 do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
109 else
110 do_IRQ(intbit + IRQ_INTERNAL_BASE);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100111}
112
Maxime Bizone7300d02009-08-18 13:23:37 +0100113/*
114 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
115 * prioritize any interrupt relatively to another. the static counter
116 * will resume the loop where it ended the last time we left this
117 * function.
118 */
Maxime Bizonf61cced2011-11-04 19:09:31 +0100119static void __dispatch_internal(void)
Maxime Bizone7300d02009-08-18 13:23:37 +0100120{
121 u32 pending;
122 static int i;
123
Maxime Bizonf61cced2011-11-04 19:09:31 +0100124 pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100125
126 if (!pending)
127 return ;
128
129 while (1) {
130 int to_call = i;
131
132 i = (i + 1) & 0x1f;
133 if (pending & (1 << to_call)) {
Maxime Bizonf61cced2011-11-04 19:09:31 +0100134 handle_internal(to_call);
Maxime Bizone7300d02009-08-18 13:23:37 +0100135 break;
136 }
137 }
138}
139
140asmlinkage void plat_irq_dispatch(void)
141{
142 u32 cause;
143
144 do {
145 cause = read_c0_cause() & read_c0_status() & ST0_IM;
146
147 if (!cause)
148 break;
149
150 if (cause & CAUSEF_IP7)
151 do_IRQ(7);
152 if (cause & CAUSEF_IP2)
Maxime Bizonf61cced2011-11-04 19:09:31 +0100153 dispatch_internal();
Maxime Bizon37c42a72011-11-04 19:09:32 +0100154 if (!is_ext_irq_cascaded) {
155 if (cause & CAUSEF_IP3)
156 do_IRQ(IRQ_EXT_0);
157 if (cause & CAUSEF_IP4)
158 do_IRQ(IRQ_EXT_1);
159 if (cause & CAUSEF_IP5)
160 do_IRQ(IRQ_EXT_2);
161 if (cause & CAUSEF_IP6)
162 do_IRQ(IRQ_EXT_3);
163 }
Maxime Bizone7300d02009-08-18 13:23:37 +0100164 } while (1);
165}
166
167/*
168 * internal IRQs operations: only mask/unmask on PERF irq mask
169 * register.
170 */
Maxime Bizon37c42a72011-11-04 19:09:32 +0100171static void internal_irq_mask(unsigned int irq)
Maxime Bizone7300d02009-08-18 13:23:37 +0100172{
173 u32 mask;
174
Maxime Bizonf61cced2011-11-04 19:09:31 +0100175 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100176 mask &= ~(1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100177 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100178}
179
Maxime Bizon37c42a72011-11-04 19:09:32 +0100180static void internal_irq_unmask(unsigned int irq)
Maxime Bizone7300d02009-08-18 13:23:37 +0100181{
182 u32 mask;
183
Maxime Bizonf61cced2011-11-04 19:09:31 +0100184 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100185 mask |= (1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100186 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100187}
188
Maxime Bizon37c42a72011-11-04 19:09:32 +0100189static void bcm63xx_internal_irq_mask(struct irq_data *d)
190{
191 internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
192}
193
194static void bcm63xx_internal_irq_unmask(struct irq_data *d)
195{
196 internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
197}
198
Maxime Bizone7300d02009-08-18 13:23:37 +0100199/*
200 * external IRQs operations: mask/unmask and clear on PERF external
201 * irq control register.
202 */
Thomas Gleixner93f29362011-03-23 21:08:47 +0000203static void bcm63xx_external_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100204{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100205 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100206 u32 reg;
207
Maxime Bizone7300d02009-08-18 13:23:37 +0100208 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
209 reg &= ~EXTIRQ_CFG_MASK(irq);
210 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
Maxime Bizon37c42a72011-11-04 19:09:32 +0100211 if (is_ext_irq_cascaded)
212 internal_irq_mask(irq + ext_irq_start);
Maxime Bizone7300d02009-08-18 13:23:37 +0100213}
214
Thomas Gleixner93f29362011-03-23 21:08:47 +0000215static void bcm63xx_external_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100216{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100217 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100218 u32 reg;
219
Maxime Bizone7300d02009-08-18 13:23:37 +0100220 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
221 reg |= EXTIRQ_CFG_MASK(irq);
222 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
Maxime Bizon37c42a72011-11-04 19:09:32 +0100223 if (is_ext_irq_cascaded)
224 internal_irq_unmask(irq + ext_irq_start);
Maxime Bizone7300d02009-08-18 13:23:37 +0100225}
226
Thomas Gleixner93f29362011-03-23 21:08:47 +0000227static void bcm63xx_external_irq_clear(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100228{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100229 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100230 u32 reg;
231
Maxime Bizone7300d02009-08-18 13:23:37 +0100232 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
233 reg |= EXTIRQ_CFG_CLEAR(irq);
234 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
235}
236
Thomas Gleixner93f29362011-03-23 21:08:47 +0000237static int bcm63xx_external_irq_set_type(struct irq_data *d,
Maxime Bizone7300d02009-08-18 13:23:37 +0100238 unsigned int flow_type)
239{
Maxime Bizon37c42a72011-11-04 19:09:32 +0100240 unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100241 u32 reg;
Maxime Bizone7300d02009-08-18 13:23:37 +0100242
243 flow_type &= IRQ_TYPE_SENSE_MASK;
244
245 if (flow_type == IRQ_TYPE_NONE)
246 flow_type = IRQ_TYPE_LEVEL_LOW;
247
248 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
249 switch (flow_type) {
250 case IRQ_TYPE_EDGE_BOTH:
251 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
252 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
253 break;
254
255 case IRQ_TYPE_EDGE_RISING:
256 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
257 reg |= EXTIRQ_CFG_SENSE(irq);
258 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
259 break;
260
261 case IRQ_TYPE_EDGE_FALLING:
262 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
263 reg &= ~EXTIRQ_CFG_SENSE(irq);
264 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
265 break;
266
267 case IRQ_TYPE_LEVEL_HIGH:
268 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
269 reg |= EXTIRQ_CFG_SENSE(irq);
270 break;
271
272 case IRQ_TYPE_LEVEL_LOW:
273 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
274 reg &= ~EXTIRQ_CFG_SENSE(irq);
275 break;
276
277 default:
278 printk(KERN_ERR "bogus flow type combination given !\n");
279 return -EINVAL;
280 }
281 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
282
Thomas Gleixner93f29362011-03-23 21:08:47 +0000283 irqd_set_trigger_type(d, flow_type);
284 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
285 __irq_set_handler_locked(d->irq, handle_level_irq);
286 else
287 __irq_set_handler_locked(d->irq, handle_edge_irq);
Maxime Bizone7300d02009-08-18 13:23:37 +0100288
Thomas Gleixner93f29362011-03-23 21:08:47 +0000289 return IRQ_SET_MASK_OK_NOCOPY;
Maxime Bizone7300d02009-08-18 13:23:37 +0100290}
291
292static struct irq_chip bcm63xx_internal_irq_chip = {
293 .name = "bcm63xx_ipic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000294 .irq_mask = bcm63xx_internal_irq_mask,
295 .irq_unmask = bcm63xx_internal_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100296};
297
298static struct irq_chip bcm63xx_external_irq_chip = {
299 .name = "bcm63xx_epic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000300 .irq_ack = bcm63xx_external_irq_clear,
Maxime Bizone7300d02009-08-18 13:23:37 +0100301
Thomas Gleixner93f29362011-03-23 21:08:47 +0000302 .irq_mask = bcm63xx_external_irq_mask,
303 .irq_unmask = bcm63xx_external_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100304
Thomas Gleixner93f29362011-03-23 21:08:47 +0000305 .irq_set_type = bcm63xx_external_irq_set_type,
Maxime Bizone7300d02009-08-18 13:23:37 +0100306};
307
308static struct irqaction cpu_ip2_cascade_action = {
309 .handler = no_action,
310 .name = "cascade_ip2",
Wu Zhangjin5a4a4ad2011-07-23 12:41:24 +0000311 .flags = IRQF_NO_THREAD,
Maxime Bizone7300d02009-08-18 13:23:37 +0100312};
313
Maxime Bizon37c42a72011-11-04 19:09:32 +0100314static struct irqaction cpu_ext_cascade_action = {
315 .handler = no_action,
316 .name = "cascade_extirq",
317 .flags = IRQF_NO_THREAD,
318};
319
Maxime Bizone7300d02009-08-18 13:23:37 +0100320void __init arch_init_irq(void)
321{
322 int i;
323
Maxime Bizonf61cced2011-11-04 19:09:31 +0100324 bcm63xx_init_irq();
Maxime Bizone7300d02009-08-18 13:23:37 +0100325 mips_cpu_irq_init();
326 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200327 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100328 handle_level_irq);
329
Maxime Bizon37c42a72011-11-04 19:09:32 +0100330 for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + 4; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200331 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100332 handle_edge_irq);
333
Maxime Bizon37c42a72011-11-04 19:09:32 +0100334 if (!is_ext_irq_cascaded) {
335 for (i = 3; i < 7; ++i)
336 setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
337 }
338
339 setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
Maxime Bizone7300d02009-08-18 13:23:37 +0100340}