]> err.no Git - linux-2.6/commitdiff
x86_64: convert to clock events
authorThomas Gleixner <tglx@linutronix.de>
Fri, 12 Oct 2007 21:04:07 +0000 (23:04 +0200)
committerThomas Gleixner <tglx@inhelltoy.tec.linutronix.de>
Fri, 12 Oct 2007 21:04:07 +0000 (23:04 +0200)
Finally switch to the clockevents code. Share code with i386 for
hpet and PIT.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
arch/x86/kernel/Makefile_64
arch/x86/kernel/apic_64.c
arch/x86/kernel/i8259_64.c
arch/x86/kernel/smpboot_64.c
arch/x86/kernel/time_64.c
arch/x86_64/Kconfig
include/asm-x86/hpet.h
include/asm-x86/hpet_32.h [deleted file]
include/asm-x86/hpet_64.h [deleted file]

index 3ab017a0a3b94dc436e1ca26e992591d2a7af7ab..080154e3150266ba504d07c45caf25a4fbaddacb 100644 (file)
@@ -8,8 +8,8 @@ obj-y   := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \
                ptrace_64.o time_64.o ioport_64.o ldt_64.o setup_64.o i8259_64.o sys_x86_64.o \
                x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \
                setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \
-               pci-dma_64.o pci-nommu_64.o alternative.o hpet_64.o tsc_64.o bugs_64.o \
-               perfctr-watchdog.o
+               pci-dma_64.o pci-nommu_64.o alternative.o hpet_32.o tsc_64.o bugs_64.o \
+               perfctr-watchdog.o i8253_32.o
 
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_X86_MCE)          += mce_64.o therm_throt.o
index 2c2807abe1d4b7916cdaa2760d53df8aabb8075c..b0237caff712b286fda6c9157025775e0efe2927 100644 (file)
@@ -857,25 +857,12 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
 
 static void setup_APIC_timer(void)
 {
-       unsigned long flags;
-       int irqen;
+       struct clock_event_device *levt = &__get_cpu_var(lapic_events);
 
-       local_irq_save(flags);
+       memcpy(levt, &lapic_clockevent, sizeof(*levt));
+       levt->cpumask = cpumask_of_cpu(smp_processor_id());
 
-       irqen = ! cpu_isset(smp_processor_id(),
-                           timer_interrupt_broadcast_ipi_mask);
-       __setup_APIC_LVTT(calibration_result, 0, irqen);
-       /* Turn off PIT interrupt if we use APIC timer as main timer.
-          Only works with the PM timer right now
-          TBD fix it for HPET too. */
-       if ((pmtmr_ioport != 0) &&
-               smp_processor_id() == boot_cpu_id &&
-               apic_runs_main_timer == 1 &&
-               !cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) {
-               stop_timer_interrupt();
-               apic_runs_main_timer++;
-       }
-       local_irq_restore(flags);
+       clockevents_register_device(levt);
 }
 
 /*
@@ -950,18 +937,34 @@ static void __init calibrate_APIC_clock(void)
 
 void __init setup_boot_APIC_clock (void)
 {
+       /*
+        * The local apic timer can be disabled via the kernel commandline.
+        * Register the lapic timer as a dummy clock event source on SMP
+        * systems, so the broadcast mechanism is used. On UP systems simply
+        * ignore it.
+        */
        if (disable_apic_timer) {
                printk(KERN_INFO "Disabling APIC timer\n");
+               /* No broadcast on UP ! */
+               if (num_possible_cpus() > 1)
+                       setup_APIC_timer();
                return;
        }
 
        printk(KERN_INFO "Using local APIC timer interrupts.\n");
-       using_apic_timer = 1;
-
        calibrate_APIC_clock();
+
        /*
-        * Now set up the timer for real.
+        * If nmi_watchdog is set to IO_APIC, we need the
+        * PIT/HPET going.  Otherwise register lapic as a dummy
+        * device.
         */
+       if (nmi_watchdog != NMI_IO_APIC)
+               lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;
+       else
+               printk(KERN_WARNING "APIC timer registered as dummy,"
+                      " due to nmi_watchdog=1!\n");
+
        setup_APIC_timer();
 }
 
