Linux 2.6.33-rc8
[linux-2.6/lguest.git] / arch / mips / bcm63xx / irq.c
bloba0c5cd18c192a4aef91634b58ca851576cd2b4c6
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 <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
25 * function.
27 static void bcm63xx_irq_dispatch_internal(void)
29 u32 pending;
30 static int i;
32 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33 bcm_perf_readl(PERF_IRQSTAT_REG);
35 if (!pending)
36 return ;
38 while (1) {
39 int to_call = i;
41 i = (i + 1) & 0x1f;
42 if (pending & (1 << to_call)) {
43 do_IRQ(to_call + IRQ_INTERNAL_BASE);
44 break;
49 asmlinkage void plat_irq_dispatch(void)
51 u32 cause;
53 do {
54 cause = read_c0_cause() & read_c0_status() & ST0_IM;
56 if (!cause)
57 break;
59 if (cause & CAUSEF_IP7)
60 do_IRQ(7);
61 if (cause & CAUSEF_IP2)
62 bcm63xx_irq_dispatch_internal();
63 if (cause & CAUSEF_IP3)
64 do_IRQ(IRQ_EXT_0);
65 if (cause & CAUSEF_IP4)
66 do_IRQ(IRQ_EXT_1);
67 if (cause & CAUSEF_IP5)
68 do_IRQ(IRQ_EXT_2);
69 if (cause & CAUSEF_IP6)
70 do_IRQ(IRQ_EXT_3);
71 } while (1);
75 * internal IRQs operations: only mask/unmask on PERF irq mask
76 * register.
78 static inline void bcm63xx_internal_irq_mask(unsigned int irq)
80 u32 mask;
82 irq -= IRQ_INTERNAL_BASE;
83 mask = bcm_perf_readl(PERF_IRQMASK_REG);
84 mask &= ~(1 << irq);
85 bcm_perf_writel(mask, PERF_IRQMASK_REG);
88 static void bcm63xx_internal_irq_unmask(unsigned int irq)
90 u32 mask;
92 irq -= IRQ_INTERNAL_BASE;
93 mask = bcm_perf_readl(PERF_IRQMASK_REG);
94 mask |= (1 << irq);
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);
101 return 0;
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)
110 u32 reg;
112 irq -= IRQ_EXT_BASE;
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)
120 u32 reg;
122 irq -= IRQ_EXT_BASE;
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)
130 u32 reg;
132 irq -= IRQ_EXT_BASE;
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));
141 irq_enable_hazard();
142 bcm63xx_external_irq_unmask(irq);
143 return 0;
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)
156 u32 reg;
157 struct irq_desc *desc = irq_desc + irq;
159 irq -= IRQ_EXT_BASE;
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);
167 switch (flow_type) {
168 case IRQ_TYPE_EDGE_BOTH:
169 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
170 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
171 break;
173 case IRQ_TYPE_EDGE_RISING:
174 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
175 reg |= EXTIRQ_CFG_SENSE(irq);
176 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
177 break;
179 case IRQ_TYPE_EDGE_FALLING:
180 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
181 reg &= ~EXTIRQ_CFG_SENSE(irq);
182 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
183 break;
185 case IRQ_TYPE_LEVEL_HIGH:
186 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187 reg |= EXTIRQ_CFG_SENSE(irq);
188 break;
190 case IRQ_TYPE_LEVEL_LOW:
191 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192 reg &= ~EXTIRQ_CFG_SENSE(irq);
193 break;
195 default:
196 printk(KERN_ERR "bogus flow type combination given !\n");
197 return -EINVAL;
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;
204 } else {
205 desc->handle_irq = handle_edge_irq;
208 return 0;
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)
241 int i;
243 mips_cpu_irq_init();
244 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
245 set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
246 handle_level_irq);
248 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
249 set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
250 handle_edge_irq);
252 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);