VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / arch / mips / vr41xx / common / giu.c
1 /*
2  *  giu.c, General-purpose I/O Unit Interrupt routines for NEC VR4100 series.
3  *
4  *  Copyright (C) 2002 MontaVista Software Inc.
5  *    Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com>
6  *  Copyright (C) 2003-2004  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 /*
23  * Changes:
24  *  MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com>
25  *  - New creation, NEC VR4111, VR4121, VR4122 and VR4131 are supported.
26  *
27  *  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
28  *  - Added support for NEC VR4133.
29  *  - Removed board_irq_init.
30  */
31 #include <linux/config.h>
32 #include <linux/errno.h>
33 #include <linux/init.h>
34 #include <linux/irq.h>
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/smp.h>
38 #include <linux/types.h>
39
40 #include <asm/cpu.h>
41 #include <asm/io.h>
42 #include <asm/vr41xx/vr41xx.h>
43
44 #define GIUIOSELL_TYPE1 KSEG1ADDR(0x0b000100)
45 #define GIUIOSELL_TYPE2 KSEG1ADDR(0x0f000140)
46
47 #define GIUIOSELL       0x00
48 #define GIUIOSELH       0x02
49 #define GIUINTSTATL     0x08
50 #define GIUINTSTATH     0x0a
51 #define GIUINTENL       0x0c
52 #define GIUINTENH       0x0e
53 #define GIUINTTYPL      0x10
54 #define GIUINTTYPH      0x12
55 #define GIUINTALSELL    0x14
56 #define GIUINTALSELH    0x16
57 #define GIUINTHTSELL    0x18
58 #define GIUINTHTSELH    0x1a
59 #define GIUFEDGEINHL    0x20
60 #define GIUFEDGEINHH    0x22
61 #define GIUREDGEINHL    0x24
62 #define GIUREDGEINHH    0x26
63
64 static uint32_t giu_base;
65
66 #define read_giuint(offset)             readw(giu_base + (offset))
67 #define write_giuint(val, offset)       writew((val), giu_base + (offset))
68
69 #define GIUINT_HIGH_OFFSET      16
70
71 static inline uint16_t set_giuint(uint8_t offset, uint16_t set)
72 {
73         uint16_t res;
74
75         res = read_giuint(offset);
76         res |= set;
77         write_giuint(res, offset);
78
79         return res;
80 }
81
82 static inline uint16_t clear_giuint(uint8_t offset, uint16_t clear)
83 {
84         uint16_t res;
85
86         res = read_giuint(offset);
87         res &= ~clear;
88         write_giuint(res, offset);
89
90         return res;
91 }
92
93 static unsigned int startup_giuint_low_irq(unsigned int irq)
94 {
95         unsigned int pin;
96
97         pin = GIU_IRQ_TO_PIN(irq);
98         write_giuint((uint16_t)1 << pin, GIUINTSTATL);
99         set_giuint(GIUINTENL, (uint16_t)1 << pin);
100
101         return 0;
102 }
103
104 static void shutdown_giuint_low_irq(unsigned int irq)
105 {
106         clear_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq));
107 }
108
109 static void enable_giuint_low_irq(unsigned int irq)
110 {
111         set_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq));
112 }
113
114 #define disable_giuint_low_irq  shutdown_giuint_low_irq
115
116 static void ack_giuint_low_irq(unsigned int irq)
117 {
118         unsigned int pin;
119
120         pin = GIU_IRQ_TO_PIN(irq);
121         clear_giuint(GIUINTENL, (uint16_t)1 << pin);
122         write_giuint((uint16_t)1 << pin, GIUINTSTATL);
123 }
124
125 static void end_giuint_low_irq(unsigned int irq)
126 {
127         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
128                 set_giuint(GIUINTENL, (uint16_t)1 << GIU_IRQ_TO_PIN(irq));
129 }
130
131 static struct hw_interrupt_type giuint_low_irq_type = {
132         .typename       = "GIUINTL",
133         .startup        = startup_giuint_low_irq,
134         .shutdown       = shutdown_giuint_low_irq,
135         .enable         = enable_giuint_low_irq,
136         .disable        = disable_giuint_low_irq,
137         .ack            = ack_giuint_low_irq,
138         .end            = end_giuint_low_irq,
139 };
140
141 static unsigned int startup_giuint_high_irq(unsigned int irq)
142 {
143         unsigned int pin;
144
145         pin = GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET);
146         write_giuint((uint16_t)1 << pin, GIUINTSTATH);
147         set_giuint(GIUINTENH, (uint16_t)1 << pin);
148
149         return 0;
150 }
151
152 static void shutdown_giuint_high_irq(unsigned int irq)
153 {
154         clear_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET));
155 }
156
157 static void enable_giuint_high_irq(unsigned int irq)
158 {
159         set_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET));
160 }
161
162 #define disable_giuint_high_irq shutdown_giuint_high_irq
163
164 static void ack_giuint_high_irq(unsigned int irq)
165 {
166         unsigned int pin;
167
168         pin = GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET);
169         clear_giuint(GIUINTENH, (uint16_t)1 << pin);
170         write_giuint((uint16_t)1 << pin, GIUINTSTATH);
171 }
172
173 static void end_giuint_high_irq(unsigned int irq)
174 {
175         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
176                 set_giuint(GIUINTENH, (uint16_t)1 << GIU_IRQ_TO_PIN(irq - GIUINT_HIGH_OFFSET));
177 }
178
179 static struct hw_interrupt_type giuint_high_irq_type = {
180         .typename       = "GIUINTH",
181         .startup        = startup_giuint_high_irq,
182         .shutdown       = shutdown_giuint_high_irq,
183         .enable         = enable_giuint_high_irq,
184         .disable        = disable_giuint_high_irq,
185         .ack            = ack_giuint_high_irq,
186         .end            = end_giuint_high_irq,
187 };
188
189 void __init init_vr41xx_giuint_irq(void)
190 {
191         int i;
192
193         for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
194                 if (i < (GIU_IRQ_BASE + GIUINT_HIGH_OFFSET))
195                         irq_desc[i].handler = &giuint_low_irq_type;
196                 else
197                         irq_desc[i].handler = &giuint_high_irq_type;
198         }
199
200         setup_irq(GIUINT_CASCADE_IRQ, &giu_cascade);
201 }
202
203 void vr41xx_set_irq_trigger(int pin, int trigger, int hold)
204 {
205         uint16_t mask;
206
207         if (pin < GIUINT_HIGH_OFFSET) {
208                 mask = (uint16_t)1 << pin;
209                 if (trigger != TRIGGER_LEVEL) {
210                         set_giuint(GIUINTTYPL, mask);
211                         if (hold == SIGNAL_HOLD)
212                                 set_giuint(GIUINTHTSELL, mask);
213                         else
214                                 clear_giuint(GIUINTHTSELL, mask);
215                         if (current_cpu_data.cputype == CPU_VR4133) {
216                                 switch (trigger) {
217                                 case TRIGGER_EDGE_FALLING:
218                                         set_giuint(GIUFEDGEINHL, mask);
219                                         clear_giuint(GIUREDGEINHL, mask);
220                                         break;
221                                 case TRIGGER_EDGE_RISING:
222                                         clear_giuint(GIUFEDGEINHL, mask);
223                                         set_giuint(GIUREDGEINHL, mask);
224                                         break;
225                                 default:
226                                         set_giuint(GIUFEDGEINHL, mask);
227                                         set_giuint(GIUREDGEINHL, mask);
228                                         break;
229                                 }
230                         }
231                 } else {
232                         clear_giuint(GIUINTTYPL, mask);
233                         clear_giuint(GIUINTHTSELL, mask);
234                 }
235                 write_giuint(mask, GIUINTSTATL);
236         } else {
237                 mask = (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET);
238                 if (trigger != TRIGGER_LEVEL) {
239                         set_giuint(GIUINTTYPH, mask);
240                         if (hold == SIGNAL_HOLD)
241                                 set_giuint(GIUINTHTSELH, mask);
242                         else
243                                 clear_giuint(GIUINTHTSELH, mask);
244                         if (current_cpu_data.cputype == CPU_VR4133) {
245                                 switch (trigger) {
246                                 case TRIGGER_EDGE_FALLING:
247                                         set_giuint(GIUFEDGEINHH, mask);
248                                         clear_giuint(GIUREDGEINHH, mask);
249                                         break;
250                                 case TRIGGER_EDGE_RISING:
251                                         clear_giuint(GIUFEDGEINHH, mask);
252                                         set_giuint(GIUREDGEINHH, mask);
253                                         break;
254                                 default:
255                                         set_giuint(GIUFEDGEINHH, mask);
256                                         set_giuint(GIUREDGEINHH, mask);
257                                         break;
258                                 }
259                         }
260                 } else {
261                         clear_giuint(GIUINTTYPH, mask);
262                         clear_giuint(GIUINTHTSELH, mask);
263                 }
264                 write_giuint(mask, GIUINTSTATH);
265         }
266 }
267
268 EXPORT_SYMBOL(vr41xx_set_irq_trigger);
269
270 void vr41xx_set_irq_level(int pin, int level)
271 {
272         uint16_t mask;
273
274         if (pin < GIUINT_HIGH_OFFSET) {
275                 mask = (uint16_t)1 << pin;
276                 if (level == LEVEL_HIGH)
277                         set_giuint(GIUINTALSELL, mask);
278                 else
279                         clear_giuint(GIUINTALSELL, mask);
280                 write_giuint(mask, GIUINTSTATL);
281         } else {
282                 mask = (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET);
283                 if (level == LEVEL_HIGH)
284                         set_giuint(GIUINTALSELH, mask);
285                 else
286                         clear_giuint(GIUINTALSELH, mask);
287                 write_giuint(mask, GIUINTSTATH);
288         }
289 }
290
291 EXPORT_SYMBOL(vr41xx_set_irq_level);
292
293 #define GIUINT_NR_IRQS          32
294
295 enum {
296         GIUINT_NO_CASCADE,
297         GIUINT_CASCADE
298 };
299
300 struct vr41xx_giuint_cascade {
301         unsigned int flag;
302         int (*get_irq_number)(int irq);
303 };
304
305 static struct vr41xx_giuint_cascade giuint_cascade[GIUINT_NR_IRQS];
306 static struct irqaction giu_cascade = {no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL};
307
308 static int no_irq_number(int irq)
309 {
310         return -EINVAL;
311 }
312
313 int vr41xx_cascade_irq(unsigned int irq, int (*get_irq_number)(int irq))
314 {
315         unsigned int pin;
316         int retval;
317
318         if (irq < GIU_IRQ(0) || irq > GIU_IRQ(31))
319                 return -EINVAL;
320
321         if(!get_irq_number)
322                 return -EINVAL;
323
324         pin = GIU_IRQ_TO_PIN(irq);
325         giuint_cascade[pin].flag = GIUINT_CASCADE;
326         giuint_cascade[pin].get_irq_number = get_irq_number;
327
328         retval = setup_irq(irq, &giu_cascade);
329         if (retval != 0) {
330                 giuint_cascade[pin].flag = GIUINT_NO_CASCADE;
331                 giuint_cascade[pin].get_irq_number = no_irq_number;
332         }
333
334         return retval;
335 }
336
337 EXPORT_SYMBOL(vr41xx_cascade_irq);
338
339 static inline int get_irq_pin_number(void)
340 {
341         uint16_t pendl, pendh, maskl, maskh;
342         int i;
343
344         pendl = read_giuint(GIUINTSTATL);
345         pendh = read_giuint(GIUINTSTATH);
346         maskl = read_giuint(GIUINTENL);
347         maskh = read_giuint(GIUINTENH);
348
349         maskl &= pendl;
350         maskh &= pendh;
351
352         if (maskl) {
353                 for (i = 0; i < 16; i++) {
354                         if (maskl & ((uint16_t)1 << i))
355                                 return i;
356                 }
357         } else if (maskh) {
358                 for (i = 0; i < 16; i++) {
359                         if (maskh & ((uint16_t)1 << i))
360                                 return i + GIUINT_HIGH_OFFSET;
361                 }
362         }
363
364         printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n",
365                maskl, pendl, maskh, pendh);
366
367         atomic_inc(&irq_err_count);
368
369         return -1;
370 }
371
372 static inline void ack_giuint_irq(int pin)
373 {
374         if (pin < GIUINT_HIGH_OFFSET) {
375                 clear_giuint(GIUINTENL, (uint16_t)1 << pin);
376                 write_giuint((uint16_t)1 << pin, GIUINTSTATL);
377         } else {
378                 pin -= GIUINT_HIGH_OFFSET;
379                 clear_giuint(GIUINTENH, (uint16_t)1 << pin);
380                 write_giuint((uint16_t)1 << pin, GIUINTSTATH);
381         }
382 }
383
384 static inline void end_giuint_irq(int pin)
385 {
386         if (pin < GIUINT_HIGH_OFFSET)
387                 set_giuint(GIUINTENL, (uint16_t)1 << pin);
388         else
389                 set_giuint(GIUINTENH, (uint16_t)1 << (pin - GIUINT_HIGH_OFFSET));
390 }
391
392 void giuint_irq_dispatch(struct pt_regs *regs)
393 {
394         struct vr41xx_giuint_cascade *cascade;
395         unsigned int giuint_irq;
396         int pin;
397
398         pin = get_irq_pin_number();
399         if (pin < 0)
400                 return;
401
402         disable_irq(GIUINT_CASCADE_IRQ);
403
404         cascade = &giuint_cascade[pin];
405         giuint_irq = GIU_IRQ(pin);
406         if (cascade->flag == GIUINT_CASCADE) {
407                 int irq = cascade->get_irq_number(giuint_irq);
408                 ack_giuint_irq(pin);
409                 if (irq >= 0)
410                         do_IRQ(irq, regs);
411                 end_giuint_irq(pin);
412         } else {
413                 do_IRQ(giuint_irq, regs);
414         }
415
416         enable_irq(GIUINT_CASCADE_IRQ);
417 }
418
419 static int __init vr41xx_giu_init(void)
420 {
421         int i;
422
423         switch (current_cpu_data.cputype) {
424         case CPU_VR4111:
425         case CPU_VR4121:
426                 giu_base = GIUIOSELL_TYPE1;
427                 break;
428         case CPU_VR4122:
429         case CPU_VR4131:
430         case CPU_VR4133:
431                 giu_base = GIUIOSELL_TYPE2;
432                 break;
433         default:
434                 printk(KERN_ERR "GIU: Unexpected CPU of NEC VR4100 series\n");
435                 return -EINVAL;
436         }
437
438         for (i = 0; i < GIUINT_NR_IRQS; i++) {
439                 if (i < GIUINT_HIGH_OFFSET)
440                         clear_giuint(GIUINTENL, (uint16_t)1 << i);
441                 else
442                         clear_giuint(GIUINTENH, (uint16_t)1 << (i - GIUINT_HIGH_OFFSET));
443                 giuint_cascade[i].flag = GIUINT_NO_CASCADE;
444                 giuint_cascade[i].get_irq_number = no_irq_number;
445         }
446
447         return 0;
448 }
449
450 early_initcall(vr41xx_giu_init);