@@ -1073,22 +1076,34 @@ void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector,
 
 void smp_local_timer_interrupt(void)
 {
-       profile_tick(CPU_PROFILING);
-#ifdef CONFIG_SMP
-       update_process_times(user_mode(get_irq_regs()));
-#endif
-       if (apic_runs_main_timer > 1 && smp_processor_id() == boot_cpu_id)
-               main_timer_handler();
+       int cpu = smp_processor_id();
+       struct clock_event_device *evt = &per_cpu(lapic_events, cpu);
+
        /*
-        * We take the 'long' return path, and there every subsystem
-        * grabs the appropriate locks (kernel lock/ irq lock).
-        *
-        * We might want to decouple profiling from the 'long path',
-        * and do the profiling totally in assembly.
+        * Normally we should not be here till LAPIC has been initialized but
+        * in some cases like kdump, its possible that there is a pending LAPIC
+        * timer interrupt from previous kernel's context and is delivered in
+        * new kernel the moment interrupts are enabled.
         *
-        * Currently this isn't too much of an issue (performance wise),
-        * we can take more than 100K local irqs per second on a 100 MHz P5.
+        * Interrupts are enabled early and LAPIC is setup much later, hence
+        * its possible that when we get here evt->event_handler is NULL.
+        * Check for event_handler being NULL and discard the interrupt as
+        * spurious.
         */
+       if (!evt->event_handler) {
+               printk(KERN_WARNING
+                      "Spurious LAPIC timer interrupt on cpu %d\n", cpu);
+               /* Switch it off */
+               lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt);
+               return;
+       }
+
+       /*
+        * the NMI deadlock-detector uses this.
+        */
+       add_pda(apic_timer_irqs, 1);
+
+       evt->event_handler(evt);
 }
 
 /*
@@ -1103,11 +1118,6 @@ void smp_apic_timer_interrupt(struct pt_regs *regs)
 {
        struct pt_regs *old_regs = set_irq_regs(regs);
 
-       /*
-        * the NMI deadlock-detector uses this.
-        */
-       add_pda(apic_timer_irqs, 1);
-
        /*
         * NOTE! We'd better ACK the irq immediately,
         * because timer handling can be slow.
@@ -1291,7 +1301,7 @@ static __init int setup_noapictimer(char *str)
 static __init int setup_apicmaintimer(char *str)
 {
        apic_runs_main_timer = 1;
-       nohpet = 1;
+
        return 1;
 }
 __setup("apicmaintimer", setup_apicmaintimer);
@@ -1307,7 +1317,7 @@ static __init int setup_apicpmtimer(char *s)
 {
        apic_calibrate_pmtmr = 1;
        notsc_setup(NULL);
-       return setup_apicmaintimer(NULL);
+       return 0;
 }
 __setup("apicpmtimer", setup_apicpmtimer);
 
index 948cae646099cc451a834e53c9a8b0ab5da037b7..eb72976cc13c177f38a178bb093b5cc721ce3d1e 100644 (file)
@@ -444,46 +444,6 @@ void __init init_ISA_irqs (void)
        }
 }
 
-static void setup_timer_hardware(void)
-{
-       outb_p(0x34,0x43);              /* binary, mode 2, LSB/MSB, ch 0 */
-       udelay(10);
-       outb_p(LATCH & 0xff , 0x40);    /* LSB */
-       udelay(10);
-       outb(LATCH >> 8 , 0x40);        /* MSB */
-}
-
-static int timer_resume(struct sys_device *dev)
-{
-       setup_timer_hardware();
-       return 0;
-}
-
-void i8254_timer_resume(void)
-{
-       setup_timer_hardware();
-}
-
-static struct sysdev_class timer_sysclass = {
-       set_kset_name("timer_pit"),
-       .resume         = timer_resume,
-};
-
-static struct sys_device device_timer = {
-       .id             = 0,
-       .cls            = &timer_sysclass,
-};
-
-static int __init init_timer_sysfs(void)
-{
-       int error = sysdev_class_register(&timer_sysclass);
-       if (!error)
-               error = sysdev_register(&device_timer);
-       return error;
-}
-
-device_initcall(init_timer_sysfs);
-
 void __init init_IRQ(void)
 {
        int i;
@@ -533,12 +493,6 @@ void __init init_IRQ(void)
        set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
        set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
 
-       /*
-        * Set the clock to HZ Hz, we already have a valid
-        * vector now:
-        */
-       setup_timer_hardware();
-
        if (!acpi_ioapic)
                setup_irq(2, &irq2);
 }
index 32f50783edc812e4d5d6618bb754d2051d3cea38..57ccf7cb6b913315263c06548b6fc37ec6f6f380 100644 (file)
@@ -223,8 +223,6 @@ void __cpuinit smp_callin(void)
        local_irq_disable();
        Dprintk("Stack at about %p\n",&cpuid);
 
