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 | /* default prio for interrupts */ |
42 | /* first one is a no-no so therefore always prio 0 (disabled) */ | |
43 | static char gic_prio[PNX8550_INT_GIC_TOTINT] = { | |
44 | 0, 1, 1, 1, 1, 15, 1, 1, 1, 1, // 0 - 9 | |
45 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 - 19 | |
46 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20 - 29 | |
47 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30 - 39 | |
48 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 - 49 | |
49 | 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, // 50 - 59 | |
50 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 - 69 | |
51 | 1 // 70 | |
52 | }; | |
53 | ||
937a8015 | 54 | static void hw0_irqdispatch(int irq) |
bdf21b18 PP |
55 | { |
56 | /* find out which interrupt */ | |
57 | irq = PNX8550_GIC_VECTOR_0 >> 3; | |
58 | ||
59 | if (irq == 0) { | |
60 | printk("hw0_irqdispatch: irq 0, spurious interrupt?\n"); | |
61 | return; | |
62 | } | |
937a8015 | 63 | do_IRQ(PNX8550_INT_GIC_MIN + irq); |
bdf21b18 PP |
64 | } |
65 | ||
66 | ||
937a8015 | 67 | static void timer_irqdispatch(int irq) |
bdf21b18 PP |
68 | { |
69 | irq = (0x01c0 & read_c0_config7()) >> 6; | |
70 | ||
937a8015 | 71 | if (unlikely(irq == 0)) { |
bdf21b18 PP |
72 | printk("timer_irqdispatch: irq 0, spurious interrupt?\n"); |
73 | return; | |
74 | } | |
75 | ||
937a8015 RB |
76 | if (irq & 0x1) |
77 | do_IRQ(PNX8550_INT_TIMER1); | |
78 | if (irq & 0x2) | |
79 | do_IRQ(PNX8550_INT_TIMER2); | |
80 | if (irq & 0x4) | |
81 | do_IRQ(PNX8550_INT_TIMER3); | |
bdf21b18 PP |
82 | } |
83 | ||
937a8015 | 84 | asmlinkage void plat_irq_dispatch(void) |
e4ac58af RB |
85 | { |
86 | unsigned int pending = read_c0_status() & read_c0_cause(); | |
87 | ||
88 | if (pending & STATUSF_IP2) | |
937a8015 | 89 | hw0_irqdispatch(2); |
e4ac58af RB |
90 | else if (pending & STATUSF_IP7) { |
91 | if (read_c0_config7() & 0x01c0) | |
937a8015 | 92 | timer_irqdispatch(7); |
e4ac58af RB |
93 | } |
94 | ||
937a8015 | 95 | spurious_interrupt(); |
e4ac58af RB |
96 | } |
97 | ||
bdf21b18 PP |
98 | static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask) |
99 | { | |
100 | unsigned long status = read_c0_status(); | |
101 | ||
102 | status &= ~((clr_mask & 0xFF) << 8); | |
103 | status |= (set_mask & 0xFF) << 8; | |
104 | ||
105 | write_c0_status(status); | |
106 | } | |
107 | ||
108 | static inline void mask_gic_int(unsigned int irq_nr) | |
109 | { | |
110 | /* interrupt disabled, bit 26(WE_ENABLE)=1 and bit 16(enable)=0 */ | |
111 | PNX8550_GIC_REQ(irq_nr) = 1<<28; /* set priority to 0 */ | |
112 | } | |
113 | ||
114 | static inline void unmask_gic_int(unsigned int irq_nr) | |
115 | { | |
116 | /* set prio mask to lower four bits and enable interrupt */ | |
117 | PNX8550_GIC_REQ(irq_nr) = (1<<26 | 1<<16) | (1<<28) | gic_prio[irq_nr]; | |
118 | } | |
119 | ||
120 | static inline void mask_irq(unsigned int irq_nr) | |
121 | { | |
122 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
123 | modify_cp0_intmask(1 << irq_nr, 0); | |
124 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
125 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
126 | mask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
127 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
128 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
129 | modify_cp0_intmask(1 << 7, 0); | |
130 | } else { | |
131 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
132 | } | |
133 | } | |
134 | ||
135 | static inline void unmask_irq(unsigned int irq_nr) | |
136 | { | |
137 | if ((PNX8550_INT_CP0_MIN <= irq_nr) && (irq_nr <= PNX8550_INT_CP0_MAX)) { | |
138 | modify_cp0_intmask(0, 1 << irq_nr); | |
139 | } else if ((PNX8550_INT_GIC_MIN <= irq_nr) && | |
140 | (irq_nr <= PNX8550_INT_GIC_MAX)) { | |
141 | unmask_gic_int(irq_nr - PNX8550_INT_GIC_MIN); | |
142 | } else if ((PNX8550_INT_TIMER_MIN <= irq_nr) && | |
143 | (irq_nr <= PNX8550_INT_TIMER_MAX)) { | |
144 | modify_cp0_intmask(0, 1 << 7); | |
145 | } else { | |
146 | printk("mask_irq: irq %d doesn't exist!\n", irq_nr); | |
147 | } | |
148 | } | |
149 | ||
bdf21b18 PP |
150 | int pnx8550_set_gic_priority(int irq, int priority) |
151 | { | |
152 | int gic_irq = irq-PNX8550_INT_GIC_MIN; | |
153 | int prev_priority = PNX8550_GIC_REQ(gic_irq) & 0xf; | |
154 | ||
155 | gic_prio[gic_irq] = priority; | |
156 | PNX8550_GIC_REQ(gic_irq) |= (0x10000000 | gic_prio[gic_irq]); | |
157 | ||
158 | return prev_priority; | |
159 | } | |
160 | ||
bdf21b18 PP |
161 | static void end_irq(unsigned int irq) |
162 | { | |
163 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) { | |
1603b5ac | 164 | unmask_irq(irq); |
bdf21b18 PP |
165 | } |
166 | } | |
167 | ||
94dee171 | 168 | static struct irq_chip level_irq_type = { |
bdf21b18 | 169 | .typename = "PNX Level IRQ", |
1603b5ac AN |
170 | .ack = mask_irq, |
171 | .mask = mask_irq, | |
172 | .mask_ack = mask_irq, | |
173 | .unmask = unmask_irq, | |
bdf21b18 PP |
174 | .end = end_irq, |
175 | }; | |
176 | ||
177 | static struct irqaction gic_action = { | |
178 | .handler = no_action, | |
f40298fd | 179 | .flags = IRQF_DISABLED, |
bdf21b18 PP |
180 | .name = "GIC", |
181 | }; | |
182 | ||
183 | static struct irqaction timer_action = { | |
184 | .handler = no_action, | |
f40298fd | 185 | .flags = IRQF_DISABLED, |
bdf21b18 PP |
186 | .name = "Timer", |
187 | }; | |
188 | ||
189 | void __init arch_init_irq(void) | |
190 | { | |
191 | int i; | |
192 | int configPR; | |
193 | ||
bdf21b18 | 194 | for (i = 0; i < PNX8550_INT_CP0_TOTINT; i++) { |
1417836e | 195 | set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); |
1603b5ac | 196 | mask_irq(i); /* mask the irq just in case */ |
bdf21b18 PP |
197 | } |
198 | ||
199 | /* init of GIC/IPC interrupts */ | |
200 | /* should be done before cp0 since cp0 init enables the GIC int */ | |
201 | for (i = PNX8550_INT_GIC_MIN; i <= PNX8550_INT_GIC_MAX; i++) { | |
202 | int gic_int_line = i - PNX8550_INT_GIC_MIN; | |
203 | if (gic_int_line == 0 ) | |
204 | continue; // don't fiddle with int 0 | |
205 | /* | |
206 | * enable change of TARGET, ENABLE and ACTIVE_LOW bits | |
207 | * set TARGET 0 to route through hw0 interrupt | |
208 | * set ACTIVE_LOW 0 active high (correct?) | |
209 | * | |
210 | * We really should setup an interrupt description table | |
211 | * to do this nicely. | |
212 | * Note, PCI INTA is active low on the bus, but inverted | |
213 | * in the GIC, so to us it's active high. | |
214 | */ | |
215 | #ifdef CONFIG_PNX8550_V2PCI | |
216 | if (gic_int_line == (PNX8550_INT_GPIO0 - PNX8550_INT_GIC_MIN)) { | |
217 | /* PCI INT through gpio 8, which is setup in | |
218 | * pnx8550_setup.c and routed to GPIO | |
a3dddd56 | 219 | * Interrupt Level 0 (GPIO Connection 58). |
bdf21b18 PP |
220 | * Set it active low. */ |
221 | ||
222 | PNX8550_GIC_REQ(gic_int_line) = 0x1E020000; | |
223 | } else | |
224 | #endif | |
225 | { | |
226 | PNX8550_GIC_REQ(i - PNX8550_INT_GIC_MIN) = 0x1E000000; | |
227 | } | |
228 | ||
229 | /* mask/priority is still 0 so we will not get any | |
230 | * interrupts until it is unmasked */ | |
231 | ||
1417836e | 232 | set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); |
bdf21b18 PP |
233 | } |
234 | ||
235 | /* Priority level 0 */ | |
236 | PNX8550_GIC_PRIMASK_0 = PNX8550_GIC_PRIMASK_1 = 0; | |
237 | ||
238 | /* Set int vector table address */ | |
239 | PNX8550_GIC_VECTOR_0 = PNX8550_GIC_VECTOR_1 = 0; | |
240 | ||
1417836e AN |
241 | set_irq_chip_and_handler(MIPS_CPU_GIC_IRQ, &level_irq_type, |
242 | handle_level_irq); | |
bdf21b18 PP |
243 | setup_irq(MIPS_CPU_GIC_IRQ, &gic_action); |
244 | ||
245 | /* init of Timer interrupts */ | |
1603b5ac | 246 | for (i = PNX8550_INT_TIMER_MIN; i <= PNX8550_INT_TIMER_MAX; i++) |
1417836e | 247 | set_irq_chip_and_handler(i, &level_irq_type, handle_level_irq); |
bdf21b18 PP |
248 | |
249 | /* Stop Timer 1-3 */ | |
250 | configPR = read_c0_config7(); | |
251 | configPR |= 0x00000038; | |
252 | write_c0_config7(configPR); | |
253 | ||
1417836e AN |
254 | set_irq_chip_and_handler(MIPS_CPU_TIMER_IRQ, &level_irq_type, |
255 | handle_level_irq); | |
bdf21b18 PP |
256 | setup_irq(MIPS_CPU_TIMER_IRQ, &timer_action); |
257 | } | |
258 | ||
259 | EXPORT_SYMBOL(pnx8550_set_gic_priority); |