[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
|
||||
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
|
||||
|
||||
syntax: earlyprintk=vga
|
||||
|
|
|
@ -50,6 +50,8 @@ static int no_timer_check;
|
|||
|
||||
int disable_timer_pin_1 __initdata;
|
||||
|
||||
int timer_over_8254 __initdata = 1;
|
||||
|
||||
/* Where if anywhere is the i8259 connect in external int mode */
|
||||
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("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 <linux/pci_ids.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -309,27 +325,20 @@ void __init check_ioapic(void)
|
|||
#endif
|
||||
/* RED-PEN skip them on mptables too? */
|
||||
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:
|
||||
if (apic_runs_main_timer != 0)
|
||||
break;
|
||||
#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
|
||||
"ATI board detected, but seems to be a laptop. Timer might be shakey, sorry\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (timer_over_8254 == 1) {
|
||||
timer_over_8254 = 0;
|
||||
printk(KERN_INFO
|
||||
"ATI board detected. Using APIC/PM timer.\n");
|
||||
apic_runs_main_timer = 1;
|
||||
nohpet = 1;
|
||||
"ATI board detected. Disabling timer routing over 8254.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* No multi-function device? */
|
||||
type = read_pci_config_byte(num,slot,func,
|
||||
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
|
||||
* is so screwy. Thanks to Brian Perkins for testing/hacking this beast
|
||||
* fanatically on his truly buggy board.
|
||||
*
|
||||
* FIXME: really need to revamp this for modern platforms only.
|
||||
*/
|
||||
static inline void check_timer(void)
|
||||
{
|
||||
|
@ -1795,7 +1806,8 @@ static inline void check_timer(void)
|
|||
*/
|
||||
apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
|
||||
init_8259A(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);
|
||||
|
|
Loading…
Reference in a new issue