Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab9b32ee authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds
Browse files

[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: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e8b91777
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
+29 −17
Original line number Diff line number Diff line
@@ -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) {
					if (timer_over_8254 == 1) {	
						timer_over_8254 = 0;	
					printk(KERN_INFO
"ATI board detected, but seems to be a laptop. Timer might be shakey, sorry\n");
						break;
		"ATI board detected. Disabling timer routing over 8254.\n");
					}	
#endif					
					printk(KERN_INFO
	     "ATI board detected. Using APIC/PM timer.\n");
					apic_runs_main_timer = 1;
					nohpet = 1;
					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,6 +1806,7 @@ static inline void check_timer(void)
	 */
	apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
	init_8259A(1);
	if (timer_over_8254 > 0)
		enable_8259A_irq(0);

	pin1  = find_isa_irq_pin(0, mp_INT);