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