-       disable_APIC_timer();
-
        /*
         * Save our processor parameters
         */
@@ -348,8 +346,6 @@ void __cpuinit start_secondary(void)
                enable_8259A_irq(0);
        }
 
-       enable_APIC_timer();
-
        /*
         * The sibling maps must be set before turing the online map on for
         * this cpu
index d899216e01caaebbf57a0c3d1b1727b91c2bb602..7781df1d50e3db2067198b994c95a02126d9bb64 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/cpu.h>
 #include <linux/kallsyms.h>
 #include <linux/acpi.h>
+#include <linux/clockchips.h>
+
 #ifdef CONFIG_ACPI
 #include <acpi/achware.h>      /* for PM timer frequency */
 #include <acpi/acpi_bus.h>
 #include <asm/nmi.h>
 #include <asm/vgtod.h>
 
-static char *timename = NULL;
-
 DEFINE_SPINLOCK(rtc_lock);
 EXPORT_SYMBOL(rtc_lock);
-DEFINE_SPINLOCK(i8253_lock);
-EXPORT_SYMBOL(i8253_lock);
 
 volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
 
@@ -194,6 +192,13 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
+{
+       global_clock_event->event_handler(global_clock_event);
+
+       return IRQ_HANDLED;
+}
+
 unsigned long read_persistent_clock(void)
 {
        unsigned int year, mon, day, hour, min, sec;
@@ -291,42 +296,8 @@ static unsigned int __init tsc_calibrate_cpu_khz(void)
        return pmc_now * tsc_khz / (tsc_now - tsc_start);
 }
 
