diff options
-rw-r--r-- | arch/x86/kernel/i8259_64.c | 154 |
1 files changed, 81 insertions, 73 deletions
diff --git a/arch/x86/kernel/i8259_64.c b/arch/x86/kernel/i8259_64.c index ba6d57286f56..be82b1217691 100644 --- a/arch/x86/kernel/i8259_64.c +++ b/arch/x86/kernel/i8259_64.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <asm/delay.h> | 21 | #include <asm/delay.h> |
22 | #include <asm/desc.h> | 22 | #include <asm/desc.h> |
23 | #include <asm/apic.h> | 23 | #include <asm/apic.h> |
24 | #include <asm/i8259.h> | ||
24 | 25 | ||
25 | /* | 26 | /* |
26 | * Common place to define all x86 IRQ vectors | 27 | * Common place to define all x86 IRQ vectors |
@@ -48,7 +49,7 @@ | |||
48 | */ | 49 | */ |
49 | 50 | ||
50 | /* | 51 | /* |
51 | * The IO-APIC gives us many more interrupt sources. Most of these | 52 | * The IO-APIC gives us many more interrupt sources. Most of these |
52 | * are unused but an SMP system is supposed to have enough memory ... | 53 | * are unused but an SMP system is supposed to have enough memory ... |
53 | * sometimes (mostly wrt. hw bugs) we get corrupted vectors all | 54 | * sometimes (mostly wrt. hw bugs) we get corrupted vectors all |
54 | * across the spectrum, so we really want to be prepared to get all | 55 | * across the spectrum, so we really want to be prepared to get all |
@@ -114,11 +115,7 @@ static struct irq_chip i8259A_chip = { | |||
114 | /* | 115 | /* |
115 | * This contains the irq mask for both 8259A irq controllers, | 116 | * This contains the irq mask for both 8259A irq controllers, |
116 | */ | 117 | */ |
117 | static unsigned int cached_irq_mask = 0xffff; | 118 | unsigned int cached_irq_mask = 0xffff; |
118 | |||
119 | #define __byte(x,y) (((unsigned char *)&(y))[x]) | ||
120 | #define cached_21 (__byte(0,cached_irq_mask)) | ||
121 | #define cached_A1 (__byte(1,cached_irq_mask)) | ||
122 | 119 | ||
123 | /* | 120 | /* |
124 | * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) | 121 | * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) |
@@ -139,9 +136,9 @@ void disable_8259A_irq(unsigned int irq) | |||
139 | spin_lock_irqsave(&i8259A_lock, flags); | 136 | spin_lock_irqsave(&i8259A_lock, flags); |
140 | cached_irq_mask |= mask; | 137 | cached_irq_mask |= mask; |
141 | if (irq & 8) | 138 | if (irq & 8) |
142 | outb(cached_A1,0xA1); | 139 | outb(cached_slave_mask, PIC_SLAVE_IMR); |
143 | else | 140 | else |
144 | outb(cached_21,0x21); | 141 | outb(cached_master_mask, PIC_MASTER_IMR); |
145 | spin_unlock_irqrestore(&i8259A_lock, flags); | 142 | spin_unlock_irqrestore(&i8259A_lock, flags); |
146 | } | 143 | } |
147 | 144 | ||
@@ -153,9 +150,9 @@ void enable_8259A_irq(unsigned int irq) | |||
153 | spin_lock_irqsave(&i8259A_lock, flags); | 150 | spin_lock_irqsave(&i8259A_lock, flags); |
154 | cached_irq_mask &= mask; | 151 | cached_irq_mask &= mask; |
155 | if (irq & 8) | 152 | if (irq & 8) |
156 | outb(cached_A1,0xA1); | 153 | outb(cached_slave_mask, PIC_SLAVE_IMR); |
157 | else | 154 | else |
158 | outb(cached_21,0x21); | 155 | outb(cached_master_mask, PIC_MASTER_IMR); |
159 | spin_unlock_irqrestore(&i8259A_lock, flags); | 156 | spin_unlock_irqrestore(&i8259A_lock, flags); |
160 | } | 157 | } |
161 | 158 | ||
@@ -167,9 +164,9 @@ int i8259A_irq_pending(unsigned int irq) | |||
167 | 164 | ||
168 | spin_lock_irqsave(&i8259A_lock, flags); | 165 | spin_lock_irqsave(&i8259A_lock, flags); |
169 | if (irq < 8) | 166 | if (irq < 8) |
170 | ret = inb(0x20) & mask; | 167 | ret = inb(PIC_MASTER_CMD) & mask; |
171 | else | 168 | else |
172 | ret = inb(0xA0) & (mask >> 8); | 169 | ret = inb(PIC_SLAVE_CMD) & (mask >> 8); |
173 | spin_unlock_irqrestore(&i8259A_lock, flags); | 170 | spin_unlock_irqrestore(&i8259A_lock, flags); |
174 | 171 | ||
175 | return ret; | 172 | return ret; |
@@ -196,14 +193,14 @@ static inline int i8259A_irq_real(unsigned int irq) | |||
196 | int irqmask = 1<<irq; | 193 | int irqmask = 1<<irq; |
197 | 194 | ||
198 | if (irq < 8) { | 195 | if (irq < 8) { |
199 | outb(0x0B,0x20); /* ISR register */ | 196 | outb(0x0B,PIC_MASTER_CMD); /* ISR register */ |
200 | value = inb(0x20) & irqmask; | 197 | value = inb(PIC_MASTER_CMD) & irqmask; |
201 | outb(0x0A,0x20); /* back to the IRR register */ | 198 | outb(0x0A,PIC_MASTER_CMD); /* back to the IRR register */ |
202 | return value; | 199 | return value; |
203 | } | 200 | } |
204 | outb(0x0B,0xA0); /* ISR register */ | 201 | outb(0x0B,PIC_SLAVE_CMD); /* ISR register */ |
205 | value = inb(0xA0) & (irqmask >> 8); | 202 | value = inb(PIC_SLAVE_CMD) & (irqmask >> 8); |
206 | outb(0x0A,0xA0); /* back to the IRR register */ | 203 | outb(0x0A,PIC_SLAVE_CMD); /* back to the IRR register */ |
207 | return value; | 204 | return value; |
208 | } | 205 | } |
209 | 206 | ||
@@ -240,14 +237,17 @@ static void mask_and_ack_8259A(unsigned int irq) | |||
240 | 237 | ||
241 | handle_real_irq: | 238 | handle_real_irq: |
242 | if (irq & 8) { | 239 | if (irq & 8) { |
243 | inb(0xA1); /* DUMMY - (do we need this?) */ | 240 | inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */ |
244 | outb(cached_A1,0xA1); | 241 | outb(cached_slave_mask, PIC_SLAVE_IMR); |
245 | outb(0x60+(irq&7),0xA0);/* 'Specific EOI' to slave */ | 242 | /* 'Specific EOI' to slave */ |
246 | outb(0x62,0x20); /* 'Specific EOI' to master-IRQ2 */ | 243 | outb(0x60+(irq&7),PIC_SLAVE_CMD); |
244 | /* 'Specific EOI' to master-IRQ2 */ | ||
245 | outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); | ||
247 | } else { | 246 | } else { |
248 | inb(0x21); /* DUMMY - (do we need this?) */ | 247 | inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */ |
249 | outb(cached_21,0x21); | 248 | outb(cached_master_mask, PIC_MASTER_IMR); |
250 | outb(0x60+irq,0x20); /* 'Specific EOI' to master */ | 249 | /* 'Specific EOI' to master */ |
250 | outb(0x60+irq,PIC_MASTER_CMD); | ||
251 | } | 251 | } |
252 | spin_unlock_irqrestore(&i8259A_lock, flags); | 252 | spin_unlock_irqrestore(&i8259A_lock, flags); |
253 | return; | 253 | return; |
@@ -270,7 +270,8 @@ spurious_8259A_irq: | |||
270 | * lets ACK and report it. [once per IRQ] | 270 | * lets ACK and report it. [once per IRQ] |
271 | */ | 271 | */ |
272 | if (!(spurious_irq_mask & irqmask)) { | 272 | if (!(spurious_irq_mask & irqmask)) { |
273 | printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); | 273 | printk(KERN_DEBUG |
274 | "spurious 8259A interrupt: IRQ%d.\n", irq); | ||
274 | spurious_irq_mask |= irqmask; | 275 | spurious_irq_mask |= irqmask; |
275 | } | 276 | } |
276 | atomic_inc(&irq_err_count); | 277 | atomic_inc(&irq_err_count); |
@@ -283,51 +284,6 @@ spurious_8259A_irq: | |||
283 | } | 284 | } |
284 | } | 285 | } |
285 | 286 | ||
286 | void init_8259A(int auto_eoi) | ||
287 | { | ||
288 | unsigned long flags; | ||
289 | |||
290 | i8259A_auto_eoi = auto_eoi; | ||
291 | |||
292 | spin_lock_irqsave(&i8259A_lock, flags); | ||
293 | |||
294 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | ||
295 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | ||
296 | |||
297 | /* | ||
298 | * outb_p - this has to work on a wide range of PC hardware. | ||
299 | */ | ||
300 | outb_p(0x11, 0x20); /* ICW1: select 8259A-1 init */ | ||
301 | outb_p(IRQ0_VECTOR, 0x21); /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */ | ||
302 | outb_p(0x04, 0x21); /* 8259A-1 (the master) has a slave on IR2 */ | ||
303 | if (auto_eoi) | ||
304 | outb_p(0x03, 0x21); /* master does Auto EOI */ | ||
305 | else | ||
306 | outb_p(0x01, 0x21); /* master expects normal EOI */ | ||
307 | |||
308 | outb_p(0x11, 0xA0); /* ICW1: select 8259A-2 init */ | ||
309 | outb_p(IRQ8_VECTOR, 0xA1); /* ICW2: 8259A-2 IR0-7 mapped to 0x38-0x3f */ | ||
310 | outb_p(0x02, 0xA1); /* 8259A-2 is a slave on master's IR2 */ | ||
311 | outb_p(0x01, 0xA1); /* (slave's support for AEOI in flat mode | ||
312 | is to be investigated) */ | ||
313 | |||
314 | if (auto_eoi) | ||
315 | /* | ||
316 | * in AEOI mode we just have to mask the interrupt | ||
317 | * when acking. | ||
318 | */ | ||
319 | i8259A_chip.mask_ack = disable_8259A_irq; | ||
320 | else | ||
321 | i8259A_chip.mask_ack = mask_and_ack_8259A; | ||
322 | |||
323 | udelay(100); /* wait for 8259A to initialize */ | ||
324 | |||
325 | outb(cached_21, 0x21); /* restore master IRQ mask */ | ||
326 | outb(cached_A1, 0xA1); /* restore slave IRQ mask */ | ||
327 | |||
328 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
329 | } | ||
330 | |||
331 | static char irq_trigger[2]; | 287 | static char irq_trigger[2]; |
332 | /** | 288 | /** |
333 | * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ | 289 | * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ |
@@ -364,8 +320,8 @@ static int i8259A_shutdown(struct sys_device *dev) | |||
364 | * the kernel initialization code can get it | 320 | * the kernel initialization code can get it |
365 | * out of. | 321 | * out of. |
366 | */ | 322 | */ |
367 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | 323 | outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */ |
368 | outb(0xff, 0xA1); /* mask all of 8259A-1 */ | 324 | outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-1 */ |
369 | return 0; | 325 | return 0; |
370 | } | 326 | } |
371 | 327 | ||
@@ -391,6 +347,58 @@ static int __init i8259A_init_sysfs(void) | |||
391 | 347 | ||
392 | device_initcall(i8259A_init_sysfs); | 348 | device_initcall(i8259A_init_sysfs); |
393 | 349 | ||
350 | void init_8259A(int auto_eoi) | ||
351 | { | ||
352 | unsigned long flags; | ||
353 | |||
354 | i8259A_auto_eoi = auto_eoi; | ||
355 | |||
356 | spin_lock_irqsave(&i8259A_lock, flags); | ||
357 | |||
358 | outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */ | ||
359 | outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */ | ||
360 | |||
361 | /* | ||
362 | * outb_p - this has to work on a wide range of PC hardware. | ||
363 | */ | ||
364 | outb_p(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */ | ||
365 | /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */ | ||
366 | outb_p(IRQ0_VECTOR, PIC_MASTER_IMR); | ||
367 | /* 8259A-1 (the master) has a slave on IR2 */ | ||
368 | outb_p(0x04, PIC_MASTER_IMR); | ||
369 | if (auto_eoi) /* master does Auto EOI */ | ||
370 | outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR); | ||
371 | else /* master expects normal EOI */ | ||
372 | outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR); | ||
373 | |||
374 | outb_p(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */ | ||
375 | /* ICW2: 8259A-2 IR0-7 mapped to 0x38-0x3f */ | ||
376 | outb_p(IRQ8_VECTOR, PIC_SLAVE_IMR); | ||
377 | /* 8259A-2 is a slave on master's IR2 */ | ||
378 | outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR); | ||
379 | /* (slave's support for AEOI in flat mode is to be investigated) */ | ||
380 | outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); | ||
381 | |||
382 | if (auto_eoi) | ||
383 | /* | ||
384 | * In AEOI mode we just have to mask the interrupt | ||
385 | * when acking. | ||
386 | */ | ||
387 | i8259A_chip.mask_ack = disable_8259A_irq; | ||
388 | else | ||
389 | i8259A_chip.mask_ack = mask_and_ack_8259A; | ||
390 | |||
391 | udelay(100); /* wait for 8259A to initialize */ | ||
392 | |||
393 | outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */ | ||
394 | outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */ | ||
395 | |||
396 | spin_unlock_irqrestore(&i8259A_lock, flags); | ||
397 | } | ||
398 | |||
399 | |||
400 | |||
401 | |||
394 | /* | 402 | /* |
395 | * IRQ2 is cascade interrupt to second interrupt controller | 403 | * IRQ2 is cascade interrupt to second interrupt controller |
396 | */ | 404 | */ |