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"); |