Merge commit 'v2.6.32' into reiserfs/kill-bkl
[pandora-kernel.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 <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>
20
21 /*
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.
26  */
27 static void bcm63xx_irq_dispatch_internal(void)
28 {
29         u32 pending;
30         static int i;
31
32         pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33                 bcm_perf_readl(PERF_IRQSTAT_REG);
34
35         if (!pending)
36                 return ;
37
38         while (1) {
39                 int to_call = i;
40
41                 i = (i + 1) & 0x1f;
42                 if (pending & (1 << to_call)) {
43                         do_IRQ(to_call + IRQ_INTERNAL_BASE);
44                         break;
45                 }
46         }
47 }
48
49 asmlinkage void plat_irq_dispatch(void)
50 {
51         u32 cause;
52
53         do {
54                 cause = read_c0_cause() & read_c0_status() & ST0_IM;
55
56                 if (!cause)
57                         break;
58
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);
72 }
73
74 /*
75  * internal IRQs operations: only mask/unmask on PERF irq mask
76  * register.
77  */
78 static inline void bcm63xx_internal_irq_mask(unsigned int irq)
79 {
80         u32 mask;
81
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);
86 }
87
88 static void bcm63xx_internal_irq_unmask(unsigned int irq)
89 {
90         u32 mask;
91
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);
96 }
97
98 static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
99 {
100         bcm63xx_internal_irq_unmask(irq);
101         return 0;
102 }
103
104 /*
105  * external IRQs operations: mask/unmask and clear on PERF external
106  * irq control register.
107  */
108 static void bcm63xx_external_irq_mask(unsigned int irq)
109 {
110         u32 reg;
111
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);
116 }
117
118 static void bcm63xx_external_irq_unmask(unsigned int irq)
119 {
120         u32 reg;
121
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);
126 }
127
128 static void bcm63xx_external_irq_clear(unsigned int irq)
129 {
130         u32 reg;
131
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);
136 }
137
138 static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
139 {
140         set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
141         irq_enable_hazard();
142         bcm63xx_external_irq_unmask(irq);
143         return 0;
144 }
145
146 static void bcm63xx_external_irq_shutdown(unsigned int irq)
147 {
148         bcm63xx_external_irq_mask(irq);
149         clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
150         irq_disable_hazard();
151 }
152
153 static int bcm63xx_external_irq_set_type(unsigned int irq,
154                                          unsigned int flow_type)
155 {
156         u32 reg;
157         struct irq_desc *desc = irq_desc + irq;
158
159         irq -= IRQ_EXT_BASE;
160
161         flow_type &= IRQ_TYPE_SENSE_MASK;
162
163         if (flow_type == IRQ_TYPE_NONE)
164                 flow_type = IRQ_TYPE_LEVEL_LOW;
165
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;
172
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;
178
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;
184
185         case IRQ_TYPE_LEVEL_HIGH:
186                 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187                 reg |= EXTIRQ_CFG_SENSE(irq);
188                 break;
189
190         case IRQ_TYPE_LEVEL_LOW:
191                 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192                 reg &= ~EXTIRQ_CFG_SENSE(irq);
193                 break;
194
195         default:
196                 printk(KERN_ERR "bogus flow type combination given !\n");
197                 return -EINVAL;
198         }
199         bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
200
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;
206         }
207
208         return 0;
209 }
210
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,
215
216         .mask           = bcm63xx_internal_irq_mask,
217         .mask_ack       = bcm63xx_internal_irq_mask,
218         .unmask         = bcm63xx_internal_irq_unmask,
219 };
220
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,
225
226         .ack            = bcm63xx_external_irq_clear,
227
228         .mask           = bcm63xx_external_irq_mask,
229         .unmask         = bcm63xx_external_irq_unmask,
230
231         .set_type       = bcm63xx_external_irq_set_type,
232 };
233
234 static struct irqaction cpu_ip2_cascade_action = {
235         .handler        = no_action,
236         .name           = "cascade_ip2",
237 };
238
239 void __init arch_init_irq(void)
240 {
241         int i;
242
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);
247
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);
251
252         setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
253 }