diff options
| -rw-r--r-- | Documentation/x86_64/boot-options.txt | 4 | ||||
| -rw-r--r-- | arch/x86_64/kernel/early-quirks.c | 5 | ||||
| -rw-r--r-- | arch/x86_64/kernel/io_apic.c | 124 |
3 files changed, 82 insertions, 51 deletions
diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86_64/boot-options.txt index dbdcaf68e3ea..5c86ed6f0448 100644 --- a/Documentation/x86_64/boot-options.txt +++ b/Documentation/x86_64/boot-options.txt | |||
| @@ -52,6 +52,10 @@ APICs | |||
| 52 | apicmaintimer. Useful when your PIT timer is totally | 52 | apicmaintimer. Useful when your PIT timer is totally |
| 53 | broken. | 53 | broken. |
| 54 | 54 | ||
| 55 | disable_8254_timer / enable_8254_timer | ||
| 56 | Enable interrupt 0 timer routing over the 8254 in addition to over | ||
| 57 | the IO-APIC. The kernel tries to set a sensible default. | ||
| 58 | |||
| 55 | Early Console | 59 | Early Console |
| 56 | 60 | ||
| 57 | syntax: earlyprintk=vga | 61 | syntax: earlyprintk=vga |
diff --git a/arch/x86_64/kernel/early-quirks.c b/arch/x86_64/kernel/early-quirks.c index 829698f6d049..49802f1bee94 100644 --- a/arch/x86_64/kernel/early-quirks.c +++ b/arch/x86_64/kernel/early-quirks.c | |||
| @@ -69,6 +69,11 @@ static void nvidia_bugs(void) | |||
| 69 | 69 | ||
| 70 | static void ati_bugs(void) | 70 | static void ati_bugs(void) |
| 71 | { | 71 | { |
| 72 | if (timer_over_8254 == 1) { | ||
| 73 | timer_over_8254 = 0; | ||
| 74 | printk(KERN_INFO | ||
| 75 | "ATI board detected. Disabling timer routing over 8254.\n"); | ||
| 76 | } | ||
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | static void intel_bugs(void) | 79 | static void intel_bugs(void) |
diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index 2a1dcd5f69c2..d7bad90a5ad8 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c | |||
| @@ -55,6 +55,10 @@ int sis_apic_bug; /* not actually supported, dummy for compile */ | |||
| 55 | 55 | ||
| 56 | static int no_timer_check; | 56 | static int no_timer_check; |
| 57 | 57 | ||
| 58 | static int disable_timer_pin_1 __initdata; | ||
| 59 | |||
| 60 | int timer_over_8254 __initdata = 1; | ||
| 61 | |||
| 58 | /* Where if anywhere is the i8259 connect in external int mode */ | 62 | /* Where if anywhere is the i8259 connect in external int mode */ |
| 59 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 63 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; |
| 60 | 64 | ||
| @@ -350,6 +354,29 @@ static int __init disable_ioapic_setup(char *str) | |||
| 350 | } | 354 | } |
| 351 | early_param("noapic", disable_ioapic_setup); | 355 | early_param("noapic", disable_ioapic_setup); |
| 352 | 356 | ||
| 357 | /* Actually the next is obsolete, but keep it for paranoid reasons -AK */ | ||
| 358 | static int __init disable_timer_pin_setup(char *arg) | ||
| 359 | { | ||
| 360 | disable_timer_pin_1 = 1; | ||
| 361 | return 1; | ||
| 362 | } | ||
| 363 | __setup("disable_timer_pin_1", disable_timer_pin_setup); | ||
| 364 | |||
| 365 | static int __init setup_disable_8254_timer(char *s) | ||
| 366 | { | ||
| 367 | timer_over_8254 = -1; | ||
| 368 | return 1; | ||
| 369 | } | ||
| 370 | static int __init setup_enable_8254_timer(char *s) | ||
| 371 | { | ||
| 372 | timer_over_8254 = 2; | ||
| 373 | return 1; | ||
| 374 | } | ||
| 375 | |||
| 376 | __setup("disable_8254_timer", setup_disable_8254_timer); | ||
| 377 | __setup("enable_8254_timer", setup_enable_8254_timer); | ||
| 378 | |||
| 379 | |||
| 353 | /* | 380 | /* |
| 354 | * Find the IRQ entry number of a certain pin. | 381 | * Find the IRQ entry number of a certain pin. |
| 355 | */ | 382 | */ |
| @@ -1568,33 +1595,10 @@ static inline void unlock_ExtINT_logic(void) | |||
| 1568 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ | 1595 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ |
| 1569 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast | 1596 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast |
| 1570 | * fanatically on his truly buggy board. | 1597 | * fanatically on his truly buggy board. |
| 1598 | * | ||
| 1599 | * FIXME: really need to revamp this for modern platforms only. | ||
| 1571 | */ | 1600 | */ |
| 1572 | 1601 | static inline void check_timer(void) | |
| 1573 | static int try_apic_pin(int apic, int pin, char *msg) | ||
| 1574 | { | ||
| 1575 | apic_printk(APIC_VERBOSE, KERN_INFO | ||
| 1576 | "..TIMER: trying IO-APIC=%d PIN=%d %s", | ||
| 1577 | apic, pin, msg); | ||
| 1578 | |||
| 1579 | /* | ||
| 1580 | * Ok, does IRQ0 through the IOAPIC work? | ||
| 1581 | */ | ||
| 1582 | if (!no_timer_check && timer_irq_works()) { | ||
| 1583 | nmi_watchdog_default(); | ||
| 1584 | if (nmi_watchdog == NMI_IO_APIC) { | ||
| 1585 | disable_8259A_irq(0); | ||
| 1586 | setup_nmi(); | ||
| 1587 | enable_8259A_irq(0); | ||
| 1588 | } | ||
| 1589 | return 1; | ||
| 1590 | } | ||
| 1591 | clear_IO_APIC_pin(apic, pin); | ||
| 1592 | apic_printk(APIC_QUIET, KERN_ERR " .. failed\n"); | ||
| 1593 | return 0; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | /* The function from hell */ | ||
| 1597 | static void check_timer(void) | ||
| 1598 | { | 1602 | { |
| 1599 | int apic1, pin1, apic2, pin2; | 1603 | int apic1, pin1, apic2, pin2; |
| 1600 | int vector; | 1604 | int vector; |
| @@ -1615,43 +1619,61 @@ static void check_timer(void) | |||
| 1615 | */ | 1619 | */ |
| 1616 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 1620 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); |
| 1617 | init_8259A(1); | 1621 | init_8259A(1); |
| 1622 | if (timer_over_8254 > 0) | ||
| 1623 | enable_8259A_irq(0); | ||
| 1618 | 1624 | ||
| 1619 | pin1 = find_isa_irq_pin(0, mp_INT); | 1625 | pin1 = find_isa_irq_pin(0, mp_INT); |
| 1620 | apic1 = find_isa_irq_apic(0, mp_INT); | 1626 | apic1 = find_isa_irq_apic(0, mp_INT); |
| 1621 | pin2 = ioapic_i8259.pin; | 1627 | pin2 = ioapic_i8259.pin; |
| 1622 | apic2 = ioapic_i8259.apic; | 1628 | apic2 = ioapic_i8259.apic; |
| 1623 | 1629 | ||
| 1624 | /* Do this first, otherwise we get double interrupts on ATI boards */ | 1630 | apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", |
| 1625 | if ((pin1 != -1) && try_apic_pin(apic1, pin1,"with 8259 IRQ0 disabled")) | 1631 | vector, apic1, pin1, apic2, pin2); |
| 1626 | return; | ||
| 1627 | 1632 | ||
| 1628 | /* Now try again with IRQ0 8259A enabled. | 1633 | if (pin1 != -1) { |
| 1629 | Assumes timer is on IO-APIC 0 ?!? */ | 1634 | /* |
| 1630 | enable_8259A_irq(0); | 1635 | * Ok, does IRQ0 through the IOAPIC work? |
| 1631 | unmask_IO_APIC_irq(0); | 1636 | */ |
| 1632 | if (try_apic_pin(apic1, pin1, "with 8259 IRQ0 enabled")) | 1637 | unmask_IO_APIC_irq(0); |
| 1633 | return; | 1638 | if (!no_timer_check && timer_irq_works()) { |
| 1634 | disable_8259A_irq(0); | 1639 | nmi_watchdog_default(); |
| 1635 | 1640 | if (nmi_watchdog == NMI_IO_APIC) { | |
| 1636 | /* Always try pin0 and pin2 on APIC 0 to handle buggy timer overrides | 1641 | disable_8259A_irq(0); |
| 1637 | on Nvidia boards */ | 1642 | setup_nmi(); |
| 1638 | if (!(apic1 == 0 && pin1 == 0) && | 1643 | enable_8259A_irq(0); |
| 1639 | try_apic_pin(0, 0, "fallback with 8259 IRQ0 disabled")) | 1644 | } |
| 1640 | return; | 1645 | if (disable_timer_pin_1 > 0) |
| 1641 | if (!(apic1 == 0 && pin1 == 2) && | 1646 | clear_IO_APIC_pin(0, pin1); |
| 1642 | try_apic_pin(0, 2, "fallback with 8259 IRQ0 disabled")) | 1647 | return; |
| 1643 | return; | 1648 | } |
| 1649 | clear_IO_APIC_pin(apic1, pin1); | ||
| 1650 | apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " | ||
| 1651 | "connected to IO-APIC\n"); | ||
| 1652 | } | ||
| 1644 | 1653 | ||
| 1645 | /* Then try pure 8259A routing on the 8259 as reported by BIOS*/ | 1654 | apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " |
| 1646 | enable_8259A_irq(0); | 1655 | "through the 8259A ... "); |
| 1647 | if (pin2 != -1) { | 1656 | if (pin2 != -1) { |
| 1657 | apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", | ||
| 1658 | apic2, pin2); | ||
| 1659 | /* | ||
| 1660 | * legacy devices should be connected to IO APIC #0 | ||
| 1661 | */ | ||
| 1648 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); | 1662 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); |
| 1649 | if (try_apic_pin(apic2,pin2,"8259A broadcast ExtINT from BIOS")) | 1663 | if (timer_irq_works()) { |
| 1664 | apic_printk(APIC_VERBOSE," works.\n"); | ||
| 1665 | nmi_watchdog_default(); | ||
| 1666 | if (nmi_watchdog == NMI_IO_APIC) { | ||
| 1667 | setup_nmi(); | ||
| 1668 | } | ||
| 1650 | return; | 1669 | return; |
| 1670 | } | ||
| 1671 | /* | ||
| 1672 | * Cleanup, just in case ... | ||
| 1673 | */ | ||
| 1674 | clear_IO_APIC_pin(apic2, pin2); | ||
| 1651 | } | 1675 | } |
| 1652 | 1676 | apic_printk(APIC_VERBOSE," failed.\n"); | |
| 1653 | /* Tried all possibilities to go through the IO-APIC. Now come the | ||
| 1654 | really cheesy fallbacks. */ | ||
| 1655 | 1677 | ||
| 1656 | if (nmi_watchdog == NMI_IO_APIC) { | 1678 | if (nmi_watchdog == NMI_IO_APIC) { |
| 1657 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); | 1679 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); |