-static void __pit_init(int val, u8 mode)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&i8253_lock, flags);
-       outb_p(mode, PIT_MODE);
-       outb_p(val & 0xff, PIT_CH0);    /* LSB */
-       outb_p(val >> 8, PIT_CH0);      /* MSB */
-       spin_unlock_irqrestore(&i8253_lock, flags);
-}
-
-void __init pit_init(void)
-{
-       __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
-}
-
-void pit_stop_interrupt(void)
-{
-       __pit_init(0, 0x30); /* mode 0 */
-}
-
-void stop_timer_interrupt(void)
-{
-       char *name;
-       if (hpet_address) {
-               name = "HPET";
-               hpet_timer_stop_set_go(0);
-       } else {
-               name = "PIT";
-               pit_stop_interrupt();
-       }
-       printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
-}
-
 static struct irqaction irq0 = {
-       .handler        = timer_interrupt,
+       .handler        = timer_event_interrupt,
        .flags          = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
        .mask           = CPU_MASK_NONE,
        .name           = "timer"
@@ -334,20 +305,10 @@ static struct irqaction irq0 = {
 
 void __init time_init(void)
 {
-       if (nohpet)
-               hpet_address = 0;
-
-       if (hpet_arch_init())
-               hpet_address = 0;
+       if (!hpet_enable())
+               setup_pit_timer();
 
-       if (hpet_use_timer) {
-               /* set tick_nsec to use the proper rate for HPET */
-               tick_nsec = TICK_NSEC_HPET;
-               timename = "HPET";
-       } else {
-               pit_init();
-               timename = "PIT";
-       }
+       setup_irq(0, &irq0);
 
        tsc_calibrate();
 
@@ -369,46 +330,4 @@ void __init time_init(void)
        printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
                cpu_khz / 1000, cpu_khz % 1000);
        init_tsc_clocksource();
-
-       setup_irq(0, &irq0);
-}
-
-/*
- * sysfs support for the timer.
- */
-
-static int timer_suspend(struct sys_device *dev, pm_message_t state)
-{
-       return 0;
 }
-
-static int timer_resume(struct sys_device *dev)
-{
-       if (hpet_address)
-               hpet_reenable();
-       else
-               i8254_timer_resume();
-       return 0;
-}
-
-static struct sysdev_class timer_sysclass = {
-       .resume = timer_resume,
-       .suspend = timer_suspend,
-       set_kset_name("timer"),
-};
-
-/* XXX this sysfs stuff should probably go elsewhere later -john */
-static struct sys_device device_timer = {
-       .id     = 0,
-       .cls    = &timer_sysclass,
-};
-
-static int time_init_device(void)
-{
-       int error = sysdev_class_register(&timer_sysclass);
-       if (!error)
-               error = sysdev_register(&device_timer);
-       return error;
-}
-
-device_initcall(time_init_device);
index a9534c21c306836de715f45405e290612b89791d..eb80f5aca54e820bbda5451eac65bfcc53b14ba8 100644 (file)
@@ -40,7 +40,11 @@ config CLOCKSOURCE_WATCHDOG
        bool
        default y
 
-config GENERIC_CLOCKEVENTS_MIGR
+config GENERIC_CLOCKEVENTS
+       bool
+       default y
+
+config GENERIC_CLOCKEVENTS_BROADCAST
        bool
        default y
 
index 9eff48601254a88ed6cb0c54cf0a7ebb79fe2ccf..e2976ed30b70306f824c02222721c2497851564a 100644 (file)
@@ -1,5 +1,89 @@
-#ifdef CONFIG_X86_32
-# include "hpet_32.h"
+#ifndef ASM_X86_HPET_H
+#define ASM_X86_HPET_H
+
+#ifdef CONFIG_HPET_TIMER
+
+/*
+ * Documentation on HPET can be found at:
+ *      http://www.intel.com/ial/home/sp/pcmmspec.htm
+ *      ftp://download.intel.com/ial/home/sp/mmts098.pdf
+ */
+
+#define HPET_MMAP_SIZE         1024
+
+#define HPET_ID                        0x000
+#define HPET_PERIOD            0x004
+#define HPET_CFG               0x010
+#define HPET_STATUS            0x020
+#define HPET_COUNTER           0x0f0
+#define HPET_T0_CFG            0x100
+#define HPET_T0_CMP            0x108
+#define HPET_T0_ROUTE          0x110
+#define HPET_T1_CFG            0x120
+#define HPET_T1_CMP            0x128
+#define HPET_T1_ROUTE          0x130
+#define HPET_T2_CFG            0x140
+#define HPET_T2_CMP            0x148
+#define HPET_T2_ROUTE          0x150
+
+#define HPET_ID_REV            0x000000ff
+#define HPET_ID_NUMBER         0x00001f00
+#define HPET_ID_64BIT          0x00002000
+#define HPET_ID_LEGSUP         0x00008000
+#define HPET_ID_VENDOR         0xffff0000
+#define        HPET_ID_NUMBER_SHIFT    8
+#define HPET_ID_VENDOR_SHIFT   16
+
+#define HPET_ID_VENDOR_8086    0x8086
+
+#define HPET_CFG_ENABLE                0x001
+#define HPET_CFG_LEGACY                0x002
+#define        HPET_LEGACY_8254        2
+#define        HPET_LEGACY_RTC         8
+
+#define HPET_TN_LEVEL          0x0002
+#define HPET_TN_ENABLE         0x0004
+#define HPET_TN_PERIODIC       0x0008
+#define HPET_TN_PERIODIC_CAP   0x0010
+#define HPET_TN_64BIT_CAP      0x0020
+#define HPET_TN_SETVAL         0x0040
+#define HPET_TN_32BIT          0x0100
+#define HPET_TN_ROUTE          0x3e00
+#define HPET_TN_FSB            0x4000
+#define HPET_TN_FSB_CAP                0x8000
+#define HPET_TN_ROUTE_SHIFT    9
+
+/* Max HPET Period is 10^8 femto sec as in HPET spec */
+#define HPET_MAX_PERIOD                100000000UL
+/*
+ * Min HPET period is 10^5 femto sec just for safety. If it is less than this,
+ * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
+ */
+#define HPET_MIN_PERIOD                100000UL
+
+/* hpet memory map physical address */
+extern unsigned long hpet_address;
+extern int is_hpet_enabled(void);
+extern int hpet_enable(void);
+
+#ifdef CONFIG_HPET_EMULATE_RTC
+
+#include <linux/interrupt.h>
+
+extern int hpet_mask_rtc_irq_bit(unsigned long bit_mask);
+extern int hpet_set_rtc_irq_bit(unsigned long bit_mask);
+extern int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
+                              unsigned char sec);
+extern int hpet_set_periodic_freq(unsigned long freq);
+extern int hpet_rtc_dropped_irq(void);
+extern int hpet_rtc_timer_init(void);
+extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
+
+#endif /* CONFIG_HPET_EMULATE_RTC */
+
 #else
