diff options
author | Andi Kleen <ak@suse.de> | 2006-02-25 22:18:52 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-26 12:53:31 -0500 |
commit | ab9b32ee626e9b6df4ce2560a70ae15e62423cf4 (patch) | |
tree | 4bae7b0a38b9e69d33dc7b80ad434274019d9e38 | |
parent | e8b917775b572bc27de105f1317c2de4335db5b3 (diff) |
[PATCH] x86_64: Better ATI timer fix
The previous experiment for using apicmaintimer on ATI systems didn't
work out very well. In particular laptops with C2/C3 support often
don't let it tick during idle, which makes it useless. There were also
some other bugs that made the apicmaintimer often not used at all.
I tried some other experiments - running timer over RTC and some other
things but they didn't really work well neither.
I rechecked the specs now and it turns out this simple change is
actually enough to avoid the double ticks on the ATI systems. We just
turn off IRQ 0 in the 8254 and only route it directly using the IO-APIC.
I tested it on a few ATI systems and it worked there. In fact it worked
on all chipsets (NVidia, Intel, AMD, ATI) I tried it on.
According to the ACPI spec routing should always work through the
IO-APIC so I think it's the correct thing to do anyways (and most of the
old gunk in check_timer should be thrown away for x86-64).
But for 2.6.16 it's best to do a fairly minimal change:
- Use the known to be working everywhere-but-ATI IRQ0 both over 8254
and IO-APIC setup everywhere
- Except on ATI disable IRQ0 in the 8254
- Remove the code to select apicmaintimer on ATI chipsets
- Add some boot options to allow to override this (just paranoia)
In 2.6.17 I hope to switch the default over to this for everybody.
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | Documentation/x86_64/boot-options.txt | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/io_apic.c | 46 |
2 files changed, 33 insertions, 17 deletions
diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86_64/boot-options.txt index 153740f460a6..1921353259ae 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/io_apic.c b/arch/x86_64/kernel/io_apic.c index ffab8a756664..ffed464e6b12 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c | |||
@@ -50,6 +50,8 @@ static int no_timer_check; | |||
50 | 50 | ||
51 | int disable_timer_pin_1 __initdata; | 51 | int disable_timer_pin_1 __initdata; |
52 | 52 | ||
53 | int timer_over_8254 __initdata = 1; | ||
54 | |||
53 | /* Where if anywhere is the i8259 connect in external int mode */ | 55 | /* Where if anywhere is the i8259 connect in external int mode */ |
54 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 56 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; |
55 | 57 | ||
@@ -251,6 +253,20 @@ static int __init enable_ioapic_setup(char *str) | |||
251 | __setup("noapic", disable_ioapic_setup); | 253 | __setup("noapic", disable_ioapic_setup); |
252 | __setup("apic", enable_ioapic_setup); | 254 | __setup("apic", enable_ioapic_setup); |
253 | 255 | ||
256 | static int __init setup_disable_8254_timer(char *s) | ||
257 | { | ||
258 | timer_over_8254 = -1; | ||
259 | return 1; | ||
260 | } | ||
261 | static int __init setup_enable_8254_timer(char *s) | ||
262 | { | ||
263 | timer_over_8254 = 2; | ||
264 | return 1; | ||
265 | } | ||
266 | |||
267 | __setup("disable_8254_timer", setup_disable_8254_timer); | ||
268 | __setup("enable_8254_timer", setup_enable_8254_timer); | ||
269 | |||
254 | #include <asm/pci-direct.h> | 270 | #include <asm/pci-direct.h> |
255 | #include <linux/pci_ids.h> | 271 | #include <linux/pci_ids.h> |
256 | #include <linux/pci.h> | 272 | #include <linux/pci.h> |
@@ -309,27 +325,20 @@ void __init check_ioapic(void) | |||
309 | #endif | 325 | #endif |
310 | /* RED-PEN skip them on mptables too? */ | 326 | /* RED-PEN skip them on mptables too? */ |
311 | return; | 327 | return; |
328 | |||
329 | /* This should be actually default, but | ||
330 | for 2.6.16 let's do it for ATI only where | ||
331 | it's really needed. */ | ||
312 | case PCI_VENDOR_ID_ATI: | 332 | case PCI_VENDOR_ID_ATI: |
313 | if (apic_runs_main_timer != 0) | 333 | if (timer_over_8254 == 1) { |
314 | break; | 334 | timer_over_8254 = 0; |
315 | #ifdef CONFIG_ACPI | ||
316 | /* Don't do this for laptops right | ||
317 | right now because their timer | ||
318 | doesn't necessarily tick in C2/3 */ | ||
319 | if (acpi_fadt.revision >= 3 && | ||
320 | (acpi_fadt.plvl2_lat + acpi_fadt.plvl3_lat) < 1100) { | ||
321 | printk(KERN_INFO | ||
322 | "ATI board detected, but seems to be a laptop. Timer might be shakey, sorry\n"); | ||
323 | break; | ||
324 | } | ||
325 | #endif | ||
326 | printk(KERN_INFO | 335 | printk(KERN_INFO |
327 | "ATI board detected. Using APIC/PM timer.\n"); | 336 | "ATI board detected. Disabling timer routing over 8254.\n"); |
328 | apic_runs_main_timer = 1; | 337 | } |
329 | nohpet = 1; | ||
330 | return; | 338 | return; |
331 | } | 339 | } |
332 | 340 | ||
341 | |||
333 | /* No multi-function device? */ | 342 | /* No multi-function device? */ |
334 | type = read_pci_config_byte(num,slot,func, | 343 | type = read_pci_config_byte(num,slot,func, |
335 | PCI_HEADER_TYPE); | 344 | PCI_HEADER_TYPE); |
@@ -1773,6 +1782,8 @@ static inline void unlock_ExtINT_logic(void) | |||
1773 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ | 1782 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ |
1774 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast | 1783 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast |
1775 | * fanatically on his truly buggy board. | 1784 | * fanatically on his truly buggy board. |
1785 | * | ||
1786 | * FIXME: really need to revamp this for modern platforms only. | ||
1776 | */ | 1787 | */ |
1777 | static inline void check_timer(void) | 1788 | static inline void check_timer(void) |
1778 | { | 1789 | { |
@@ -1795,7 +1806,8 @@ static inline void check_timer(void) | |||
1795 | */ | 1806 | */ |
1796 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 1807 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); |
1797 | init_8259A(1); | 1808 | init_8259A(1); |
1798 | enable_8259A_irq(0); | 1809 | if (timer_over_8254 > 0) |
1810 | enable_8259A_irq(0); | ||
1799 | 1811 | ||
1800 | pin1 = find_isa_irq_pin(0, mp_INT); | 1812 | pin1 = find_isa_irq_pin(0, mp_INT); |
1801 | apic1 = find_isa_irq_apic(0, mp_INT); | 1813 | apic1 = find_isa_irq_apic(0, mp_INT); |