Merge branch 'for-2.6.39' into for-2.6.40
[deliverable/linux.git] / arch / mips / bcm63xx / irq.c
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.
5 *
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
9
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>
21
22 /*
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.
27 */
28 static void bcm63xx_irq_dispatch_internal(void)
29 {
30 u32 pending;
31 static int i;
32
33 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
34 bcm_perf_readl(PERF_IRQSTAT_REG);
35
36 if (!pending)
37 return ;
38
39 while (1) {
40 int to_call = i;
41
42 i = (i + 1) & 0x1f;
43 if (pending & (1 << to_call)) {
44 do_IRQ(to_call + IRQ_INTERNAL_BASE);
45 break;
46 }
47 }
48 }
49
50 asmlinkage void plat_irq_dispatch(void)
51 {
52 u32 cause;
53
54 do {
55 cause = read_c0_cause() & read_c0_status() & ST0_IM;
56
57 if (!cause)
58 break;
59
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);
73 }
74
75 /*
76 * internal IRQs operations: only mask/unmask on PERF irq mask
77 * register.
78 */
79 static inline void bcm63xx_internal_irq_mask(struct irq_data *d)
80 {
81 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
82 u32 mask;
83
84 mask = bcm_perf_readl(PERF_IRQMASK_REG);
85 mask &= ~(1 << irq);
86 bcm_perf_writel(mask, PERF_IRQMASK_REG);
87 }
88
89 static void bcm63xx_internal_irq_unmask(struct irq_data *d)
90 {
91 unsigned int irq = d->irq - IRQ_INTERNAL_BASE;
92 u32 mask;
93
94 mask = bcm_perf_readl(PERF_IRQMASK_REG);
95 mask |= (1 << irq);
96 bcm_perf_writel(mask, PERF_IRQMASK_REG);
97 }
98
99 /*
100 * external IRQs operations: mask/unmask and clear on PERF external
101 * irq control register.
102 */
103 static void bcm63xx_external_irq_mask(struct irq_data *d)
104 {
105 unsigned int irq = d->irq - IRQ_EXT_BASE;
106 u32 reg;
107
108 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
109 reg &= ~EXTIRQ_CFG_MASK(irq);
110 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
111 }
112
113 static void bcm63xx_external_irq_unmask(struct irq_data *d)
114 {
115 unsigned int irq = d->irq - IRQ_EXT_BASE;
116 u32 reg;
117
118 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
119 reg |= EXTIRQ_CFG_MASK(irq);
120 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
121 }
122
123 static void bcm63xx_external_irq_clear(struct irq_data *d)
124 {
125 unsigned int irq = d->irq - IRQ_EXT_BASE;
126 u32 reg;
127
128 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
129 reg |= EXTIRQ_CFG_CLEAR(irq);
130 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
131 }
132
133 static unsigned int bcm63xx_external_irq_startup(struct irq_data *d)
134 {
135 set_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
136 irq_enable_hazard();
137 bcm63xx_external_irq_unmask(d);
138 return 0;
139 }
140
141 static void bcm63xx_external_irq_shutdown(struct irq_data *d)
142 {
143 bcm63xx_external_irq_mask(d);
144 clear_c0_status(0x100 << (d->irq - IRQ_MIPS_BASE));
145 irq_disable_hazard();
146 }
147
148 static int bcm63xx_external_irq_set_type(struct irq_data *d,
149 unsigned int flow_type)
150 {
151 unsigned int irq = d->irq - IRQ_EXT_BASE;
152 u32 reg;
153
154 flow_type &= IRQ_TYPE_SENSE_MASK;
155
156 if (flow_type == IRQ_TYPE_NONE)
157 flow_type = IRQ_TYPE_LEVEL_LOW;
158
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;
165
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;
171
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;
177
178 case IRQ_TYPE_LEVEL_HIGH:
179 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
180 reg |= EXTIRQ_CFG_SENSE(irq);
181 break;
182
183 case IRQ_TYPE_LEVEL_LOW:
184 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
185 reg &= ~EXTIRQ_CFG_SENSE(irq);
186 break;
187
188 default:
189 printk(KERN_ERR "bogus flow type combination given !\n");
190 return -EINVAL;
191 }
192 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
193
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);
199
200 return IRQ_SET_MASK_OK_NOCOPY;
201 }
202
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,
207 };
208
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,
213
214 .irq_ack = bcm63xx_external_irq_clear,
215
216 .irq_mask = bcm63xx_external_irq_mask,
217 .irq_unmask = bcm63xx_external_irq_unmask,
218
219 .irq_set_type = bcm63xx_external_irq_set_type,
220 };
221
222 static struct irqaction cpu_ip2_cascade_action = {
223 .handler = no_action,
224 .name = "cascade_ip2",
225 };
226
227 void __init arch_init_irq(void)
228 {
229 int i;
230
231 mips_cpu_irq_init();
232 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
233 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
234 handle_level_irq);
235
236 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
237 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
238 handle_edge_irq);
239
240 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
241 }
This page took 0.037097 seconds and 6 git commands to generate.