[PATCH] x86_64: Better ATI timer fix
authorAndi Kleen <ak@suse.de>
Sun, 26 Feb 2006 03:18:52 +0000 (04:18 +0100)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 26 Feb 2006 17:53:31 +0000 (09:53 -0800)
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>
Documentation/x86_64/boot-options.txt
arch/x86_64/kernel/io_apic.c

index 153740f460a6cc67713662c2a2cf7b6c50169ebe..1921353259aeeca2ab07f4ced32a8cd0f3edc559 100644 (file)
@@ -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
index ffab8a75666428bf681326ed83f20caef0b6fd9b..ffed464e6b12274ff62ce9fa0a76e5dca9852663 100644 (file)
@@ -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);