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(struct irq_data
*d
)
81 unsigned int irq
= d
->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(struct irq_data
*d
)
91 unsigned int irq
= d
->irq
- IRQ_INTERNAL_BASE
;
94 mask
= bcm_perf_readl(PERF_IRQMASK_REG
);
96 bcm_perf_writel(mask
, PERF_IRQMASK_REG
);
100 * external IRQs operations: mask/unmask and clear on PERF external
101 * irq control register.
103 static void bcm63xx_external_irq_mask(struct irq_data
*d
)
105 unsigned int irq
= d
->irq
- IRQ_EXT_BASE
;
108 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
109 reg
&= ~EXTIRQ_CFG_MASK(irq
);
110 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
113 static void bcm63xx_external_irq_unmask(struct irq_data
*d
)
115 unsigned int irq
= d
->irq
- IRQ_EXT_BASE
;
118 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
119 reg
|= EXTIRQ_CFG_MASK(irq
);
120 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
123 static void bcm63xx_external_irq_clear(struct irq_data
*d
)
125 unsigned int irq
= d
->irq
- IRQ_EXT_BASE
;
128 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
129 reg
|= EXTIRQ_CFG_CLEAR(irq
);
130 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
133 static unsigned int bcm63xx_external_irq_startup(struct irq_data
*d
)
135 set_c0_status(0x100 << (d
->irq
- IRQ_MIPS_BASE
));
137 bcm63xx_external_irq_unmask(d
);
141 static void bcm63xx_external_irq_shutdown(struct irq_data
*d
)
143 bcm63xx_external_irq_mask(d
);
144 clear_c0_status(0x100 << (d
->irq
- IRQ_MIPS_BASE
));
145 irq_disable_hazard();
148 static int bcm63xx_external_irq_set_type(struct irq_data
*d
,
149 unsigned int flow_type
)
151 unsigned int irq
= d
->irq
- IRQ_EXT_BASE
;
154 flow_type
&= IRQ_TYPE_SENSE_MASK
;
156 if (flow_type
== IRQ_TYPE_NONE
)
157 flow_type
= IRQ_TYPE_LEVEL_LOW
;
159 reg
= bcm_perf_readl(PERF_EXTIRQ_CFG_REG
);
161 case IRQ_TYPE_EDGE_BOTH
:
162 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
163 reg
|= EXTIRQ_CFG_BOTHEDGE(irq
);
166 case IRQ_TYPE_EDGE_RISING
:
167 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
168 reg
|= EXTIRQ_CFG_SENSE(irq
);
169 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
172 case IRQ_TYPE_EDGE_FALLING
:
173 reg
&= ~EXTIRQ_CFG_LEVELSENSE(irq
);
174 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
175 reg
&= ~EXTIRQ_CFG_BOTHEDGE(irq
);
178 case IRQ_TYPE_LEVEL_HIGH
:
179 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
180 reg
|= EXTIRQ_CFG_SENSE(irq
);
183 case IRQ_TYPE_LEVEL_LOW
:
184 reg
|= EXTIRQ_CFG_LEVELSENSE(irq
);
185 reg
&= ~EXTIRQ_CFG_SENSE(irq
);
189 printk(KERN_ERR
"bogus flow type combination given !\n");
192 bcm_perf_writel(reg
, PERF_EXTIRQ_CFG_REG
);
194 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
);
198 __irq_set_handler_locked(d
->irq
, handle_edge_irq
);
200 return IRQ_SET_MASK_OK_NOCOPY
;
203 static struct irq_chip bcm63xx_internal_irq_chip
= {
204 .name
= "bcm63xx_ipic",
205 .irq_mask
= bcm63xx_internal_irq_mask
,
206 .irq_unmask
= bcm63xx_internal_irq_unmask
,
209 static struct irq_chip bcm63xx_external_irq_chip
= {
210 .name
= "bcm63xx_epic",
211 .irq_startup
= bcm63xx_external_irq_startup
,
212 .irq_shutdown
= bcm63xx_external_irq_shutdown
,
214 .irq_ack
= bcm63xx_external_irq_clear
,
216 .irq_mask
= bcm63xx_external_irq_mask
,
217 .irq_unmask
= bcm63xx_external_irq_unmask
,
219 .irq_set_type
= bcm63xx_external_irq_set_type
,
222 static struct irqaction cpu_ip2_cascade_action
= {
223 .handler
= no_action
,
224 .name
= "cascade_ip2",
225 .flags
= IRQF_NO_THREAD
,
228 void __init
arch_init_irq(void)
233 for (i
= IRQ_INTERNAL_BASE
; i
< NR_IRQS
; ++i
)
234 irq_set_chip_and_handler(i
, &bcm63xx_internal_irq_chip
,
237 for (i
= IRQ_EXT_BASE
; i
< IRQ_EXT_BASE
+ 4; ++i
)
238 irq_set_chip_and_handler(i
, &bcm63xx_external_irq_chip
,
241 setup_irq(IRQ_MIPS_BASE
+ 2, &cpu_ip2_cascade_action
);