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
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
10 #include <linux/kernel.h>
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/irq.h>
15 #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>
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
28 static void bcm63xx_irq_dispatch_internal(void)
33 pending
= bcm_perf_readl(PERF_IRQMASK_REG
) &
34 bcm_perf_readl(PERF_IRQSTAT_REG
);
43 if (pending
& (1 << to_call
)) {
44 do_IRQ(to_call
+ IRQ_INTERNAL_BASE
);
50 asmlinkage
void plat_irq_dispatch(void)
55 cause
= read_c0_cause() & read_c0_status() & ST0_IM
;
60 if (cause
& CAUSEF_IP7
)
62 if (cause
& CAUSEF_IP2
)
63 bcm63xx_irq_dispatch_internal();
64 if (cause
& CAUSEF_IP3
)
66 if (cause
& CAUSEF_IP4
)
68 if (cause
& CAUSEF_IP5
)
70 if (cause
& CAUSEF_IP6
)
76 * internal IRQs operations: only mask/unmask on PERF irq mask
79 static inline void bcm63xx_internal_irq_mask(unsigned int irq
)
83 irq
-= IRQ_INTERNAL_BASE
;
84 mask
= bcm_perf_readl(PERF_IRQMASK_REG
);
86 bcm_perf_writel(mask
, PERF_IRQMASK_REG
);
89 static void bcm63xx_internal_irq_unmask(unsigned int irq
)
93 irq
-= IRQ_INTERNAL_BASE
;
94 mask
= bcm_perf_readl(PERF_IRQMASK_REG
);
96 bcm_perf_writel(mask
, PERF_IRQMASK_REG
);
99 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq
)
101 bcm63xx_internal_irq_unmask(irq
);
106 * external IRQs operations: mask/unmask and clear on PERF external
107 * irq control register.
109 static void bcm63xx_external_irq_mask(unsigned int irq
)
114 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
115 reg
&= ~EXTIRQ_CFG_MASK(irq
);
116 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
119 static void bcm63xx_external_irq_unmask(unsigned int irq
)
124 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
125 reg
|= EXTIRQ_CFG_MASK(irq
);
126 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
129 static void bcm63xx_external_irq_clear(unsigned int irq
)
134 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
135 reg
|= EXTIRQ_CFG_CLEAR(irq
);
136 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
139 static unsigned int bcm63xx_external_irq_startup(unsigned int irq
)
141 set_c0_status(0x100 << (irq
- IRQ_MIPS_BASE
));
143 bcm63xx_external_irq_unmask(irq
);
147 static void bcm63xx_external_irq_shutdown(unsigned int irq
)
149 bcm63xx_external_irq_mask(irq
);
150 clear_c0_status(0x100 << (irq
- IRQ_MIPS_BASE
));
151 irq_disable_hazard();
154 static int bcm63xx_external_irq_set_type(unsigned int irq
,
155 unsigned int flow_type
)
158 struct irq_desc
*desc
= irq_desc
+ irq
;
162 flow_type
&= IRQ_TYPE_SENSE_MASK
;
164 if (flow_type
== IRQ_TYPE_NONE
)
165 flow_type
= IRQ_TYPE_LEVEL_LOW
;
167 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
169 case IRQ_TYPE_EDGE_BOTH
:
170 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
171 reg
|= EXTIRQ_CFG_BOTHEDGE(irq
);
174 case IRQ_TYPE_EDGE_RISING
:
175 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
176 reg
|= EXTIRQ_CFG_SENSE(irq
);
177 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
180 case IRQ_TYPE_EDGE_FALLING
:
181 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
182 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
183 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
186 case IRQ_TYPE_LEVEL_HIGH
:
187 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
188 reg
|= EXTIRQ_CFG_SENSE(irq
);
191 case IRQ_TYPE_LEVEL_LOW
:
192 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
193 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
197 printk(KERN_ERR
"bogus flow type combination given !\n");
200 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
202 if (flow_type
& (IRQ_TYPE_LEVEL_LOW
| IRQ_TYPE_LEVEL_HIGH
)) {
203 desc
->status
|= IRQ_LEVEL
;
204 desc
->handle_irq
= handle_level_irq
;
206 desc
->handle_irq
= handle_edge_irq
;
212 static struct irq_chip bcm63xx_internal_irq_chip
= {
213 .name
= "bcm63xx_ipic",
214 .startup
= bcm63xx_internal_irq_startup
,
215 .shutdown
= bcm63xx_internal_irq_mask
,
217 .mask
= bcm63xx_internal_irq_mask
,
218 .mask_ack
= bcm63xx_internal_irq_mask
,
219 .unmask
= bcm63xx_internal_irq_unmask
,
222 static struct irq_chip bcm63xx_external_irq_chip
= {
223 .name
= "bcm63xx_epic",
224 .startup
= bcm63xx_external_irq_startup
,
225 .shutdown
= bcm63xx_external_irq_shutdown
,
227 .ack
= bcm63xx_external_irq_clear
,
229 .mask
= bcm63xx_external_irq_mask
,
230 .unmask
= bcm63xx_external_irq_unmask
,
232 .set_type
= bcm63xx_external_irq_set_type
,
235 static struct irqaction cpu_ip2_cascade_action
= {
236 .handler
= no_action
,
237 .name
= "cascade_ip2",
240 void __init
arch_init_irq(void)
245 for (i
= IRQ_INTERNAL_BASE
; i
< NR_IRQS
; ++i
)
246 set_irq_chip_and_handler(i
, &bcm63xx_internal_irq_chip
,
249 for (i
= IRQ_EXT_BASE
; i
< IRQ_EXT_BASE
+ 4; ++i
)
250 set_irq_chip_and_handler(i
, &bcm63xx_external_irq_chip
,
253 setup_irq(IRQ_MIPS_BASE
+ 2, &cpu_ip2_cascade_action
);