Merge remote-tracking branch 'leds/for-mm'
[linux-2.6/next.git] / arch / mips / bcm63xx / irq.c
blob162e11b4ed755f7e227d4cfc485d40170716abdf
1 /*
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.
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
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
26 * function.
28 static void bcm63xx_irq_dispatch_internal(void)
30 u32 pending;
31 static int i;
33 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
34 bcm_perf_readl(PERF_IRQSTAT_REG);
36 if (!pending)
37 return ;
39 while (1) {
40 int to_call = i;
42 i = (i + 1) & 0x1f;
43 if (pending & (1 << to_call)) {
44 do_IRQ(to_call + IRQ_INTERNAL_BASE);
45 break;
50 asmlinkage void plat_irq_dispatch(void)
52 u32 cause;
54 do {
55 cause = read_c0_cause() & read_c0_status() & ST0_IM;
57 if (!cause)
58 break;
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);
76 * internal IRQs operations: only mask/unmask on PERF irq mask
77 * register.
79 static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
81 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
82 u32 mask;
84 mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 mask &= ~(1 << irq);
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;
92 u32 mask;
94 mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 mask |= (1 << irq);
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;
106 u32 reg;
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;
116 u32 reg;
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;
126 u32 reg;
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));
136 irq_enable_hazard();
137 bcm63xx_external_irq_unmask(d);
138 return 0;
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;
152 u32 reg;
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);
160 switch (flow_type) {
161 case IRQ_TYPE_EDGE_BOTH:
162 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
163 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
164 break;
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;
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;
178 case IRQ_TYPE_LEVEL_HIGH:
179 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
180 reg |= EXTIRQ_CFG_SENSE(irq);
181 break;
183 case IRQ_TYPE_LEVEL_LOW:
184 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
185 reg &= ~EXTIRQ_CFG_SENSE(irq);
186 break;
188 default:
189 printk(KERN_ERR "bogus flow type combination given !\n");
190 return -EINVAL;
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);
197 else
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)
230 int i;
232 mips_cpu_irq_init();
233 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
234 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
235 handle_level_irq);
237 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
238 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
239 handle_edge_irq);
241 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);