8d6fbe6a3c60a6fa2582181efc7e24d15bd807e0
[openwrt.git] / target / linux / adm5120-2.6 / files / arch / mips / adm5120 / irq.c
1 /*
2  *  $Id$
3  *
4  *  ADM5120 specific interrupt handlers
5  *
6  *  Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
7  *  Copyright (C) 2007 OpenWrt.org
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation; either version 2
12  *  of the License, or (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the
21  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA  02110-1301, USA.
23  *
24  */
25
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/version.h>
29 #include <linux/interrupt.h>
30 #include <linux/ioport.h>
31
32 #include <asm/irq.h>
33 #include <asm/irq_cpu.h>
34 #include <asm/mipsregs.h>
35 #include <asm/bitops.h>
36
37 #include <asm/mach-adm5120/adm5120_defs.h>
38 #include <asm/mach-adm5120/adm5120_irq.h>
39
40 #define INTC_REG(r) (*(volatile u32 *)(KSEG1ADDR(ADM5120_INTC_BASE) + r))
41
42 static void adm5120_intc_irq_unmask(unsigned int irq);
43 static void adm5120_intc_irq_mask(unsigned int irq);
44 static int  adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type);
45
46 static struct irq_chip adm5120_intc_irq_chip = {
47 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
48         .name           = "INTC",
49 #else
50         .typename       = "INTC",
51 #endif
52         .unmask         = adm5120_intc_irq_unmask,
53         .mask           = adm5120_intc_irq_mask,
54         .mask_ack       = adm5120_intc_irq_mask,
55         .set_type       = adm5120_intc_irq_set_type
56 };
57
58 static struct irqaction adm5120_intc_irq_action = {
59         .handler        = no_action,
60         .name           = "cascade [INTC]"
61 };
62
63 static void adm5120_intc_irq_unmask(unsigned int irq)
64 {
65         unsigned long flags;
66
67         irq -= ADM5120_INTC_IRQ_BASE;
68         local_irq_save(flags);
69         INTC_REG(INTC_REG_IRQ_ENABLE) = (1 << irq);
70         local_irq_restore(flags);
71 }
72
73 static void adm5120_intc_irq_mask(unsigned int irq)
74 {
75         unsigned long flags;
76
77         irq -= ADM5120_INTC_IRQ_BASE;
78         local_irq_save(flags);
79         INTC_REG(INTC_REG_IRQ_DISABLE) = (1 << irq);
80         local_irq_restore(flags);
81 }
82
83 static int adm5120_intc_irq_set_type(unsigned int irq, unsigned int flow_type)
84 {
85         /* TODO: not yet tested */
86 #if 1
87         unsigned int sense;
88         unsigned long mode;
89         int err;
90
91         err = 0;
92         sense = flow_type & (IRQ_TYPE_SENSE_MASK);
93         switch (sense) {
94         case IRQ_TYPE_NONE:
95         case IRQ_TYPE_LEVEL_HIGH:
96                 break;
97         case IRQ_TYPE_LEVEL_LOW:
98                 switch (irq) {
99                 case ADM5120_IRQ_GPIO2:
100                 case ADM5120_IRQ_GPIO4:
101                         break;
102                 default:
103                         err = -EINVAL;
104                         break;
105                 }
106                 break;
107         default:
108                 err = -EINVAL;
109                 break;
110         }
111         
112         if (err)
113                 return err;
114         
115         switch (irq) {
116         case ADM5120_IRQ_GPIO2:
117         case ADM5120_IRQ_GPIO4:
118                 mode = INTC_REG(INTC_REG_INT_MODE);
119                 if (sense == IRQ_TYPE_LEVEL_LOW)
120                         mode |= (1 << (irq-ADM5120_INTC_IRQ_BASE));
121                 else
122                         mode &= (1 << (irq-ADM5120_INTC_IRQ_BASE));
123                         
124                 INTC_REG(INTC_REG_INT_MODE) = mode;
125                 /* fallthrogh */
126         default:
127                 irq_desc[irq].status &= ~IRQ_TYPE_SENSE_MASK;
128                 irq_desc[irq].status |= sense;
129                 break;
130         }
131 #endif
132         return 0;
133 }
134
135 static void adm5120_intc_irq_dispatch(void)
136 {
137         unsigned long status;
138         int irq;
139
140 #if 1
141         /* dispatch only one IRQ at a time */
142         status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL;
143
144         if (status) {
145                 irq = ADM5120_INTC_IRQ_BASE+fls(status)-1;
146                 do_IRQ(irq);
147         } else
148                 spurious_interrupt();
149 #else
150         status = INTC_REG(INTC_REG_IRQ_STATUS) & INTC_INT_ALL;
151         if (status) {
152                 for (irq=ADM5120_INTC_IRQ_BASE; irq <= ADM5120_INTC_IRQ_BASE +
153                         INTC_IRQ_LAST;  irq++, status >>=1) {
154                         if ((status & 1) == 1)
155                                 do_IRQ(irq);
156                 }
157         } else
158                 spurious_interrupt();
159 #endif
160 }
161
162 asmlinkage void plat_irq_dispatch(void)
163 {
164         unsigned long pending;
165
166         pending = read_c0_status() & read_c0_cause();
167
168         if (pending & STATUSF_IP7)
169                 do_IRQ(ADM5120_IRQ_COUNTER);
170         else if (pending & STATUSF_IP2)
171                 adm5120_intc_irq_dispatch();
172         else
173                 spurious_interrupt();
174 }
175
176 #define INTC_IRQ_STATUS (IRQ_LEVEL | IRQ_TYPE_LEVEL_HIGH | IRQ_DISABLED)
177 static void __init adm5120_intc_irq_init(int base)
178 {
179         int i;
180
181         /* disable all interrupts */
182         INTC_REG(INTC_REG_IRQ_DISABLE) = INTC_INT_ALL;
183         /* setup all interrupts to generate IRQ instead of FIQ */
184         INTC_REG(INTC_REG_INT_MODE) = 0;
185         /* set active level for all external interrupts to HIGH */
186         INTC_REG(INTC_REG_INT_LEVEL) = 0;
187         /* disable usage of the TEST_SOURCE register */
188         INTC_REG(INTC_REG_IRQ_SOURCE_SELECT) = 0;
189
190         for(i=ADM5120_INTC_IRQ_BASE; i <= ADM5120_INTC_IRQ_BASE+INTC_IRQ_LAST;
191                 i++) {
192                 irq_desc[i].status = INTC_IRQ_STATUS;
193                 set_irq_chip_and_handler(i, &adm5120_intc_irq_chip, 
194                         handle_level_irq);
195         }
196
197         setup_irq(ADM5120_IRQ_INTC, &adm5120_intc_irq_action);
198 }
199
200 void __init arch_init_irq(void) {
201         mips_cpu_irq_init();
202         adm5120_intc_irq_init(ADM5120_INTC_IRQ_BASE);
203 }