Inlining will result in back-to-back mtc0 mfc0 instructions. Break the
[safe/jmp/linux-2.6] / arch / mips / kernel / irq_cpu.c
1 /*
2  * Copyright 2001 MontaVista Software Inc.
3  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
4  *
5  * Copyright (C) 2001 Ralf Baechle
6  * Copyright (C) 2005  MIPS Technologies, Inc.  All rights reserved.
7  *      Author: Maciej W. Rozycki <macro@mips.com>
8  *
9  * This file define the irq handler for MIPS CPU interrupts.
10  *
11  * This program is free software; you can redistribute  it and/or modify it
12  * under  the terms of  the GNU General  Public License as published by the
13  * Free Software Foundation;  either version 2 of the  License, or (at your
14  * option) any later version.
15  */
16
17 /*
18  * Almost all MIPS CPUs define 8 interrupt sources.  They are typically
19  * level triggered (i.e., cannot be cleared from CPU; must be cleared from
20  * device).  The first two are software interrupts which we don't really
21  * use or support.  The last one is usually the CPU timer interrupt if
22  * counter register is present or, for CPUs with an external FPU, by
23  * convention it's the FPU exception interrupt.
24  *
25  * Don't even think about using this on SMP.  You have been warned.
26  *
27  * This file exports one global function:
28  *      void mips_cpu_irq_init(int irq_base);
29  */
30 #include <linux/init.h>
31 #include <linux/interrupt.h>
32 #include <linux/kernel.h>
33
34 #include <asm/irq_cpu.h>
35 #include <asm/mipsregs.h>
36 #include <asm/system.h>
37
38 static int mips_cpu_irq_base;
39
40 static inline void unmask_mips_irq(unsigned int irq)
41 {
42         set_c0_status(0x100 << (irq - mips_cpu_irq_base));
43         irq_enable_hazard();
44 }
45
46 static inline void mask_mips_irq(unsigned int irq)
47 {
48         clear_c0_status(0x100 << (irq - mips_cpu_irq_base));
49         irq_disable_hazard();
50 }
51
52 static inline void mips_cpu_irq_enable(unsigned int irq)
53 {
54         unsigned long flags;
55
56         local_irq_save(flags);
57         unmask_mips_irq(irq);
58         back_to_back_c0_hazard();
59         local_irq_restore(flags);
60 }
61
62 static void mips_cpu_irq_disable(unsigned int irq)
63 {
64         unsigned long flags;
65
66         local_irq_save(flags);
67         mask_mips_irq(irq);
68         back_to_back_c0_hazard();
69         local_irq_restore(flags);
70 }
71
72 static unsigned int mips_cpu_irq_startup(unsigned int irq)
73 {
74         mips_cpu_irq_enable(irq);
75
76         return 0;
77 }
78
79 #define mips_cpu_irq_shutdown   mips_cpu_irq_disable
80
81 /*
82  * While we ack the interrupt interrupts are disabled and thus we don't need
83  * to deal with concurrency issues.  Same for mips_cpu_irq_end.
84  */
85 static void mips_cpu_irq_ack(unsigned int irq)
86 {
87         /* Only necessary for soft interrupts */
88         clear_c0_cause(0x100 << (irq - mips_cpu_irq_base));
89
90         mask_mips_irq(irq);
91 }
92
93 static void mips_cpu_irq_end(unsigned int irq)
94 {
95         if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
96                 unmask_mips_irq(irq);
97 }
98
99 static hw_irq_controller mips_cpu_irq_controller = {
100         .typename = "MIPS",
101         .startup = mips_cpu_irq_startup,
102         .shutdown = mips_cpu_irq_shutdown,
103         .enable = mips_cpu_irq_enable,
104         .disable = mips_cpu_irq_disable,
105         .ack = mips_cpu_irq_ack,
106         .end = mips_cpu_irq_end,
107 };
108
109
110 void __init mips_cpu_irq_init(int irq_base)
111 {
112         int i;
113
114         /* Mask interrupts. */
115         clear_c0_status(ST0_IM);
116         clear_c0_cause(CAUSEF_IP);
117
118         for (i = irq_base; i < irq_base + 8; i++) {
119                 irq_desc[i].status = IRQ_DISABLED;
120                 irq_desc[i].action = NULL;
121                 irq_desc[i].depth = 1;
122                 irq_desc[i].handler = &mips_cpu_irq_controller;
123         }
124
125         mips_cpu_irq_base = irq_base;
126 }