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 <asm/irq_cpu.h>
15 #include <asm/mipsregs.h>
16 #include <bcm63xx_cpu.h>
17 #include <bcm63xx_regs.h>
18 #include <bcm63xx_io.h>
19 #include <bcm63xx_irq.h>
22 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
23 * prioritize any interrupt relatively to another. the static counter
24 * will resume the loop where it ended the last time we left this
27 static void bcm63xx_irq_dispatch_internal(void)
32 pending
= bcm_perf_readl(PERF_IRQMASK_REG
) &
33 bcm_perf_readl(PERF_IRQSTAT_REG
);
42 if (pending
& (1 << to_call
)) {
43 do_IRQ(to_call
+ IRQ_INTERNAL_BASE
);
49 asmlinkage
void plat_irq_dispatch(void)
54 cause
= read_c0_cause() & read_c0_status() & ST0_IM
;
59 if (cause
& CAUSEF_IP7
)
61 if (cause
& CAUSEF_IP2
)
62 bcm63xx_irq_dispatch_internal();
63 if (cause
& CAUSEF_IP3
)
65 if (cause
& CAUSEF_IP4
)
67 if (cause
& CAUSEF_IP5
)
69 if (cause
& CAUSEF_IP6
)
75 * internal IRQs operations: only mask/unmask on PERF irq mask
78 static inline void bcm63xx_internal_irq_mask(unsigned int irq
)
82 irq
-= IRQ_INTERNAL_BASE
;
83 mask
= bcm_perf_readl(PERF_IRQMASK_REG
);
85 bcm_perf_writel(mask
, PERF_IRQMASK_REG
);
88 static void bcm63xx_internal_irq_unmask(unsigned int irq
)
92 irq
-= IRQ_INTERNAL_BASE
;
93 mask
= bcm_perf_readl(PERF_IRQMASK_REG
);
95 bcm_perf_writel(mask
, PERF_IRQMASK_REG
);
98 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq
)
100 bcm63xx_internal_irq_unmask(irq
);
105 * external IRQs operations: mask/unmask and clear on PERF external
106 * irq control register.
108 static void bcm63xx_external_irq_mask(unsigned int irq
)
113 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
114 reg
&= ~EXTIRQ_CFG_MASK(irq
);
115 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
118 static void bcm63xx_external_irq_unmask(unsigned int irq
)
123 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
124 reg
|= EXTIRQ_CFG_MASK(irq
);
125 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
128 static void bcm63xx_external_irq_clear(unsigned int irq
)
133 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
134 reg
|= EXTIRQ_CFG_CLEAR(irq
);
135 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
138 static unsigned int bcm63xx_external_irq_startup(unsigned int irq
)
140 set_c0_status(0x100 << (irq
- IRQ_MIPS_BASE
));
142 bcm63xx_external_irq_unmask(irq
);
146 static void bcm63xx_external_irq_shutdown(unsigned int irq
)
148 bcm63xx_external_irq_mask(irq
);
149 clear_c0_status(0x100 << (irq
- IRQ_MIPS_BASE
));
150 irq_disable_hazard();
153 static int bcm63xx_external_irq_set_type(unsigned int irq
,
154 unsigned int flow_type
)
157 struct irq_desc
*desc
= irq_desc
+ irq
;
161 flow_type
&= IRQ_TYPE_SENSE_MASK
;
163 if (flow_type
== IRQ_TYPE_NONE
)
164 flow_type
= IRQ_TYPE_LEVEL_LOW
;
166 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
168 case IRQ_TYPE_EDGE_BOTH
:
169 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
170 reg
|= EXTIRQ_CFG_BOTHEDGE(irq
);
173 case IRQ_TYPE_EDGE_RISING
:
174 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
175 reg
|= EXTIRQ_CFG_SENSE(irq
);
176 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
179 case IRQ_TYPE_EDGE_FALLING
:
180 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
181 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
182 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
185 case IRQ_TYPE_LEVEL_HIGH
:
186 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
187 reg
|= EXTIRQ_CFG_SENSE(irq
);
190 case IRQ_TYPE_LEVEL_LOW
:
191 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
192 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
196 printk(KERN_ERR
"bogus flow type combination given !\n");
199 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
201 if (flow_type
& (IRQ_TYPE_LEVEL_LOW
| IRQ_TYPE_LEVEL_HIGH
)) {
202 desc
->status
|= IRQ_LEVEL
;
203 desc
->handle_irq
= handle_level_irq
;
205 desc
->handle_irq
= handle_edge_irq
;
211 static struct irq_chip bcm63xx_internal_irq_chip
= {
212 .name
= "bcm63xx_ipic",
213 .startup
= bcm63xx_internal_irq_startup
,
214 .shutdown
= bcm63xx_internal_irq_mask
,
216 .mask
= bcm63xx_internal_irq_mask
,
217 .mask_ack
= bcm63xx_internal_irq_mask
,
218 .unmask
= bcm63xx_internal_irq_unmask
,
221 static struct irq_chip bcm63xx_external_irq_chip
= {
222 .name
= "bcm63xx_epic",
223 .startup
= bcm63xx_external_irq_startup
,
224 .shutdown
= bcm63xx_external_irq_shutdown
,
226 .ack
= bcm63xx_external_irq_clear
,
228 .mask
= bcm63xx_external_irq_mask
,
229 .unmask
= bcm63xx_external_irq_unmask
,
231 .set_type
= bcm63xx_external_irq_set_type
,
234 static struct irqaction cpu_ip2_cascade_action
= {
235 .handler
= no_action
,
236 .name
= "cascade_ip2",
239 void __init
arch_init_irq(void)
244 for (i
= IRQ_INTERNAL_BASE
; i
< NR_IRQS
; ++i
)
245 set_irq_chip_and_handler(i
, &bcm63xx_internal_irq_chip
,
248 for (i
= IRQ_EXT_BASE
; i
< IRQ_EXT_BASE
+ 4; ++i
)
249 set_irq_chip_and_handler(i
, &bcm63xx_external_irq_chip
,
252 setup_irq(IRQ_MIPS_BASE
+ 2, &cpu_ip2_cascade_action
);