aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/i8259.c
diff options
context:
space:
mode:
authorPavel Machek <pavel@suse.cz>2008-05-21 05:44:02 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-24 10:44:26 -0400
commit21fd5132b223a10bdf17713dd0bf321cbd6471d2 (patch)
tree32e851b6310535e5a61f5398d80d0b07aeac0844 /arch/x86/kernel/i8259.c
parent403d8efc94cd02ae36e7db13c4edf1d06d7b7fac (diff)
x86: automatical unification of i8259.c
Make conversion of i8259 very mechanical -- i8259 was generated by diff -D, with too different parts left in i8259_32 and i8259_64.c. Only "by hand" changes were removal of #ifdef from middle of the comment (prevented compilation) and removal of one static to allow splitting into files. Of course, it will need some cleanups now, and those will follow. Signed-of-by: Pavel Machek <pavel@suse.cz>
Diffstat (limited to 'arch/x86/kernel/i8259.c')
-rw-r--r--arch/x86/kernel/i8259.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
new file mode 100644
index 000000000000..2decba6b0101
--- /dev/null
+++ b/arch/x86/kernel/i8259.c
@@ -0,0 +1,368 @@
1#ifdef CONFIG_X86_64
2#include <linux/linkage.h>
3#endif /* CONFIG_X86_64 */
4#include <linux/errno.h>
5#include <linux/signal.h>
6#include <linux/sched.h>
7#include <linux/ioport.h>
8#include <linux/interrupt.h>
9#ifdef CONFIG_X86_64
10#include <linux/timex.h>
11#endif /* CONFIG_X86_64 */
12#include <linux/slab.h>
13#include <linux/random.h>
14#include <linux/init.h>
15#include <linux/kernel_stat.h>
16#include <linux/sysdev.h>
17#include <linux/bitops.h>
18
19#ifdef CONFIG_X86_64
20#include <asm/acpi.h>
21#endif /* CONFIG_X86_64 */
22#include <asm/atomic.h>
23#include <asm/system.h>
24#include <asm/io.h>
25#ifndef CONFIG_X86_64
26#include <asm/timer.h>
27#else /* CONFIG_X86_64 */
28#include <asm/hw_irq.h>
29#endif /* CONFIG_X86_64 */
30#include <asm/pgtable.h>
31#include <asm/delay.h>
32#include <asm/desc.h>
33#include <asm/apic.h>
34#ifndef CONFIG_X86_64
35#include <asm/arch_hooks.h>
36#endif /* ! CONFIG_X86_64 */
37#include <asm/i8259.h>
38
39/*
40 * This is the 'legacy' 8259A Programmable Interrupt Controller,
41 * present in the majority of PC/AT boxes.
42 * plus some generic x86 specific things if generic specifics makes
43 * any sense at all.
44 */
45
46static int i8259A_auto_eoi;
47DEFINE_SPINLOCK(i8259A_lock);
48static void mask_and_ack_8259A(unsigned int);
49
50struct irq_chip i8259A_chip = {
51 .name = "XT-PIC",
52 .mask = disable_8259A_irq,
53 .disable = disable_8259A_irq,
54 .unmask = enable_8259A_irq,
55 .mask_ack = mask_and_ack_8259A,
56};
57
58/*
59 * 8259A PIC functions to handle ISA devices:
60 */
61
62/*
63 * This contains the irq mask for both 8259A irq controllers,
64 */
65unsigned int cached_irq_mask = 0xffff;
66
67/*
68 * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
69 * boards the timer interrupt is not really connected to any IO-APIC pin,
70 * it's fed to the master 8259A's IR0 line only.
71 *
72 * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
73 * this 'mixed mode' IRQ handling costs nothing because it's only used
74 * at IRQ setup time.
75 */
76unsigned long io_apic_irqs;
77
78void disable_8259A_irq(unsigned int irq)
79{
80 unsigned int mask = 1 << irq;
81 unsigned long flags;
82
83 spin_lock_irqsave(&i8259A_lock, flags);
84 cached_irq_mask |= mask;
85 if (irq & 8)
86 outb(cached_slave_mask, PIC_SLAVE_IMR);
87 else
88 outb(cached_master_mask, PIC_MASTER_IMR);
89 spin_unlock_irqrestore(&i8259A_lock, flags);
90}
91
92void enable_8259A_irq(unsigned int irq)
93{
94 unsigned int mask = ~(1 << irq);
95 unsigned long flags;
96
97 spin_lock_irqsave(&i8259A_lock, flags);
98 cached_irq_mask &= mask;
99 if (irq & 8)
100 outb(cached_slave_mask, PIC_SLAVE_IMR);
101 else
102 outb(cached_master_mask, PIC_MASTER_IMR);
103 spin_unlock_irqrestore(&i8259A_lock, flags);
104}
105
106int i8259A_irq_pending(unsigned int irq)
107{
108 unsigned int mask = 1<<irq;
109 unsigned long flags;
110 int ret;
111
112 spin_lock_irqsave(&i8259A_lock, flags);
113 if (irq < 8)
114 ret = inb(PIC_MASTER_CMD) & mask;
115 else
116 ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
117 spin_unlock_irqrestore(&i8259A_lock, flags);
118
119 return ret;
120}
121
122void make_8259A_irq(unsigned int irq)
123{
124 disable_irq_nosync(irq);
125 io_apic_irqs &= ~(1<<irq);
126 set_irq_chip_and_handler_name(irq, &i8259A_chip, handle_level_irq,
127 "XT");
128 enable_irq(irq);
129}
130
131/*
132 * This function assumes to be called rarely. Switching between
133 * 8259A registers is slow.
134 * This has to be protected by the irq controller spinlock
135 * before being called.
136 */
137static inline int i8259A_irq_real(unsigned int irq)
138{
139 int value;
140 int irqmask = 1<<irq;
141
142 if (irq < 8) {
143 outb(0x0B,PIC_MASTER_CMD); /* ISR register */
144 value = inb(PIC_MASTER_CMD) & irqmask;
145 outb(0x0A,PIC_MASTER_CMD); /* back to the IRR register */
146 return value;
147 }
148 outb(0x0B,PIC_SLAVE_CMD); /* ISR register */
149 value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
150 outb(0x0A,PIC_SLAVE_CMD); /* back to the IRR register */
151 return value;
152}
153
154/*
155 * Careful! The 8259A is a fragile beast, it pretty
156 * much _has_ to be done exactly like this (mask it
157 * first, _then_ send the EOI, and the order of EOI
158 * to the two 8259s is important!
159 */
160static void mask_and_ack_8259A(unsigned int irq)
161{
162 unsigned int irqmask = 1 << irq;
163 unsigned long flags;
164
165 spin_lock_irqsave(&i8259A_lock, flags);
166 /*
167 * Lightweight spurious IRQ detection. We do not want
168 * to overdo spurious IRQ handling - it's usually a sign
169 * of hardware problems, so we only do the checks we can
170 * do without slowing down good hardware unnecessarily.
171 *
172 * Note that IRQ7 and IRQ15 (the two spurious IRQs
173 * usually resulting from the 8259A-1|2 PICs) occur
174 * even if the IRQ is masked in the 8259A. Thus we
175 * can check spurious 8259A IRQs without doing the
176 * quite slow i8259A_irq_real() call for every IRQ.
177 * This does not cover 100% of spurious interrupts,
178 * but should be enough to warn the user that there
179 * is something bad going on ...
180 */
181 if (cached_irq_mask & irqmask)
182 goto spurious_8259A_irq;
183 cached_irq_mask |= irqmask;
184
185handle_real_irq:
186 if (irq & 8) {
187 inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
188 outb(cached_slave_mask, PIC_SLAVE_IMR);
189#ifndef CONFIG_X86_64
190 outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
191 outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
192#else /* CONFIG_X86_64 */
193 /* 'Specific EOI' to slave */
194 outb(0x60+(irq&7),PIC_SLAVE_CMD);
195 /* 'Specific EOI' to master-IRQ2 */
196 outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD);
197#endif /* CONFIG_X86_64 */
198 } else {
199 inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
200 outb(cached_master_mask, PIC_MASTER_IMR);
201#ifndef CONFIG_X86_64
202 outb(0x60+irq,PIC_MASTER_CMD); /* 'Specific EOI to master */
203#else /* CONFIG_X86_64 */
204 /* 'Specific EOI' to master */
205 outb(0x60+irq,PIC_MASTER_CMD);
206#endif /* CONFIG_X86_64 */
207 }
208 spin_unlock_irqrestore(&i8259A_lock, flags);
209 return;
210
211spurious_8259A_irq:
212 /*
213 * this is the slow path - should happen rarely.
214 */
215 if (i8259A_irq_real(irq))
216 /*
217 * oops, the IRQ _is_ in service according to the
218 * 8259A - not spurious, go handle it.
219 */
220 goto handle_real_irq;
221
222 {
223 static int spurious_irq_mask;
224 /*
225 * At this point we can be sure the IRQ is spurious,
226 * lets ACK and report it. [once per IRQ]
227 */
228 if (!(spurious_irq_mask & irqmask)) {
229#ifndef CONFIG_X86_64
230 printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
231#else /* CONFIG_X86_64 */
232 printk(KERN_DEBUG
233 "spurious 8259A interrupt: IRQ%d.\n", irq);
234#endif /* CONFIG_X86_64 */
235 spurious_irq_mask |= irqmask;
236 }
237 atomic_inc(&irq_err_count);
238 /*
239 * Theoretically we do not have to handle this IRQ,
240 * but in Linux this does not cause problems and is
241 * simpler for us.
242 */
243 goto handle_real_irq;
244 }
245}
246
247static char irq_trigger[2];
248/**
249 * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ
250 */
251static void restore_ELCR(char *trigger)
252{
253 outb(trigger[0], 0x4d0);
254 outb(trigger[1], 0x4d1);
255}
256
257static void save_ELCR(char *trigger)
258{
259 /* IRQ 0,1,2,8,13 are marked as reserved */
260 trigger[0] = inb(0x4d0) & 0xF8;
261 trigger[1] = inb(0x4d1) & 0xDE;
262}
263
264static int i8259A_resume(struct sys_device *dev)
265{
266 init_8259A(i8259A_auto_eoi);
267 restore_ELCR(irq_trigger);
268 return 0;
269}
270
271static int i8259A_suspend(struct sys_device *dev, pm_message_t state)
272{
273 save_ELCR(irq_trigger);
274 return 0;
275}
276
277static int i8259A_shutdown(struct sys_device *dev)
278{
279 /* Put the i8259A into a quiescent state that
280 * the kernel initialization code can get it
281 * out of.
282 */
283 outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
284 outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-1 */
285 return 0;
286}
287
288static struct sysdev_class i8259_sysdev_class = {
289 .name = "i8259",
290 .suspend = i8259A_suspend,
291 .resume = i8259A_resume,
292 .shutdown = i8259A_shutdown,
293};
294
295static struct sys_device device_i8259A = {
296 .id = 0,
297 .cls = &i8259_sysdev_class,
298};
299
300static int __init i8259A_init_sysfs(void)
301{
302 int error = sysdev_class_register(&i8259_sysdev_class);
303 if (!error)
304 error = sysdev_register(&device_i8259A);
305 return error;
306}
307
308device_initcall(i8259A_init_sysfs);
309
310void init_8259A(int auto_eoi)
311{
312 unsigned long flags;
313
314 i8259A_auto_eoi = auto_eoi;
315
316 spin_lock_irqsave(&i8259A_lock, flags);
317
318 outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
319 outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
320
321 /*
322 * outb_pic - this has to work on a wide range of PC hardware.
323 */
324 outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
325#ifndef CONFIG_X86_64
326 outb_pic(0x20 + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
327 outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */
328#else /* CONFIG_X86_64 */
329 /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */
330 outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);
331 /* 8259A-1 (the master) has a slave on IR2 */
332 outb_pic(0x04, PIC_MASTER_IMR);
333#endif /* CONFIG_X86_64 */
334 if (auto_eoi) /* master does Auto EOI */
335 outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
336 else /* master expects normal EOI */
337 outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
338
339 outb_pic(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
340#ifndef CONFIG_X86_64
341 outb_pic(0x20 + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
342 outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
343 outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
344#else /* CONFIG_X86_64 */
345 /* ICW2: 8259A-2 IR0-7 mapped to 0x38-0x3f */
346 outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
347 /* 8259A-2 is a slave on master's IR2 */
348 outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
349 /* (slave's support for AEOI in flat mode is to be investigated) */
350 outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
351
352#endif /* CONFIG_X86_64 */
353 if (auto_eoi)
354 /*
355 * In AEOI mode we just have to mask the interrupt
356 * when acking.
357 */
358 i8259A_chip.mask_ack = disable_8259A_irq;
359 else
360 i8259A_chip.mask_ack = mask_and_ack_8259A;
361
362 udelay(100); /* wait for 8259A to initialize */
363
364 outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
365 outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
366
367 spin_unlock_irqrestore(&i8259A_lock, flags);
368}