diff options
author | Andi Kleen <ak@suse.de> | 2006-12-06 20:14:06 -0500 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2006-12-06 20:14:06 -0500 |
commit | b026872601976f666bae77b609dc490d1834bf77 (patch) | |
tree | c1e9180ed6fd29cd025ba1d40407da8a3aeeaaa6 /arch/x86_64 | |
parent | 11a4180c0b03e2ee0c948fd8430ee092dc1625b3 (diff) |
[PATCH] x86-64: Try multiple timer variants in check_timer
Instead of adding all kinds of more quirks try various timer
routing variants in check_timer.
In particular this tries to handle quirks from:
- Nvidia NF2-4 reference BIOS: wrong timer override
- Asus: Wrong timer override but no HPET table
- ATI: require timer disabled in 8259
- Some boards: require timer enabled in 8259
We just try many of the the known variants in the hopefully right order
in check_timer.
Trying pin 0/2 on Nvidia suggested by Tim Hockin.
TBD Experimental. Needs a lot of testing
Signed-off-by: Andi Kleen <ak@suse.de>
Diffstat (limited to 'arch/x86_64')
-rw-r--r-- | arch/x86_64/kernel/early-quirks.c | 5 | ||||
-rw-r--r-- | arch/x86_64/kernel/io_apic.c | 124 |
2 files changed, 51 insertions, 78 deletions
diff --git a/arch/x86_64/kernel/early-quirks.c b/arch/x86_64/kernel/early-quirks.c index 68273bff58cc..fb0c6da41b7e 100644 --- a/arch/x86_64/kernel/early-quirks.c +++ b/arch/x86_64/kernel/early-quirks.c | |||
@@ -69,11 +69,6 @@ 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 | } | ||
77 | } | 72 | } |
78 | 73 | ||
79 | struct chipset { | 74 | struct chipset { |
diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index f71461b1f03d..88fcc4ebbf6e 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c | |||
@@ -55,10 +55,6 @@ 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 | |||
62 | /* Where if anywhere is the i8259 connect in external int mode */ | 58 | /* Where if anywhere is the i8259 connect in external int mode */ |
63 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 59 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; |
64 | 60 | ||
@@ -348,29 +344,6 @@ static int __init disable_ioapic_setup(char *str) | |||
348 | } | 344 | } |
349 | early_param("noapic", disable_ioapic_setup); | 345 | early_param("noapic", disable_ioapic_setup); |
350 | 346 | ||
351 | /* Actually the next is obsolete, but keep it for paranoid reasons -AK */ | ||
352 | static int __init disable_timer_pin_setup(char *arg) | ||
353 | { | ||
354 | disable_timer_pin_1 = 1; | ||
355 | return 1; | ||
356 | } | ||
357 | __setup("disable_timer_pin_1", disable_timer_pin_setup); | ||
358 | |||
359 | static int __init setup_disable_8254_timer(char *s) | ||
360 | { | ||
361 | timer_over_8254 = -1; | ||
362 | return 1; | ||
363 | } | ||
364 | static int __init setup_enable_8254_timer(char *s) | ||
365 | { | ||
366 | timer_over_8254 = 2; | ||
367 | return 1; | ||
368 | } | ||
369 | |||
370 | __setup("disable_8254_timer", setup_disable_8254_timer); | ||
371 | __setup("enable_8254_timer", setup_enable_8254_timer); | ||
372 | |||
373 | |||
374 | /* | 347 | /* |
375 | * Find the IRQ entry number of a certain pin. | 348 | * Find the IRQ entry number of a certain pin. |
376 | */ | 349 | */ |
@@ -1579,10 +1552,33 @@ static inline void unlock_ExtINT_logic(void) | |||
1579 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ | 1552 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ |
1580 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast | 1553 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast |
1581 | * fanatically on his truly buggy board. | 1554 | * fanatically on his truly buggy board. |
1582 | * | ||
1583 | * FIXME: really need to revamp this for modern platforms only. | ||
1584 | */ | 1555 | */ |
1585 | static inline void check_timer(void) | 1556 | |
1557 | static int try_apic_pin(int apic, int pin, char *msg) | ||
1558 | { | ||
1559 | apic_printk(APIC_VERBOSE, KERN_INFO | ||
1560 | "..TIMER: trying IO-APIC=%d PIN=%d %s", | ||
1561 | apic, pin, msg); | ||
1562 | |||
1563 | /* | ||
1564 | * Ok, does IRQ0 through the IOAPIC work? | ||
1565 | */ | ||
1566 | if (!no_timer_check && timer_irq_works()) { | ||
1567 | nmi_watchdog_default(); | ||
1568 | if (nmi_watchdog == NMI_IO_APIC) { | ||
1569 | disable_8259A_irq(0); | ||
1570 | setup_nmi(); | ||
1571 | enable_8259A_irq(0); | ||
1572 | } | ||
1573 | return 1; | ||
1574 | } | ||
1575 | clear_IO_APIC_pin(apic, pin); | ||
1576 | apic_printk(APIC_QUIET, KERN_ERR " .. failed\n"); | ||
1577 | return 0; | ||
1578 | } | ||
1579 | |||
1580 | /* The function from hell */ | ||
1581 | static void check_timer(void) | ||
1586 | { | 1582 | { |
1587 | int apic1, pin1, apic2, pin2; | 1583 | int apic1, pin1, apic2, pin2; |
1588 | int vector; | 1584 | int vector; |
@@ -1603,61 +1599,43 @@ static inline void check_timer(void) | |||
1603 | */ | 1599 | */ |
1604 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 1600 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); |
1605 | init_8259A(1); | 1601 | init_8259A(1); |
1606 | if (timer_over_8254 > 0) | ||
1607 | enable_8259A_irq(0); | ||
1608 | 1602 | ||
1609 | pin1 = find_isa_irq_pin(0, mp_INT); | 1603 | pin1 = find_isa_irq_pin(0, mp_INT); |
1610 | apic1 = find_isa_irq_apic(0, mp_INT); | 1604 | apic1 = find_isa_irq_apic(0, mp_INT); |
1611 | pin2 = ioapic_i8259.pin; | 1605 | pin2 = ioapic_i8259.pin; |
1612 | apic2 = ioapic_i8259.apic; | 1606 | apic2 = ioapic_i8259.apic; |
1613 | 1607 | ||
1614 | apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", | 1608 | /* Do this first, otherwise we get double interrupts on ATI boards */ |
1615 | vector, apic1, pin1, apic2, pin2); | 1609 | if ((pin1 != -1) && try_apic_pin(apic1, pin1,"with 8259 IRQ0 disabled")) |
1610 | return; | ||
1616 | 1611 | ||
1617 | if (pin1 != -1) { | 1612 | /* Now try again with IRQ0 8259A enabled. |
1618 | /* | 1613 | Assumes timer is on IO-APIC 0 ?!? */ |
1619 | * Ok, does IRQ0 through the IOAPIC work? | 1614 | enable_8259A_irq(0); |
1620 | */ | 1615 | unmask_IO_APIC_irq(0); |
1621 | unmask_IO_APIC_irq(0); | 1616 | if (try_apic_pin(apic1, pin1, "with 8259 IRQ0 enabled")) |
1622 | if (!no_timer_check && timer_irq_works()) { | 1617 | return; |
1623 | nmi_watchdog_default(); | 1618 | disable_8259A_irq(0); |
1624 | if (nmi_watchdog == NMI_IO_APIC) { | ||
1625 | disable_8259A_irq(0); | ||
1626 | setup_nmi(); | ||
1627 | enable_8259A_irq(0); | ||
1628 | } | ||
1629 | if (disable_timer_pin_1 > 0) | ||
1630 | clear_IO_APIC_pin(0, pin1); | ||
1631 | return; | ||
1632 | } | ||
1633 | clear_IO_APIC_pin(apic1, pin1); | ||
1634 | apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " | ||
1635 | "connected to IO-APIC\n"); | ||
1636 | } | ||
1637 | 1619 | ||
1638 | apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " | 1620 | /* Always try pin0 and pin2 on APIC 0 to handle buggy timer overrides |
1639 | "through the 8259A ... "); | 1621 | on Nvidia boards */ |
1622 | if (!(apic1 == 0 && pin1 == 0) && | ||
1623 | try_apic_pin(0, 0, "fallback with 8259 IRQ0 disabled")) | ||
1624 | return; | ||
1625 | if (!(apic1 == 0 && pin1 == 2) && | ||
1626 | try_apic_pin(0, 2, "fallback with 8259 IRQ0 disabled")) | ||
1627 | return; | ||
1628 | |||
1629 | /* Then try pure 8259A routing on the 8259 as reported by BIOS*/ | ||
1630 | enable_8259A_irq(0); | ||
1640 | if (pin2 != -1) { | 1631 | if (pin2 != -1) { |
1641 | apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", | ||
1642 | apic2, pin2); | ||
1643 | /* | ||
1644 | * legacy devices should be connected to IO APIC #0 | ||
1645 | */ | ||
1646 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); | 1632 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); |
1647 | if (timer_irq_works()) { | 1633 | if (try_apic_pin(apic2,pin2,"8259A broadcast ExtINT from BIOS")) |
1648 | apic_printk(APIC_VERBOSE," works.\n"); | ||
1649 | nmi_watchdog_default(); | ||
1650 | if (nmi_watchdog == NMI_IO_APIC) { | ||
1651 | setup_nmi(); | ||
1652 | } | ||
1653 | return; | 1634 | return; |
1654 | } | ||
1655 | /* | ||
1656 | * Cleanup, just in case ... | ||
1657 | */ | ||
1658 | clear_IO_APIC_pin(apic2, pin2); | ||
1659 | } | 1635 | } |
1660 | apic_printk(APIC_VERBOSE," failed.\n"); | 1636 | |
1637 | /* Tried all possibilities to go through the IO-APIC. Now come the | ||
1638 | really cheesy fallbacks. */ | ||
1661 | 1639 | ||
1662 | if (nmi_watchdog == NMI_IO_APIC) { | 1640 | if (nmi_watchdog == NMI_IO_APIC) { |
1663 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); | 1641 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); |