Skip to content

Commit

Permalink
[PATCH] i386: port ATI timer fix from x86_64 to i386 II
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andi Kleen authored and Linus Torvalds committed Mar 9, 2006
1 parent 979ce80 commit f9262c1
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 6 deletions.
6 changes: 6 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ running once the system is up.
timesource is not avalible, it defaults to PIT.
Format: { pit | tsc | cyclone | pmtmr }

disable_8254_timer
enable_8254_timer
[IA32/X86_64] Disable/Enable interrupt 0 timer routing
over the 8254 in addition to over the IO-APIC. The
kernel tries to set a sensible default.

hpet= [IA-32,HPET] option to disable HPET and use PIT.
Format: disable

Expand Down
2 changes: 1 addition & 1 deletion arch/i386/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \

obj-y += cpu/
obj-y += timers/
obj-$(CONFIG_ACPI) += acpi/
obj-y += acpi/
obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o
obj-$(CONFIG_MCA) += mca.o
obj-$(CONFIG_X86_MSR) += msr.o
Expand Down
2 changes: 1 addition & 1 deletion arch/i386/kernel/acpi/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
obj-y := boot.o
obj-$(CONFIG_ACPI) += boot.o
obj-$(CONFIG_X86_IO_APIC) += earlyquirk.o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup.o

Expand Down
3 changes: 0 additions & 3 deletions arch/i386/kernel/acpi/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1111,9 +1111,6 @@ int __init acpi_boot_table_init(void)
disable_acpi();
return error;
}
#ifdef __i386__
check_acpi_pci();
#endif

acpi_table_parse(ACPI_BOOT, acpi_parse_sbf);

Expand Down
8 changes: 8 additions & 0 deletions arch/i386/kernel/acpi/earlyquirk.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@
#include <linux/pci.h>
#include <asm/pci-direct.h>
#include <asm/acpi.h>
#include <asm/apic.h>

static int __init check_bridge(int vendor, int device)
{
#ifdef CONFIG_ACPI
/* According to Nvidia all timer overrides are bogus. Just ignore
them all. */
if (vendor == PCI_VENDOR_ID_NVIDIA) {
acpi_skip_timer_override = 1;
}
#endif
if (vendor == PCI_VENDOR_ID_ATI && timer_over_8254 == 1) {
timer_over_8254 = 0;
printk(KERN_INFO "ATI board detected. Disabling timer routing "
"over 8254.\n");
}
return 0;
}

Expand Down
19 changes: 18 additions & 1 deletion arch/i386/kernel/io_apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };

static DEFINE_SPINLOCK(ioapic_lock);

int timer_over_8254 __initdata = 1;

/*
* Is the SiS APIC rmw bug present ?
* -1 = don't know, 0 = no, 1 = yes
Expand Down Expand Up @@ -2267,7 +2269,8 @@ static inline void check_timer(void)
apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
init_8259A(1);
timer_ack = 1;
enable_8259A_irq(0);
if (timer_over_8254 > 0)
enable_8259A_irq(0);

pin1 = find_isa_irq_pin(0, mp_INT);
apic1 = find_isa_irq_apic(0, mp_INT);
Expand Down Expand Up @@ -2392,6 +2395,20 @@ void __init setup_IO_APIC(void)
print_IO_APIC();
}

static int __init setup_disable_8254_timer(char *s)
{
timer_over_8254 = -1;
return 1;
}
static int __init setup_enable_8254_timer(char *s)
{
timer_over_8254 = 2;
return 1;
}

__setup("disable_8254_timer", setup_disable_8254_timer);
__setup("enable_8254_timer", setup_enable_8254_timer);

/*
* Called after all the initialization is done. If we didnt find any
* APIC bugs then we can allow the modify fast path
Expand Down
4 changes: 4 additions & 0 deletions arch/i386/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -1599,6 +1599,10 @@ void __init setup_arch(char **cmdline_p)
if (efi_enabled)
efi_map_memmap();

#ifdef CONFIG_X86_IO_APIC
check_acpi_pci(); /* Checks more than just ACPI actually */
#endif

#ifdef CONFIG_ACPI
/*
* Parse the ACPI tables for possible boot-time SMP configuration.
Expand Down
2 changes: 2 additions & 0 deletions include/asm-i386/apic.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ void switch_APIC_timer_to_ipi(void *cpumask);
void switch_ipi_to_APIC_timer(void *cpumask);
#define ARCH_APICTIMER_STOPS_ON_C3 1

extern int timer_over_8254;

#else /* !CONFIG_X86_LOCAL_APIC */
static inline void lapic_shutdown(void) { }

Expand Down

0 comments on commit f9262c1

Please sign in to comment.