blob: a81cd828c769e3f3033cb6406aa41b6ac2740243 [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
28#endif
29#ifdef CONFIG_BCM63XX_CPU_6345
30#define irq_stat_reg PERF_IRQSTAT_6345_REG
31#define irq_mask_reg PERF_IRQMASK_6345_REG
32#endif
33#ifdef CONFIG_BCM63XX_CPU_6348
34#define irq_stat_reg PERF_IRQSTAT_6348_REG
35#define irq_mask_reg PERF_IRQMASK_6348_REG
36#endif
37#ifdef CONFIG_BCM63XX_CPU_6358
38#define irq_stat_reg PERF_IRQSTAT_6358_REG
39#define irq_mask_reg PERF_IRQMASK_6358_REG
40#endif
41
42#define dispatch_internal __dispatch_internal
43
44#define irq_stat_addr (bcm63xx_regset_address(RSET_PERF) + irq_stat_reg)
45#define irq_mask_addr (bcm63xx_regset_address(RSET_PERF) + irq_mask_reg)
46
47static inline void bcm63xx_init_irq(void)
48{
49}
50#else /* ! BCMCPU_RUNTIME_DETECT */
51
52static u32 irq_stat_addr, irq_mask_addr;
53static void (*dispatch_internal)(void);
54
55static void bcm63xx_init_irq(void)
56{
57 irq_stat_addr = bcm63xx_regset_address(RSET_PERF);
58 irq_mask_addr = bcm63xx_regset_address(RSET_PERF);
59
60 switch (bcm63xx_get_cpu_id()) {
61 case BCM6338_CPU_ID:
62 irq_stat_addr += PERF_IRQSTAT_6338_REG;
63 irq_mask_addr += PERF_IRQMASK_6338_REG;
64 break;
65 case BCM6345_CPU_ID:
66 irq_stat_addr += PERF_IRQSTAT_6345_REG;
67 irq_mask_addr += PERF_IRQMASK_6345_REG;
68 break;
69 case BCM6348_CPU_ID:
70 irq_stat_addr += PERF_IRQSTAT_6348_REG;
71 irq_mask_addr += PERF_IRQMASK_6348_REG;
72 break;
73 case BCM6358_CPU_ID:
74 irq_stat_addr += PERF_IRQSTAT_6358_REG;
75 irq_mask_addr += PERF_IRQMASK_6358_REG;
76 break;
77 default:
78 BUG();
79 }
80
81 dispatch_internal = __dispatch_internal;
82}
83#endif /* ! BCMCPU_RUNTIME_DETECT */
84
85static inline void handle_internal(int intbit)
86{
87 do_IRQ(intbit + IRQ_INTERNAL_BASE);
88}
89
Maxime Bizone7300d02009-08-18 13:23:37 +010090/*
91 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
92 * prioritize any interrupt relatively to another. the static counter
93 * will resume the loop where it ended the last time we left this
94 * function.
95 */
Maxime Bizonf61cced2011-11-04 19:09:31 +010096static void __dispatch_internal(void)
Maxime Bizone7300d02009-08-18 13:23:37 +010097{
98 u32 pending;
99 static int i;
100
Maxime Bizonf61cced2011-11-04 19:09:31 +0100101 pending = bcm_readl(irq_stat_addr) & bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100102
103 if (!pending)
104 return ;
105
106 while (1) {
107 int to_call = i;
108
109 i = (i + 1) & 0x1f;
110 if (pending & (1 << to_call)) {
Maxime Bizonf61cced2011-11-04 19:09:31 +0100111 handle_internal(to_call);
Maxime Bizone7300d02009-08-18 13:23:37 +0100112 break;
113 }
114 }
115}
116
117asmlinkage void plat_irq_dispatch(void)
118{
119 u32 cause;
120
121 do {
122 cause = read_c0_cause() & read_c0_status() & ST0_IM;
123
124 if (!cause)
125 break;
126
127 if (cause & CAUSEF_IP7)
128 do_IRQ(7);
129 if (cause & CAUSEF_IP2)
Maxime Bizonf61cced2011-11-04 19:09:31 +0100130 dispatch_internal();
Maxime Bizone7300d02009-08-18 13:23:37 +0100131 if (cause & CAUSEF_IP3)
132 do_IRQ(IRQ_EXT_0);
133 if (cause & CAUSEF_IP4)
134 do_IRQ(IRQ_EXT_1);
135 if (cause & CAUSEF_IP5)
136 do_IRQ(IRQ_EXT_2);
137 if (cause & CAUSEF_IP6)
138 do_IRQ(IRQ_EXT_3);
139 } while (1);
140}
141
142/*
143 * internal IRQs operations: only mask/unmask on PERF irq mask
144 * register.
145 */
Thomas Gleixner93f29362011-03-23 21:08:47 +0000146static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100147{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000148 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100149 u32 mask;
150
Maxime Bizonf61cced2011-11-04 19:09:31 +0100151 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100152 mask &= ~(1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100153 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100154}
155
Thomas Gleixner93f29362011-03-23 21:08:47 +0000156static void bcm63xx_internal_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100157{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000158 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100159 u32 mask;
160
Maxime Bizonf61cced2011-11-04 19:09:31 +0100161 mask = bcm_readl(irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100162 mask |= (1 << irq);
Maxime Bizonf61cced2011-11-04 19:09:31 +0100163 bcm_writel(mask, irq_mask_addr);
Maxime Bizone7300d02009-08-18 13:23:37 +0100164}
165
Maxime Bizone7300d02009-08-18 13:23:37 +0100166/*
167 * external IRQs operations: mask/unmask and clear on PERF external
168 * irq control register.
169 */
Thomas Gleixner93f29362011-03-23 21:08:47 +0000170static void bcm63xx_external_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100171{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000172 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100173 u32 reg;
174
Maxime Bizone7300d02009-08-18 13:23:37 +0100175 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
176 reg &= ~EXTIRQ_CFG_MASK(irq);
177 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
178}
179
Thomas Gleixner93f29362011-03-23 21:08:47 +0000180static void bcm63xx_external_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100181{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000182 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100183 u32 reg;
184
Maxime Bizone7300d02009-08-18 13:23:37 +0100185 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
186 reg |= EXTIRQ_CFG_MASK(irq);
187 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
188}
189
Thomas Gleixner93f29362011-03-23 21:08:47 +0000190static void bcm63xx_external_irq_clear(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100191{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000192 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100193 u32 reg;
194
Maxime Bizone7300d02009-08-18 13:23:37 +0100195 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
196 reg |= EXTIRQ_CFG_CLEAR(irq);
197 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
198}
199
Thomas Gleixner93f29362011-03-23 21:08:47 +0000200static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100201{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000202 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
Maxime Bizone7300d02009-08-18 13:23:37 +0100203 irq_enable_hazard();
Thomas Gleixner93f29362011-03-23 21:08:47 +0000204 bcm63xx_external_irq_unmask(d);
Maxime Bizone7300d02009-08-18 13:23:37 +0100205 return 0;
206}
207
Thomas Gleixner93f29362011-03-23 21:08:47 +0000208static void bcm63xx_external_irq_shutdown(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100209{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000210 bcm63xx_external_irq_mask(d);
211 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
Maxime Bizone7300d02009-08-18 13:23:37 +0100212 irq_disable_hazard();
213}
214
Thomas Gleixner93f29362011-03-23 21:08:47 +0000215static int bcm63xx_external_irq_set_type(struct irq_data *d,
Maxime Bizone7300d02009-08-18 13:23:37 +0100216 unsigned int flow_type)
217{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000218 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100219 u32 reg;
Maxime Bizone7300d02009-08-18 13:23:37 +0100220
221 flow_type &= IRQ_TYPE_SENSE_MASK;
222
223 if (flow_type == IRQ_TYPE_NONE)
224 flow_type = IRQ_TYPE_LEVEL_LOW;
225
226 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
227 switch (flow_type) {
228 case IRQ_TYPE_EDGE_BOTH:
229 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
230 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
231 break;
232
233 case IRQ_TYPE_EDGE_RISING:
234 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
235 reg |= EXTIRQ_CFG_SENSE(irq);
236 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
237 break;
238
239 case IRQ_TYPE_EDGE_FALLING:
240 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
241 reg &= ~EXTIRQ_CFG_SENSE(irq);
242 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
243 break;
244
245 case IRQ_TYPE_LEVEL_HIGH:
246 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
247 reg |= EXTIRQ_CFG_SENSE(irq);
248 break;
249
250 case IRQ_TYPE_LEVEL_LOW:
251 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
252 reg &= ~EXTIRQ_CFG_SENSE(irq);
253 break;
254
255 default:
256 printk(KERN_ERR "bogus flow type combination given !\n");
257 return -EINVAL;
258 }
259 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
260
Thomas Gleixner93f29362011-03-23 21:08:47 +0000261 irqd_set_trigger_type(d, flow_type);
262 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
263 __irq_set_handler_locked(d->irq, handle_level_irq);
264 else
265 __irq_set_handler_locked(d->irq, handle_edge_irq);
Maxime Bizone7300d02009-08-18 13:23:37 +0100266
Thomas Gleixner93f29362011-03-23 21:08:47 +0000267 return IRQ_SET_MASK_OK_NOCOPY;
Maxime Bizone7300d02009-08-18 13:23:37 +0100268}
269
270static struct irq_chip bcm63xx_internal_irq_chip = {
271 .name = "bcm63xx_ipic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000272 .irq_mask = bcm63xx_internal_irq_mask,
273 .irq_unmask = bcm63xx_internal_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100274};
275
276static struct irq_chip bcm63xx_external_irq_chip = {
277 .name = "bcm63xx_epic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000278 .irq_startup = bcm63xx_external_irq_startup,
279 .irq_shutdown = bcm63xx_external_irq_shutdown,
Maxime Bizone7300d02009-08-18 13:23:37 +0100280
Thomas Gleixner93f29362011-03-23 21:08:47 +0000281 .irq_ack = bcm63xx_external_irq_clear,
Maxime Bizone7300d02009-08-18 13:23:37 +0100282
Thomas Gleixner93f29362011-03-23 21:08:47 +0000283 .irq_mask = bcm63xx_external_irq_mask,
284 .irq_unmask = bcm63xx_external_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100285
Thomas Gleixner93f29362011-03-23 21:08:47 +0000286 .irq_set_type = bcm63xx_external_irq_set_type,
Maxime Bizone7300d02009-08-18 13:23:37 +0100287};
288
289static struct irqaction cpu_ip2_cascade_action = {
290 .handler = no_action,
291 .name = "cascade_ip2",
Wu Zhangjin5a4a4ad2011-07-23 12:41:24 +0000292 .flags = IRQF_NO_THREAD,
Maxime Bizone7300d02009-08-18 13:23:37 +0100293};
294
295void __init arch_init_irq(void)
296{
297 int i;
298
Maxime Bizonf61cced2011-11-04 19:09:31 +0100299 bcm63xx_init_irq();
Maxime Bizone7300d02009-08-18 13:23:37 +0100300 mips_cpu_irq_init();
301 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200302 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100303 handle_level_irq);
304
305 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200306 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100307 handle_edge_irq);
308
309 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
310}