]>
Commit | Line | Data |
---|---|---|
bdf21b18 PP |
1 | /* |
2 | * | |
3 | * Copyright (C) 2005 Embedded Alley Solutions, Inc | |
4 | * Ported to 2.6. | |
5 | * | |
6 | * Per Hallsmark, per.hallsmark@mvista.com | |
7 | * Copyright (C) 2000, 2001 MIPS Technologies, Inc. | |
8 | * Copyright (C) 2001 Ralf Baechle | |
9 | * | |
10 | * Cleaned up and bug fixing: Pete Popov, ppopov@embeddedalley.com | |
11 | * | |
12 | * This program is free software; you can distribute it and/or modify it | |
13 | * under the terms of the GNU General Public License (Version 2) as | |
14 | * published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope it will be useful, but WITHOUT | |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
19 | * for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along | |
22 | * with this program; if not, write to the Free Software Foundation, Inc., | |
23 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
24 | * | |
25 | */ | |
937a8015 | 26 | #include <linux/compiler.h> |
bdf21b18 PP |
27 | #include <linux/init.h> |
28 | #include <linux/irq.h> | |
29 | #include <linux/sched.h> | |
30 | #include <linux/slab.h> | |
31 | #include <linux/interrupt.h> | |
32 | #include <linux/kernel_stat.h> | |
33 | #include <linux/random.h> | |
34 | #include <linux/module.h> | |
35 | ||
36 | #include <asm/io.h> | |
37 | #include <asm/gdb-stub.h> | |
38 | #include <int.h> | |
39 | #include <uart.h> | |
40 | ||
bdf21b18 PP |
41 | static DEFINE_SPINLOCK(irq_lock); |
42 | ||
43 | /* default prio for interrupts */ | |
44 | /* first one is a no-no so therefore always prio 0 (disabled) */ | |
45 | static char gic_prio[PNX8550_INT_GIC_TOTINT] = { | |
46 | 0, 1, 1, 1, 1, 15, 1, 1, 1, 1, // 0 - 9 | |
47 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 - 19 | |
48 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20 - 29 | |
49 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30 - 39 | |
50 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 - 49 | |
51 | 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 50 - 59 | |
52 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 - 69 | |
53 | 1 // 70 | |
54 | }; | |
55 | ||
937a8015 | 56 | static void hw0_irqdispatch(int irq) |
bdf21b18 PP |
57 | { |
58 | /* find out which interrupt */ | |
59 | irq = PNX8550_GIC_VECTOR_0 >> 3; | |
60 | ||
61 | if (irq == 0) { | |
62 | printk("hw0_irqdispatch: irq 0, spurious interrupt?\n"); | |
63 | return; | |
64 | } | |
937a8015 | 65 | do_IRQ(PNX8550_INT_GIC_MIN + irq); |
bdf21b18 PP |
66 | } |
67 | ||
68 | ||
937a8015 | 69 | static void timer_irqdispatch(int irq) |
bdf21b18 PP |
70 | { |
71 | irq = (0x01c0 & read_c0_config7()) >> 6; | |
72 | ||
937a8015 | 73 | if (unlikely(irq == 0)) { |
bdf21b18 PP |
74 | printk("timer_irqdispatch: irq 0, spurious interrupt?\n"); |
75 | return; | |
76 | } | |
77 | ||
937a8015 RB |
78 | if (irq & 0x1) |
79 | do_IRQ(PNX8550_INT_TIMER1); | |
80 | if (irq & 0x2) | |
81 | do_IRQ(PNX8550_INT_TIMER2); | |
82 | if (irq & 0x4) | |
83 | do_IRQ(PNX8550_INT_TIMER3); | |
bdf21b18 PP |
84 | } |
85 | ||
937a8015 | 86 | asmlinkage void plat_irq_dispatch(void) |
e4ac58af RB |
87 | { |
88 | unsigned int pending = read_c0_status() & read_c0_cause(); | |
89 | ||
90 | if (pending & STATUSF_IP2) | |
937a8015 | 91 | hw0_irqdispatch(2); |
e4ac58af RB |
92 | else if (pending & STATUSF_IP7) { |
93 | if (read_c0_config7() & 0x01c0) | |
937a8015 | 94 | timer_irqdispatch(7); |
e4ac58af RB |
95 | } |
96 | ||
937a8015 | 97 | spurious_interrupt(); |
e4ac58af RB |
98 | } |
99 | ||
bdf21b18 PP |
100 | static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask) |
101 | { | |
102 | unsigned long status = read_c0_status(); | |
103 | ||
104 | status &= ~((clr_mask & 0xFF) << 8); | |
105 | status |= (set_mask & 0xFF) << 8; | |
106 | ||
107 | write_c0_status(status); | |
108 | } | |
109 | ||
110 | static inline void mask_gic_int(unsigned int irq_nr) | |
111 | { | |
112 | /* interrupt disabled, bit 26(WE_ENABLE)=1 and bit 16(enable)=0 */ | |
113 | PNX8550_GIC_REQ(irq_nr) = 1<<28; /* set priority to 0 */ | |
114 | } | |
115 | ||
116 | static inline void unmask_gic_int(unsigned int irq_nr) | |
117 | { | |
118 | /* set prio mask to lower four bits and enable interrupt */ | |
119 | PNX8550_GIC_REQ(irq_nr) = (1<<26 | 1<<16) | (1<<28) | gic_prio[irq_nr]; | |
120 | } | |
121 | ||
122 | static inline void mask_irq(unsigned int irq_nr) | |
123 | { | |
124 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
125 | modify_cp0_intmask(1 << irq_nr, 0); | |
126 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
127 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
128 | mask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
129 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
130 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
131 | modify_cp0_intmask(1 << 7, 0); | |
132 | } else { | |
133 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
134 | } | |
135 | } | |
136 | ||
137 | static inline void unmask_irq(unsigned int irq_nr) | |
138 | { | |
139 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
140 | modify_cp0_intmask(0, 1 << irq_nr); | |
141 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
142 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
143 | unmask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
144 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
145 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
146 | modify_cp0_intmask(0, 1 << 7); | |
147 | } else { | |
148 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
149 | } | |
150 | } | |
151 | ||
152 | #define pnx8550_disable pnx8550_ack | |
153 | static void pnx8550_ack(unsigned int irq) | |
154 | { | |
155 | unsigned long flags; | |
156 | ||
157 | spin_lock_irqsave(&irq_lock, flags); | |
158 | mask_irq(irq); | |
159 | spin_unlock_irqrestore(&irq_lock, flags); | |
160 | } | |
161 | ||
162 | #define pnx8550_enable pnx8550_unmask | |
163 | static void pnx8550_unmask(unsigned int irq) | |
164 | { | |
165 | unsigned long flags; | |
166 | ||
167 | spin_lock_irqsave(&irq_lock, flags); | |
168 | unmask_irq(irq); | |
169 | spin_unlock_irqrestore(&irq_lock, flags); | |
170 | } | |
171 | ||
172 | static unsigned int startup_irq(unsigned int irq_nr) | |
173 | { | |
174 | pnx8550_unmask(irq_nr); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static void shutdown_irq(unsigned int irq_nr) | |
179 | { | |
180 | pnx8550_ack(irq_nr); | |
181 | return; | |
182 | } | |
183 | ||
184 | int pnx8550_set_gic_priority(int irq, int priority) | |
185 | { | |
186 | int gic_irq = irq-PNX8550_INT_GIC_MIN; | |
187 | int prev_priority = PNX8550_GIC_REQ(gic_irq) & 0xf; | |
188 | ||
189 | gic_prio[gic_irq] = priority; | |
190 | PNX8550_GIC_REQ(gic_irq) |= (0x10000000 | gic_prio[gic_irq]); | |
191 | ||
192 | return prev_priority; | |
193 | } | |
194 | ||
195 | static inline void mask_and_ack_level_irq(unsigned int irq) | |
196 | { | |
197 | pnx8550_disable(irq); | |
198 | return; | |
199 | } | |
200 | ||
201 | static void end_irq(unsigned int irq) | |
202 | { | |
203 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { | |
204 | pnx8550_enable(irq); | |
205 | } | |
206 | } | |
207 | ||
94dee171 | 208 | static struct irq_chip level_irq_type = { |
bdf21b18 PP |
209 | .typename = "PNX Level IRQ", |
210 | .startup = startup_irq, | |
211 | .shutdown = shutdown_irq, | |
212 | .enable = pnx8550_enable, | |
213 | .disable = pnx8550_disable, | |
214 | .ack = mask_and_ack_level_irq, | |
215 | .end = end_irq, | |
216 | }; | |
217 | ||
218 | static struct irqaction gic_action = { | |
219 | .handler = no_action, | |
f40298fd | 220 | .flags = IRQF_DISABLED, |
bdf21b18 PP |
221 | .name = "GIC", |
222 | }; | |
223 | ||
224 | static struct irqaction timer_action = { | |
225 | .handler = no_action, | |
f40298fd | 226 | .flags = IRQF_DISABLED, |
bdf21b18 PP |
227 | .name = "Timer", |
228 | }; | |
229 | ||
230 | void __init arch_init_irq(void) | |
231 | { | |
232 | int i; | |
233 | int configPR; | |
234 | ||
bdf21b18 | 235 | for (i = 0; i < PNX8550_INT_CP0_TOTINT; i++) { |
d1bef4ed | 236 | irq_desc[i].chip = &level_irq_type; |
bdf21b18 PP |
237 | pnx8550_ack(i); /* mask the irq just in case */ |
238 | } | |
239 | ||
240 | /* init of GIC/IPC interrupts */ | |
241 | /* should be done before cp0 since cp0 init enables the GIC int */ | |
242 | for (i = PNX8550_INT_GIC_MIN; i <= PNX8550_INT_GIC_MAX; i++) { | |
243 | int gic_int_line = i - PNX8550_INT_GIC_MIN; | |
244 | if (gic_int_line == 0 ) | |
245 | continue; // don't fiddle with int 0 | |
246 | /* | |
247 | * enable change of TARGET, ENABLE and ACTIVE_LOW bits | |
248 | * set TARGET 0 to route through hw0 interrupt | |
249 | * set ACTIVE_LOW 0 active high (correct?) | |
250 | * | |
251 | * We really should setup an interrupt description table | |
252 | * to do this nicely. | |
253 | * Note, PCI INTA is active low on the bus, but inverted | |
254 | * in the GIC, so to us it's active high. | |
255 | */ | |
256 | #ifdef CONFIG_PNX8550_V2PCI | |
257 | if (gic_int_line == (PNX8550_INT_GPIO0 - PNX8550_INT_GIC_MIN)) { | |
258 | /* PCI INT through gpio 8, which is setup in | |
259 | * pnx8550_setup.c and routed to GPIO | |
a3dddd56 | 260 | * Interrupt Level 0 (GPIO Connection 58). |
bdf21b18 PP |
261 | * Set it active low. */ |
262 | ||
263 | PNX8550_GIC_REQ(gic_int_line) = 0x1E020000; | |
264 | } else | |
265 | #endif | |
266 | { | |
267 | PNX8550_GIC_REQ(i - PNX8550_INT_GIC_MIN) = 0x1E000000; | |
268 | } | |
269 | ||
270 | /* mask/priority is still 0 so we will not get any | |
271 | * interrupts until it is unmasked */ | |
272 | ||
d1bef4ed | 273 | irq_desc[i].chip = &level_irq_type; |
bdf21b18 PP |
274 | } |
275 | ||
276 | /* Priority level 0 */ | |
277 | PNX8550_GIC_PRIMASK_0 = PNX8550_GIC_PRIMASK_1 = 0; | |
278 | ||
279 | /* Set int vector table address */ | |
280 | PNX8550_GIC_VECTOR_0 = PNX8550_GIC_VECTOR_1 = 0; | |
281 | ||
d1bef4ed | 282 | irq_desc[MIPS_CPU_GIC_IRQ].chip = &level_irq_type; |
bdf21b18 PP |
283 | setup_irq(MIPS_CPU_GIC_IRQ, &gic_action); |
284 | ||
285 | /* init of Timer interrupts */ | |
286 | for (i = PNX8550_INT_TIMER_MIN; i <= PNX8550_INT_TIMER_MAX; i++) { | |
d1bef4ed | 287 | irq_desc[i].chip = &level_irq_type; |
bdf21b18 PP |
288 | } |
289 | ||
290 | /* Stop Timer 1-3 */ | |
291 | configPR = read_c0_config7(); | |
292 | configPR |= 0x00000038; | |
293 | write_c0_config7(configPR); | |
294 | ||
d1bef4ed | 295 | irq_desc[MIPS_CPU_TIMER_IRQ].chip = &level_irq_type; |
bdf21b18 PP |
296 | setup_irq(MIPS_CPU_TIMER_IRQ, &timer_action); |
297 | } | |
298 | ||
299 | EXPORT_SYMBOL(pnx8550_set_gic_priority); |