Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
fc095a90 MR |
2 | * Copyright (C) 1999, 2000, 2006 MIPS Technologies, Inc. |
3 | * All rights reserved. | |
4 | * Authors: Carsten Langgaard <carstenl@mips.com> | |
5 | * Maciej W. Rozycki <macro@mips.com> | |
1da177e4 LT |
6 | * |
7 | * ######################################################################## | |
8 | * | |
9 | * This program is free software; you can distribute it and/or modify it | |
10 | * under the terms of the GNU General Public License (Version 2) as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | * for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | |
20 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
21 | * | |
22 | * ######################################################################## | |
23 | * | |
24 | * Routines for generic manipulation of the interrupts found on the MIPS | |
25 | * Atlas board. | |
26 | * | |
27 | */ | |
28 | #include <linux/compiler.h> | |
29 | #include <linux/init.h> | |
fc095a90 | 30 | #include <linux/irq.h> |
1da177e4 LT |
31 | #include <linux/sched.h> |
32 | #include <linux/slab.h> | |
33 | #include <linux/interrupt.h> | |
34 | #include <linux/kernel_stat.h> | |
35 | ||
fc095a90 | 36 | #include <asm/gdb-stub.h> |
1da177e4 | 37 | #include <asm/io.h> |
fc095a90 MR |
38 | #include <asm/irq_cpu.h> |
39 | #include <asm/msc01_ic.h> | |
40 | ||
1da177e4 LT |
41 | #include <asm/mips-boards/atlas.h> |
42 | #include <asm/mips-boards/atlasint.h> | |
fc095a90 | 43 | #include <asm/mips-boards/generic.h> |
1da177e4 LT |
44 | |
45 | static struct atlas_ictrl_regs *atlas_hw0_icregs; | |
46 | ||
1da177e4 LT |
47 | #if 0 |
48 | #define DEBUG_INT(x...) printk(x) | |
49 | #else | |
50 | #define DEBUG_INT(x...) | |
51 | #endif | |
52 | ||
53 | void disable_atlas_irq(unsigned int irq_nr) | |
54 | { | |
fc095a90 | 55 | atlas_hw0_icregs->intrsten = 1 << (irq_nr - ATLAS_INT_BASE); |
1da177e4 LT |
56 | iob(); |
57 | } | |
58 | ||
59 | void enable_atlas_irq(unsigned int irq_nr) | |
60 | { | |
fc095a90 | 61 | atlas_hw0_icregs->intseten = 1 << (irq_nr - ATLAS_INT_BASE); |
1da177e4 LT |
62 | iob(); |
63 | } | |
64 | ||
1da177e4 LT |
65 | static void end_atlas_irq(unsigned int irq) |
66 | { | |
67 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | |
68 | enable_atlas_irq(irq); | |
69 | } | |
70 | ||
94dee171 | 71 | static struct irq_chip atlas_irq_type = { |
8ab00b9a | 72 | .typename = "Atlas", |
1603b5ac AN |
73 | .ack = disable_atlas_irq, |
74 | .mask = disable_atlas_irq, | |
75 | .mask_ack = disable_atlas_irq, | |
76 | .unmask = enable_atlas_irq, | |
1417836e | 77 | .eoi = enable_atlas_irq, |
8ab00b9a | 78 | .end = end_atlas_irq, |
1da177e4 LT |
79 | }; |
80 | ||
81 | static inline int ls1bit32(unsigned int x) | |
82 | { | |
83 | int b = 31, s; | |
84 | ||
85 | s = 16; if (x << 16 == 0) s = 0; b -= s; x <<= s; | |
86 | s = 8; if (x << 8 == 0) s = 0; b -= s; x <<= s; | |
87 | s = 4; if (x << 4 == 0) s = 0; b -= s; x <<= s; | |
88 | s = 2; if (x << 2 == 0) s = 0; b -= s; x <<= s; | |
89 | s = 1; if (x << 1 == 0) s = 0; b -= s; | |
90 | ||
91 | return b; | |
92 | } | |
93 | ||
937a8015 | 94 | static inline void atlas_hw0_irqdispatch(void) |
1da177e4 LT |
95 | { |
96 | unsigned long int_status; | |
97 | int irq; | |
98 | ||
99 | int_status = atlas_hw0_icregs->intstatus; | |
100 | ||
101 | /* if int_status == 0, then the interrupt has already been cleared */ | |
102 | if (unlikely(int_status == 0)) | |
103 | return; | |
104 | ||
fc095a90 | 105 | irq = ATLAS_INT_BASE + ls1bit32(int_status); |
1da177e4 LT |
106 | |
107 | DEBUG_INT("atlas_hw0_irqdispatch: irq=%d\n", irq); | |
108 | ||
937a8015 | 109 | do_IRQ(irq); |
1da177e4 LT |
110 | } |
111 | ||
e4ac58af RB |
112 | static inline int clz(unsigned long x) |
113 | { | |
114 | __asm__ ( | |
115 | " .set push \n" | |
116 | " .set mips32 \n" | |
117 | " clz %0, %1 \n" | |
118 | " .set pop \n" | |
119 | : "=r" (x) | |
120 | : "r" (x)); | |
121 | ||
122 | return x; | |
123 | } | |
124 | ||
125 | /* | |
126 | * Version of ffs that only looks at bits 12..15. | |
127 | */ | |
128 | static inline unsigned int irq_ffs(unsigned int pending) | |
129 | { | |
130 | #if defined(CONFIG_CPU_MIPS32) || defined(CONFIG_CPU_MIPS64) | |
131 | return -clz(pending) + 31 - CAUSEB_IP; | |
132 | #else | |
133 | unsigned int a0 = 7; | |
134 | unsigned int t0; | |
135 | ||
136 | t0 = s0 & 0xf000; | |
137 | t0 = t0 < 1; | |
138 | t0 = t0 << 2; | |
139 | a0 = a0 - t0; | |
140 | s0 = s0 << t0; | |
141 | ||
142 | t0 = s0 & 0xc000; | |
143 | t0 = t0 < 1; | |
144 | t0 = t0 << 1; | |
145 | a0 = a0 - t0; | |
146 | s0 = s0 << t0; | |
147 | ||
148 | t0 = s0 & 0x8000; | |
149 | t0 = t0 < 1; | |
150 | //t0 = t0 << 2; | |
151 | a0 = a0 - t0; | |
152 | //s0 = s0 << t0; | |
153 | ||
154 | return a0; | |
155 | #endif | |
156 | } | |
157 | ||
158 | /* | |
fc095a90 MR |
159 | * IRQs on the Atlas board look basically like (all external interrupt |
160 | * sources are combined together on hardware interrupt 0 (MIPS IRQ 2)): | |
e4ac58af | 161 | * |
fc095a90 | 162 | * MIPS IRQ Source |
e4ac58af | 163 | * -------- ------ |
fc095a90 MR |
164 | * 0 Software 0 (reschedule IPI on MT) |
165 | * 1 Software 1 (remote call IPI on MT) | |
166 | * 2 Combined Atlas hardware interrupt (hw0) | |
e4ac58af RB |
167 | * 3 Hardware (ignored) |
168 | * 4 Hardware (ignored) | |
169 | * 5 Hardware (ignored) | |
170 | * 6 Hardware (ignored) | |
171 | * 7 R4k timer (what we use) | |
172 | * | |
173 | * We handle the IRQ according to _our_ priority which is: | |
174 | * | |
175 | * Highest ---- R4k Timer | |
fc095a90 | 176 | * Lowest ---- Software 0 |
e4ac58af RB |
177 | * |
178 | * then we just return, if multiple IRQs are pending then we will just take | |
179 | * another exception, big deal. | |
180 | */ | |
937a8015 | 181 | asmlinkage void plat_irq_dispatch(void) |
e4ac58af RB |
182 | { |
183 | unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM; | |
184 | int irq; | |
185 | ||
186 | irq = irq_ffs(pending); | |
187 | ||
188 | if (irq == MIPSCPU_INT_ATLAS) | |
937a8015 | 189 | atlas_hw0_irqdispatch(); |
fc095a90 | 190 | else if (irq >= 0) |
937a8015 | 191 | do_IRQ(MIPSCPU_INT_BASE + irq); |
e4ac58af | 192 | else |
937a8015 | 193 | spurious_interrupt(); |
e4ac58af RB |
194 | } |
195 | ||
fc095a90 | 196 | static inline void init_atlas_irqs (int base) |
1da177e4 LT |
197 | { |
198 | int i; | |
199 | ||
fc095a90 MR |
200 | atlas_hw0_icregs = (struct atlas_ictrl_regs *) |
201 | ioremap(ATLAS_ICTRL_REGS_BASE, | |
202 | sizeof(struct atlas_ictrl_regs *)); | |
42a3b4f2 | 203 | |
1da177e4 LT |
204 | /* |
205 | * Mask out all interrupt by writing "1" to all bit position in | |
206 | * the interrupt reset reg. | |
207 | */ | |
208 | atlas_hw0_icregs->intrsten = 0xffffffff; | |
209 | ||
1603b5ac | 210 | for (i = ATLAS_INT_BASE; i <= ATLAS_INT_END; i++) |
1417836e | 211 | set_irq_chip_and_handler(i, &atlas_irq_type, handle_level_irq); |
1da177e4 | 212 | } |
fc095a90 MR |
213 | |
214 | static struct irqaction atlasirq = { | |
215 | .handler = no_action, | |
216 | .name = "Atlas cascade" | |
217 | }; | |
218 | ||
219 | msc_irqmap_t __initdata msc_irqmap[] = { | |
220 | {MSC01C_INT_TMR, MSC01_IRQ_EDGE, 0}, | |
221 | {MSC01C_INT_PCI, MSC01_IRQ_LEVEL, 0}, | |
222 | }; | |
223 | int __initdata msc_nr_irqs = sizeof(msc_irqmap) / sizeof(*msc_irqmap); | |
224 | ||
225 | msc_irqmap_t __initdata msc_eicirqmap[] = { | |
226 | {MSC01E_INT_SW0, MSC01_IRQ_LEVEL, 0}, | |
227 | {MSC01E_INT_SW1, MSC01_IRQ_LEVEL, 0}, | |
228 | {MSC01E_INT_ATLAS, MSC01_IRQ_LEVEL, 0}, | |
229 | {MSC01E_INT_TMR, MSC01_IRQ_EDGE, 0}, | |
230 | {MSC01E_INT_PCI, MSC01_IRQ_LEVEL, 0}, | |
231 | {MSC01E_INT_PERFCTR, MSC01_IRQ_LEVEL, 0}, | |
232 | {MSC01E_INT_CPUCTR, MSC01_IRQ_LEVEL, 0} | |
233 | }; | |
234 | int __initdata msc_nr_eicirqs = sizeof(msc_eicirqmap) / sizeof(*msc_eicirqmap); | |
235 | ||
236 | void __init arch_init_irq(void) | |
237 | { | |
238 | init_atlas_irqs(ATLAS_INT_BASE); | |
239 | ||
240 | if (!cpu_has_veic) | |
241 | mips_cpu_irq_init(MIPSCPU_INT_BASE); | |
242 | ||
243 | switch(mips_revision_corid) { | |
244 | case MIPS_REVISION_CORID_CORE_MSC: | |
245 | case MIPS_REVISION_CORID_CORE_FPGA2: | |
246 | case MIPS_REVISION_CORID_CORE_FPGA3: | |
247 | case MIPS_REVISION_CORID_CORE_24K: | |
248 | case MIPS_REVISION_CORID_CORE_EMUL_MSC: | |
249 | if (cpu_has_veic) | |
250 | init_msc_irqs (MSC01E_INT_BASE, | |
251 | msc_eicirqmap, msc_nr_eicirqs); | |
252 | else | |
253 | init_msc_irqs (MSC01C_INT_BASE, | |
254 | msc_irqmap, msc_nr_irqs); | |
255 | } | |
256 | ||
257 | ||
258 | if (cpu_has_veic) { | |
259 | set_vi_handler (MSC01E_INT_ATLAS, atlas_hw0_irqdispatch); | |
260 | setup_irq (MSC01E_INT_BASE + MSC01E_INT_ATLAS, &atlasirq); | |
261 | } else if (cpu_has_vint) { | |
262 | set_vi_handler (MIPSCPU_INT_ATLAS, atlas_hw0_irqdispatch); | |
263 | #ifdef CONFIG_MIPS_MT_SMTC | |
264 | setup_irq_smtc (MIPSCPU_INT_BASE + MIPSCPU_INT_ATLAS, | |
265 | &atlasirq, (0x100 << MIPSCPU_INT_ATLAS)); | |
266 | #else /* Not SMTC */ | |
267 | setup_irq(MIPSCPU_INT_BASE + MIPSCPU_INT_ATLAS, &atlasirq); | |
268 | #endif /* CONFIG_MIPS_MT_SMTC */ | |
269 | } else | |
270 | setup_irq(MIPSCPU_INT_BASE + MIPSCPU_INT_ATLAS, &atlasirq); | |
271 | } |