diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/jmr3927/rbhma3100/irq.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/jmr3927/rbhma3100/irq.c')
-rw-r--r-- | arch/mips/jmr3927/rbhma3100/irq.c | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/arch/mips/jmr3927/rbhma3100/irq.c b/arch/mips/jmr3927/rbhma3100/irq.c new file mode 100644 index 000000000000..b9799b86fc79 --- /dev/null +++ b/arch/mips/jmr3927/rbhma3100/irq.c | |||
@@ -0,0 +1,466 @@ | |||
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 | */ | ||
32 | #include <linux/config.h> | ||
33 | #include <linux/init.h> | ||
34 | |||
35 | #include <linux/errno.h> | ||
36 | #include <linux/irq.h> | ||
37 | #include <linux/kernel_stat.h> | ||
38 | #include <linux/signal.h> | ||
39 | #include <linux/sched.h> | ||
40 | #include <linux/types.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/ioport.h> | ||
43 | #include <linux/timex.h> | ||
44 | #include <linux/slab.h> | ||
45 | #include <linux/random.h> | ||
46 | #include <linux/smp.h> | ||
47 | #include <linux/smp_lock.h> | ||
48 | #include <linux/bitops.h> | ||
49 | |||
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 | |||
80 | extern asmlinkage void jmr3927_IRQ(void); | ||
81 | |||
82 | #define irc_dlevel 0 | ||
83 | #define irc_elevel 1 | ||
84 | |||
85 | static unsigned char irc_level[TX3927_NUM_IR] = { | ||
86 | 5, 5, 5, 5, 5, 5, /* INT[5:0] */ | ||
87 | 7, 7, /* SIO */ | ||
88 | 5, 5, 5, 0, 0, /* DMA, PIO, PCI */ | ||
89 | 6, 6, 6 /* TMR */ | ||
90 | }; | ||
91 | |||
92 | static void jmr3927_irq_disable(unsigned int irq_nr); | ||
93 | static void jmr3927_irq_enable(unsigned int irq_nr); | ||
94 | |||
95 | static DEFINE_SPINLOCK(jmr3927_irq_lock); | ||
96 | |||
97 | static unsigned int jmr3927_irq_startup(unsigned int irq) | ||
98 | { | ||
99 | jmr3927_irq_enable(irq); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | #define jmr3927_irq_shutdown jmr3927_irq_disable | ||
105 | |||
106 | static void jmr3927_irq_ack(unsigned int irq) | ||
107 | { | ||
108 | if (irq == JMR3927_IRQ_IRC_TMR0) | ||
109 | jmr3927_tmrptr->tisr = 0; /* ack interrupt */ | ||
110 | |||
111 | jmr3927_irq_disable(irq); | ||
112 | } | ||
113 | |||
114 | static void jmr3927_irq_end(unsigned int irq) | ||
115 | { | ||
116 | jmr3927_irq_enable(irq); | ||
117 | } | ||
118 | |||
119 | static void jmr3927_irq_disable(unsigned int irq_nr) | ||
120 | { | ||
121 | struct tb_irq_space* sp; | ||
122 | unsigned long flags; | ||
123 | |||
124 | spinlock_irqsave(&jmr3927_irq_lock, flags); | ||
125 | for (sp = tb_irq_spaces; sp; sp = sp->next) { | ||
126 | if (sp->start_irqno <= irq_nr && | ||
127 | irq_nr < sp->start_irqno + sp->nr_irqs) { | ||
128 | if (sp->mask_func) | ||
129 | sp->mask_func(irq_nr - sp->start_irqno, | ||
130 | sp->space_id); | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | spinlock_irqrestore(&jmr3927_irq_lock, flags); | ||
135 | } | ||
136 | |||
137 | static void jmr3927_irq_enable(unsigned int irq_nr) | ||
138 | { | ||
139 | struct tb_irq_space* sp; | ||
140 | unsigned long flags; | ||
141 | |||
142 | spinlock_irqsave(&jmr3927_irq_lock, flags); | ||
143 | for (sp = tb_irq_spaces; sp; sp = sp->next) { | ||
144 | if (sp->start_irqno <= irq_nr && | ||
145 | irq_nr < sp->start_irqno + sp->nr_irqs) { | ||
146 | if (sp->unmask_func) | ||
147 | sp->unmask_func(irq_nr - sp->start_irqno, | ||
148 | sp->space_id); | ||
149 | break; | ||
150 | } | ||
151 | } | ||
152 | spinlock_irqrestore(&jmr3927_irq_lock, flags); | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * CP0_STATUS is a thread's resource (saved/restored on context switch). | ||
157 | * So disable_irq/enable_irq MUST handle IOC/ISAC/IRC registers. | ||
158 | */ | ||
159 | static void mask_irq_isac(int irq_nr, int space_id) | ||
160 | { | ||
161 | /* 0: mask */ | ||
162 | unsigned char imask = | ||
163 | jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); | ||
164 | unsigned int bit = 1 << irq_nr; | ||
165 | jmr3927_isac_reg_out(imask & ~bit, JMR3927_ISAC_INTM_ADDR); | ||
166 | /* flush write buffer */ | ||
167 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | ||
168 | } | ||
169 | static void unmask_irq_isac(int irq_nr, int space_id) | ||
170 | { | ||
171 | /* 0: mask */ | ||
172 | unsigned char imask = jmr3927_isac_reg_in(JMR3927_ISAC_INTM_ADDR); | ||
173 | unsigned int bit = 1 << irq_nr; | ||
174 | jmr3927_isac_reg_out(imask | bit, JMR3927_ISAC_INTM_ADDR); | ||
175 | /* flush write buffer */ | ||
176 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | ||
177 | } | ||
178 | |||
179 | static void mask_irq_ioc(int irq_nr, int space_id) | ||
180 | { | ||
181 | /* 0: mask */ | ||
182 | unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); | ||
183 | unsigned int bit = 1 << irq_nr; | ||
184 | jmr3927_ioc_reg_out(imask & ~bit, JMR3927_IOC_INTM_ADDR); | ||
185 | /* flush write buffer */ | ||
186 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | ||
187 | } | ||
188 | static void unmask_irq_ioc(int irq_nr, int space_id) | ||
189 | { | ||
190 | /* 0: mask */ | ||
191 | unsigned char imask = jmr3927_ioc_reg_in(JMR3927_IOC_INTM_ADDR); | ||
192 | unsigned int bit = 1 << irq_nr; | ||
193 | jmr3927_ioc_reg_out(imask | bit, JMR3927_IOC_INTM_ADDR); | ||
194 | /* flush write buffer */ | ||
195 | (void)jmr3927_ioc_reg_in(JMR3927_IOC_REV_ADDR); | ||
196 | } | ||
197 | |||
198 | static void mask_irq_irc(int irq_nr, int space_id) | ||
199 | { | ||
200 | volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; | ||
201 | if (irq_nr & 1) | ||
202 | *ilrp = (*ilrp & 0x00ff) | (irc_dlevel << 8); | ||
203 | else | ||
204 | *ilrp = (*ilrp & 0xff00) | irc_dlevel; | ||
205 | /* update IRCSR */ | ||
206 | tx3927_ircptr->imr = 0; | ||
207 | tx3927_ircptr->imr = irc_elevel; | ||
208 | } | ||
209 | static void unmask_irq_irc(int irq_nr, int space_id) | ||
210 | { | ||
211 | volatile unsigned long *ilrp = &tx3927_ircptr->ilr[irq_nr / 2]; | ||
212 | if (irq_nr & 1) | ||
213 | *ilrp = (*ilrp & 0x00ff) | (irc_level[irq_nr] << 8); | ||
214 | else | ||
215 | *ilrp = (*ilrp & 0xff00) | irc_level[irq_nr]; | ||
216 | /* update IRCSR */ | ||
217 | tx3927_ircptr->imr = 0; | ||
218 | tx3927_ircptr->imr = irc_elevel; | ||
219 | } | ||
220 | |||
221 | struct tb_irq_space jmr3927_isac_irqspace = { | ||
222 | .next = NULL, | ||
223 | .start_irqno = JMR3927_IRQ_ISAC, | ||
224 | nr_irqs : JMR3927_NR_IRQ_ISAC, | ||
225 | .mask_func = mask_irq_isac, | ||
226 | .unmask_func = unmask_irq_isac, | ||
227 | .name = "ISAC", | ||
228 | .space_id = 0, | ||
229 | can_share : 0 | ||
230 | }; | ||
231 | struct tb_irq_space jmr3927_ioc_irqspace = { | ||
232 | .next = NULL, | ||
233 | .start_irqno = JMR3927_IRQ_IOC, | ||
234 | nr_irqs : JMR3927_NR_IRQ_IOC, | ||
235 | .mask_func = mask_irq_ioc, | ||
236 | .unmask_func = unmask_irq_ioc, | ||
237 | .name = "IOC", | ||
238 | .space_id = 0, | ||
239 | can_share : 1 | ||
240 | }; | ||
241 | struct tb_irq_space jmr3927_irc_irqspace = { | ||
242 | .next = NULL, | ||
243 | .start_irqno = JMR3927_IRQ_IRC, | ||
244 | nr_irqs : JMR3927_NR_IRQ_IRC, | ||
245 | .mask_func = mask_irq_irc, | ||
246 | .unmask_func = unmask_irq_irc, | ||
247 | .name = "on-chip", | ||
248 | .space_id = 0, | ||
249 | can_share : 0 | ||
250 | }; | ||
251 | |||
252 | void jmr3927_spurious(struct pt_regs *regs) | ||
253 | { | ||
254 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND | ||
255 | tx_branch_likely_bug_fixup(regs); | ||
256 | #endif | ||
257 | printk(KERN_WARNING "spurious interrupt (cause 0x%lx, pc 0x%lx, ra 0x%lx).\n", | ||
258 | regs->cp0_cause, regs->cp0_epc, regs->regs[31]); | ||
259 | } | ||
260 | |||
261 | void jmr3927_irc_irqdispatch(struct pt_regs *regs) | ||
262 | { | ||
263 | int irq; | ||
264 | |||
265 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND | ||
266 | tx_branch_likely_bug_fixup(regs); | ||
267 | #endif | ||
268 | if ((regs->cp0_cause & CAUSEF_IP7) == 0) { | ||
269 | #if 0 | ||
270 | jmr3927_spurious(regs); | ||
271 | #endif | ||
272 | return; | ||
273 | } | ||
274 | irq = (regs->cp0_cause >> CAUSEB_IP2) & 0x0f; | ||
275 | |||
276 | do_IRQ(irq + JMR3927_IRQ_IRC, regs); | ||
277 | } | ||
278 | |||
279 | static void jmr3927_ioc_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
280 | { | ||
281 | unsigned char istat = jmr3927_ioc_reg_in(JMR3927_IOC_INTS2_ADDR); | ||
282 | int i; | ||
283 | |||
284 | for (i = 0; i < JMR3927_NR_IRQ_IOC; i++) { | ||
285 | if (istat & (1 << i)) { | ||
286 | irq = JMR3927_IRQ_IOC + i; | ||
287 | do_IRQ(irq, regs); | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static struct irqaction ioc_action = { | ||
293 | jmr3927_ioc_interrupt, 0, CPU_MASK_NONE, "IOC", NULL, NULL, | ||
294 | }; | ||
295 | |||
296 | static void jmr3927_isac_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
297 | { | ||
298 | unsigned char istat = jmr3927_isac_reg_in(JMR3927_ISAC_INTS2_ADDR); | ||
299 | int i; | ||
300 | |||
301 | for (i = 0; i < JMR3927_NR_IRQ_ISAC; i++) { | ||
302 | if (istat & (1 << i)) { | ||
303 | irq = JMR3927_IRQ_ISAC + i; | ||
304 | do_IRQ(irq, regs); | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static struct irqaction isac_action = { | ||
310 | jmr3927_isac_interrupt, 0, CPU_MASK_NONE, "ISAC", NULL, NULL, | ||
311 | }; | ||
312 | |||
313 | |||
314 | static void jmr3927_isaerr_interrupt(int irq, void * dev_id, struct pt_regs * regs) | ||
315 | { | ||
316 | printk(KERN_WARNING "ISA error interrupt (irq 0x%x).\n", irq); | ||
317 | } | ||
318 | static struct irqaction isaerr_action = { | ||
319 | jmr3927_isaerr_interrupt, 0, CPU_MASK_NONE, "ISA error", NULL, NULL, | ||
320 | }; | ||
321 | |||
322 | static void jmr3927_pcierr_interrupt(int irq, void * dev_id, struct pt_regs * regs) | ||
323 | { | ||
324 | printk(KERN_WARNING "PCI error interrupt (irq 0x%x).\n", irq); | ||
325 | printk(KERN_WARNING "pcistat:%02x, lbstat:%04lx\n", | ||
326 | tx3927_pcicptr->pcistat, tx3927_pcicptr->lbstat); | ||
327 | } | ||
328 | static struct irqaction pcierr_action = { | ||
329 | jmr3927_pcierr_interrupt, 0, CPU_MASK_NONE, "PCI error", NULL, NULL, | ||
330 | }; | ||
331 | |||
332 | int jmr3927_ether1_irq = 0; | ||
333 | |||
334 | void jmr3927_irq_init(u32 irq_base); | ||
335 | |||
336 | void __init arch_init_irq(void) | ||
337 | { | ||
338 | /* look for io board's presence */ | ||
339 | int have_isac = jmr3927_have_isac(); | ||
340 | |||
341 | /* Now, interrupt control disabled, */ | ||
342 | /* all IRC interrupts are masked, */ | ||
343 | /* all IRC interrupt mode are Low Active. */ | ||
344 | |||
345 | if (have_isac) { | ||
346 | |||
347 | /* ETHER1 (NE2000 compatible 10M-Ether) parameter setup */ | ||
348 | /* temporary enable interrupt control */ | ||
349 | tx3927_ircptr->cer = 1; | ||
350 | /* ETHER1 Int. Is High-Active. */ | ||
351 | if (tx3927_ircptr->ssr & (1 << 0)) | ||
352 | jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT0; | ||
353 | #if 0 /* INT3 may be asserted by ether0 (even after reboot...) */ | ||
354 | else if (tx3927_ircptr->ssr & (1 << 3)) | ||
355 | jmr3927_ether1_irq = JMR3927_IRQ_IRC_INT3; | ||
356 | #endif | ||
357 | /* disable interrupt control */ | ||
358 | tx3927_ircptr->cer = 0; | ||
359 | |||
360 | /* Ether1: High Active */ | ||
361 | if (jmr3927_ether1_irq) { | ||
362 | int ether1_irc = jmr3927_ether1_irq - JMR3927_IRQ_IRC; | ||
363 | tx3927_ircptr->cr[ether1_irc / 8] |= | ||
364 | TX3927_IRCR_HIGH << ((ether1_irc % 8) * 2); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | /* mask all IOC interrupts */ | ||
369 | jmr3927_ioc_reg_out(0, JMR3927_IOC_INTM_ADDR); | ||
370 | /* setup IOC interrupt mode (SOFT:High Active, Others:Low Active) */ | ||
371 | jmr3927_ioc_reg_out(JMR3927_IOC_INTF_SOFT, JMR3927_IOC_INTP_ADDR); | ||
372 | |||
373 | if (have_isac) { | ||
374 | /* mask all ISAC interrupts */ | ||
375 | jmr3927_isac_reg_out(0, JMR3927_ISAC_INTM_ADDR); | ||
376 | /* setup ISAC interrupt mode (ISAIRQ3,ISAIRQ5:Low Active ???) */ | ||
377 | jmr3927_isac_reg_out(JMR3927_ISAC_INTF_IRQ3|JMR3927_ISAC_INTF_IRQ5, JMR3927_ISAC_INTP_ADDR); | ||
378 | } | ||
379 | |||
380 | /* clear PCI Soft interrupts */ | ||
381 | jmr3927_ioc_reg_out(0, JMR3927_IOC_INTS1_ADDR); | ||
382 | /* clear PCI Reset interrupts */ | ||
383 | jmr3927_ioc_reg_out(0, JMR3927_IOC_RESET_ADDR); | ||
384 | |||
385 | /* enable interrupt control */ | ||
386 | tx3927_ircptr->cer = TX3927_IRCER_ICE; | ||
387 | tx3927_ircptr->imr = irc_elevel; | ||
388 | |||
389 | jmr3927_irq_init(NR_ISA_IRQS); | ||
390 | |||
391 | set_except_vector(0, jmr3927_IRQ); | ||
392 | |||
393 | /* setup irq space */ | ||
394 | add_tb_irq_space(&jmr3927_isac_irqspace); | ||
395 | add_tb_irq_space(&jmr3927_ioc_irqspace); | ||
396 | add_tb_irq_space(&jmr3927_irc_irqspace); | ||
397 | |||
398 | /* setup IOC interrupt 1 (PCI, MODEM) */ | ||
399 | setup_irq(JMR3927_IRQ_IOCINT, &ioc_action); | ||
400 | |||
401 | if (have_isac) { | ||
402 | setup_irq(JMR3927_IRQ_ISACINT, &isac_action); | ||
403 | setup_irq(JMR3927_IRQ_ISAC_ISAER, &isaerr_action); | ||
404 | } | ||
405 | |||
406 | #ifdef CONFIG_PCI | ||
407 | setup_irq(JMR3927_IRQ_IRC_PCI, &pcierr_action); | ||
408 | #endif | ||
409 | |||
410 | /* enable all CPU interrupt bits. */ | ||
411 | set_c0_status(ST0_IM); /* IE bit is still 0. */ | ||
412 | } | ||
413 | |||
414 | static hw_irq_controller jmr3927_irq_controller = { | ||
415 | "jmr3927_irq", | ||
416 | jmr3927_irq_startup, | ||
417 | jmr3927_irq_shutdown, | ||
418 | jmr3927_irq_enable, | ||
419 | jmr3927_irq_disable, | ||
420 | jmr3927_irq_ack, | ||
421 | jmr3927_irq_end, | ||
422 | }; | ||
423 | |||
424 | void jmr3927_irq_init(u32 irq_base) | ||
425 | { | ||
426 | u32 i; | ||
427 | |||
428 | for (i= irq_base; i< irq_base + JMR3927_NR_IRQ_IRC + JMR3927_NR_IRQ_IOC; i++) { | ||
429 | irq_desc[i].status = IRQ_DISABLED; | ||
430 | irq_desc[i].action = NULL; | ||
431 | irq_desc[i].depth = 1; | ||
432 | irq_desc[i].handler = &jmr3927_irq_controller; | ||
433 | } | ||
434 | |||
435 | jmr3927_irq_base = irq_base; | ||
436 | } | ||
437 | |||
438 | #ifdef CONFIG_TX_BRANCH_LIKELY_BUG_WORKAROUND | ||
439 | static int tx_branch_likely_bug_count = 0; | ||
440 | static int have_tx_branch_likely_bug = 0; | ||
441 | void tx_branch_likely_bug_fixup(struct pt_regs *regs) | ||
442 | { | ||
443 | /* TX39/49-BUG: Under this condition, the insn in delay slot | ||
444 | of the branch likely insn is executed (not nullified) even | ||
445 | the branch condition is false. */ | ||
446 | if (!have_tx_branch_likely_bug) | ||
447 | return; | ||
448 | if ((regs->cp0_epc & 0xfff) == 0xffc && | ||
449 | KSEGX(regs->cp0_epc) != KSEG0 && | ||
450 | KSEGX(regs->cp0_epc) != KSEG1) { | ||
451 | unsigned int insn = *(unsigned int*)(regs->cp0_epc - 4); | ||
452 | /* beql,bnel,blezl,bgtzl */ | ||
453 | /* bltzl,bgezl,blezall,bgezall */ | ||
454 | /* bczfl, bcztl */ | ||
455 | if ((insn & 0xf0000000) == 0x50000000 || | ||
456 | (insn & 0xfc0e0000) == 0x04020000 || | ||
457 | (insn & 0xf3fe0000) == 0x41020000) { | ||
458 | regs->cp0_epc -= 4; | ||
459 | tx_branch_likely_bug_count++; | ||
460 | printk(KERN_INFO | ||
461 | "fix branch-likery bug in %s (insn %08x)\n", | ||
462 | current->comm, insn); | ||
463 | } | ||
464 | } | ||
465 | } | ||
466 | #endif | ||