blob: 162e11b4ed755f7e227d4cfc485d40170716abdf [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
22/*
23 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
24 * prioritize any interrupt relatively to another. the static counter
25 * will resume the loop where it ended the last time we left this
26 * function.
27 */
28static void bcm63xx_irq_dispatch_internal(void)
29{
30 u32 pending;
31 static int i;
32
33 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
34 bcm_perf_readl(PERF_IRQSTAT_REG);
35
36 if (!pending)
37 return ;
38
39 while (1) {
40 int to_call = i;
41
42 i = (i + 1) & 0x1f;
43 if (pending & (1 << to_call)) {
44 do_IRQ(to_call + IRQ_INTERNAL_BASE);
45 break;
46 }
47 }
48}
49
50asmlinkage void plat_irq_dispatch(void)
51{
52 u32 cause;
53
54 do {
55 cause = read_c0_cause() & read_c0_status() & ST0_IM;
56
57 if (!cause)
58 break;
59
60 if (cause & CAUSEF_IP7)
61 do_IRQ(7);
62 if (cause & CAUSEF_IP2)
63 bcm63xx_irq_dispatch_internal();
64 if (cause & CAUSEF_IP3)
65 do_IRQ(IRQ_EXT_0);
66 if (cause & CAUSEF_IP4)
67 do_IRQ(IRQ_EXT_1);
68 if (cause & CAUSEF_IP5)
69 do_IRQ(IRQ_EXT_2);
70 if (cause & CAUSEF_IP6)
71 do_IRQ(IRQ_EXT_3);
72 } while (1);
73}
74
75/*
76 * internal IRQs operations: only mask/unmask on PERF irq mask
77 * register.
78 */
Thomas Gleixner93f29362011-03-23 21:08:47 +000079static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +010080{
Thomas Gleixner93f29362011-03-23 21:08:47 +000081 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +010082 u32 mask;
83
Maxime Bizone7300d02009-08-18 13:23:37 +010084 mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 mask &= ~(1 << irq);
86 bcm_perf_writel(mask, PERF_IRQMASK_REG);
87}
88
Thomas Gleixner93f29362011-03-23 21:08:47 +000089static void bcm63xx_internal_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +010090{
Thomas Gleixner93f29362011-03-23 21:08:47 +000091 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +010092 u32 mask;
93
Maxime Bizone7300d02009-08-18 13:23:37 +010094 mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 mask |= (1 << irq);
96 bcm_perf_writel(mask, PERF_IRQMASK_REG);
97}
98
Maxime Bizone7300d02009-08-18 13:23:37 +010099/*
100 * external IRQs operations: mask/unmask and clear on PERF external
101 * irq control register.
102 */
Thomas Gleixner93f29362011-03-23 21:08:47 +0000103static void bcm63xx_external_irq_mask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100104{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000105 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100106 u32 reg;
107
Maxime Bizone7300d02009-08-18 13:23:37 +0100108 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
109 reg &= ~EXTIRQ_CFG_MASK(irq);
110 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
111}
112
Thomas Gleixner93f29362011-03-23 21:08:47 +0000113static void bcm63xx_external_irq_unmask(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100114{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000115 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100116 u32 reg;
117
Maxime Bizone7300d02009-08-18 13:23:37 +0100118 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
119 reg |= EXTIRQ_CFG_MASK(irq);
120 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
121}
122
Thomas Gleixner93f29362011-03-23 21:08:47 +0000123static void bcm63xx_external_irq_clear(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100124{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000125 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100126 u32 reg;
127
Maxime Bizone7300d02009-08-18 13:23:37 +0100128 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
129 reg |= EXTIRQ_CFG_CLEAR(irq);
130 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
131}
132
Thomas Gleixner93f29362011-03-23 21:08:47 +0000133static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100134{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000135 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
Maxime Bizone7300d02009-08-18 13:23:37 +0100136 irq_enable_hazard();
Thomas Gleixner93f29362011-03-23 21:08:47 +0000137 bcm63xx_external_irq_unmask(d);
Maxime Bizone7300d02009-08-18 13:23:37 +0100138 return 0;
139}
140
Thomas Gleixner93f29362011-03-23 21:08:47 +0000141static void bcm63xx_external_irq_shutdown(struct irq_data *d)
Maxime Bizone7300d02009-08-18 13:23:37 +0100142{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000143 bcm63xx_external_irq_mask(d);
144 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
Maxime Bizone7300d02009-08-18 13:23:37 +0100145 irq_disable_hazard();
146}
147
Thomas Gleixner93f29362011-03-23 21:08:47 +0000148static int bcm63xx_external_irq_set_type(struct irq_data *d,
Maxime Bizone7300d02009-08-18 13:23:37 +0100149 unsigned int flow_type)
150{
Thomas Gleixner93f29362011-03-23 21:08:47 +0000151 unsigned int irq = d->irq - IRQ_EXT_BASE;
Maxime Bizone7300d02009-08-18 13:23:37 +0100152 u32 reg;
Maxime Bizone7300d02009-08-18 13:23:37 +0100153
154 flow_type &= IRQ_TYPE_SENSE_MASK;
155
156 if (flow_type == IRQ_TYPE_NONE)
157 flow_type = IRQ_TYPE_LEVEL_LOW;
158
159 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
160 switch (flow_type) {
161 case IRQ_TYPE_EDGE_BOTH:
162 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
163 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
164 break;
165
166 case IRQ_TYPE_EDGE_RISING:
167 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
168 reg |= EXTIRQ_CFG_SENSE(irq);
169 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
170 break;
171
172 case IRQ_TYPE_EDGE_FALLING:
173 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
174 reg &= ~EXTIRQ_CFG_SENSE(irq);
175 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
176 break;
177
178 case IRQ_TYPE_LEVEL_HIGH:
179 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
180 reg |= EXTIRQ_CFG_SENSE(irq);
181 break;
182
183 case IRQ_TYPE_LEVEL_LOW:
184 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
185 reg &= ~EXTIRQ_CFG_SENSE(irq);
186 break;
187
188 default:
189 printk(KERN_ERR "bogus flow type combination given !\n");
190 return -EINVAL;
191 }
192 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
193
Thomas Gleixner93f29362011-03-23 21:08:47 +0000194 irqd_set_trigger_type(d, flow_type);
195 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
196 __irq_set_handler_locked(d->irq, handle_level_irq);
197 else
198 __irq_set_handler_locked(d->irq, handle_edge_irq);
Maxime Bizone7300d02009-08-18 13:23:37 +0100199
Thomas Gleixner93f29362011-03-23 21:08:47 +0000200 return IRQ_SET_MASK_OK_NOCOPY;
Maxime Bizone7300d02009-08-18 13:23:37 +0100201}
202
203static struct irq_chip bcm63xx_internal_irq_chip = {
204 .name = "bcm63xx_ipic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000205 .irq_mask = bcm63xx_internal_irq_mask,
206 .irq_unmask = bcm63xx_internal_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100207};
208
209static struct irq_chip bcm63xx_external_irq_chip = {
210 .name = "bcm63xx_epic",
Thomas Gleixner93f29362011-03-23 21:08:47 +0000211 .irq_startup = bcm63xx_external_irq_startup,
212 .irq_shutdown = bcm63xx_external_irq_shutdown,
Maxime Bizone7300d02009-08-18 13:23:37 +0100213
Thomas Gleixner93f29362011-03-23 21:08:47 +0000214 .irq_ack = bcm63xx_external_irq_clear,
Maxime Bizone7300d02009-08-18 13:23:37 +0100215
Thomas Gleixner93f29362011-03-23 21:08:47 +0000216 .irq_mask = bcm63xx_external_irq_mask,
217 .irq_unmask = bcm63xx_external_irq_unmask,
Maxime Bizone7300d02009-08-18 13:23:37 +0100218
Thomas Gleixner93f29362011-03-23 21:08:47 +0000219 .irq_set_type = bcm63xx_external_irq_set_type,
Maxime Bizone7300d02009-08-18 13:23:37 +0100220};
221
222static struct irqaction cpu_ip2_cascade_action = {
223 .handler = no_action,
224 .name = "cascade_ip2",
Wu Zhangjin5a4a4ad2011-07-23 12:41:24 +0000225 .flags = IRQF_NO_THREAD,
Maxime Bizone7300d02009-08-18 13:23:37 +0100226};
227
228void __init arch_init_irq(void)
229{
230 int i;
231
232 mips_cpu_irq_init();
233 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200234 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100235 handle_level_irq);
236
237 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
Thomas Gleixnere4ec7982011-03-27 15:19:28 +0200238 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
Maxime Bizone7300d02009-08-18 13:23:37 +0100239 handle_edge_irq);
240
241 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
242}