x86: I/O APIC: timer through 8259A second-chance

Some systems incorrectly report the ExtINTA pin of the I/O APIC as the
genuine target of the timer interrupt.  Here is a change that copies timer
pin information found to the other pin if one has been found only.  This
way both a direct and a through-8259A route is tested with the pin letting
these problematic systems work well enough.  If no timer pin information
has been found for the I/O APIC, then local APIC variations are tried
only, similarly to what is done without the change (except without the
misleading messages).

Obviously if we try the first-chance path without being told by the BIOS
to do so, we should not complain either, so do not print the message in
this case.

The 64-bit variation should be updated with a call to
replace_pin_at_irq() which can be done with the upcoming merge.  Since
add_pin_to_irq() is now always called in the first-chance path, the
condition to require it in the second-chance path no longer happens.

Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Maciej W. Rozycki 2008-05-27 21:19:51 +01:00 committed by Ingo Molnar
parent 03be750559
commit 691874fa96
2 changed files with 57 additions and 17 deletions

View File

@ -2122,6 +2122,7 @@ static inline void __init unlock_ExtINT_logic(void)
static inline void __init check_timer(void) static inline void __init check_timer(void)
{ {
int apic1, pin1, apic2, pin2; int apic1, pin1, apic2, pin2;
int no_pin1 = 0;
int vector; int vector;
unsigned int ver; unsigned int ver;
unsigned long flags; unsigned long flags;
@ -2159,10 +2160,30 @@ static inline void __init check_timer(void)
printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
vector, apic1, pin1, apic2, pin2); vector, apic1, pin1, apic2, pin2);
/*
* Some BIOS writers are clueless and report the ExtINTA
* I/O APIC input from the cascaded 8259A as the timer
* interrupt input. So just in case, if only one pin
* was found above, try it both directly and through the
* 8259A.
*/
if (pin1 == -1) {
pin1 = pin2;
apic1 = apic2;
no_pin1 = 1;
} else if (pin2 == -1) {
pin2 = pin1;
apic2 = apic1;
}
if (pin1 != -1) { if (pin1 != -1) {
/* /*
* Ok, does IRQ0 through the IOAPIC work? * Ok, does IRQ0 through the IOAPIC work?
*/ */
if (no_pin1) {
add_pin_to_irq(0, apic1, pin1);
setup_timer_IRQ0_pin(apic1, pin1, vector);
}
unmask_IO_APIC_irq(0); unmask_IO_APIC_irq(0);
if (timer_irq_works()) { if (timer_irq_works()) {
if (nmi_watchdog == NMI_IO_APIC) { if (nmi_watchdog == NMI_IO_APIC) {
@ -2174,26 +2195,23 @@ static inline void __init check_timer(void)
goto out; goto out;
} }
clear_IO_APIC_pin(apic1, pin1); clear_IO_APIC_pin(apic1, pin1);
printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to " if (!no_pin1)
"IO-APIC\n"); printk(KERN_ERR "..MP-BIOS bug: "
} "8254 timer not connected to IO-APIC\n");
printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); printk(KERN_INFO "...trying to set up timer (IRQ0) "
if (pin2 != -1) { "through the 8259A ... ");
printk("\n..... (found pin %d) ...", pin2); printk("\n..... (found pin %d) ...", pin2);
/* /*
* legacy devices should be connected to IO APIC #0 * legacy devices should be connected to IO APIC #0
*/ */
replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
setup_timer_IRQ0_pin(apic2, pin2, vector); setup_timer_IRQ0_pin(apic2, pin2, vector);
unmask_IO_APIC_irq(0); unmask_IO_APIC_irq(0);
enable_8259A_irq(0); enable_8259A_irq(0);
if (timer_irq_works()) { if (timer_irq_works()) {
printk("works.\n"); printk("works.\n");
timer_through_8259 = 1; timer_through_8259 = 1;
if (pin1 != -1)
replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
else
add_pin_to_irq(0, apic2, pin2);
if (nmi_watchdog == NMI_IO_APIC) { if (nmi_watchdog == NMI_IO_APIC) {
disable_8259A_irq(0); disable_8259A_irq(0);
setup_nmi(); setup_nmi();
@ -2206,8 +2224,8 @@ static inline void __init check_timer(void)
*/ */
disable_8259A_irq(0); disable_8259A_irq(0);
clear_IO_APIC_pin(apic2, pin2); clear_IO_APIC_pin(apic2, pin2);
printk(" failed.\n");
} }
printk(" failed.\n");
if (nmi_watchdog == NMI_IO_APIC) { if (nmi_watchdog == NMI_IO_APIC) {
printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");

View File

@ -1638,6 +1638,7 @@ static inline void __init check_timer(void)
struct irq_cfg *cfg = irq_cfg + 0; struct irq_cfg *cfg = irq_cfg + 0;
int apic1, pin1, apic2, pin2; int apic1, pin1, apic2, pin2;
unsigned long flags; unsigned long flags;
int no_pin1 = 0;
local_irq_save(flags); local_irq_save(flags);
@ -1662,10 +1663,30 @@ static inline void __init check_timer(void)
apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
cfg->vector, apic1, pin1, apic2, pin2); cfg->vector, apic1, pin1, apic2, pin2);
/*
* Some BIOS writers are clueless and report the ExtINTA
* I/O APIC input from the cascaded 8259A as the timer
* interrupt input. So just in case, if only one pin
* was found above, try it both directly and through the
* 8259A.
*/
if (pin1 == -1) {
pin1 = pin2;
apic1 = apic2;
no_pin1 = 1;
} else if (pin2 == -1) {
pin2 = pin1;
apic2 = apic1;
}
if (pin1 != -1) { if (pin1 != -1) {
/* /*
* Ok, does IRQ0 through the IOAPIC work? * Ok, does IRQ0 through the IOAPIC work?
*/ */
if (no_pin1) {
add_pin_to_irq(0, apic1, pin1);
setup_timer_IRQ0_pin(apic1, pin1, vector);
}
unmask_IO_APIC_irq(0); unmask_IO_APIC_irq(0);
if (!no_timer_check && timer_irq_works()) { if (!no_timer_check && timer_irq_works()) {
nmi_watchdog_default(); nmi_watchdog_default();
@ -1678,18 +1699,19 @@ static inline void __init check_timer(void)
goto out; goto out;
} }
clear_IO_APIC_pin(apic1, pin1); clear_IO_APIC_pin(apic1, pin1);
apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " if (!no_pin1)
"connected to IO-APIC\n"); apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: "
} "8254 timer not connected to IO-APIC\n");
apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " apic_printk(APIC_VERBOSE,KERN_INFO
"through the 8259A ... "); "...trying to set up timer (IRQ0) "
if (pin2 != -1) { "through the 8259A ... ");
apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...",
apic2, pin2); apic2, pin2);
/* /*
* legacy devices should be connected to IO APIC #0 * legacy devices should be connected to IO APIC #0
*/ */
/* replace_pin_at_irq(0, apic1, pin1, apic2, pin2); */
setup_timer_IRQ0_pin(apic2, pin2, cfg->vector); setup_timer_IRQ0_pin(apic2, pin2, cfg->vector);
unmask_IO_APIC_irq(0); unmask_IO_APIC_irq(0);
enable_8259A_irq(0); enable_8259A_irq(0);
@ -1709,8 +1731,8 @@ static inline void __init check_timer(void)
*/ */
disable_8259A_irq(0); disable_8259A_irq(0);
clear_IO_APIC_pin(apic2, pin2); clear_IO_APIC_pin(apic2, pin2);
apic_printk(APIC_VERBOSE," failed.\n");
} }
apic_printk(APIC_VERBOSE," failed.\n");
if (nmi_watchdog == NMI_IO_APIC) { if (nmi_watchdog == NMI_IO_APIC) {
printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");