Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright 2001 MontaVista Software Inc. | |
3 | * Author: MontaVista Software, Inc. | |
4 | * ahennessy@mvista.com | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | * | |
10 | * Copyright (C) 2000-2001 Toshiba Corporation | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |
20 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
23 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
24 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | * | |
28 | * You should have received a copy of the GNU General Public License along | |
29 | * with this program; if not, write to the Free Software Foundation, Inc., | |
30 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
31 | */ | |
1da177e4 LT |
32 | #include <linux/init.h> |
33 | ||
34 | #include <linux/errno.h> | |
35 | #include <linux/irq.h> | |
36 | #include <linux/kernel_stat.h> | |
37 | #include <linux/signal.h> | |
38 | #include <linux/sched.h> | |
39 | #include <linux/types.h> | |
40 | #include <linux/interrupt.h> | |
41 | #include <linux/ioport.h> | |
42 | #include <linux/timex.h> | |
43 | #include <linux/slab.h> | |
44 | #include <linux/random.h> | |
45 | #include <linux/smp.h> | |
46 | #include <linux/smp_lock.h> | |
47 | #include <linux/bitops.h> | |
48 | ||
937a8015 | 49 | #include <asm/irq_regs.h> |
1da177e4 LT |
50 | #include <asm/io.h> |
51 | #include <asm/mipsregs.h> | |
52 | #include <asm/system.h> | |
53 | ||
54 | #include <asm/ptrace.h> | |
55 | #include <asm/processor.h> | |
56 | #include <asm/jmr3927/irq.h> | |
57 | #include <asm/debug.h> | |
58 | #include <asm/jmr3927/jmr3927.h> | |
59 | ||
60 | #if JMR3927_IRQ_END > NR_IRQS | |
61 | #error JMR3927_IRQ_END > NR_IRQS | |
62 | #endif | |
63 | ||
64 | struct tb_irq_space* tb_irq_spaces; | |
65 | ||
66 | static int jmr3927_irq_base = -1; | |
67 | ||
68 | #ifdef CONFIG_PCI | |
69 | static int jmr3927_gen_iack(void) | |
70 | { | |
71 | /* generate ACK cycle */ | |
72 | #ifdef __BIG_ENDIAN | |
73 | return (tx3927_pcicptr->iiadp >> 24) & 0xff; | |
74 | #else | |
75 | return tx3927_pcicptr->iiadp & 0xff; | |
76 | #endif | |
77 | } | |
78 | #endif | |
79 | ||
1da177e4 LT |
80 | #define irc_dlevel 0 |
81 | #define irc_elevel 1 | |
82 | ||
83 | static unsigned char irc_level[TX3927_NUM_IR] = { | |
84 | 5, 5, 5, 5, 5, 5, /* INT[5:0] */ | |
85 | 7, 7, /* SIO */ | |
86 | 5, 5, 5, 0, 0, /* DMA, PIO, PCI */ | |
87 | 6, 6, 6 /* TMR */ | |
88 | }; | |
89 | ||
90 | static void jmr3927_irq_disable(unsigned int irq_nr); | |
91 | static void jmr3927_irq_enable(unsigned int irq_nr); | |
92 | ||
1da177e4 LT |
93 | static void jmr3927_irq_ack(unsigned int irq) |
94 | { | |
95 | if (irq == JMR3927_IRQ_IRC_TMR0) | |
96 | jmr3927_tmrptr->tisr = 0; /* ack interrupt */ | |
97 | ||
98 | jmr3927_irq_disable(irq); | |
99 | } | |
100 | ||
101 | static void jmr3927_irq_end(unsigned int irq) | |
102 | { | |
702a96a6 SS |
103 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) |
104 | jmr3927_irq_enable(irq); | |
1da177e4 LT |
105 | } |
106 | ||
107 | static void jmr3927_irq_disable(unsigned int irq_nr) | |
108 | { | |
109 | struct tb_irq_space* sp; | |
1da177e4 | 110 | |
1da177e4 LT |
111 | for (sp = tb_irq_spaces; sp; sp = sp->next) { |
112 | if (sp->start_irqno <= irq_nr && | |
113 | irq_nr < sp->start_irqno + sp->nr_irqs) { | |
114 | if (sp->mask_func) | |
115 | sp->mask_func(irq_nr - sp->start_irqno, | |
116 | sp->space_id); | |
117 | break; | |
118 | } | |
119 | } | |
1da177e4 LT |
120 | } |
121 | ||
122 | static void jmr3927_irq_enable(unsigned int irq_nr) | |
123 | { | |
124 | struct tb_irq_space* sp; | |
1da177e4 | 125 | |
1da177e4 LT |
126 | for (sp = tb_irq_spaces; sp; sp = sp->next) { |
127 | if (sp->start_irqno <= irq_nr && | |
128 | irq_nr < sp->start_irqno + sp->nr_irqs) { | |
129 | if (sp->unmask_func) | |
130 | sp->unmask_func(irq_nr - sp->start_irqno, | |
131 | sp->space_id); | |
132 | break; | |
133 | } | |
134 | } | |
1da177e4 LT |
135 | } |
136 | ||
137 | /* | |
138 | * CP0_STATUS is a thread's resource (saved/restored on context switch). | |
139 | * So disable_irq/enable_irq MUST handle IOC/ISAC/IRC registers. | |
140 | */ | |
141 | static void mask_irq_isac(int irq_nr, int space_id) | |
142 | { | |
143 | /* 0: mask */ | |
144 | unsigned char imask = | |
145 | jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); | |
146 | unsigned int bit = 1 << irq_nr; | |
147 | jmr3927_isac_reg_out(imask & ~bit, JMR3927_ISAC_INTM_ADDR); | |
148 | /* flush write buffer */ | |
149 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | |
150 | } | |
151 | static void unmask_irq_isac(int irq_nr, int space_id) | |
152 | { | |
153 | /* 0: mask */ | |
154 | unsigned char imask = jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); | |
155 | unsigned int bit = 1 << irq_nr; | |
156 | jmr3927_isac_reg_out(imask | bit, JMR3927_ISAC_INTM_ADDR); | |
157 | /* flush write buffer */ | |
158 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | |
159 | } | |
160 | ||
161 | static void mask_irq_ioc(int irq_nr, int space_id) | |
162 | { | |
163 | /* 0: mask */ | |
164 | unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); | |
165 | unsigned int bit = 1 << irq_nr; | |
166 | jmr3927_ioc_reg_out(imask & ~bit, JMR3927_IOC_INTM_ADDR); | |
167 | /* flush write buffer */ | |
168 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | |
169 | } | |
170 | static void unmask_irq_ioc(int irq_nr, int space_id) | |
171 | { | |
172 | /* 0: mask */ | |
173 | unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); | |
174 | unsigned int bit = 1 << irq_nr; | |
175 | jmr3927_ioc_reg_out(imask | bit, JMR3927_IOC_INTM_ADDR); | |
176 | /* flush write buffer */ | |
177 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | |
178 | } | |
179 | ||
180 | static void mask_irq_irc(int irq_nr, int space_id) | |
181 | { | |
182 | volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; | |
183 | if (irq_nr & 1) | |
184 | *ilrp = (*ilrp & 0x00ff) | (irc_dlevel << 8); | |
185 | else | |
186 | *ilrp = (*ilrp & 0xff00) | irc_dlevel; | |
187 | /* update IRCSR */ | |
188 | tx3927_ircptr->imr = 0; | |
189 | tx3927_ircptr->imr = irc_elevel; | |
702a96a6 SS |
190 | /* flush write buffer */ |
191 | (void)tx3927_ircptr->ssr; | |
1da177e4 | 192 | } |
702a96a6 | 193 | |
1da177e4 LT |
194 | static void unmask_irq_irc(int irq_nr, int space_id) |
195 | { | |
196 | volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; | |
197 | if (irq_nr & 1) | |
198 | *ilrp = (*ilrp & 0x00ff) | (irc_level[irq_nr] << 8); | |
199 | else | |
200 | *ilrp = (*ilrp & 0xff00) | irc_level[irq_nr]; | |
201 | /* update IRCSR */ | |
202 | tx3927_ircptr->imr = 0; | |
203 | tx3927_ircptr->imr = irc_elevel; | |
204 | } | |
205 | ||
206 | struct tb_irq_space jmr3927_isac_irqspace = { | |
207 | .next = NULL, | |
208 | .start_irqno = JMR3927_IRQ_ISAC, | |
209 | nr_irqs : JMR3927_NR_IRQ_ISAC, | |
210 | .mask_func = mask_irq_isac, | |
211 | .unmask_func = unmask_irq_isac, | |
212 | .name = "ISAC", | |
213 | .space_id = 0, | |
214 | can_share : 0 | |
215 | }; | |
216 | struct tb_irq_space jmr3927_ioc_irqspace = { | |
217 | .next = NULL, | |
218 | .start_irqno = JMR3927_IRQ_IOC, | |
219 | nr_irqs : JMR3927_NR_IRQ_IOC, | |
220 | .mask_func = mask_irq_ioc, | |
221 | .unmask_func = unmask_irq_ioc, | |
222 | .name = "IOC", | |
223 | .space_id = 0, | |
224 | can_share : 1 | |
225 | }; | |
937a8015 | 226 | |
1da177e4 | 227 | struct tb_irq_space jmr3927_irc_irqspace = { |
937a8015 RB |
228 | .next = NULL, |
229 | .start_irqno = JMR3927_IRQ_IRC, | |
230 | .nr_irqs = JMR3927_NR_IRQ_IRC, | |
231 | .mask_func = mask_irq_irc, | |
232 | .unmask_func = unmask_irq_irc, | |
233 | .name = "on-chip", | |
234 | .space_id = 0, | |
235 | .can_share = 0 | |
1da177e4 LT |
236 | }; |
237 | ||
937a8015 RB |
238 | |
239 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND | |
240 | static int tx_branch_likely_bug_count = 0; | |
241 | static int have_tx_branch_likely_bug = 0; | |
242 | ||
243 | static void tx_branch_likely_bug_fixup(void) | |
244 | { | |
245 | struct pt_regs *regs = get_irq_regs(); | |
246 | ||
247 | /* TX39/49-BUG: Under this condition, the insn in delay slot | |
248 | of the branch likely insn is executed (not nullified) even | |
249 | the branch condition is false. */ | |
250 | if (!have_tx_branch_likely_bug) | |
251 | return; | |
252 | if ((regs->cp0_epc & 0xfff) == 0xffc && | |
253 | KSEGX(regs->cp0_epc) != KSEG0 && | |
254 | KSEGX(regs->cp0_epc) != KSEG1) { | |
255 | unsigned int insn = *(unsigned int*)(regs->cp0_epc - 4); | |
256 | /* beql,bnel,blezl,bgtzl */ | |
257 | /* bltzl,bgezl,blezall,bgezall */ | |
258 | /* bczfl, bcztl */ | |
259 | if ((insn & 0xf0000000) == 0x50000000 || | |
260 | (insn & 0xfc0e0000) == 0x04020000 || | |
261 | (insn & 0xf3fe0000) == 0x41020000) { | |
262 | regs->cp0_epc -= 4; | |
263 | tx_branch_likely_bug_count++; | |
264 | printk(KERN_INFO | |
265 | "fix branch-likery bug in %s (insn %08x)\n", | |
266 | current->comm, insn); | |
267 | } | |
268 | } | |
269 | } | |
270 | #endif | |
271 | ||
272 | static void jmr3927_spurious(void) | |
1da177e4 | 273 | { |
e5233184 RB |
274 | struct pt_regs * regs = get_irq_regs(); |
275 | ||
1da177e4 | 276 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND |
937a8015 | 277 | tx_branch_likely_bug_fixup(); |
1da177e4 LT |
278 | #endif |
279 | printk(KERN_WARNING "spurious interrupt (cause 0x%lx, pc 0x%lx, ra 0x%lx).\n", | |
280 | regs->cp0_cause, regs->cp0_epc, regs->regs[31]); | |
281 | } | |
282 | ||
937a8015 | 283 | asmlinkage void plat_irq_dispatch(void) |
1da177e4 | 284 | { |
e5233184 | 285 | struct pt_regs * regs = get_irq_regs(); |
1da177e4 LT |
286 | int irq; |
287 | ||
288 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND | |
937a8015 | 289 | tx_branch_likely_bug_fixup(); |
1da177e4 LT |
290 | #endif |
291 | if ((regs->cp0_cause & CAUSEF_IP7) == 0) { | |
292 | #if 0 | |
937a8015 | 293 | jmr3927_spurious(); |
1da177e4 LT |
294 | #endif |
295 | return; | |
296 | } | |
297 | irq = (regs->cp0_cause >> CAUSEB_IP2) & 0x0f; | |
298 | ||
937a8015 | 299 | do_IRQ(irq + JMR3927_IRQ_IRC); |
1da177e4 LT |
300 | } |
301 | ||
937a8015 | 302 | static irqreturn_t jmr3927_ioc_interrupt(int irq, void *dev_id) |
1da177e4 LT |
303 | { |
304 | unsigned char istat = jmr3927_ioc_reg_in(JMR3927_IOC_INTS2_ADDR); | |
305 | int i; | |
306 | ||
307 | for (i = 0; i < JMR3927_NR_IRQ_IOC; i++) { | |
308 | if (istat & (1 << i)) { | |
309 | irq = JMR3927_IRQ_IOC + i; | |
937a8015 | 310 | do_IRQ(irq); |
1da177e4 LT |
311 | } |
312 | } | |
702a96a6 | 313 | return IRQ_HANDLED; |
1da177e4 LT |
314 | } |
315 | ||
316 | static struct irqaction ioc_action = { | |
317 | jmr3927_ioc_interrupt, 0, CPU_MASK_NONE, "IOC", NULL, NULL, | |
318 | }; | |
319 | ||
937a8015 | 320 | static irqreturn_t jmr3927_isac_interrupt(int irq, void *dev_id) |
1da177e4 LT |
321 | { |
322 | unsigned char istat = jmr3927_isac_reg_in(JMR3927_ISAC_INTS2_ADDR); | |
323 | int i; | |
324 | ||
325 | for (i = 0; i < JMR3927_NR_IRQ_ISAC; i++) { | |
326 | if (istat & (1 << i)) { | |
327 | irq = JMR3927_IRQ_ISAC + i; | |
937a8015 | 328 | do_IRQ(irq); |
1da177e4 LT |
329 | } |
330 | } | |
702a96a6 | 331 | return IRQ_HANDLED; |
1da177e4 LT |
332 | } |
333 | ||
334 | static struct irqaction isac_action = { | |
335 | jmr3927_isac_interrupt, 0, CPU_MASK_NONE, "ISAC", NULL, NULL, | |
336 | }; | |
337 | ||
338 | ||
937a8015 | 339 | static irqreturn_t jmr3927_isaerr_interrupt(int irq, void *dev_id) |
1da177e4 LT |
340 | { |
341 | printk(KERN_WARNING "ISA error interrupt (irq 0x%x).\n", irq); | |
702a96a6 SS |
342 | |
343 | return IRQ_HANDLED; | |
1da177e4 LT |
344 | } |
345 | static struct irqaction isaerr_action = { | |
346 | jmr3927_isaerr_interrupt, 0, CPU_MASK_NONE, "ISA error", NULL, NULL, | |
347 | }; | |
348 | ||
937a8015 | 349 | static irqreturn_t jmr3927_pcierr_interrupt(int irq, void *dev_id) |
1da177e4 LT |
350 | { |
351 | printk(KERN_WARNING "PCI error interrupt (irq 0x%x).\n", irq); | |
352 | printk(KERN_WARNING "pcistat:%02x, lbstat:%04lx\n", | |
353 | tx3927_pcicptr->pcistat, tx3927_pcicptr->lbstat); | |
702a96a6 SS |
354 | |
355 | return IRQ_HANDLED; | |
1da177e4 LT |
356 | } |
357 | static struct irqaction pcierr_action = { | |
358 | jmr3927_pcierr_interrupt, 0, CPU_MASK_NONE, "PCI error", NULL, NULL, | |
359 | }; | |
360 | ||
361 | int jmr3927_ether1_irq = 0; | |
362 | ||
363 | void jmr3927_irq_init(u32 irq_base); | |
364 | ||
365 | void __init arch_init_irq(void) | |
366 | { | |
367 | /* look for io board's presence */ | |
368 | int have_isac = jmr3927_have_isac(); | |
369 | ||
370 | /* Now, interrupt control disabled, */ | |
371 | /* all IRC interrupts are masked, */ | |
372 | /* all IRC interrupt mode are Low Active. */ | |
373 | ||
374 | if (have_isac) { | |
375 | ||
376 | /* ETHER1 (NE2000 compatible 10M-Ether) parameter setup */ | |
377 | /* temporary enable interrupt control */ | |
378 | tx3927_ircptr->cer = 1; | |
379 | /* ETHER1 Int. Is High-Active. */ | |
380 | if (tx3927_ircptr->ssr & (1 << 0)) | |
381 | jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT0; | |
382 | #if 0 /* INT3 may be asserted by ether0 (even after reboot...) */ | |
383 | else if (tx3927_ircptr->ssr & (1 << 3)) | |
384 | jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT3; | |
385 | #endif | |
386 | /* disable interrupt control */ | |
387 | tx3927_ircptr->cer = 0; | |
388 | ||
389 | /* Ether1: High Active */ | |
390 | if (jmr3927_ether1_irq) { | |
391 | int ether1_irc = jmr3927_ether1_irq - JMR3927_IRQ_IRC; | |
392 | tx3927_ircptr->cr[ether1_irc / 8] |= | |
393 | TX3927_IRCR_HIGH << ((ether1_irc % 8) * 2); | |
394 | } | |
395 | } | |
396 | ||
397 | /* mask all IOC interrupts */ | |
398 | jmr3927_ioc_reg_out(0, JMR3927_IOC_INTM_ADDR); | |
399 | /* setup IOC interrupt mode (SOFT:High Active, Others:Low Active) */ | |
400 | jmr3927_ioc_reg_out(JMR3927_IOC_INTF_SOFT, JMR3927_IOC_INTP_ADDR); | |
401 | ||
402 | if (have_isac) { | |
403 | /* mask all ISAC interrupts */ | |
404 | jmr3927_isac_reg_out(0, JMR3927_ISAC_INTM_ADDR); | |
405 | /* setup ISAC interrupt mode (ISAIRQ3,ISAIRQ5:Low Active ???) */ | |
406 | jmr3927_isac_reg_out(JMR3927_ISAC_INTF_IRQ3|JMR3927_ISAC_INTF_IRQ5, JMR3927_ISAC_INTP_ADDR); | |
407 | } | |
408 | ||
409 | /* clear PCI Soft interrupts */ | |
410 | jmr3927_ioc_reg_out(0, JMR3927_IOC_INTS1_ADDR); | |
411 | /* clear PCI Reset interrupts */ | |
412 | jmr3927_ioc_reg_out(0, JMR3927_IOC_RESET_ADDR); | |
413 | ||
414 | /* enable interrupt control */ | |
415 | tx3927_ircptr->cer = TX3927_IRCER_ICE; | |
416 | tx3927_ircptr->imr = irc_elevel; | |
417 | ||
418 | jmr3927_irq_init(NR_ISA_IRQS); | |
419 | ||
1da177e4 LT |
420 | /* setup irq space */ |
421 | add_tb_irq_space(&jmr3927_isac_irqspace); | |
422 | add_tb_irq_space(&jmr3927_ioc_irqspace); | |
423 | add_tb_irq_space(&jmr3927_irc_irqspace); | |
424 | ||
425 | /* setup IOC interrupt 1 (PCI, MODEM) */ | |
426 | setup_irq(JMR3927_IRQ_IOCINT, &ioc_action); | |
427 | ||
428 | if (have_isac) { | |
429 | setup_irq(JMR3927_IRQ_ISACINT, &isac_action); | |
430 | setup_irq(JMR3927_IRQ_ISAC_ISAER, &isaerr_action); | |
431 | } | |
432 | ||
433 | #ifdef CONFIG_PCI | |
434 | setup_irq(JMR3927_IRQ_IRC_PCI, &pcierr_action); | |
435 | #endif | |
436 | ||
437 | /* enable all CPU interrupt bits. */ | |
438 | set_c0_status(ST0_IM); /* IE bit is still 0. */ | |
439 | } | |
440 | ||
94dee171 | 441 | static struct irq_chip jmr3927_irq_controller = { |
70d21cde | 442 | .name = "jmr3927_irq", |
8ab00b9a | 443 | .ack = jmr3927_irq_ack, |
1603b5ac AN |
444 | .mask = jmr3927_irq_disable, |
445 | .mask_ack = jmr3927_irq_ack, | |
446 | .unmask = jmr3927_irq_enable, | |
8ab00b9a | 447 | .end = jmr3927_irq_end, |
1da177e4 LT |
448 | }; |
449 | ||
450 | void jmr3927_irq_init(u32 irq_base) | |
451 | { | |
452 | u32 i; | |
453 | ||
1603b5ac AN |
454 | for (i= irq_base; i< irq_base + JMR3927_NR_IRQ_IRC + JMR3927_NR_IRQ_IOC; i++) |
455 | set_irq_chip(i, &jmr3927_irq_controller); | |
1da177e4 LT |
456 | |
457 | jmr3927_irq_base = irq_base; | |
458 | } |