diff options
Diffstat (limited to 'arch/mips/kernel/i8259.c')
-rw-r--r-- | arch/mips/kernel/i8259.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c new file mode 100644 index 000000000000..7eec7568bfea --- /dev/null +++ b/arch/mips/kernel/i8259.c | |||
@@ -0,0 +1,331 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Code to handle x86 style IRQs plus some generic interrupt stuff. | ||
7 | * | ||
8 | * Copyright (C) 1992 Linus Torvalds | ||
9 | * Copyright (C) 1994 - 2000 Ralf Baechle | ||
10 | */ | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/ioport.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/sysdev.h> | ||
18 | |||
19 | #include <asm/i8259.h> | ||
20 | #include <asm/io.h> | ||
21 | |||
22 | void enable_8259A_irq(unsigned int irq); | ||
23 | void disable_8259A_irq(unsigned int irq); | ||
24 | |||
25 | /* | ||
26 | * This is the 'legacy' 8259A Programmable Interrupt Controller, | ||
27 | * present in the majority of PC/AT boxes. | ||
28 | * plus some generic x86 specific things if generic specifics makes | ||
29 | * any sense at all. | ||
30 | * this file should become arch/i386/kernel/irq.c when the old irq.c | ||
31 | * moves to arch independent land | ||
32 | */ | ||
33 | |||
34 | spinlock_t i8259A_lock = SPIN_LOCK_UNLOCKED; | ||
35 | |||
36 | static void end_8259A_irq (unsigned int irq) | ||
37 | { | ||
38 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && | ||
39 | irq_desc[irq].action) | ||
40 | enable_8259A_irq(irq); | ||
41 | } | ||
42 | |||
43 | #define shutdown_8259A_irq disable_8259A_irq | ||
44 | |||
45 | void mask_and_ack_8259A(unsigned int); | ||
46 | |||
47 | static unsigned int startup_8259A_irq(unsigned int irq) | ||
48 | { | ||
49 | enable_8259A_irq(irq); | ||
50 | |||
51 | return 0; /* never anything pending */ | ||
52 | } | ||
53 | |||
54 | static struct hw_interrupt_type i8259A_irq_type = { | ||
55 | "XT-PIC", | ||
56 | startup_8259A_irq, | ||
57 | shutdown_8259A_irq, | ||
58 | enable_8259A_irq, | ||
59 | disable_8259A_irq, | ||
60 | mask_and_ack_8259A, | ||
61 | end_8259A_irq, | ||
62 | NULL | ||
63 | }; | ||
64 | |||
65 | /* | ||
66 | * 8259A PIC functions to handle ISA devices: | ||
67 | */ | ||
68 | |||
69 | /* | ||
70 | * This contains the irq mask for both 8259A irq controllers, | ||
71 | */ | ||
72 | static unsigned int cached_irq_mask = 0xffff; | ||
73 | |||
74 | #define cached_21 (cached_irq_mask) | ||
75 | #define cached_A1 (cached_irq_mask >> 8) | ||
76 | |||
77 | void disable_8259A_irq(unsigned int irq) | ||
78 | { | ||
79 | unsigned int mask = 1 << irq; | ||
80 | unsigned long flags; | ||
81 | |||
82 | spin_lock_irqsave(&i8259A_lock, flags); | ||
83 | cached_irq_mask |= mask; | ||
84 | if (irq & 8) | ||
85 | outb(cached_A1,0xA1); | ||
86 | else | ||
87 | outb(cached_21,0x21); | ||
88 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
89 | } | ||
90 | |||
91 | void enable_8259A_irq(unsigned int irq) | ||
92 | { | ||
93 | unsigned int mask = ~(1 << irq); | ||
94 | unsigned long flags; | ||
95 | |||
96 | spin_lock_irqsave(&i8259A_lock, flags); | ||
97 | cached_irq_mask &= mask; | ||
98 | if (irq & 8) | ||
99 | outb(cached_A1,0xA1); | ||
100 | else | ||
101 | outb(cached_21,0x21); | ||
102 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
103 | } | ||
104 | |||
105 | int i8259A_irq_pending(unsigned int irq) | ||
106 | { | ||
107 | unsigned int mask = 1 << irq; | ||
108 | unsigned long flags; | ||
109 | int ret; | ||
110 | |||
111 | spin_lock_irqsave(&i8259A_lock, flags); | ||
112 | if (irq < 8) | ||
113 | ret = inb(0x20) & mask; | ||
114 | else | ||
115 | ret = inb(0xA0) & (mask >> 8); | ||
116 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | void make_8259A_irq(unsigned int irq) | ||
122 | { | ||
123 | disable_irq_nosync(irq); | ||
124 | irq_desc[irq].handler = &i8259A_irq_type; | ||
125 | enable_irq(irq); | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * This function assumes to be called rarely. Switching between | ||
130 | * 8259A registers is slow. | ||
131 | * This has to be protected by the irq controller spinlock | ||
132 | * before being called. | ||
133 | */ | ||
134 | static inline int i8259A_irq_real(unsigned int irq) | ||
135 | { | ||
136 | int value; | ||
137 | int irqmask = 1 << irq; | ||
138 | |||
139 | if (irq < 8) { | ||
140 | outb(0x0B,0x20); /* ISR register */ | ||
141 | value = inb(0x20) & irqmask; | ||
142 | outb(0x0A,0x20); /* back to the IRR register */ | ||
143 | return value; | ||
144 | } | ||
145 | outb(0x0B,0xA0); /* ISR register */ | ||
146 | value = inb(0xA0) & (irqmask >> 8); | ||
147 | outb(0x0A,0xA0); /* back to the IRR register */ | ||
148 | return value; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Careful! The 8259A is a fragile beast, it pretty | ||
153 | * much _has_ to be done exactly like this (mask it | ||
154 | * first, _then_ send the EOI, and the order of EOI | ||
155 | * to the two 8259s is important! | ||
156 | */ | ||
157 | void mask_and_ack_8259A(unsigned int irq) | ||
158 | { | ||
159 | unsigned int irqmask = 1 << irq; | ||
160 | unsigned long flags; | ||
161 | |||
162 | spin_lock_irqsave(&i8259A_lock, flags); | ||
163 | /* | ||
164 | * Lightweight spurious IRQ detection. We do not want to overdo | ||
165 | * spurious IRQ handling - it's usually a sign of hardware problems, so | ||
166 | * we only do the checks we can do without slowing down good hardware | ||
167 | * nnecesserily. | ||
168 | * | ||
169 | * Note that IRQ7 and IRQ15 (the two spurious IRQs usually resulting | ||
170 | * rom the 8259A-1|2 PICs) occur even if the IRQ is masked in the 8259A. | ||
171 | * Thus we can check spurious 8259A IRQs without doing the quite slow | ||
172 | * i8259A_irq_real() call for every IRQ. This does not cover 100% of | ||
173 | * spurious interrupts, but should be enough to warn the user that | ||
174 | * there is something bad going on ... | ||
175 | */ | ||
176 | if (cached_irq_mask & irqmask) | ||
177 | goto spurious_8259A_irq; | ||
178 | cached_irq_mask |= irqmask; | ||
179 | |||
180 | handle_real_irq: | ||
181 | if (irq & 8) { | ||
182 | inb(0xA1); /* DUMMY - (do we need this?) */ | ||
183 | outb(cached_A1,0xA1); | ||
184 | outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */ | ||
185 | outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ | ||
186 | } else { | ||
187 | inb(0x21); /* DUMMY - (do we need this?) */ | ||
188 | outb(cached_21,0x21); | ||
189 | outb(0x60+irq,0x20); /* 'Specific EOI' to master */ | ||
190 | } | ||
191 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
192 | return; | ||
193 | |||
194 | spurious_8259A_irq: | ||
195 | /* | ||
196 | * this is the slow path - should happen rarely. | ||
197 | */ | ||
198 | if (i8259A_irq_real(irq)) | ||
199 | /* | ||
200 | * oops, the IRQ _is_ in service according to the | ||
201 | * 8259A - not spurious, go handle it. | ||
202 | */ | ||
203 | goto handle_real_irq; | ||
204 | |||
205 | { | ||
206 | static int spurious_irq_mask = 0; | ||
207 | /* | ||
208 | * At this point we can be sure the IRQ is spurious, | ||
209 | * lets ACK and report it. [once per IRQ] | ||
210 | */ | ||
211 | if (!(spurious_irq_mask & irqmask)) { | ||
212 | printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); | ||
213 | spurious_irq_mask |= irqmask; | ||
214 | } | ||
215 | atomic_inc(&irq_err_count); | ||
216 | /* | ||
217 | * Theoretically we do not have to handle this IRQ, | ||
218 | * but in Linux this does not cause problems and is | ||
219 | * simpler for us. | ||
220 | */ | ||
221 | goto handle_real_irq; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static int i8259A_resume(struct sys_device *dev) | ||
226 | { | ||
227 | init_8259A(0); | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static struct sysdev_class i8259_sysdev_class = { | ||
232 | set_kset_name("i8259"), | ||
233 | .resume = i8259A_resume, | ||
234 | }; | ||
235 | |||
236 | static struct sys_device device_i8259A = { | ||
237 | .id = 0, | ||
238 | .cls = &i8259_sysdev_class, | ||
239 | }; | ||
240 | |||
241 | static int __init i8259A_init_sysfs(void) | ||
242 | { | ||
243 | int error = sysdev_class_register(&i8259_sysdev_class); | ||
244 | if (!error) | ||
245 | error = sysdev_register(&device_i8259A); | ||
246 | return error; | ||
247 | } | ||
248 | |||
249 | device_initcall(i8259A_init_sysfs); | ||
250 | |||
251 | void __init init_8259A(int auto_eoi) | ||
252 | { | ||
253 | unsigned long flags; | ||
254 | |||
255 | spin_lock_irqsave(&i8259A_lock, flags); | ||
256 | |||
257 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | ||
258 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | ||
259 | |||
260 | /* | ||
261 | * outb_p - this has to work on a wide range of PC hardware. | ||
262 | */ | ||
263 | outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ | ||
264 | outb_p(0x00, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x00-0x07 */ | ||
265 | outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ | ||
266 | if (auto_eoi) | ||
267 | outb_p(0x03, 0x21); /* master does Auto EOI */ | ||
268 | else | ||
269 | outb_p(0x01, 0x21); /* master expects normal EOI */ | ||
270 | |||
271 | outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ | ||
272 | outb_p(0x08, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x08-0x0f */ | ||
273 | outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ | ||
274 | outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode | ||
275 | is to be investigated) */ | ||
276 | |||
277 | if (auto_eoi) | ||
278 | /* | ||
279 | * in AEOI mode we just have to mask the interrupt | ||
280 | * when acking. | ||
281 | */ | ||
282 | i8259A_irq_type.ack = disable_8259A_irq; | ||
283 | else | ||
284 | i8259A_irq_type.ack = mask_and_ack_8259A; | ||
285 | |||
286 | udelay(100); /* wait for 8259A to initialize */ | ||
287 | |||
288 | outb(cached_21, 0x21); /* restore master IRQ mask */ | ||
289 | outb(cached_A1, 0xA1); /* restore slave IRQ mask */ | ||
290 | |||
291 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * IRQ2 is cascade interrupt to second interrupt controller | ||
296 | */ | ||
297 | static struct irqaction irq2 = { | ||
298 | no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL | ||
299 | }; | ||
300 | |||
301 | static struct resource pic1_io_resource = { | ||
302 | "pic1", 0x20, 0x3f, IORESOURCE_BUSY | ||
303 | }; | ||
304 | |||
305 | static struct resource pic2_io_resource = { | ||
306 | "pic2", 0xa0, 0xbf, IORESOURCE_BUSY | ||
307 | }; | ||
308 | |||
309 | /* | ||
310 | * On systems with i8259-style interrupt controllers we assume for | ||
311 | * driver compatibility reasons interrupts 0 - 15 to be the i8295 | ||
312 | * interrupts even if the hardware uses a different interrupt numbering. | ||
313 | */ | ||
314 | void __init init_i8259_irqs (void) | ||
315 | { | ||
316 | int i; | ||
317 | |||
318 | request_resource(&ioport_resource, &pic1_io_resource); | ||
319 | request_resource(&ioport_resource, &pic2_io_resource); | ||
320 | |||
321 | init_8259A(0); | ||
322 | |||
323 | for (i = 0; i < 16; i++) { | ||
324 | irq_desc[i].status = IRQ_DISABLED; | ||
325 | irq_desc[i].action = 0; | ||
326 | irq_desc[i].depth = 1; | ||
327 | irq_desc[i].handler = &i8259A_irq_type; | ||
328 | } | ||
329 | |||
330 | setup_irq(2, &irq2); | ||
331 | } | ||