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