-# include "hpet_64.h"
-#endif
+
+static inline int hpet_enable(void) { return 0; }
+
+#endif /* CONFIG_HPET_TIMER */
+#endif /* ASM_X86_HPET_H */
diff --git a/include/asm-x86/hpet_32.h b/include/asm-x86/hpet_32.h
deleted file mode 100644 (file)
index c82dc7e..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-
-#ifndef _I386_HPET_H
-#define _I386_HPET_H
-
-#ifdef CONFIG_HPET_TIMER
-
-/*
- * Documentation on HPET can be found at:
- *      http://www.intel.com/ial/home/sp/pcmmspec.htm
- *      ftp://download.intel.com/ial/home/sp/mmts098.pdf
- */
-
-#define HPET_MMAP_SIZE         1024
-
-#define HPET_ID                        0x000
-#define HPET_PERIOD            0x004
-#define HPET_CFG               0x010
-#define HPET_STATUS            0x020
-#define HPET_COUNTER           0x0f0
-#define HPET_T0_CFG            0x100
-#define HPET_T0_CMP            0x108
-#define HPET_T0_ROUTE          0x110
-#define HPET_T1_CFG            0x120
-#define HPET_T1_CMP            0x128
-#define HPET_T1_ROUTE          0x130
-#define HPET_T2_CFG            0x140
-#define HPET_T2_CMP            0x148
-#define HPET_T2_ROUTE          0x150
-
-#define HPET_ID_REV            0x000000ff
-#define HPET_ID_NUMBER         0x00001f00
-#define HPET_ID_64BIT          0x00002000
-#define HPET_ID_LEGSUP         0x00008000
-#define HPET_ID_VENDOR         0xffff0000
-#define        HPET_ID_NUMBER_SHIFT    8
-#define HPET_ID_VENDOR_SHIFT   16
-
-#define HPET_ID_VENDOR_8086    0x8086
-
-#define HPET_CFG_ENABLE                0x001
-#define HPET_CFG_LEGACY                0x002
-#define        HPET_LEGACY_8254        2
-#define        HPET_LEGACY_RTC         8
-
-#define HPET_TN_LEVEL          0x0002
-#define HPET_TN_ENABLE         0x0004
-#define HPET_TN_PERIODIC       0x0008
-#define HPET_TN_PERIODIC_CAP   0x0010
-#define HPET_TN_64BIT_CAP      0x0020
-#define HPET_TN_SETVAL         0x0040
-#define HPET_TN_32BIT          0x0100
-#define HPET_TN_ROUTE          0x3e00
-#define HPET_TN_FSB            0x4000
-#define HPET_TN_FSB_CAP                0x8000
-#define HPET_TN_ROUTE_SHIFT    9
-
-/* Max HPET Period is 10^8 femto sec as in HPET spec */
-#define HPET_MAX_PERIOD                100000000UL
-/*
- * Min HPET period is 10^5 femto sec just for safety. If it is less than this,
- * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
- */
-#define HPET_MIN_PERIOD                100000UL
-
-/* hpet memory map physical address */
-extern unsigned long hpet_address;
-extern int is_hpet_enabled(void);
-extern int hpet_enable(void);
-
-#ifdef CONFIG_HPET_EMULATE_RTC
-
-#include <linux/interrupt.h>
-
-extern int hpet_mask_rtc_irq_bit(unsigned long bit_mask);
-extern int hpet_set_rtc_irq_bit(unsigned long bit_mask);
-extern int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
-                              unsigned char sec);
-extern int hpet_set_periodic_freq(unsigned long freq);
-extern int hpet_rtc_dropped_irq(void);
-extern int hpet_rtc_timer_init(void);
-extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
-
-#endif /* CONFIG_HPET_EMULATE_RTC */
-
-#else
-
-static inline int hpet_enable(void) { return 0; }
-
-#endif /* CONFIG_HPET_TIMER */
-#endif /* _I386_HPET_H */
diff --git a/include/asm-x86/hpet_64.h b/include/asm-x86/hpet_64.h
deleted file mode 100644 (file)
index fd4deca..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef _ASM_X8664_HPET_H
-#define _ASM_X8664_HPET_H 1
-
-#include <asm/hpet_32.h>
-
-#define HPET_TICK_RATE (HZ * 100000UL)
-
-extern int hpet_rtc_timer_init(void);
-extern int hpet_arch_init(void);
-extern int hpet_timer_stop_set_go(unsigned long tick);
-extern int hpet_reenable(void);
-extern unsigned int hpet_calibrate_tsc(void);
-
-extern int hpet_use_timer;
-extern unsigned long hpet_period;
-extern unsigned long hpet_tick;
-
-#endif