Merge branch 'master' into upstream-fixes
[pandora-kernel.git] / arch / mips / tx4938 / common / irq.c
1 /*
2  * linux/arch/mips/tx4938/common/irq.c
3  *
4  * Common tx4938 irq handler
5  * Copyright (C) 2000-2001 Toshiba Corporation
6  *
7  * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
8  * terms of the GNU General Public License version 2. This program is
9  * licensed "as is" without any warranty of any kind, whether express
10  * or implied.
11  *
12  * Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
13  */
14 #include <linux/errno.h>
15 #include <linux/init.h>
16 #include <linux/kernel_stat.h>
17 #include <linux/module.h>
18 #include <linux/signal.h>
19 #include <linux/sched.h>
20 #include <linux/types.h>
21 #include <linux/interrupt.h>
22 #include <linux/ioport.h>
23 #include <linux/timex.h>
24 #include <linux/slab.h>
25 #include <linux/random.h>
26 #include <linux/irq.h>
27 #include <asm/bitops.h>
28 #include <asm/bootinfo.h>
29 #include <asm/io.h>
30 #include <asm/irq.h>
31 #include <asm/mipsregs.h>
32 #include <asm/system.h>
33 #include <asm/wbflush.h>
34 #include <asm/tx4938/rbtx4938.h>
35
36 /**********************************************************************************/
37 /* Forwad definitions for all pic's                                               */
38 /**********************************************************************************/
39
40 static unsigned int tx4938_irq_cp0_startup(unsigned int irq);
41 static void tx4938_irq_cp0_shutdown(unsigned int irq);
42 static void tx4938_irq_cp0_enable(unsigned int irq);
43 static void tx4938_irq_cp0_disable(unsigned int irq);
44 static void tx4938_irq_cp0_mask_and_ack(unsigned int irq);
45 static void tx4938_irq_cp0_end(unsigned int irq);
46
47 static unsigned int tx4938_irq_pic_startup(unsigned int irq);
48 static void tx4938_irq_pic_shutdown(unsigned int irq);
49 static void tx4938_irq_pic_enable(unsigned int irq);
50 static void tx4938_irq_pic_disable(unsigned int irq);
51 static void tx4938_irq_pic_mask_and_ack(unsigned int irq);
52 static void tx4938_irq_pic_end(unsigned int irq);
53
54 /**********************************************************************************/
55 /* Kernel structs for all pic's                                                   */
56 /**********************************************************************************/
57 DEFINE_SPINLOCK(tx4938_cp0_lock);
58 DEFINE_SPINLOCK(tx4938_pic_lock);
59
60 #define TX4938_CP0_NAME "TX4938-CP0"
61 static struct irq_chip tx4938_irq_cp0_type = {
62         .typename = TX4938_CP0_NAME,
63         .startup = tx4938_irq_cp0_startup,
64         .shutdown = tx4938_irq_cp0_shutdown,
65         .enable = tx4938_irq_cp0_enable,
66         .disable = tx4938_irq_cp0_disable,
67         .ack = tx4938_irq_cp0_mask_and_ack,
68         .end = tx4938_irq_cp0_end,
69         .set_affinity = NULL
70 };
71
72 #define TX4938_PIC_NAME "TX4938-PIC"
73 static struct irq_chip tx4938_irq_pic_type = {
74         .typename = TX4938_PIC_NAME,
75         .startup = tx4938_irq_pic_startup,
76         .shutdown = tx4938_irq_pic_shutdown,
77         .enable = tx4938_irq_pic_enable,
78         .disable = tx4938_irq_pic_disable,
79         .ack = tx4938_irq_pic_mask_and_ack,
80         .end = tx4938_irq_pic_end,
81         .set_affinity = NULL
82 };
83
84 static struct irqaction tx4938_irq_pic_action = {
85         .handler = no_action,
86         .flags = 0,
87         .mask = CPU_MASK_NONE,
88         .name = TX4938_PIC_NAME
89 };
90
91 /**********************************************************************************/
92 /* Functions for cp0                                                              */
93 /**********************************************************************************/
94
95 #define tx4938_irq_cp0_mask(irq) ( 1 << ( irq-TX4938_IRQ_CP0_BEG+8 ) )
96
97 static void __init
98 tx4938_irq_cp0_init(void)
99 {
100         int i;
101
102         for (i = TX4938_IRQ_CP0_BEG; i <= TX4938_IRQ_CP0_END; i++) {
103                 irq_desc[i].status = IRQ_DISABLED;
104                 irq_desc[i].action = 0;
105                 irq_desc[i].depth = 1;
106                 irq_desc[i].chip = &tx4938_irq_cp0_type;
107         }
108 }
109
110 static unsigned int
111 tx4938_irq_cp0_startup(unsigned int irq)
112 {
113         tx4938_irq_cp0_enable(irq);
114
115         return 0;
116 }
117
118 static void
119 tx4938_irq_cp0_shutdown(unsigned int irq)
120 {
121         tx4938_irq_cp0_disable(irq);
122 }
123
124 static void
125 tx4938_irq_cp0_enable(unsigned int irq)
126 {
127         unsigned long flags;
128
129         spin_lock_irqsave(&tx4938_cp0_lock, flags);
130
131         set_c0_status(tx4938_irq_cp0_mask(irq));
132
133         spin_unlock_irqrestore(&tx4938_cp0_lock, flags);
134 }
135
136 static void
137 tx4938_irq_cp0_disable(unsigned int irq)
138 {
139         unsigned long flags;
140
141         spin_lock_irqsave(&tx4938_cp0_lock, flags);
142
143         clear_c0_status(tx4938_irq_cp0_mask(irq));
144
145         spin_unlock_irqrestore(&tx4938_cp0_lock, flags);
146 }
147
148 static void
149 tx4938_irq_cp0_mask_and_ack(unsigned int irq)
150 {
151         tx4938_irq_cp0_disable(irq);
152 }
153
154 static void
155 tx4938_irq_cp0_end(unsigned int irq)
156 {
157         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
158                 tx4938_irq_cp0_enable(irq);
159         }
160 }
161
162 /**********************************************************************************/
163 /* Functions for pic                                                              */
164 /**********************************************************************************/
165
166 u32
167 tx4938_irq_pic_addr(int irq)
168 {
169         /* MVMCP -- need to formulize this */
170         irq -= TX4938_IRQ_PIC_BEG;
171
172         switch (irq) {
173         case 17:
174         case 16:
175         case 1:
176         case 0:{
177                         return (TX4938_MKA(TX4938_IRC_IRLVL0));
178                 }
179         case 19:
180         case 18:
181         case 3:
182         case 2:{
183                         return (TX4938_MKA(TX4938_IRC_IRLVL1));
184                 }
185         case 21:
186         case 20:
187         case 5:
188         case 4:{
189                         return (TX4938_MKA(TX4938_IRC_IRLVL2));
190                 }
191         case 23:
192         case 22:
193         case 7:
194         case 6:{
195                         return (TX4938_MKA(TX4938_IRC_IRLVL3));
196                 }
197         case 25:
198         case 24:
199         case 9:
200         case 8:{
201                         return (TX4938_MKA(TX4938_IRC_IRLVL4));
202                 }
203         case 27:
204         case 26:
205         case 11:
206         case 10:{
207                         return (TX4938_MKA(TX4938_IRC_IRLVL5));
208                 }
209         case 29:
210         case 28:
211         case 13:
212         case 12:{
213                         return (TX4938_MKA(TX4938_IRC_IRLVL6));
214                 }
215         case 31:
216         case 30:
217         case 15:
218         case 14:{
219                         return (TX4938_MKA(TX4938_IRC_IRLVL7));
220                 }
221         }
222
223         return 0;
224 }
225
226 u32
227 tx4938_irq_pic_mask(int irq)
228 {
229         /* MVMCP -- need to formulize this */
230         irq -= TX4938_IRQ_PIC_BEG;
231
232         switch (irq) {
233         case 31:
234         case 29:
235         case 27:
236         case 25:
237         case 23:
238         case 21:
239         case 19:
240         case 17:{
241                         return (0x07000000);
242                 }
243         case 30:
244         case 28:
245         case 26:
246         case 24:
247         case 22:
248         case 20:
249         case 18:
250         case 16:{
251                         return (0x00070000);
252                 }
253         case 15:
254         case 13:
255         case 11:
256         case 9:
257         case 7:
258         case 5:
259         case 3:
260         case 1:{
261                         return (0x00000700);
262                 }
263         case 14:
264         case 12:
265         case 10:
266         case 8:
267         case 6:
268         case 4:
269         case 2:
270         case 0:{
271                         return (0x00000007);
272                 }
273         }
274         return 0x00000000;
275 }
276
277 static void
278 tx4938_irq_pic_modify(unsigned pic_reg, unsigned clr_bits, unsigned set_bits)
279 {
280         unsigned long val = 0;
281
282         val = TX4938_RD(pic_reg);
283         val &= (~clr_bits);
284         val |= (set_bits);
285         TX4938_WR(pic_reg, val);
286         mmiowb();
287         TX4938_RD(pic_reg);
288 }
289
290 static void __init
291 tx4938_irq_pic_init(void)
292 {
293         unsigned long flags;
294         int i;
295
296         for (i = TX4938_IRQ_PIC_BEG; i <= TX4938_IRQ_PIC_END; i++) {
297                 irq_desc[i].status = IRQ_DISABLED;
298                 irq_desc[i].action = 0;
299                 irq_desc[i].depth = 2;
300                 irq_desc[i].chip = &tx4938_irq_pic_type;
301         }
302
303         setup_irq(TX4938_IRQ_NEST_PIC_ON_CP0, &tx4938_irq_pic_action);
304
305         spin_lock_irqsave(&tx4938_pic_lock, flags);
306
307         TX4938_WR(0xff1ff640, 0x6);     /* irq level mask -- only accept hightest */
308         TX4938_WR(0xff1ff600, TX4938_RD(0xff1ff600) | 0x1);     /* irq enable */
309
310         spin_unlock_irqrestore(&tx4938_pic_lock, flags);
311 }
312
313 static unsigned int
314 tx4938_irq_pic_startup(unsigned int irq)
315 {
316         tx4938_irq_pic_enable(irq);
317
318         return 0;
319 }
320
321 static void
322 tx4938_irq_pic_shutdown(unsigned int irq)
323 {
324         tx4938_irq_pic_disable(irq);
325 }
326
327 static void
328 tx4938_irq_pic_enable(unsigned int irq)
329 {
330         unsigned long flags;
331
332         spin_lock_irqsave(&tx4938_pic_lock, flags);
333
334         tx4938_irq_pic_modify(tx4938_irq_pic_addr(irq), 0,
335                               tx4938_irq_pic_mask(irq));
336
337         spin_unlock_irqrestore(&tx4938_pic_lock, flags);
338 }
339
340 static void
341 tx4938_irq_pic_disable(unsigned int irq)
342 {
343         unsigned long flags;
344
345         spin_lock_irqsave(&tx4938_pic_lock, flags);
346
347         tx4938_irq_pic_modify(tx4938_irq_pic_addr(irq),
348                               tx4938_irq_pic_mask(irq), 0);
349
350         spin_unlock_irqrestore(&tx4938_pic_lock, flags);
351 }
352
353 static void
354 tx4938_irq_pic_mask_and_ack(unsigned int irq)
355 {
356         tx4938_irq_pic_disable(irq);
357 }
358
359 static void
360 tx4938_irq_pic_end(unsigned int irq)
361 {
362         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
363                 tx4938_irq_pic_enable(irq);
364         }
365 }
366
367 /**********************************************************************************/
368 /* Main init functions                                                            */
369 /**********************************************************************************/
370
371 void __init
372 tx4938_irq_init(void)
373 {
374         tx4938_irq_cp0_init();
375         tx4938_irq_pic_init();
376 }
377
378 int
379 tx4938_irq_nested(void)
380 {
381         int sw_irq = 0;
382         u32 level2;
383
384         level2 = TX4938_RD(0xff1ff6a0);
385         if ((level2 & 0x10000) == 0) {
386                 level2 &= 0x1f;
387                 sw_irq = TX4938_IRQ_PIC_BEG + level2;
388                 if (sw_irq == 26) {
389                         {
390                                 extern int toshiba_rbtx4938_irq_nested(int sw_irq);
391                                 sw_irq = toshiba_rbtx4938_irq_nested(sw_irq);
392                         }
393                 }
394         }
395
396         wbflush();
397         return sw_irq;
398 }
399
400 asmlinkage void plat_irq_dispatch(void)
401 {
402         unsigned int pending = read_c0_cause() & read_c0_status();
403
404         if (pending & STATUSF_IP7)
405                 do_IRQ(TX4938_IRQ_CPU_TIMER);
406         else if (pending & STATUSF_IP2) {
407                 int irq = tx4938_irq_nested();
408                 if (irq)
409                         do_IRQ(irq);
410                 else
411                         spurious_interrupt();
412         } else if (pending & STATUSF_IP1)
413                 do_IRQ(TX4938_IRQ_USER1);
414         else if (pending & STATUSF_IP0)
415                 do_IRQ(TX4938_IRQ_USER0);
416 }