]>
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 | */ | |
26 | #include <linux/config.h> | |
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 | ||
e4ac58af | 56 | static void hw0_irqdispatch(int irq, struct pt_regs *regs) |
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 | } | |
65 | do_IRQ(PNX8550_INT_GIC_MIN + irq, regs); | |
66 | } | |
67 | ||
68 | ||
e4ac58af | 69 | static void timer_irqdispatch(int irq, struct pt_regs *regs) |
bdf21b18 PP |
70 | { |
71 | irq = (0x01c0 & read_c0_config7()) >> 6; | |
72 | ||
73 | if (irq == 0) { | |
74 | printk("timer_irqdispatch: irq 0, spurious interrupt?\n"); | |
75 | return; | |
76 | } | |
77 | ||
78 | if (irq & 0x1) { | |
79 | do_IRQ(PNX8550_INT_TIMER1, regs); | |
80 | } | |
81 | if (irq & 0x2) { | |
82 | do_IRQ(PNX8550_INT_TIMER2, regs); | |
83 | } | |
84 | if (irq & 0x4) { | |
85 | do_IRQ(PNX8550_INT_TIMER3, regs); | |
86 | } | |
87 | } | |
88 | ||
e4ac58af RB |
89 | asmlinkage void plat_irq_dispatch(struct pt_regs *regs) |
90 | { | |
91 | unsigned int pending = read_c0_status() & read_c0_cause(); | |
92 | ||
93 | if (pending & STATUSF_IP2) | |
94 | do_IRQ(2, regs); | |
95 | else if (pending & STATUSF_IP7) { | |
96 | if (read_c0_config7() & 0x01c0) | |
97 | timer_irqdispatch(7, regs); | |
98 | } | |
99 | ||
100 | spurious_interrupt(regs); | |
101 | } | |
102 | ||
bdf21b18 PP |
103 | static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask) |
104 | { | |
105 | unsigned long status = read_c0_status(); | |
106 | ||
107 | status &= ~((clr_mask & 0xFF) << 8); | |
108 | status |= (set_mask & 0xFF) << 8; | |
109 | ||
110 | write_c0_status(status); | |
111 | } | |
112 | ||
113 | static inline void mask_gic_int(unsigned int irq_nr) | |
114 | { | |
115 | /* interrupt disabled, bit 26(WE_ENABLE)=1 and bit 16(enable)=0 */ | |
116 | PNX8550_GIC_REQ(irq_nr) = 1<<28; /* set priority to 0 */ | |
117 | } | |
118 | ||
119 | static inline void unmask_gic_int(unsigned int irq_nr) | |
120 | { | |
121 | /* set prio mask to lower four bits and enable interrupt */ | |
122 | PNX8550_GIC_REQ(irq_nr) = (1<<26 | 1<<16) | (1<<28) | gic_prio[irq_nr]; | |
123 | } | |
124 | ||
125 | static inline void mask_irq(unsigned int irq_nr) | |
126 | { | |
127 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
128 | modify_cp0_intmask(1 << irq_nr, 0); | |
129 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
130 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
131 | mask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
132 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
133 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
134 | modify_cp0_intmask(1 << 7, 0); | |
135 | } else { | |
136 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
137 | } | |
138 | } | |
139 | ||
140 | static inline void unmask_irq(unsigned int irq_nr) | |
141 | { | |
142 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
143 | modify_cp0_intmask(0, 1 << irq_nr); | |
144 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
145 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
146 | unmask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
147 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
148 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
149 | modify_cp0_intmask(0, 1 << 7); | |
150 | } else { | |
151 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
152 | } | |
153 | } | |
154 | ||
155 | #define pnx8550_disable pnx8550_ack | |
156 | static void pnx8550_ack(unsigned int irq) | |
157 | { | |
158 | unsigned long flags; | |
159 | ||
160 | spin_lock_irqsave(&irq_lock, flags); | |
161 | mask_irq(irq); | |
162 | spin_unlock_irqrestore(&irq_lock, flags); | |
163 | } | |
164 | ||
165 | #define pnx8550_enable pnx8550_unmask | |
166 | static void pnx8550_unmask(unsigned int irq) | |
167 | { | |
168 | unsigned long flags; | |
169 | ||
170 | spin_lock_irqsave(&irq_lock, flags); | |
171 | unmask_irq(irq); | |
172 | spin_unlock_irqrestore(&irq_lock, flags); | |
173 | } | |
174 | ||
175 | static unsigned int startup_irq(unsigned int irq_nr) | |
176 | { | |
177 | pnx8550_unmask(irq_nr); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | static void shutdown_irq(unsigned int irq_nr) | |
182 | { | |
183 | pnx8550_ack(irq_nr); | |
184 | return; | |
185 | } | |
186 | ||
187 | int pnx8550_set_gic_priority(int irq, int priority) | |
188 | { | |
189 | int gic_irq = irq-PNX8550_INT_GIC_MIN; | |
190 | int prev_priority = PNX8550_GIC_REQ(gic_irq) & 0xf; | |
191 | ||
192 | gic_prio[gic_irq] = priority; | |
193 | PNX8550_GIC_REQ(gic_irq) |= (0x10000000 | gic_prio[gic_irq]); | |
194 | ||
195 | return prev_priority; | |
196 | } | |
197 | ||
198 | static inline void mask_and_ack_level_irq(unsigned int irq) | |
199 | { | |
200 | pnx8550_disable(irq); | |
201 | return; | |
202 | } | |
203 | ||
204 | static void end_irq(unsigned int irq) | |
205 | { | |
206 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { | |
207 | pnx8550_enable(irq); | |
208 | } | |
209 | } | |
210 | ||
211 | static struct hw_interrupt_type level_irq_type = { | |
212 | .typename = "PNX Level IRQ", | |
213 | .startup = startup_irq, | |
214 | .shutdown = shutdown_irq, | |
215 | .enable = pnx8550_enable, | |
216 | .disable = pnx8550_disable, | |
217 | .ack = mask_and_ack_level_irq, | |
218 | .end = end_irq, | |
219 | }; | |
220 | ||
221 | static struct irqaction gic_action = { | |
222 | .handler = no_action, | |
223 | .flags = SA_INTERRUPT, | |
224 | .name = "GIC", | |
225 | }; | |
226 | ||
227 | static struct irqaction timer_action = { | |
228 | .handler = no_action, | |
229 | .flags = SA_INTERRUPT, | |
230 | .name = "Timer", | |
231 | }; | |
232 | ||
233 | void __init arch_init_irq(void) | |
234 | { | |
235 | int i; | |
236 | int configPR; | |
237 | ||
bdf21b18 PP |
238 | for (i = 0; i < PNX8550_INT_CP0_TOTINT; i++) { |
239 | irq_desc[i].handler = &level_irq_type; | |
240 | pnx8550_ack(i); /* mask the irq just in case */ | |
241 | } | |
242 | ||
243 | /* init of GIC/IPC interrupts */ | |
244 | /* should be done before cp0 since cp0 init enables the GIC int */ | |
245 | for (i = PNX8550_INT_GIC_MIN; i <= PNX8550_INT_GIC_MAX; i++) { | |
246 | int gic_int_line = i - PNX8550_INT_GIC_MIN; | |
247 | if (gic_int_line == 0 ) | |
248 | continue; // don't fiddle with int 0 | |
249 | /* | |
250 | * enable change of TARGET, ENABLE and ACTIVE_LOW bits | |
251 | * set TARGET 0 to route through hw0 interrupt | |
252 | * set ACTIVE_LOW 0 active high (correct?) | |
253 | * | |
254 | * We really should setup an interrupt description table | |
255 | * to do this nicely. | |
256 | * Note, PCI INTA is active low on the bus, but inverted | |
257 | * in the GIC, so to us it's active high. | |
258 | */ | |
259 | #ifdef CONFIG_PNX8550_V2PCI | |
260 | if (gic_int_line == (PNX8550_INT_GPIO0 - PNX8550_INT_GIC_MIN)) { | |
261 | /* PCI INT through gpio 8, which is setup in | |
262 | * pnx8550_setup.c and routed to GPIO | |
a3dddd56 | 263 | * Interrupt Level 0 (GPIO Connection 58). |
bdf21b18 PP |
264 | * Set it active low. */ |
265 | ||
266 | PNX8550_GIC_REQ(gic_int_line) = 0x1E020000; | |
267 | } else | |
268 | #endif | |
269 | { | |
270 | PNX8550_GIC_REQ(i - PNX8550_INT_GIC_MIN) = 0x1E000000; | |
271 | } | |
272 | ||
273 | /* mask/priority is still 0 so we will not get any | |
274 | * interrupts until it is unmasked */ | |
275 | ||
276 | irq_desc[i].handler = &level_irq_type; | |
277 | } | |
278 | ||
279 | /* Priority level 0 */ | |
280 | PNX8550_GIC_PRIMASK_0 = PNX8550_GIC_PRIMASK_1 = 0; | |
281 | ||
282 | /* Set int vector table address */ | |
283 | PNX8550_GIC_VECTOR_0 = PNX8550_GIC_VECTOR_1 = 0; | |
284 | ||
285 | irq_desc[MIPS_CPU_GIC_IRQ].handler = &level_irq_type; | |
286 | setup_irq(MIPS_CPU_GIC_IRQ, &gic_action); | |
287 | ||
288 | /* init of Timer interrupts */ | |
289 | for (i = PNX8550_INT_TIMER_MIN; i <= PNX8550_INT_TIMER_MAX; i++) { | |
290 | irq_desc[i].handler = &level_irq_type; | |
291 | } | |
292 | ||
293 | /* Stop Timer 1-3 */ | |
294 | configPR = read_c0_config7(); | |
295 | configPR |= 0x00000038; | |
296 | write_c0_config7(configPR); | |
297 | ||
298 | irq_desc[MIPS_CPU_TIMER_IRQ].handler = &level_irq_type; | |
299 | setup_irq(MIPS_CPU_TIMER_IRQ, &timer_action); | |
300 | } | |
301 | ||
302 | EXPORT_SYMBOL(pnx8550_set_gic_priority); |