[PATCH] i386: port ATI timer fix from x86_64 to i386 II

ATI chipsets tend to generate double timer interrupts for the local APIC
timer when both the 8254 and the IO-APIC timer pins are enabled. This is
because they route it to both and the result is anded together and the CPU
ends up processing it twice.

This patch changes check_timer to disable the 8254 routing for interrupt 0.

I think it would be safe on all chipsets actually (i tested it on a couple
and it worked everywhere) and Windows seems to do it in a similar way, but
to be conservative this patch only enables this mode on ATI (and adds
options to enable/disable too)

Ported over from a similar x86-64 change.

I reused the ACPI earlyquirk infrastructure for the ATI bridge check, but
tweaked it a bit to work even without ACPI.

Inspired by a patch from Chuck Ebbert, but redone.

Cc: Chuck Ebbert <76306.1226@compuserve.com>
Cc: "Brown, Len" <len.brown@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Andi Kleen and committed by Linus Torvalds f9262c12 979ce809

+40 -6
+6
Documentation/kernel-parameters.txt
··· 335 335 timesource is not avalible, it defaults to PIT. 336 336 Format: { pit | tsc | cyclone | pmtmr } 337 337 338 + disable_8254_timer 339 + enable_8254_timer 340 + [IA32/X86_64] Disable/Enable interrupt 0 timer routing 341 + over the 8254 in addition to over the IO-APIC. The 342 + kernel tries to set a sensible default. 343 + 338 344 hpet= [IA-32,HPET] option to disable HPET and use PIT. 339 345 Format: disable 340 346
+1 -1
arch/i386/kernel/Makefile
··· 11 11 12 12 obj-y += cpu/ 13 13 obj-y += timers/ 14 - obj-$(CONFIG_ACPI) += acpi/ 14 + obj-y += acpi/ 15 15 obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o 16 16 obj-$(CONFIG_MCA) += mca.o 17 17 obj-$(CONFIG_X86_MSR) += msr.o
+1 -1
arch/i386/kernel/acpi/Makefile
··· 1 - obj-y := boot.o 1 + obj-$(CONFIG_ACPI) += boot.o 2 2 obj-$(CONFIG_X86_IO_APIC) += earlyquirk.o 3 3 obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup.o 4 4
-3
arch/i386/kernel/acpi/boot.c
··· 1111 1111 disable_acpi(); 1112 1112 return error; 1113 1113 } 1114 - #ifdef __i386__ 1115 - check_acpi_pci(); 1116 - #endif 1117 1114 1118 1115 acpi_table_parse(ACPI_BOOT, acpi_parse_sbf); 1119 1116
+8
arch/i386/kernel/acpi/earlyquirk.c
··· 7 7 #include <linux/pci.h> 8 8 #include <asm/pci-direct.h> 9 9 #include <asm/acpi.h> 10 + #include <asm/apic.h> 10 11 11 12 static int __init check_bridge(int vendor, int device) 12 13 { 14 + #ifdef CONFIG_ACPI 13 15 /* According to Nvidia all timer overrides are bogus. Just ignore 14 16 them all. */ 15 17 if (vendor == PCI_VENDOR_ID_NVIDIA) { 16 18 acpi_skip_timer_override = 1; 19 + } 20 + #endif 21 + if (vendor == PCI_VENDOR_ID_ATI && timer_over_8254 == 1) { 22 + timer_over_8254 = 0; 23 + printk(KERN_INFO "ATI board detected. Disabling timer routing " 24 + "over 8254.\n"); 17 25 } 18 26 return 0; 19 27 }
+18 -1
arch/i386/kernel/io_apic.c
··· 51 51 52 52 static DEFINE_SPINLOCK(ioapic_lock); 53 53 54 + int timer_over_8254 __initdata = 1; 55 + 54 56 /* 55 57 * Is the SiS APIC rmw bug present ? 56 58 * -1 = don't know, 0 = no, 1 = yes ··· 2269 2267 apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); 2270 2268 init_8259A(1); 2271 2269 timer_ack = 1; 2272 - enable_8259A_irq(0); 2270 + if (timer_over_8254 > 0) 2271 + enable_8259A_irq(0); 2273 2272 2274 2273 pin1 = find_isa_irq_pin(0, mp_INT); 2275 2274 apic1 = find_isa_irq_apic(0, mp_INT); ··· 2394 2391 if (!acpi_ioapic) 2395 2392 print_IO_APIC(); 2396 2393 } 2394 + 2395 + static int __init setup_disable_8254_timer(char *s) 2396 + { 2397 + timer_over_8254 = -1; 2398 + return 1; 2399 + } 2400 + static int __init setup_enable_8254_timer(char *s) 2401 + { 2402 + timer_over_8254 = 2; 2403 + return 1; 2404 + } 2405 + 2406 + __setup("disable_8254_timer", setup_disable_8254_timer); 2407 + __setup("enable_8254_timer", setup_enable_8254_timer); 2397 2408 2398 2409 /* 2399 2410 * Called after all the initialization is done. If we didnt find any
+4
arch/i386/kernel/setup.c
··· 1599 1599 if (efi_enabled) 1600 1600 efi_map_memmap(); 1601 1601 1602 + #ifdef CONFIG_X86_IO_APIC 1603 + check_acpi_pci(); /* Checks more than just ACPI actually */ 1604 + #endif 1605 + 1602 1606 #ifdef CONFIG_ACPI 1603 1607 /* 1604 1608 * Parse the ACPI tables for possible boot-time SMP configuration.
+2
include/asm-i386/apic.h
··· 137 137 void switch_ipi_to_APIC_timer(void *cpumask); 138 138 #define ARCH_APICTIMER_STOPS_ON_C3 1 139 139 140 + extern int timer_over_8254; 141 + 140 142 #else /* !CONFIG_X86_LOCAL_APIC */ 141 143 static inline void lapic_shutdown(void) { } 142 144