[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>
This commit is contained in:
parent
e8b917775b
commit
ab9b32ee62
2 changed files with 33 additions and 17 deletions
|
@ -52,6 +52,10 @@ APICs
|
||||||
apicmaintimer. Useful when your PIT timer is totally
|
apicmaintimer. Useful when your PIT timer is totally
|
||||||
broken.
|
broken.
|
||||||
|
|
||||||
|
disable_8254_timer / enable_8254_timer
|
||||||
|
Enable interrupt 0 timer routing over the 8254 in addition to over
|
||||||
|
the IO-APIC. The kernel tries to set a sensible default.
|
||||||
|
|
||||||
Early Console
|
Early Console
|
||||||
|
|
||||||
syntax: earlyprintk=vga
|
syntax: earlyprintk=vga
|
||||||
|
|
|
@ -50,6 +50,8 @@ static int no_timer_check;
|
||||||
|
|
||||||
int disable_timer_pin_1 __initdata;
|
int disable_timer_pin_1 __initdata;
|
||||||
|
|
||||||
|
int timer_over_8254 __initdata = 1;
|
||||||
|
|
||||||
/* Where if anywhere is the i8259 connect in external int mode */
|
/* Where if anywhere is the i8259 connect in external int mode */
|
||||||
static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
|
static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
|
||||||
|
|
||||||
|
@ -251,6 +253,20 @@ static int __init enable_ioapic_setup(char *str)
|
||||||
__setup("noapic", disable_ioapic_setup);
|
__setup("noapic", disable_ioapic_setup);
|
||||||
__setup("apic", enable_ioapic_setup);
|
__setup("apic", enable_ioapic_setup);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
#include <asm/pci-direct.h>
|
#include <asm/pci-direct.h>
|
||||||
#include <linux/pci_ids.h>
|
#include <linux/pci_ids.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
@ -309,27 +325,20 @@ void __init check_ioapic(void)
|
||||||
#endif
|
#endif
|
||||||
/* RED-PEN skip them on mptables too? */
|
/* RED-PEN skip them on mptables too? */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* This should be actually default, but
|
||||||
|
for 2.6.16 let's do it for ATI only where
|
||||||
|
it's really needed. */
|
||||||
case PCI_VENDOR_ID_ATI:
|
case PCI_VENDOR_ID_ATI:
|
||||||
if (apic_runs_main_timer != 0)
|
if (timer_over_8254 == 1) {
|
||||||
break;
|
timer_over_8254 = 0;
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
/* Don't do this for laptops right
|
|
||||||
right now because their timer
|
|
||||||
doesn't necessarily tick in C2/3 */
|
|
||||||
if (acpi_fadt.revision >= 3 &&
|
|
||||||
(acpi_fadt.plvl2_lat + acpi_fadt.plvl3_lat) < 1100) {
|
|
||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
"ATI board detected, but seems to be a laptop. Timer might be shakey, sorry\n");
|
"ATI board detected. Disabling timer routing over 8254.\n");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
printk(KERN_INFO
|
|
||||||
"ATI board detected. Using APIC/PM timer.\n");
|
|
||||||
apic_runs_main_timer = 1;
|
|
||||||
nohpet = 1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* No multi-function device? */
|
/* No multi-function device? */
|
||||||
type = read_pci_config_byte(num,slot,func,
|
type = read_pci_config_byte(num,slot,func,
|
||||||
PCI_HEADER_TYPE);
|
PCI_HEADER_TYPE);
|
||||||
|
@ -1773,6 +1782,8 @@ static inline void unlock_ExtINT_logic(void)
|
||||||
* a wide range of boards and BIOS bugs. Fortunately only the timer IRQ
|
* a wide range of boards and BIOS bugs. Fortunately only the timer IRQ
|
||||||
* is so screwy. Thanks to Brian Perkins for testing/hacking this beast
|
* is so screwy. Thanks to Brian Perkins for testing/hacking this beast
|
||||||
* fanatically on his truly buggy board.
|
* fanatically on his truly buggy board.
|
||||||
|
*
|
||||||
|
* FIXME: really need to revamp this for modern platforms only.
|
||||||
*/
|
*/
|
||||||
static inline void check_timer(void)
|
static inline void check_timer(void)
|
||||||
{
|
{
|
||||||
|
@ -1795,6 +1806,7 @@ static inline void check_timer(void)
|
||||||
*/
|
*/
|
||||||
apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
|
apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
|
||||||
init_8259A(1);
|
init_8259A(1);
|
||||||
|
if (timer_over_8254 > 0)
|
||||||
enable_8259A_irq(0);
|
enable_8259A_irq(0);
|
||||||
|
|
||||||
pin1 = find_isa_irq_pin(0, mp_INT);
|
pin1 = find_isa_irq_pin(0, mp_INT);
|
||||||
|
|
Loading…
Reference in a new issue