From 7f8033b76c8eea89dec9f658f4a31bcd4fe84bbb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:05 +0200 Subject: clockevents: Remove unused inline function Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- include/linux/clockchips.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index def5a659b8a5..f368a6fe8cc9 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -130,7 +130,6 @@ extern void clockevents_notify(unsigned long reason, void *arg); #else -static inline void clockevents_resume_events(void) { } #define clockevents_notify(reason, arg) do { } while (0) #endif -- cgit v1.2.3 From de68d9b173ee657115dd0e584c2365b7954253a5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:05 +0200 Subject: clockevents: Allow build w/o run-tine usage for migration purposes Migration aid to allow preparatory patches which introduce not yet used parts of clock events code. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- include/linux/clockchips.h | 8 ++++++-- kernel/time/Kconfig | 5 +++++ kernel/time/Makefile | 2 +- kernel/time/clockevents.c | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index f368a6fe8cc9..d2ddea926895 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -8,7 +8,7 @@ #ifndef _LINUX_CLOCKCHIPS_H #define _LINUX_CLOCKCHIPS_H -#ifdef CONFIG_GENERIC_CLOCKEVENTS +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD #include #include @@ -126,9 +126,13 @@ extern int clockevents_register_notifier(struct notifier_block *nb); extern int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, ktime_t now); +#ifdef CONFIG_GENERIC_CLOCKEVENTS extern void clockevents_notify(unsigned long reason, void *arg); - #else +# define clockevents_notify(reason, arg) do { } while (0) +#endif + +#else /* CONFIG_GENERIC_CLOCKEVENTS_BUILD */ #define clockevents_notify(reason, arg) do { } while (0) diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index f66351126544..8d53106a0a92 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -23,3 +23,8 @@ config HIGH_RES_TIMERS hardware is not capable then this option only increases the size of the kernel image. +config GENERIC_CLOCKEVENTS_BUILD + bool + default y + depends on GENERIC_CLOCKEVENTS || GENERIC_CLOCKEVENTS_MIGR + diff --git a/kernel/time/Makefile b/kernel/time/Makefile index 99b6034fc86b..905b0b50792d 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,6 +1,6 @@ obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o -obj-$(CONFIG_GENERIC_CLOCKEVENTS) += clockevents.o +obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += tick-broadcast.o obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 41dd3105ce7f..822beebe664a 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -194,6 +194,7 @@ void clockevents_exchange_device(struct clock_event_device *old, local_irq_restore(flags); } +#ifdef CONFIG_GENERIC_CLOCKEVENTS /** * clockevents_notify - notification about relevant events */ @@ -222,4 +223,4 @@ void clockevents_notify(unsigned long reason, void *arg) spin_unlock(&clockevents_lock); } EXPORT_SYMBOL_GPL(clockevents_notify); - +#endif -- cgit v1.2.3 From c8a1d398de70a7774359b4720c392891cdd485f9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: clockevents: fix periodic broadcast for oneshot devices The next_event member of the clock event device is used to keep track of the next periodic event. For one shot only devices it is wrong to clear the variable, as the next event will be based on it. Pointed out by Ralf Baechle Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- kernel/time/tick-broadcast.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 0962e0577660..acf15b49e55b 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -176,8 +176,6 @@ static void tick_do_periodic_broadcast(void) */ static void tick_handle_periodic_broadcast(struct clock_event_device *dev) { - dev->next_event.tv64 = KTIME_MAX; - tick_do_periodic_broadcast(); /* -- cgit v1.2.3 From 3c9aea47425885ec8b1f7b0df88c2ebc6f747c9d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86: Fix irq0 / local apic timer accounting The clock events merge introduced a change to the nmi watchdog code to handle the not longer increasing local apic timer count in the broadcast mode. This is fine for UP, but on SMP it pampers over a stuck CPU which is not handling the broadcast interrupt due to the unconditional sum up of local apic timer count and irq0 count. To cover all cases we need to keep track on which CPU irq0 is handled. In theory this is CPU#0 due to the explicit disabling of irq balancing for irq0, but there are systems which ignore this on the hardware level. The per cpu irq0 accounting allows us to remove the irq0 to CPU0 binding as well. Add a per cpu counter for irq0 and evaluate this instead of the global irq0 count in the nmi watchdog code. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/nmi_32.c | 3 ++- arch/x86/kernel/time_32.c | 3 +++ include/asm-x86/hardirq_32.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/nmi_32.c b/arch/x86/kernel/nmi_32.c index c7227e2180f8..95d3fc203cf7 100644 --- a/arch/x86/kernel/nmi_32.c +++ b/arch/x86/kernel/nmi_32.c @@ -353,7 +353,8 @@ __kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) * Take the local apic timer and PIT/HPET into account. We don't * know which one is active, when we have highres/dyntick on */ - sum = per_cpu(irq_stat, cpu).apic_timer_irqs + kstat_cpu(cpu).irqs[0]; + sum = per_cpu(irq_stat, cpu).apic_timer_irqs + + per_cpu(irq_stat, cpu).irq0_irqs; /* if the none of the timers isn't firing, this cpu isn't doing much */ if (!touched && last_irq_sums[cpu] == sum) { diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c index 19a6c678d02e..56dadfc2f41c 100644 --- a/arch/x86/kernel/time_32.c +++ b/arch/x86/kernel/time_32.c @@ -157,6 +157,9 @@ EXPORT_SYMBOL(profile_pc); */ irqreturn_t timer_interrupt(int irq, void *dev_id) { + /* Keep nmi watchdog up to date */ + per_cpu(irq_stat, smp_processor_id()).irq0_irqs++; + #ifdef CONFIG_X86_IO_APIC if (timer_ack) { /* diff --git a/include/asm-x86/hardirq_32.h b/include/asm-x86/hardirq_32.h index 0e358dc405f8..34649585bb59 100644 --- a/include/asm-x86/hardirq_32.h +++ b/include/asm-x86/hardirq_32.h @@ -9,6 +9,7 @@ typedef struct { unsigned long idle_timestamp; unsigned int __nmi_count; /* arch dependent */ unsigned int apic_timer_irqs; /* arch dependent */ + unsigned int irq0_irqs; } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU(irq_cpustat_t, irq_stat); -- cgit v1.2.3 From 5fa3a246ea2e1be2ffaa01343bc183c8c0bfa472 Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86: block irq balancing for timer Disable irq balancing on IRQ0. Several SIS chipsets lock up when you try to change affinity of IRQ #0. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Andi Kleen Cc: john stultz Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Arjan van de Ven --- arch/x86/kernel/time_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 6d48a4e826d9..2db4d698a2d3 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -360,7 +360,7 @@ void stop_timer_interrupt(void) static struct irqaction irq0 = { .handler = timer_interrupt, - .flags = IRQF_DISABLED | IRQF_IRQPOLL, + .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, .mask = CPU_MASK_NONE, .name = "timer" }; -- cgit v1.2.3 From 83d7384f8d4aa216b49cf9cb286ea743054b119f Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86: Geode Multi-Function General Purpose Timers support This adds support for Multi-Function General Purpose Timers. It detects the available timers during southbridge init, and provides an API for allocating and setting the timers. They're higher resolution than the standard PIT, so the MFGPTs come in handy for quite a few things. Note that we never clobber the timers that the BIOS might have opted to use; we just check for unused timers. Signed-off-by: Jordan Crouse Signed-off-by: Andres Salomon Cc: Andi Kleen Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- Documentation/kernel-parameters.txt | 3 + arch/x86/kernel/Makefile_32 | 2 +- arch/x86/kernel/geode_32.c | 4 + arch/x86/kernel/mfgpt_32.c | 197 ++++++++++++++++++++++++++++++++++++ include/asm-x86/geode.h | 50 +++++++++ 5 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 arch/x86/kernel/mfgpt_32.c diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index a57c1f216b21..2128de6d80ea 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1160,6 +1160,9 @@ and is between 256 and 4096 characters. It is defined in the file nomce [X86-32] Machine Check Exception + nomfgpt [X86-32] Disable Multi-Function General Purpose + Timer usage (for AMD Geode machines). + noreplace-paravirt [X86-32,PV_OPS] Don't patch paravirt_ops noreplace-smp [X86-32,SMP] Don't replace SMP instructions diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32 index c624193740fd..10356443998e 100644 --- a/arch/x86/kernel/Makefile_32 +++ b/arch/x86/kernel/Makefile_32 @@ -39,7 +39,7 @@ obj-$(CONFIG_VM86) += vm86_32.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_HPET_TIMER) += hpet_32.o obj-$(CONFIG_K8_NB) += k8.o -obj-$(CONFIG_MGEODE_LX) += geode_32.o +obj-$(CONFIG_MGEODE_LX) += geode_32.o mfgpt_32.o obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o obj-$(CONFIG_PARAVIRT) += paravirt_32.o diff --git a/arch/x86/kernel/geode_32.c b/arch/x86/kernel/geode_32.c index 41e8aec4c61d..f12d8c5d9809 100644 --- a/arch/x86/kernel/geode_32.c +++ b/arch/x86/kernel/geode_32.c @@ -145,10 +145,14 @@ EXPORT_SYMBOL_GPL(geode_gpio_setup_event); static int __init geode_southbridge_init(void) { + int timers; + if (!is_geode()) return -ENODEV; init_lbars(); + timers = geode_mfgpt_detect(); + printk(KERN_INFO "geode: %d MFGPT timers available.\n", timers); return 0; } diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c new file mode 100644 index 000000000000..3a63a2dd8d27 --- /dev/null +++ b/arch/x86/kernel/mfgpt_32.c @@ -0,0 +1,197 @@ +/* + * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT) + * + * Copyright (C) 2006, Advanced Micro Devices, Inc. + * Copyright (C) 2007, Andres Salomon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book. + */ + +/* + * We are using the 32Khz input clock - its the only one that has the + * ranges we find desirable. The following table lists the suitable + * divisors and the associated hz, minimum interval + * and the maximum interval: + * + * Divisor Hz Min Delta (S) Max Delta (S) + * 1 32000 .0005 2.048 + * 2 16000 .001 4.096 + * 4 8000 .002 8.192 + * 8 4000 .004 16.384 + * 16 2000 .008 32.768 + * 32 1000 .016 65.536 + * 64 500 .032 131.072 + * 128 250 .064 262.144 + * 256 125 .128 524.288 + */ + +#include +#include +#include +#include + +#define F_AVAIL 0x01 + +static struct mfgpt_timer_t { + int flags; + struct module *owner; +} mfgpt_timers[MFGPT_MAX_TIMERS]; + +/* Selected from the table above */ + +#define MFGPT_DIVISOR 16 +#define MFGPT_SCALE 4 /* divisor = 2^(scale) */ +#define MFGPT_HZ (32000 / MFGPT_DIVISOR) +#define MFGPT_PERIODIC (MFGPT_HZ / HZ) + +/* Allow for disabling of MFGPTs */ +static int disable; +static int __init mfgpt_disable(char *s) +{ + disable = 1; + return 1; +} +__setup("nomfgpt", mfgpt_disable); + +/* + * Check whether any MFGPTs are available for the kernel to use. In most + * cases, firmware that uses AMD's VSA code will claim all timers during + * bootup; we certainly don't want to take them if they're already in use. + * In other cases (such as with VSAless OpenFirmware), the system firmware + * leaves timers available for us to use. + */ +int __init geode_mfgpt_detect(void) +{ + int count = 0, i; + u16 val; + + if (disable) { + printk(KERN_INFO "geode-mfgpt: Skipping MFGPT setup\n"); + return 0; + } + + for (i = 0; i < MFGPT_MAX_TIMERS; i++) { + val = geode_mfgpt_read(i, MFGPT_REG_SETUP); + if (!(val & MFGPT_SETUP_SETUP)) { + mfgpt_timers[i].flags = F_AVAIL; + count++; + } + } + + return count; +} + +int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable) +{ + u32 msr, mask, value, dummy; + int shift = (cmp == MFGPT_CMP1) ? 0 : 8; + + if (timer < 0 || timer >= MFGPT_MAX_TIMERS) + return -EIO; + + /* + * The register maps for these are described in sections 6.17.1.x of + * the AMD Geode CS5536 Companion Device Data Book. + */ + switch (event) { + case MFGPT_EVENT_RESET: + /* + * XXX: According to the docs, we cannot reset timers above + * 6; that is, resets for 7 and 8 will be ignored. Is this + * a problem? -dilinger + */ + msr = MFGPT_NR_MSR; + mask = 1 << (timer + 24); + break; + + case MFGPT_EVENT_NMI: + msr = MFGPT_NR_MSR; + mask = 1 << (timer + shift); + break; + + case MFGPT_EVENT_IRQ: + msr = MFGPT_IRQ_MSR; + mask = 1 << (timer + shift); + break; + + default: + return -EIO; + } + + rdmsr(msr, value, dummy); + + if (enable) + value |= mask; + else + value &= ~mask; + + wrmsr(msr, value, dummy); + return 0; +} + +int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable) +{ + u32 val, dummy; + int offset; + + if (timer < 0 || timer >= MFGPT_MAX_TIMERS) + return -EIO; + + if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable)) + return -EIO; + + rdmsr(MSR_PIC_ZSEL_LOW, val, dummy); + + offset = (timer % 4) * 4; + + val &= ~((0xF << offset) | (0xF << (offset + 16))); + + if (enable) { + val |= (irq & 0x0F) << (offset); + val |= (irq & 0x0F) << (offset + 16); + } + + wrmsr(MSR_PIC_ZSEL_LOW, val, dummy); + return 0; +} + +static int mfgpt_get(int timer, struct module *owner) +{ + mfgpt_timers[timer].flags &= ~F_AVAIL; + mfgpt_timers[timer].owner = owner; + printk(KERN_INFO "geode-mfgpt: Registered timer %d\n", timer); + return timer; +} + +int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner) +{ + int i; + + if (!geode_get_dev_base(GEODE_DEV_MFGPT)) + return -ENODEV; + if (timer >= MFGPT_MAX_TIMERS) + return -EIO; + + if (timer < 0) { + /* Try to find an available timer */ + for (i = 0; i < MFGPT_MAX_TIMERS; i++) { + if (mfgpt_timers[i].flags & F_AVAIL) + return mfgpt_get(i, owner); + + if (i == 5 && domain == MFGPT_DOMAIN_WORKING) + break; + } + } else { + /* If they requested a specific timer, try to honor that */ + if (mfgpt_timers[timer].flags & F_AVAIL) + return mfgpt_get(timer, owner); + } + + /* No timers available - too bad */ + return -1; +} + diff --git a/include/asm-x86/geode.h b/include/asm-x86/geode.h index 6da4bbbea3dc..d94898831bac 100644 --- a/include/asm-x86/geode.h +++ b/include/asm-x86/geode.h @@ -156,4 +156,54 @@ static inline int is_geode(void) return (is_geode_gx() || is_geode_lx()); } +/* MFGPTs */ + +#define MFGPT_MAX_TIMERS 8 +#define MFGPT_TIMER_ANY -1 + +#define MFGPT_DOMAIN_WORKING 1 +#define MFGPT_DOMAIN_STANDBY 2 +#define MFGPT_DOMAIN_ANY (MFGPT_DOMAIN_WORKING | MFGPT_DOMAIN_STANDBY) + +#define MFGPT_CMP1 0 +#define MFGPT_CMP2 1 + +#define MFGPT_EVENT_IRQ 0 +#define MFGPT_EVENT_NMI 1 +#define MFGPT_EVENT_RESET 3 + +#define MFGPT_REG_CMP1 0 +#define MFGPT_REG_CMP2 2 +#define MFGPT_REG_COUNTER 4 +#define MFGPT_REG_SETUP 6 + +#define MFGPT_SETUP_CNTEN (1 << 15) +#define MFGPT_SETUP_CMP2 (1 << 14) +#define MFGPT_SETUP_CMP1 (1 << 13) +#define MFGPT_SETUP_SETUP (1 << 12) +#define MFGPT_SETUP_STOPEN (1 << 11) +#define MFGPT_SETUP_EXTEN (1 << 10) +#define MFGPT_SETUP_REVEN (1 << 5) +#define MFGPT_SETUP_CLKSEL (1 << 4) + +static inline void geode_mfgpt_write(int timer, u16 reg, u16 value) +{ + u32 base = geode_get_dev_base(GEODE_DEV_MFGPT); + outw(value, base + reg + (timer * 8)); +} + +static inline u16 geode_mfgpt_read(int timer, u16 reg) +{ + u32 base = geode_get_dev_base(GEODE_DEV_MFGPT); + return inw(base + reg + (timer * 8)); +} + +extern int __init geode_mfgpt_detect(void); +extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable); +extern int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable); +extern int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner); + +#define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 1) +#define geode_mfgpt_release_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 0) + #endif -- cgit v1.2.3 From 8f36881b3c972ebc019195692849f22488f9c0a3 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86: Geode MFGPT clock event device support Add support for an MFGPT clock event device; this allows us to use MFGPTs as the basis for high-resolution timers. Signed-off-by: Jordan Crouse Signed-off-by: Andres Salomon Cc: Andi Kleen Cc: Alan Cox Cc: john stultz Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- Documentation/kernel-parameters.txt | 4 + arch/i386/Kconfig | 10 +++ arch/x86/kernel/mfgpt_32.c | 165 ++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2128de6d80ea..f27cdd7125f2 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1009,6 +1009,10 @@ and is between 256 and 4096 characters. It is defined in the file meye.*= [HW] Set MotionEye Camera parameters See Documentation/video4linux/meye.txt. + mfgpt_irq= [IA-32] Specify the IRQ to use for the + Multi-Function General Purpose Timers on AMD Geode + platforms. + mga= [HW,DRM] mousedev.tap_time= diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 2d85e4b87307..6bbbc2755e44 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -1206,6 +1206,16 @@ config SCx200HR_TIMER processor goes idle (as is done by the scheduler). The other workaround is idle=poll boot option. +config GEODE_MFGPT_TIMER + bool "Geode Multi-Function General Purpose Timer (MFGPT) events" + depends on MGEODE_LX && GENERIC_TIME && GENERIC_CLOCKEVENTS + default y + help + This driver provides a clock event source based on the MFGPT + timer(s) in the CS5535 and CS5536 companion chip for the geode. + MFGPTs have a better resolution and max interval than the + generic PIT, and are suitable for use as high-res timers. + config K8_NB def_bool y depends on AGP_AMD64 diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c index 3a63a2dd8d27..0ab680f2d9db 100644 --- a/arch/x86/kernel/mfgpt_32.c +++ b/arch/x86/kernel/mfgpt_32.c @@ -48,6 +48,12 @@ static struct mfgpt_timer_t { #define MFGPT_HZ (32000 / MFGPT_DIVISOR) #define MFGPT_PERIODIC (MFGPT_HZ / HZ) +#ifdef CONFIG_GEODE_MFGPT_TIMER +static int __init mfgpt_timer_setup(void); +#else +#define mfgpt_timer_setup() (0) +#endif + /* Allow for disabling of MFGPTs */ static int disable; static int __init mfgpt_disable(char *s) @@ -82,6 +88,9 @@ int __init geode_mfgpt_detect(void) } } + /* set up clock event device, if desired */ + i = mfgpt_timer_setup(); + return count; } @@ -195,3 +204,159 @@ int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner) return -1; } + +#ifdef CONFIG_GEODE_MFGPT_TIMER + +/* + * The MFPGT timers on the CS5536 provide us with suitable timers to use + * as clock event sources - not as good as a HPET or APIC, but certainly + * better then the PIT. This isn't a general purpose MFGPT driver, but + * a simplified one designed specifically to act as a clock event source. + * For full details about the MFGPT, please consult the CS5536 data sheet. + */ + +#include +#include + +static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN; +static u16 mfgpt_event_clock; + +static int irq = 7; +static int __init mfgpt_setup(char *str) +{ + get_option(&str, &irq); + return 1; +} +__setup("mfgpt_irq=", mfgpt_setup); + +static inline void mfgpt_disable_timer(u16 clock) +{ + u16 val = geode_mfgpt_read(clock, MFGPT_REG_SETUP); + geode_mfgpt_write(clock, MFGPT_REG_SETUP, val & ~MFGPT_SETUP_CNTEN); +} + +static int mfgpt_next_event(unsigned long, struct clock_event_device *); +static void mfgpt_set_mode(enum clock_event_mode, struct clock_event_device *); + +static struct clock_event_device mfgpt_clockevent = { + .name = "mfgpt-timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = mfgpt_set_mode, + .set_next_event = mfgpt_next_event, + .rating = 250, + .cpumask = CPU_MASK_ALL, + .shift = 32 +}; + +static inline void mfgpt_start_timer(u16 clock, u16 delta) +{ + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta); + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); + + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, + MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2); +} + +static void mfgpt_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + mfgpt_disable_timer(mfgpt_event_clock); + + if (mode == CLOCK_EVT_MODE_PERIODIC) + mfgpt_start_timer(mfgpt_event_clock, MFGPT_PERIODIC); + + mfgpt_tick_mode = mode; +} + +static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt) +{ + mfgpt_start_timer(mfgpt_event_clock, delta); + return 0; +} + +/* Assume (foolishly?), that this interrupt was due to our tick */ + +static irqreturn_t mfgpt_tick(int irq, void *dev_id) +{ + if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN) + return IRQ_HANDLED; + + /* Turn off the clock */ + mfgpt_disable_timer(mfgpt_event_clock); + + /* Clear the counter */ + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); + + /* Restart the clock in periodic mode */ + + if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) { + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, + MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2); + } + + mfgpt_clockevent.event_handler(&mfgpt_clockevent); + return IRQ_HANDLED; +} + +static struct irqaction mfgptirq = { + .handler = mfgpt_tick, + .flags = IRQF_DISABLED | IRQF_NOBALANCING, + .mask = CPU_MASK_NONE, + .name = "mfgpt-timer" +}; + +static int __init mfgpt_timer_setup(void) +{ + int timer, ret; + u16 val; + + timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING, + THIS_MODULE); + if (timer < 0) { + printk(KERN_ERR + "mfgpt-timer: Could not allocate a MFPGT timer\n"); + return -ENODEV; + } + + mfgpt_event_clock = timer; + /* Set the clock scale and enable the event mode for CMP2 */ + val = MFGPT_SCALE | (3 << 8); + + geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val); + + /* Set up the IRQ on the MFGPT side */ + if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) { + printk(KERN_ERR "mfgpt-timer: Could not set up IRQ %d\n", irq); + return -EIO; + } + + /* And register it with the kernel */ + ret = setup_irq(irq, &mfgptirq); + + if (ret) { + printk(KERN_ERR + "mfgpt-timer: Unable to set up the interrupt.\n"); + goto err; + } + + /* Set up the clock event */ + mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC, 32); + mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF, + &mfgpt_clockevent); + mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE, + &mfgpt_clockevent); + + printk(KERN_INFO + "mfgpt-timer: registering the MFGT timer as a clock event.\n"); + clockevents_register_device(&mfgpt_clockevent); + + return 0; + +err: + geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq); + printk(KERN_ERR + "mfgpt-timer: Unable to set up the MFGPT clock source\n"); + return -EIO; +} + +#endif -- cgit v1.2.3 From 89b2bbd69b89b4c5efdc112a88d72419bdeb8dfc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: Select clocksource watchdog in Kconfig TSC must be verified by a continous and reliable clocksource to allow high resolution timers and or dynamic ticks. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86_64/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index b1b98e614f7c..520f33b83bfc 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -36,6 +36,10 @@ config GENERIC_CMOS_UPDATE bool default y +config CLOCKSOURCE_WATCHDOG + bool + default y + config ZONE_DMA32 bool default y -- cgit v1.2.3 From d371698efd45c3664fd1726780c360f02e1f9580 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: Consolidate tsc calibration Move the TSC calibration code to tsc.c. Reimplement it so the pm timer can be used as a reference as well. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/hpet_64.c | 49 -------------------------- arch/x86/kernel/time_64.c | 33 ++--------------- arch/x86/kernel/tsc_64.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++ include/asm-x86/tsc.h | 4 +++ 4 files changed, 96 insertions(+), 80 deletions(-) diff --git a/arch/x86/kernel/hpet_64.c b/arch/x86/kernel/hpet_64.c index e2d1b912e154..1ebce2ec7eaf 100644 --- a/arch/x86/kernel/hpet_64.c +++ b/arch/x86/kernel/hpet_64.c @@ -184,55 +184,6 @@ int hpet_reenable(void) return hpet_timer_stop_set_go(hpet_tick); } -/* - * calibrate_tsc() calibrates the processor TSC in a very simple way, comparing - * it to the HPET timer of known frequency. - */ - -#define TICK_COUNT 100000000 -#define SMI_THRESHOLD 50000 -#define MAX_TRIES 5 - -/* - * Some platforms take periodic SMI interrupts with 5ms duration. Make sure none - * occurs between the reads of the hpet & TSC. - */ -static void __init read_hpet_tsc(int *hpet, int *tsc) -{ - int tsc1, tsc2, hpet1, i; - - for (i = 0; i < MAX_TRIES; i++) { - tsc1 = get_cycles_sync(); - hpet1 = hpet_readl(HPET_COUNTER); - tsc2 = get_cycles_sync(); - if ((tsc2 - tsc1) < SMI_THRESHOLD) - break; - } - *hpet = hpet1; - *tsc = tsc2; -} - -unsigned int __init hpet_calibrate_tsc(void) -{ - int tsc_start, hpet_start; - int tsc_now, hpet_now; - unsigned long flags; - - local_irq_save(flags); - - read_hpet_tsc(&hpet_start, &tsc_start); - - do { - local_irq_disable(); - read_hpet_tsc(&hpet_now, &tsc_now); - local_irq_restore(flags); - } while ((tsc_now - tsc_start) < TICK_COUNT && - (hpet_now - hpet_start) < TICK_COUNT); - - return (tsc_now - tsc_start) * 1000000000L - / ((hpet_now - hpet_start) * hpet_period / 1000); -} - #ifdef CONFIG_HPET_EMULATE_RTC /* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET * is enabled, we support RTC interrupt functionality in software. diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 2db4d698a2d3..e97a3ee13551 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -292,35 +292,6 @@ static unsigned int __init tsc_calibrate_cpu_khz(void) return pmc_now * tsc_khz / (tsc_now - tsc_start); } -/* - * pit_calibrate_tsc() uses the speaker output (channel 2) of - * the PIT. This is better than using the timer interrupt output, - * because we can read the value of the speaker with just one inb(), - * where we need three i/o operations for the interrupt channel. - * We count how many ticks the TSC does in 50 ms. - */ - -static unsigned int __init pit_calibrate_tsc(void) -{ - unsigned long start, end; - unsigned long flags; - - spin_lock_irqsave(&i8253_lock, flags); - - outb((inb(0x61) & ~0x02) | 0x01, 0x61); - - outb(0xb0, 0x43); - outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42); - outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42); - start = get_cycles_sync(); - while ((inb(0x61) & 0x20) == 0); - end = get_cycles_sync(); - - spin_unlock_irqrestore(&i8253_lock, flags); - - return (end - start) / 50; -} - #define PIT_MODE 0x43 #define PIT_CH0 0x40 @@ -376,14 +347,14 @@ void __init time_init(void) if (hpet_use_timer) { /* set tick_nsec to use the proper rate for HPET */ tick_nsec = TICK_NSEC_HPET; - tsc_khz = hpet_calibrate_tsc(); timename = "HPET"; } else { pit_init(); - tsc_khz = pit_calibrate_tsc(); timename = "PIT"; } + tsc_calibrate(); + cpu_khz = tsc_khz; if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && boot_cpu_data.x86_vendor == X86_VENDOR_AMD && diff --git a/arch/x86/kernel/tsc_64.c b/arch/x86/kernel/tsc_64.c index 2a59bde663f2..59baecd135ab 100644 --- a/arch/x86/kernel/tsc_64.c +++ b/arch/x86/kernel/tsc_64.c @@ -6,7 +6,9 @@ #include #include #include +#include +#include #include static int notsc __initdata = 0; @@ -118,6 +120,94 @@ core_initcall(cpufreq_tsc); #endif +#define MAX_RETRIES 5 +#define SMI_TRESHOLD 50000 + +/* + * Read TSC and the reference counters. Take care of SMI disturbance + */ +static unsigned long __init tsc_read_refs(unsigned long *pm, + unsigned long *hpet) +{ + unsigned long t1, t2; + int i; + + for (i = 0; i < MAX_RETRIES; i++) { + t1 = get_cycles_sync(); + if (hpet) + *hpet = hpet_readl(HPET_COUNTER) & 0xFFFFFFFF; + else + *pm = acpi_pm_read_early(); + t2 = get_cycles_sync(); + if ((t2 - t1) < SMI_TRESHOLD) + return t2; + } + return ULONG_MAX; +} + +/** + * tsc_calibrate - calibrate the tsc on boot + */ +void __init tsc_calibrate(void) +{ + unsigned long flags, tsc1, tsc2, tr1, tr2, pm1, pm2, hpet1, hpet2; + int hpet = is_hpet_enabled(); + + local_irq_save(flags); + + tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL); + + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + outb(0xb0, 0x43); + outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42); + outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42); + tr1 = get_cycles_sync(); + while ((inb(0x61) & 0x20) == 0); + tr2 = get_cycles_sync(); + + tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL); + + local_irq_restore(flags); + + /* + * Preset the result with the raw and inaccurate PIT + * calibration value + */ + tsc_khz = (tr2 - tr1) / 50; + + /* hpet or pmtimer available ? */ + if (!hpet && !pm1 && !pm2) { + printk(KERN_INFO "TSC calibrated against PIT\n"); + return; + } + + /* Check, whether the sampling was disturbed by an SMI */ + if (tsc1 == ULONG_MAX || tsc2 == ULONG_MAX) { + printk(KERN_WARNING "TSC calibration disturbed by SMI, " + "using PIT calibration result\n"); + return; + } + + tsc2 = (tsc2 - tsc1) * 1000000L; + + if (hpet) { + printk(KERN_INFO "TSC calibrated against HPET\n"); + if (hpet2 < hpet1) + hpet2 += 0x100000000; + hpet2 -= hpet1; + tsc1 = (hpet2 * hpet_readl(HPET_PERIOD)) / 1000000; + } else { + printk(KERN_INFO "TSC calibrated against PM_TIMER\n"); + if (pm2 < pm1) + pm2 += ACPI_PM_OVRRUN; + pm2 -= pm1; + tsc1 = (pm2 * 1000000000) / PMTMR_TICKS_PER_SEC; + } + + tsc_khz = tsc2 / tsc1; +} + /* * Make an educated guess if the TSC is trustworthy and synchronized * over all CPUs. diff --git a/include/asm-x86/tsc.h b/include/asm-x86/tsc.h index a4d806610b7f..2002f8d6d20a 100644 --- a/include/asm-x86/tsc.h +++ b/include/asm-x86/tsc.h @@ -72,4 +72,8 @@ int check_tsc_unstable(void); extern void check_tsc_sync_source(int cpu); extern void check_tsc_sync_target(void); +#ifdef CONFIG_X86_64 +extern void tsc_calibrate(void); +#endif + #endif -- cgit v1.2.3 From 06a24dec10bc4014fc0974670627efed68f5da27 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: i386: prepare sharing the hpet code with x86_64 The hpet implementations of i386 and x8664 has been mostly the same before the clock events conversion of i386. The clock events conversion of i386 hpet is already done. So it makes sense to share the code for the x86_64 clock events conversion. Abstract out the mapping functions. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/hpet_32.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/hpet_32.c b/arch/x86/kernel/hpet_32.c index 533d4932bc79..748abf0286d2 100644 --- a/arch/x86/kernel/hpet_32.c +++ b/arch/x86/kernel/hpet_32.c @@ -8,10 +8,9 @@ #include #include +#include #include -extern struct clock_event_device *global_clock_event; - #define HPET_MASK CLOCKSOURCE_MASK(32) #define HPET_SHIFT 22 @@ -22,7 +21,7 @@ extern struct clock_event_device *global_clock_event; * HPET address is set in acpi/boot.c, when an ACPI entry exists */ unsigned long hpet_address; -static void __iomem * hpet_virt_address; +static void __iomem *hpet_virt_address; static inline unsigned long hpet_readl(unsigned long a) { @@ -34,6 +33,17 @@ static inline void hpet_writel(unsigned long d, unsigned long a) writel(d, hpet_virt_address + a); } +static inline void hpet_set_mapping(void) +{ + hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); +} + +static inline void hpet_clear_mapping(void) +{ + iounmap(hpet_virt_address); + hpet_virt_address = NULL; +} + /* * HPET command line enable / disable */ @@ -83,7 +93,7 @@ static void hpet_reserve_platform_timers(unsigned long id) memset(&hd, 0, sizeof (hd)); hd.hd_phys_address = hpet_address; - hd.hd_address = hpet_virt_address; + hd.hd_address = hpet; hd.hd_nirqs = nrtimers; hd.hd_flags = HPET_DATA_PLATFORM; hpet_reserve_timer(&hd, 0); @@ -238,7 +248,7 @@ int __init hpet_enable(void) if (!is_hpet_capable()) return 0; - hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); + hpet_set_mapping(); /* * Read the period and check for a sane value: @@ -334,13 +344,11 @@ int __init hpet_enable(void) return 0; out_nohpet: - iounmap(hpet_virt_address); - hpet_virt_address = NULL; + hpet_clear_mapping(); boot_hpet_disable = 1; return 0; } - #ifdef CONFIG_HPET_EMULATE_RTC /* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET -- cgit v1.2.3 From 28769149c285e0a392d2e601ae0cc71ffc345f7d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: i386: prepare sharing the hpet code with x86_64 Add the x8664 specific bits (mapping) to share the hpet code later. Move the reserve_platform_timer call to late init. This is necessary for x86_64, as hpet enable() is called before memory is setup. i386 calls it in late_time_init, but it does not hurt to do it later for both. Pull in the x8664 hpet disable command line option as well. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/hpet_32.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/hpet_32.c b/arch/x86/kernel/hpet_32.c index 748abf0286d2..dbe0e1d44113 100644 --- a/arch/x86/kernel/hpet_32.c +++ b/arch/x86/kernel/hpet_32.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,6 +8,7 @@ #include #include +#include #include #include #include @@ -23,6 +25,10 @@ unsigned long hpet_address; static void __iomem *hpet_virt_address; +/* Temporary hack. Cleanup after x86_64 clock events conversion */ +#undef hpet_readl +#undef hpet_writel + static inline unsigned long hpet_readl(unsigned long a) { return readl(hpet_virt_address + a); @@ -33,6 +39,24 @@ static inline void hpet_writel(unsigned long d, unsigned long a) writel(d, hpet_virt_address + a); } +#ifdef CONFIG_X86_64 + +#include + +static inline void hpet_set_mapping(void) +{ + set_fixmap_nocache(FIX_HPET_BASE, hpet_address); + __set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); + hpet_virt_address = (void __iomem *)fix_to_virt(FIX_HPET_BASE); +} + +static inline void hpet_clear_mapping(void) +{ + hpet_virt_address = NULL; +} + +#else + static inline void hpet_set_mapping(void) { hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); @@ -43,6 +67,7 @@ static inline void hpet_clear_mapping(void) iounmap(hpet_virt_address); hpet_virt_address = NULL; } +#endif /* * HPET command line enable / disable @@ -59,6 +84,13 @@ static int __init hpet_setup(char* str) } __setup("hpet=", hpet_setup); +static int __init disable_hpet(char *str) +{ + boot_hpet_disable = 1; + return 1; +} +__setup("nohpet", disable_hpet); + static inline int is_hpet_capable(void) { return (!boot_hpet_disable && hpet_address); @@ -225,6 +257,13 @@ static cycle_t read_hpet(void) return (cycle_t)hpet_readl(HPET_COUNTER); } +#ifdef CONFIG_X86_64 +static cycle_t __vsyscall_fn vread_hpet(void) +{ + return readl((const void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); +} +#endif + static struct clocksource clocksource_hpet = { .name = "hpet", .rating = 250, @@ -233,6 +272,9 @@ static struct clocksource clocksource_hpet = { .shift = HPET_SHIFT, .flags = CLOCK_SOURCE_IS_CONTINUOUS, .resume = hpet_start_counter, +#ifdef CONFIG_X86_64 + .vread = vread_hpet, +#endif }; /* @@ -331,7 +373,6 @@ int __init hpet_enable(void) if (id & HPET_ID_LEGSUP) { hpet_enable_int(); - hpet_reserve_platform_timers(id); /* * Start hpet with the boot cpu mask and make it * global after the IO_APIC has been initialized. @@ -349,6 +390,22 @@ out_nohpet: return 0; } +/* + * Needs to be late, as the reserve_timer code calls kalloc ! + * + * Not a problem on i386 as hpet_enable is called from late_time_init, + * but on x86_64 it is necessary ! + */ +static __init int hpet_late_init(void) +{ + if (!is_hpet_capable()) + return -ENODEV; + + hpet_reserve_platform_timers(hpet_readl(HPET_ID)); + return 0; +} +fs_initcall(hpet_late_init); + #ifdef CONFIG_HPET_EMULATE_RTC /* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET -- cgit v1.2.3 From f5e0e93faf8421083853b2d7a217267f49e27cc3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: i386: prepare sharing the PIT code PIT clock events work already and the PIT handling is the same for i386 and x86_64. x86_64 does not support PIT as a clock source, so disable the PIT clocksource for x86_64. Prepare i8253.h to be shared with x8664 Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/i8253_32.c | 4 +++- include/asm-x86/i8253_32.h | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/i8253_32.c b/arch/x86/kernel/i8253_32.c index 6d839f2f1b1a..ac15e4cbd9c1 100644 --- a/arch/x86/kernel/i8253_32.c +++ b/arch/x86/kernel/i8253_32.c @@ -13,7 +13,6 @@ #include #include #include -#include DEFINE_SPINLOCK(i8253_lock); EXPORT_SYMBOL(i8253_lock); @@ -120,6 +119,7 @@ void __init setup_pit_timer(void) global_clock_event = &pit_clockevent; } +#ifndef CONFIG_X86_64 /* * Since the PIT overflows every tick, its not very useful * to just read by itself. So use jiffies to emulate a free @@ -204,3 +204,5 @@ static int __init init_pit_clocksource(void) return clocksource_register(&clocksource_pit); } arch_initcall(init_pit_clocksource); + +#endif diff --git a/include/asm-x86/i8253_32.h b/include/asm-x86/i8253_32.h index 7577d058d86e..28cf67da59a3 100644 --- a/include/asm-x86/i8253_32.h +++ b/include/asm-x86/i8253_32.h @@ -1,8 +1,6 @@ #ifndef __ASM_I8253_H__ #define __ASM_I8253_H__ -#include - /* i8253A PIT registers */ #define PIT_MODE 0x43 #define PIT_CH0 0x40 @@ -10,8 +8,12 @@ extern spinlock_t i8253_lock; +#ifdef CONFIG_GENERIC_CLOCKEVENTS + extern struct clock_event_device *global_clock_event; extern void setup_pit_timer(void); +#endif + #endif /* __ASM_I8253_H__ */ -- cgit v1.2.3 From 0190dae54de62fbb9ced75d134015266987eb6b8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: i386: prepare sharing the PIT code PIT clock events work already and the PIT handling is the same for i386 and x86_64. x86_64 does not support PIT as a clock source, so disable the PIT clocksource for x86_64. Use the i386 i8253.h include file for x86_64 as well to share the exports and the PIT constants. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/time_64.c | 4 ---- include/asm-x86/i8253.h | 26 ++++++++++++++++++++++---- include/asm-x86/i8253_32.h | 19 ------------------- include/asm-x86/i8253_64.h | 6 ------ 4 files changed, 22 insertions(+), 33 deletions(-) delete mode 100644 include/asm-x86/i8253_32.h delete mode 100644 include/asm-x86/i8253_64.h diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index e97a3ee13551..d899216e01ca 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -32,7 +32,6 @@ #include /* for PM timer frequency */ #include #endif -#include #include #include #include @@ -292,9 +291,6 @@ static unsigned int __init tsc_calibrate_cpu_khz(void) return pmc_now * tsc_khz / (tsc_now - tsc_start); } -#define PIT_MODE 0x43 -#define PIT_CH0 0x40 - static void __pit_init(int val, u8 mode) { unsigned long flags; diff --git a/include/asm-x86/i8253.h b/include/asm-x86/i8253.h index b2a4f995a33f..7eff6fd27474 100644 --- a/include/asm-x86/i8253.h +++ b/include/asm-x86/i8253.h @@ -1,5 +1,23 @@ -#ifdef CONFIG_X86_32 -# include "i8253_32.h" -#else -# include "i8253_64.h" +#ifndef __ASM_I8253_H__ +#define __ASM_I8253_H__ + +#ifdef CONFIG_X86_64 +# include #endif + +/* i8253A PIT registers */ +#define PIT_MODE 0x43 +#define PIT_CH0 0x40 +#define PIT_CH2 0x42 + +extern spinlock_t i8253_lock; + +#ifdef CONFIG_GENERIC_CLOCKEVENTS + +extern struct clock_event_device *global_clock_event; + +extern void setup_pit_timer(void); + +#endif + +#endif /* __ASM_I8253_H__ */ diff --git a/include/asm-x86/i8253_32.h b/include/asm-x86/i8253_32.h deleted file mode 100644 index 28cf67da59a3..000000000000 --- a/include/asm-x86/i8253_32.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __ASM_I8253_H__ -#define __ASM_I8253_H__ - -/* i8253A PIT registers */ -#define PIT_MODE 0x43 -#define PIT_CH0 0x40 -#define PIT_CH2 0x42 - -extern spinlock_t i8253_lock; - -#ifdef CONFIG_GENERIC_CLOCKEVENTS - -extern struct clock_event_device *global_clock_event; - -extern void setup_pit_timer(void); - -#endif - -#endif /* __ASM_I8253_H__ */ diff --git a/include/asm-x86/i8253_64.h b/include/asm-x86/i8253_64.h deleted file mode 100644 index 015d8df07690..000000000000 --- a/include/asm-x86/i8253_64.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_I8253_H__ -#define __ASM_I8253_H__ - -extern spinlock_t i8253_lock; - -#endif /* __ASM_I8253_H__ */ -- cgit v1.2.3 From 7ffeeb1e03c4fc1c7e8434c5496018dd035f8924 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86: remove never used apic_mapped [ tglx: arch/x86 adaptation ] Signed-off-by: Yinghai Lu Signed-off-by: Andi Kleen Cc: Christoph Lameter Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/apic_64.c | 5 ++--- include/asm-x86/apic_64.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 925758dbca0c..48206bb8de1f 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -39,7 +39,6 @@ #include #include -int apic_mapped; int apic_verbosity; int apic_runs_main_timer; int apic_calibrate_pmtmr __initdata; @@ -706,8 +705,8 @@ void __init init_apic_mappings(void) apic_phys = mp_lapic_addr; set_fixmap_nocache(FIX_APIC_BASE, apic_phys); - apic_mapped = 1; - apic_printk(APIC_VERBOSE,"mapped APIC to %16lx (%16lx)\n", APIC_BASE, apic_phys); + apic_printk(APIC_VERBOSE, "mapped APIC to %16lx (%16lx)\n", + APIC_BASE, apic_phys); /* Put local APIC into the resource map. */ lapic_resource.start = apic_phys; diff --git a/include/asm-x86/apic_64.h b/include/asm-x86/apic_64.h index 85125ef3c414..81335e95365b 100644 --- a/include/asm-x86/apic_64.h +++ b/include/asm-x86/apic_64.h @@ -19,7 +19,6 @@ extern int apic_verbosity; extern int apic_runs_main_timer; extern int ioapic_force; -extern int apic_mapped; /* * Define the default level of output to be very little -- cgit v1.2.3 From 801740971af7c6256b7ad2472706d43ed008fd3d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: prepare apic code for clock events Change __setup_APIC_LVTT so it takes the arguments which are necessary for the later clock events switch. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 48206bb8de1f..ce2f8015d5ce 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -759,14 +759,14 @@ void __init init_apic_mappings(void) #define APIC_DIVISOR 16 -static void __setup_APIC_LVTT(unsigned int clocks) +static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) { unsigned int lvtt_value, tmp_value; - int cpu = smp_processor_id(); - - lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; - if (cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) + lvtt_value = LOCAL_TIMER_VECTOR; + if (!oneshot) + lvtt_value |= APIC_LVT_TIMER_PERIODIC; + if (!irqen) lvtt_value |= APIC_LVT_MASKED; apic_write(APIC_LVTT, lvtt_value); @@ -779,12 +779,14 @@ static void __setup_APIC_LVTT(unsigned int clocks) & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | APIC_TDR_DIV_16); - apic_write(APIC_TMICT, clocks/APIC_DIVISOR); + if (!oneshot) + apic_write(APIC_TMICT, clocks/APIC_DIVISOR); } static void setup_APIC_timer(unsigned int clocks) { unsigned long flags; + int irqen; local_irq_save(flags); @@ -805,7 +807,10 @@ static void setup_APIC_timer(unsigned int clocks) c2 |= inb_p(0x40) << 8; } while (c2 - c1 < 300); } - __setup_APIC_LVTT(clocks); + + irqen = ! cpu_isset(smp_processor_id(), + timer_interrupt_broadcast_ipi_mask); + __setup_APIC_LVTT(clocks, 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. */ @@ -843,8 +848,10 @@ static int __init calibrate_APIC_clock(void) * Put whatever arbitrary (but long enough) timeout * value into the APIC clock, we just want to get the * counter running for calibration. + * + * No interrupt enable ! */ - __setup_APIC_LVTT(4000000000); + __setup_APIC_LVTT(4000000000, 0, 0); apic_start = apic_read(APIC_TMCCT); #ifdef CONFIG_X86_PM_TIMER -- cgit v1.2.3 From 500ff08b76878a45391fffc7a4c972883503050e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: remove pit synchronization The APIC timer setup code synchronizes the local APIC timer to the PIT/HPET. This is pointless as the PIT and the local APIC timer frequency are not correlated and the APIC timer calibration can never be accurate enough to avoid that the local APIC timer and the PIT/HPET drift apart. Simply remove it. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index ce2f8015d5ce..a73b443a1236 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -790,24 +790,6 @@ static void setup_APIC_timer(unsigned int clocks) local_irq_save(flags); - /* wait for irq slice */ - if (hpet_address && hpet_use_timer) { - u32 trigger = hpet_readl(HPET_T0_CMP); - while (hpet_readl(HPET_T0_CMP) == trigger) - /* do nothing */ ; - } else { - int c1, c2; - outb_p(0x00, 0x43); - c2 = inb_p(0x40); - c2 |= inb_p(0x40) << 8; - do { - c1 = c2; - outb_p(0x00, 0x43); - c2 = inb_p(0x40); - c2 |= inb_p(0x40) << 8; - } while (c2 - c1 < 300); - } - irqen = ! cpu_isset(smp_processor_id(), timer_interrupt_broadcast_ipi_mask); __setup_APIC_LVTT(clocks, 0, irqen); -- cgit v1.2.3 From d03030e917047cf7f475e641cd2d6e83647392b0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: Move apic calibration code around Let the calibration code fill in calibration_result directly and move the variable on top of the file. Fixup a printk w/o log level while at it. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index a73b443a1236..b08c08ed36a7 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -55,6 +55,8 @@ static struct resource lapic_resource = { .flags = IORESOURCE_MEM | IORESOURCE_BUSY, }; +static unsigned int calibration_result; + /* * cpu_mask that denotes the CPUs that needs timer interrupt coming in as * IPIs in place of local APIC timers @@ -821,7 +823,7 @@ static void setup_APIC_timer(unsigned int clocks) #define TICK_COUNT 100000000 -static int __init calibrate_APIC_clock(void) +static void __init calibrate_APIC_clock(void) { unsigned apic, apic_start; unsigned long tsc, tsc_start; @@ -855,17 +857,14 @@ static int __init calibrate_APIC_clock(void) result = (apic_start - apic) * 1000L * tsc_khz / (tsc - tsc_start); } - printk("result %d\n", result); - + printk(KERN_DEBUG "APIC timer calibration result %d\n", result); printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", result / 1000 / 1000, result / 1000 % 1000); - return result * APIC_DIVISOR / HZ; + calibration_result = result * APIC_DIVISOR / HZ; } -static unsigned int calibration_result; - void __init setup_boot_APIC_clock (void) { if (disable_apic_timer) { @@ -878,7 +877,7 @@ void __init setup_boot_APIC_clock (void) local_irq_disable(); - calibration_result = calibrate_APIC_clock(); + calibrate_APIC_clock(); /* * Now set up the timer for real. */ @@ -985,8 +984,6 @@ void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector, apic_write(reg, v); } -#undef APIC_DIVISOR - /* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. -- cgit v1.2.3 From b58eb00df7f7f80b7f456bf8fb740fddf14408ba Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:06 +0200 Subject: x86_64: Remove APIC_DIVISOR APIC_DIVISOR is rather useless. It makes the calibration result more accurate in the first place, but we discard this later when we write the value to the APIC timer by dividing the calibration value by APIC_DIVISOR. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index b08c08ed36a7..f746f9dc8ced 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -759,8 +759,6 @@ void __init init_apic_mappings(void) * P5 APIC double write bug. */ -#define APIC_DIVISOR 16 - static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) { unsigned int lvtt_value, tmp_value; @@ -782,7 +780,7 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) | APIC_TDR_DIV_16); if (!oneshot) - apic_write(APIC_TMICT, clocks/APIC_DIVISOR); + apic_write(APIC_TMICT, clocks); } static void setup_APIC_timer(unsigned int clocks) @@ -835,7 +833,7 @@ static void __init calibrate_APIC_clock(void) * * No interrupt enable ! */ - __setup_APIC_LVTT(4000000000, 0, 0); + __setup_APIC_LVTT(250000000, 0, 0); apic_start = apic_read(APIC_TMCCT); #ifdef CONFIG_X86_PM_TIMER @@ -862,7 +860,7 @@ static void __init calibrate_APIC_clock(void) printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", result / 1000 / 1000, result / 1000 % 1000); - calibration_result = result * APIC_DIVISOR / HZ; + calibration_result = result / HZ; } void __init setup_boot_APIC_clock (void) -- cgit v1.2.3 From abc63fcd3cc61876b2d6f5b0d08021bd1538385c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86_64: apic change setup_APIC_timer calling convention setup_APIC_timer takes the file global calibration result as an argument. Remove it. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index f746f9dc8ced..f74b1c20e5c0 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -783,7 +783,7 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) apic_write(APIC_TMICT, clocks); } -static void setup_APIC_timer(unsigned int clocks) +static void setup_APIC_timer(void) { unsigned long flags; int irqen; @@ -792,7 +792,7 @@ static void setup_APIC_timer(unsigned int clocks) irqen = ! cpu_isset(smp_processor_id(), timer_interrupt_broadcast_ipi_mask); - __setup_APIC_LVTT(clocks, 0, irqen); + __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. */ @@ -879,7 +879,7 @@ void __init setup_boot_APIC_clock (void) /* * Now set up the timer for real. */ - setup_APIC_timer(calibration_result); + setup_APIC_timer(); local_irq_enable(); } @@ -887,7 +887,7 @@ void __init setup_boot_APIC_clock (void) void __cpuinit setup_secondary_APIC_clock(void) { local_irq_disable(); /* FIXME: Do we need this? --RR */ - setup_APIC_timer(calibration_result); + setup_APIC_timer(); local_irq_enable(); } -- cgit v1.2.3 From c4d58cbd158dc9b30c55c0e3881ae7c6b8843d5a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86_64: remove nested irq disables setup_APIC_timer disables interrupts anyway. So no need to do the same in setup_boot_APIC_clock and setup_secondary_APIC_clock. Disable interrupts explicit in the calibration code. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index f74b1c20e5c0..40938ef99dc2 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -826,6 +826,9 @@ static void __init calibrate_APIC_clock(void) unsigned apic, apic_start; unsigned long tsc, tsc_start; int result; + + local_irq_disable(); + /* * Put whatever arbitrary (but long enough) timeout * value into the APIC clock, we just want to get the @@ -855,6 +858,9 @@ static void __init calibrate_APIC_clock(void) result = (apic_start - apic) * 1000L * tsc_khz / (tsc - tsc_start); } + + local_irq_enable(); + printk(KERN_DEBUG "APIC timer calibration result %d\n", result); printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", @@ -873,22 +879,16 @@ void __init setup_boot_APIC_clock (void) printk(KERN_INFO "Using local APIC timer interrupts.\n"); using_apic_timer = 1; - local_irq_disable(); - calibrate_APIC_clock(); /* * Now set up the timer for real. */ setup_APIC_timer(); - - local_irq_enable(); } void __cpuinit setup_secondary_APIC_clock(void) { - local_irq_disable(); /* FIXME: Do we need this? --RR */ setup_APIC_timer(); - local_irq_enable(); } void disable_APIC_timer(void) -- cgit v1.2.3 From 02290683343391a50f45599710295dafa2ddd018 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86_64: prepare idle loop for dynamic ticks Add tick_nohz_{stop,restart}_sched_tick to idle loop in prepartion for turning on dynticks. These are just noops until NO_HZ is enabled. Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Arjan van de Ven --- arch/x86/kernel/process_64.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 98956555450b..6f9dbbe65eef 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -208,6 +209,8 @@ void cpu_idle (void) if (__get_cpu_var(cpu_idle_state)) __get_cpu_var(cpu_idle_state) = 0; + tick_nohz_stop_sched_tick(); + rmb(); idle = pm_idle; if (!idle) @@ -228,6 +231,7 @@ void cpu_idle (void) __exit_idle(); } + tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); schedule(); preempt_disable(); -- cgit v1.2.3 From ba7eda4c60e1d070b2f6586d42719ec1d5302d3b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86_64: Add (not yet used) clock event functions Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++ arch/x86_64/Kconfig | 6 ++++ 2 files changed, 85 insertions(+) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 40938ef99dc2..2c2807abe1d4 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,77 @@ static struct resource lapic_resource = { static unsigned int calibration_result; +static int lapic_next_event(unsigned long delta, + struct clock_event_device *evt); +static void lapic_timer_setup(enum clock_event_mode mode, + struct clock_event_device *evt); + +static void lapic_timer_broadcast(cpumask_t mask); + +static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen); + +static struct clock_event_device lapic_clockevent = { + .name = "lapic", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT + | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, + .shift = 32, + .set_mode = lapic_timer_setup, + .set_next_event = lapic_next_event, + .broadcast = lapic_timer_broadcast, + .rating = 100, + .irq = -1, +}; +static DEFINE_PER_CPU(struct clock_event_device, lapic_events); + +static int lapic_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + apic_write(APIC_TMICT, delta); + return 0; +} + +static void lapic_timer_setup(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + unsigned long flags; + unsigned int v; + + /* Lapic used as dummy for broadcast ? */ + if (evt->features & CLOCK_EVT_FEAT_DUMMY) + return; + + local_irq_save(flags); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_ONESHOT: + __setup_APIC_LVTT(calibration_result, + mode != CLOCK_EVT_MODE_PERIODIC, 1); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + v = apic_read(APIC_LVTT); + v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write(APIC_LVTT, v); + break; + case CLOCK_EVT_MODE_RESUME: + /* Nothing to do here */ + break; + } + + local_irq_restore(flags); +} + +/* + * Local APIC timer broadcast function + */ +static void lapic_timer_broadcast(cpumask_t mask) +{ +#ifdef CONFIG_SMP + send_IPI_mask(mask, LOCAL_TIMER_VECTOR); +#endif +} + /* * cpu_mask that denotes the CPUs that needs timer interrupt coming in as * IPIs in place of local APIC timers @@ -866,6 +938,13 @@ static void __init calibrate_APIC_clock(void) printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", result / 1000 / 1000, result / 1000 % 1000); + /* Calculate the scaled math multiplication factor */ + lapic_clockevent.mult = div_sc(result, NSEC_PER_SEC, 32); + lapic_clockevent.max_delta_ns = + clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); + lapic_clockevent.min_delta_ns = + clockevent_delta2ns(0xF, &lapic_clockevent); + calibration_result = result / HZ; } diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 520f33b83bfc..a9534c21c306 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -40,6 +40,10 @@ config CLOCKSOURCE_WATCHDOG bool default y +config GENERIC_CLOCKEVENTS_MIGR + bool + default y + config ZONE_DMA32 bool default y @@ -134,6 +138,8 @@ source "init/Kconfig" menu "Processor type and features" +source "kernel/time/Kconfig" + choice prompt "Subarchitecture Type" default X86_PC -- cgit v1.2.3 From b8ce33590687888ebb900d09557b8807c4539022 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86_64: convert to clock events Finally switch to the clockevents code. Share code with i386 for hpet and PIT. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/Makefile_64 | 4 +- arch/x86/kernel/apic_64.c | 90 ++++++++++++++++++++---------------- arch/x86/kernel/i8259_64.c | 46 ------------------- arch/x86/kernel/smpboot_64.c | 4 -- arch/x86/kernel/time_64.c | 107 ++++++------------------------------------- arch/x86_64/Kconfig | 6 ++- include/asm-x86/hpet.h | 92 +++++++++++++++++++++++++++++++++++-- include/asm-x86/hpet_32.h | 90 ------------------------------------ include/asm-x86/hpet_64.h | 18 -------- 9 files changed, 158 insertions(+), 299 deletions(-) delete mode 100644 include/asm-x86/hpet_32.h delete mode 100644 include/asm-x86/hpet_64.h diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64 index 3ab017a0a3b9..080154e31502 100644 --- a/arch/x86/kernel/Makefile_64 +++ b/arch/x86/kernel/Makefile_64 @@ -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 diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 2c2807abe1d4..b0237caff712 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -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); diff --git a/arch/x86/kernel/i8259_64.c b/arch/x86/kernel/i8259_64.c index 948cae646099..eb72976cc13c 100644 --- a/arch/x86/kernel/i8259_64.c +++ b/arch/x86/kernel/i8259_64.c @@ -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); } diff --git a/arch/x86/kernel/smpboot_64.c b/arch/x86/kernel/smpboot_64.c index 32f50783edc8..57ccf7cb6b91 100644 --- a/arch/x86/kernel/smpboot_64.c +++ b/arch/x86/kernel/smpboot_64.c @@ -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 diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index d899216e01ca..7781df1d50e3 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -28,6 +28,8 @@ #include #include #include +#include + #ifdef CONFIG_ACPI #include /* for PM timer frequency */ #include @@ -46,12 +48,8 @@ #include #include -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); diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index a9534c21c306..eb80f5aca54e 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -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 diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h index 9eff48601254..e2976ed30b70 100644 --- a/include/asm-x86/hpet.h +++ b/include/asm-x86/hpet.h @@ -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 + +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 index c82dc7ed96b3..000000000000 --- a/include/asm-x86/hpet_32.h +++ /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 - -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 index fd4decac93a8..000000000000 --- a/include/asm-x86/hpet_64.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _ASM_X8664_HPET_H -#define _ASM_X8664_HPET_H 1 - -#include - -#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 -- cgit v1.2.3 From 4e77ae3e105d28aa9410585715d83818f0abe871 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86: Fix irq0 / local apic timer accounting The clock events merge introduced a change to the nmi watchdog code to handle the not longer increasing local apic timer count in the broadcast mode. This is fine for UP, but on SMP it pampers over a stuck CPU which is not handling the broadcast interrupt due to the unconditional sum up of local apic timer count and irq0 count. To cover all cases we need to keep track on which CPU irq0 is handled. In theory this is CPU#0 due to the explicit disabling of irq balancing for irq0, but there are systems which ignore this on the hardware level. The per cpu irq0 accounting allows us to remove the irq0 to CPU0 binding as well. Add a per cpu counter for irq0 and evaluate this instead of the global irq0 count in the nmi watchdog code. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/nmi_64.c | 2 +- arch/x86/kernel/time_64.c | 2 ++ include/asm-x86/pda.h | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/nmi_64.c b/arch/x86/kernel/nmi_64.c index 0ec6d2ddb931..e60ac0da5283 100644 --- a/arch/x86/kernel/nmi_64.c +++ b/arch/x86/kernel/nmi_64.c @@ -329,7 +329,7 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) touched = 1; } - sum = read_pda(apic_timer_irqs); + sum = read_pda(apic_timer_irqs) + read_pda(irq0_irqs); if (__get_cpu_var(nmi_touch)) { __get_cpu_var(nmi_touch) = 0; touched = 1; diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 7781df1d50e3..16f58886e8dc 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -194,6 +194,8 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) static irqreturn_t timer_event_interrupt(int irq, void *dev_id) { + add_pda(irq0_irqs, 1); + global_clock_event->event_handler(global_clock_event); return IRQ_HANDLED; diff --git a/include/asm-x86/pda.h b/include/asm-x86/pda.h index 5642634843c4..fb49f80eb94f 100644 --- a/include/asm-x86/pda.h +++ b/include/asm-x86/pda.h @@ -29,6 +29,7 @@ struct x8664_pda { short isidle; struct mm_struct *active_mm; unsigned apic_timer_irqs; + unsigned irq0_irqs; } ____cacheline_aligned_in_smp; extern struct x8664_pda *_cpu_pda[]; -- cgit v1.2.3 From fb79d22e1d4b06385796cc0db0084a2e07beccee Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:07 +0200 Subject: x86: disable apic timer for AMD C1E enabled CPUs AMDs C1E enabled CPUs stop the local apic timer, when both cores are idle. This is a hardware feature which breaks highres/dynticks. Add the same quirk as we have for 32 bit already. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/apic_64.c | 2 +- arch/x86/kernel/setup_64.c | 34 ++++++++++++++++++++++++++++++++++ include/asm-x86/apic_64.h | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index b0237caff712..118830cba1ae 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -44,7 +44,7 @@ int apic_verbosity; int apic_runs_main_timer; int apic_calibrate_pmtmr __initdata; -int disable_apic_timer __initdata; +int disable_apic_timer __cpuinitdata; /* Local APIC timer works in C2? */ int local_apic_timer_c2_ok; diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index af838f6b0b7f..32054bf5ba40 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -546,6 +546,37 @@ static void __init amd_detect_cmp(struct cpuinfo_x86 *c) #endif } +#define ENABLE_C1E_MASK 0x18000000 +#define CPUID_PROCESSOR_SIGNATURE 1 +#define CPUID_XFAM 0x0ff00000 +#define CPUID_XFAM_K8 0x00000000 +#define CPUID_XFAM_10H 0x00100000 +#define CPUID_XFAM_11H 0x00200000 +#define CPUID_XMOD 0x000f0000 +#define CPUID_XMOD_REV_F 0x00040000 + +/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */ +static __cpuinit int amd_apic_timer_broken(void) +{ + u32 lo, hi; + u32 eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE); + switch (eax & CPUID_XFAM) { + case CPUID_XFAM_K8: + if ((eax & CPUID_XMOD) < CPUID_XMOD_REV_F) + break; + case CPUID_XFAM_10H: + case CPUID_XFAM_11H: + rdmsr(MSR_K8_ENABLE_C1E, lo, hi); + if (lo & ENABLE_C1E_MASK) + return 1; + break; + default: + /* err on the side of caution */ + return 1; + } + return 0; +} + static void __cpuinit init_amd(struct cpuinfo_x86 *c) { unsigned level; @@ -617,6 +648,9 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) /* Family 10 doesn't support C states in MWAIT so don't use it */ if (c->x86 == 0x10 && !force_mwait) clear_bit(X86_FEATURE_MWAIT, &c->x86_capability); + + if (amd_apic_timer_broken()) + disable_apic_timer = 1; } static void __cpuinit detect_ht(struct cpuinfo_x86 *c) diff --git a/include/asm-x86/apic_64.h b/include/asm-x86/apic_64.h index 81335e95365b..f32f654d1f96 100644 --- a/include/asm-x86/apic_64.h +++ b/include/asm-x86/apic_64.h @@ -19,6 +19,7 @@ extern int apic_verbosity; extern int apic_runs_main_timer; extern int ioapic_force; +extern int disable_apic_timer; /* * Define the default level of output to be very little -- cgit v1.2.3 From 5d5a2989b72847e1f5763420ea31383ca63ebf53 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86: kill 8253pit.h Useless header file with 32 bit and 64 bit variants. Move the single useful line to the place where it is used. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/i8259_32.c | 1 - drivers/input/misc/pcspkr.c | 2 +- include/asm-x86/8253pit.h | 5 ----- include/asm-x86/8253pit_32.h | 12 ------------ include/asm-x86/8253pit_64.h | 10 ---------- include/asm-x86/i8253.h | 4 ---- include/asm-x86/timex_32.h | 6 +++--- include/asm-x86/timex_64.h | 2 +- 8 files changed, 5 insertions(+), 37 deletions(-) delete mode 100644 include/asm-x86/8253pit.h delete mode 100644 include/asm-x86/8253pit_32.h delete mode 100644 include/asm-x86/8253pit_64.h diff --git a/arch/x86/kernel/i8259_32.c b/arch/x86/kernel/i8259_32.c index 0499cbe9871a..679bb33acbf1 100644 --- a/arch/x86/kernel/i8259_32.c +++ b/arch/x86/kernel/i8259_32.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 906bf5e8de89..4e2ca6f0ab18 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -17,7 +17,6 @@ #include #include #include -#include #include MODULE_AUTHOR("Vojtech Pavlik "); @@ -28,6 +27,7 @@ MODULE_LICENSE("GPL"); /* Use the global PIT lock ! */ #include #else +#include static DEFINE_SPINLOCK(i8253_lock); #endif diff --git a/include/asm-x86/8253pit.h b/include/asm-x86/8253pit.h deleted file mode 100644 index d3c2b38a6618..000000000000 --- a/include/asm-x86/8253pit.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifdef CONFIG_X86_32 -# include "8253pit_32.h" -#else -# include "8253pit_64.h" -#endif diff --git a/include/asm-x86/8253pit_32.h b/include/asm-x86/8253pit_32.h deleted file mode 100644 index 96c7c3592daf..000000000000 --- a/include/asm-x86/8253pit_32.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 8253/8254 Programmable Interval Timer - */ - -#ifndef _8253PIT_H -#define _8253PIT_H - -#include - -#define PIT_TICK_RATE CLOCK_TICK_RATE - -#endif diff --git a/include/asm-x86/8253pit_64.h b/include/asm-x86/8253pit_64.h deleted file mode 100644 index 285f78488ccb..000000000000 --- a/include/asm-x86/8253pit_64.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * 8253/8254 Programmable Interval Timer - */ - -#ifndef _8253PIT_H -#define _8253PIT_H - -#define PIT_TICK_RATE 1193182UL - -#endif diff --git a/include/asm-x86/i8253.h b/include/asm-x86/i8253.h index 7eff6fd27474..28cf67da59a3 100644 --- a/include/asm-x86/i8253.h +++ b/include/asm-x86/i8253.h @@ -1,10 +1,6 @@ #ifndef __ASM_I8253_H__ #define __ASM_I8253_H__ -#ifdef CONFIG_X86_64 -# include -#endif - /* i8253A PIT registers */ #define PIT_MODE 0x43 #define PIT_CH0 0x40 diff --git a/include/asm-x86/timex_32.h b/include/asm-x86/timex_32.h index 3666044409f0..fa8839465bdb 100644 --- a/include/asm-x86/timex_32.h +++ b/include/asm-x86/timex_32.h @@ -10,11 +10,11 @@ #include #ifdef CONFIG_X86_ELAN -# define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ +# define PIT_TICK_RATE 1189200 /* AMD Elan has different frequency! */ #else -# define CLOCK_TICK_RATE 1193182 /* Underlying HZ */ +# define PIT_TICK_RATE 1193182 /* Underlying HZ */ #endif - +#define CLOCK_TICK_RATE PIT_TICK_RATE extern int read_current_timer(unsigned long *timer_value); #define ARCH_HAS_READ_CURRENT_TIMER 1 diff --git a/include/asm-x86/timex_64.h b/include/asm-x86/timex_64.h index 6ed21f44d308..901bf89ce643 100644 --- a/include/asm-x86/timex_64.h +++ b/include/asm-x86/timex_64.h @@ -6,7 +6,6 @@ #ifndef _ASMx8664_TIMEX_H #define _ASMx8664_TIMEX_H -#include #include #include #include @@ -14,6 +13,7 @@ #include #include +#define PIT_TICK_RATE 1193182UL #define CLOCK_TICK_RATE PIT_TICK_RATE /* Underlying HZ */ extern int read_current_timer(unsigned long *timer_value); -- cgit v1.2.3 From 2f0798a3b1c2155b8f30858e853557aef9da2e4e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86: unify timex.h variants Combine the timex.h variants and move the TSC related code into tsc.h. Move the set_cyc2ns_scale() call into the tsc calibraction code, where it belongs. Signed-off-by: Thomas Gleixner Signed-off-by: Arjan van de Ven --- arch/x86/kernel/time_64.c | 1 - arch/x86/kernel/tsc_64.c | 3 ++- include/asm-x86/timex.h | 19 ++++++++++++++++--- include/asm-x86/timex_32.h | 22 ---------------------- include/asm-x86/timex_64.h | 31 ------------------------------- include/asm-x86/tsc.h | 11 ++++++----- 6 files changed, 24 insertions(+), 63 deletions(-) delete mode 100644 include/asm-x86/timex_32.h delete mode 100644 include/asm-x86/timex_64.h diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 16f58886e8dc..aca081c37884 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -328,7 +328,6 @@ void __init time_init(void) else vgetcpu_mode = VGETCPU_LSL; - set_cyc2ns_scale(tsc_khz); printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); init_tsc_clocksource(); diff --git a/arch/x86/kernel/tsc_64.c b/arch/x86/kernel/tsc_64.c index 59baecd135ab..9f22e542c374 100644 --- a/arch/x86/kernel/tsc_64.c +++ b/arch/x86/kernel/tsc_64.c @@ -20,7 +20,7 @@ EXPORT_SYMBOL(tsc_khz); static unsigned int cyc2ns_scale __read_mostly; -void set_cyc2ns_scale(unsigned long khz) +static inline void set_cyc2ns_scale(unsigned long khz) { cyc2ns_scale = (NSEC_PER_MSEC << NS_SCALE) / khz; } @@ -206,6 +206,7 @@ void __init tsc_calibrate(void) } tsc_khz = tsc2 / tsc1; + set_cyc2ns_scale(tsc_khz); } /* diff --git a/include/asm-x86/timex.h b/include/asm-x86/timex.h index d01c18cfccef..39a21ab030f0 100644 --- a/include/asm-x86/timex.h +++ b/include/asm-x86/timex.h @@ -1,5 +1,18 @@ -#ifdef CONFIG_X86_32 -# include "timex_32.h" +/* x86 architecture timex specifications */ +#ifndef _ASM_X86_TIMEX_H +#define _ASM_X86_TIMEX_H + +#include +#include + +#ifdef CONFIG_X86_ELAN +# define PIT_TICK_RATE 1189200 /* AMD Elan has different frequency! */ #else -# include "timex_64.h" +# define PIT_TICK_RATE 1193182 /* Underlying HZ */ +#endif +#define CLOCK_TICK_RATE PIT_TICK_RATE + +extern int read_current_timer(unsigned long *timer_value); +#define ARCH_HAS_READ_CURRENT_TIMER 1 + #endif diff --git a/include/asm-x86/timex_32.h b/include/asm-x86/timex_32.h deleted file mode 100644 index fa8839465bdb..000000000000 --- a/include/asm-x86/timex_32.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * linux/include/asm-i386/timex.h - * - * i386 architecture timex specifications - */ -#ifndef _ASMi386_TIMEX_H -#define _ASMi386_TIMEX_H - -#include -#include - -#ifdef CONFIG_X86_ELAN -# define PIT_TICK_RATE 1189200 /* AMD Elan has different frequency! */ -#else -# define PIT_TICK_RATE 1193182 /* Underlying HZ */ -#endif -#define CLOCK_TICK_RATE PIT_TICK_RATE - -extern int read_current_timer(unsigned long *timer_value); -#define ARCH_HAS_READ_CURRENT_TIMER 1 - -#endif diff --git a/include/asm-x86/timex_64.h b/include/asm-x86/timex_64.h deleted file mode 100644 index 901bf89ce643..000000000000 --- a/include/asm-x86/timex_64.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * linux/include/asm-x86_64/timex.h - * - * x86-64 architecture timex specifications - */ -#ifndef _ASMx8664_TIMEX_H -#define _ASMx8664_TIMEX_H - -#include -#include -#include -#include -#include -#include - -#define PIT_TICK_RATE 1193182UL -#define CLOCK_TICK_RATE PIT_TICK_RATE /* Underlying HZ */ - -extern int read_current_timer(unsigned long *timer_value); -#define ARCH_HAS_READ_CURRENT_TIMER 1 - -#define USEC_PER_TICK (USEC_PER_SEC / HZ) -#define NSEC_PER_TICK (NSEC_PER_SEC / HZ) -#define FSEC_PER_TICK (FSEC_PER_SEC / HZ) - -#define NS_SCALE 10 /* 2^10, carefully chosen */ -#define US_SCALE 32 /* 2^32, arbitralrily chosen */ - -extern void mark_tsc_unstable(char *msg); -extern void set_cyc2ns_scale(unsigned long khz); -#endif diff --git a/include/asm-x86/tsc.h b/include/asm-x86/tsc.h index 2002f8d6d20a..6baab30dc2c8 100644 --- a/include/asm-x86/tsc.h +++ b/include/asm-x86/tsc.h @@ -1,13 +1,14 @@ /* - * linux/include/asm-i386/tsc.h - * - * i386 TSC related functions + * x86 TSC related functions */ -#ifndef _ASM_i386_TSC_H -#define _ASM_i386_TSC_H +#ifndef _ASM_X86_TSC_H +#define _ASM_X86_TSC_H #include +#define NS_SCALE 10 /* 2^10, carefully chosen */ +#define US_SCALE 32 /* 2^32, arbitralrily chosen */ + /* * Standard way to access the cycle counter. */ -- cgit v1.2.3 From 9f75e9b74a45d7d3c343c8979f49d5e6b92bbce3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86_64: remove now unused code Remove the unused code after the switch to clock events. Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- arch/x86/kernel/Makefile_32 | 4 +- arch/x86/kernel/Makefile_64 | 4 +- arch/x86/kernel/apic_64.c | 106 +------- arch/x86/kernel/hpet.c | 618 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/hpet_32.c | 618 -------------------------------------------- arch/x86/kernel/hpet_64.c | 444 ------------------------------- arch/x86/kernel/i8253.c | 208 +++++++++++++++ arch/x86/kernel/i8253_32.c | 208 --------------- arch/x86/kernel/time_64.c | 42 --- include/asm-x86/apic_64.h | 6 - include/asm-x86/proto.h | 7 - 11 files changed, 831 insertions(+), 1434 deletions(-) create mode 100644 arch/x86/kernel/hpet.c delete mode 100644 arch/x86/kernel/hpet_32.c delete mode 100644 arch/x86/kernel/hpet_64.c create mode 100644 arch/x86/kernel/i8253.c delete mode 100644 arch/x86/kernel/i8253_32.c diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32 index 10356443998e..7ff02063b858 100644 --- a/arch/x86/kernel/Makefile_32 +++ b/arch/x86/kernel/Makefile_32 @@ -7,7 +7,7 @@ extra-y := head_32.o init_task_32.o vmlinux.lds obj-y := process_32.o signal_32.o entry_32.o traps_32.o irq_32.o \ ptrace_32.o time_32.o ioport_32.o ldt_32.o setup_32.o i8259_32.o sys_i386_32.o \ pci-dma_32.o i386_ksyms_32.o i387_32.o bootflag.o e820_32.o\ - quirks.o i8237.o topology.o alternative.o i8253_32.o tsc_32.o + quirks.o i8237.o topology.o alternative.o i8253.o tsc_32.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += cpu/ @@ -37,7 +37,7 @@ obj-$(CONFIG_EFI) += efi_32.o efi_stub_32.o obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o obj-$(CONFIG_VM86) += vm86_32.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_HPET_TIMER) += hpet_32.o +obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_MGEODE_LX) += geode_32.o mfgpt_32.o diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64 index 080154e31502..43da66213a47 100644 --- a/arch/x86/kernel/Makefile_64 +++ b/arch/x86/kernel/Makefile_64 @@ -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_32.o tsc_64.o bugs_64.o \ - perfctr-watchdog.o i8253_32.o + pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \ + perfctr-watchdog.o i8253.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_X86_MCE) += mce_64.o therm_throt.o diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 118830cba1ae..1231365404c4 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -41,7 +41,6 @@ #include int apic_verbosity; -int apic_runs_main_timer; int apic_calibrate_pmtmr __initdata; int disable_apic_timer __cpuinitdata; @@ -129,15 +128,6 @@ static void lapic_timer_broadcast(cpumask_t mask) #endif } -/* - * cpu_mask that denotes the CPUs that needs timer interrupt coming in as - * IPIs in place of local APIC timers - */ -static cpumask_t timer_interrupt_broadcast_ipi_mask; - -/* Using APIC to generate smp_local_timer_interrupt? */ -int using_apic_timer __read_mostly = 0; - static void apic_pm_activate(void); void apic_wait_icr_idle(void) @@ -973,84 +963,6 @@ void __cpuinit setup_secondary_APIC_clock(void) setup_APIC_timer(); } -void disable_APIC_timer(void) -{ - if (using_apic_timer) { - unsigned long v; - - v = apic_read(APIC_LVTT); - /* - * When an illegal vector value (0-15) is written to an LVT - * entry and delivery mode is Fixed, the APIC may signal an - * illegal vector error, with out regard to whether the mask - * bit is set or whether an interrupt is actually seen on input. - * - * Boot sequence might call this function when the LVTT has - * '0' vector value. So make sure vector field is set to - * valid value. - */ - v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); - apic_write(APIC_LVTT, v); - } -} - -void enable_APIC_timer(void) -{ - int cpu = smp_processor_id(); - - if (using_apic_timer && - !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { - unsigned long v; - - v = apic_read(APIC_LVTT); - apic_write(APIC_LVTT, v & ~APIC_LVT_MASKED); - } -} - -void switch_APIC_timer_to_ipi(void *cpumask) -{ - cpumask_t mask = *(cpumask_t *)cpumask; - int cpu = smp_processor_id(); - - if (cpu_isset(cpu, mask) && - !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { - disable_APIC_timer(); - cpu_set(cpu, timer_interrupt_broadcast_ipi_mask); - } -} -EXPORT_SYMBOL(switch_APIC_timer_to_ipi); - -void smp_send_timer_broadcast_ipi(void) -{ - int cpu = smp_processor_id(); - cpumask_t mask; - - cpus_and(mask, cpu_online_map, timer_interrupt_broadcast_ipi_mask); - - if (cpu_isset(cpu, mask)) { - cpu_clear(cpu, mask); - add_pda(apic_timer_irqs, 1); - smp_local_timer_interrupt(); - } - - if (!cpus_empty(mask)) { - send_IPI_mask(mask, LOCAL_TIMER_VECTOR); - } -} - -void switch_ipi_to_APIC_timer(void *cpumask) -{ - cpumask_t mask = *(cpumask_t *)cpumask; - int cpu = smp_processor_id(); - - if (cpu_isset(cpu, mask) && - cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { - cpu_clear(cpu, timer_interrupt_broadcast_ipi_mask); - enable_APIC_timer(); - } -} -EXPORT_SYMBOL(switch_ipi_to_APIC_timer); - int setup_profiling_timer(unsigned int multiplier) { return -EINVAL; @@ -1297,21 +1209,7 @@ static __init int setup_noapictimer(char *str) disable_apic_timer = 1; return 1; } - -static __init int setup_apicmaintimer(char *str) -{ - apic_runs_main_timer = 1; - - return 1; -} -__setup("apicmaintimer", setup_apicmaintimer); - -static __init int setup_noapicmaintimer(char *str) -{ - apic_runs_main_timer = -1; - return 1; -} -__setup("noapicmaintimer", setup_noapicmaintimer); +__setup("noapictimer", setup_noapictimer); static __init int setup_apicpmtimer(char *s) { @@ -1321,5 +1219,3 @@ static __init int setup_apicpmtimer(char *s) } __setup("apicpmtimer", setup_apicpmtimer); -__setup("noapictimer", setup_noapictimer); - diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c new file mode 100644 index 000000000000..dbe0e1d44113 --- /dev/null +++ b/arch/x86/kernel/hpet.c @@ -0,0 +1,618 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define HPET_MASK CLOCKSOURCE_MASK(32) +#define HPET_SHIFT 22 + +/* FSEC = 10^-15 NSEC = 10^-9 */ +#define FSEC_PER_NSEC 1000000 + +/* + * HPET address is set in acpi/boot.c, when an ACPI entry exists + */ +unsigned long hpet_address; +static void __iomem *hpet_virt_address; + +/* Temporary hack. Cleanup after x86_64 clock events conversion */ +#undef hpet_readl +#undef hpet_writel + +static inline unsigned long hpet_readl(unsigned long a) +{ + return readl(hpet_virt_address + a); +} + +static inline void hpet_writel(unsigned long d, unsigned long a) +{ + writel(d, hpet_virt_address + a); +} + +#ifdef CONFIG_X86_64 + +#include + +static inline void hpet_set_mapping(void) +{ + set_fixmap_nocache(FIX_HPET_BASE, hpet_address); + __set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); + hpet_virt_address = (void __iomem *)fix_to_virt(FIX_HPET_BASE); +} + +static inline void hpet_clear_mapping(void) +{ + hpet_virt_address = NULL; +} + +#else + +static inline void hpet_set_mapping(void) +{ + hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); +} + +static inline void hpet_clear_mapping(void) +{ + iounmap(hpet_virt_address); + hpet_virt_address = NULL; +} +#endif + +/* + * HPET command line enable / disable + */ +static int boot_hpet_disable; + +static int __init hpet_setup(char* str) +{ + if (str) { + if (!strncmp("disable", str, 7)) + boot_hpet_disable = 1; + } + return 1; +} +__setup("hpet=", hpet_setup); + +static int __init disable_hpet(char *str) +{ + boot_hpet_disable = 1; + return 1; +} +__setup("nohpet", disable_hpet); + +static inline int is_hpet_capable(void) +{ + return (!boot_hpet_disable && hpet_address); +} + +/* + * HPET timer interrupt enable / disable + */ +static int hpet_legacy_int_enabled; + +/** + * is_hpet_enabled - check whether the hpet timer interrupt is enabled + */ +int is_hpet_enabled(void) +{ + return is_hpet_capable() && hpet_legacy_int_enabled; +} + +/* + * When the hpet driver (/dev/hpet) is enabled, we need to reserve + * timer 0 and timer 1 in case of RTC emulation. + */ +#ifdef CONFIG_HPET +static void hpet_reserve_platform_timers(unsigned long id) +{ + struct hpet __iomem *hpet = hpet_virt_address; + struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; + unsigned int nrtimers, i; + struct hpet_data hd; + + nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; + + memset(&hd, 0, sizeof (hd)); + hd.hd_phys_address = hpet_address; + hd.hd_address = hpet; + hd.hd_nirqs = nrtimers; + hd.hd_flags = HPET_DATA_PLATFORM; + hpet_reserve_timer(&hd, 0); + +#ifdef CONFIG_HPET_EMULATE_RTC + hpet_reserve_timer(&hd, 1); +#endif + + hd.hd_irq[0] = HPET_LEGACY_8254; + hd.hd_irq[1] = HPET_LEGACY_RTC; + + for (i = 2; i < nrtimers; timer++, i++) + hd.hd_irq[i] = (timer->hpet_config & Tn_INT_ROUTE_CNF_MASK) >> + Tn_INT_ROUTE_CNF_SHIFT; + + hpet_alloc(&hd); + +} +#else +static void hpet_reserve_platform_timers(unsigned long id) { } +#endif + +/* + * Common hpet info + */ +static unsigned long hpet_period; + +static void hpet_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt); +static int hpet_next_event(unsigned long delta, + struct clock_event_device *evt); + +/* + * The hpet clock event device + */ +static struct clock_event_device hpet_clockevent = { + .name = "hpet", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = hpet_set_mode, + .set_next_event = hpet_next_event, + .shift = 32, + .irq = 0, +}; + +static void hpet_start_counter(void) +{ + unsigned long cfg = hpet_readl(HPET_CFG); + + cfg &= ~HPET_CFG_ENABLE; + hpet_writel(cfg, HPET_CFG); + hpet_writel(0, HPET_COUNTER); + hpet_writel(0, HPET_COUNTER + 4); + cfg |= HPET_CFG_ENABLE; + hpet_writel(cfg, HPET_CFG); +} + +static void hpet_enable_int(void) +{ + unsigned long cfg = hpet_readl(HPET_CFG); + + cfg |= HPET_CFG_LEGACY; + hpet_writel(cfg, HPET_CFG); + hpet_legacy_int_enabled = 1; +} + +static void hpet_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + unsigned long cfg, cmp, now; + uint64_t delta; + + switch(mode) { + case CLOCK_EVT_MODE_PERIODIC: + delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult; + delta >>= hpet_clockevent.shift; + now = hpet_readl(HPET_COUNTER); + cmp = now + (unsigned long) delta; + cfg = hpet_readl(HPET_T0_CFG); + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | + HPET_TN_SETVAL | HPET_TN_32BIT; + hpet_writel(cfg, HPET_T0_CFG); + /* + * The first write after writing TN_SETVAL to the + * config register sets the counter value, the second + * write sets the period. + */ + hpet_writel(cmp, HPET_T0_CMP); + udelay(1); + hpet_writel((unsigned long) delta, HPET_T0_CMP); + break; + + case CLOCK_EVT_MODE_ONESHOT: + cfg = hpet_readl(HPET_T0_CFG); + cfg &= ~HPET_TN_PERIODIC; + cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; + hpet_writel(cfg, HPET_T0_CFG); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + cfg = hpet_readl(HPET_T0_CFG); + cfg &= ~HPET_TN_ENABLE; + hpet_writel(cfg, HPET_T0_CFG); + break; + + case CLOCK_EVT_MODE_RESUME: + hpet_enable_int(); + break; + } +} + +static int hpet_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long cnt; + + cnt = hpet_readl(HPET_COUNTER); + cnt += delta; + hpet_writel(cnt, HPET_T0_CMP); + + return ((long)(hpet_readl(HPET_COUNTER) - cnt ) > 0) ? -ETIME : 0; +} + +/* + * Clock source related code + */ +static cycle_t read_hpet(void) +{ + return (cycle_t)hpet_readl(HPET_COUNTER); +} + +#ifdef CONFIG_X86_64 +static cycle_t __vsyscall_fn vread_hpet(void) +{ + return readl((const void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); +} +#endif + +static struct clocksource clocksource_hpet = { + .name = "hpet", + .rating = 250, + .read = read_hpet, + .mask = HPET_MASK, + .shift = HPET_SHIFT, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .resume = hpet_start_counter, +#ifdef CONFIG_X86_64 + .vread = vread_hpet, +#endif +}; + +/* + * Try to setup the HPET timer + */ +int __init hpet_enable(void) +{ + unsigned long id; + uint64_t hpet_freq; + u64 tmp, start, now; + cycle_t t1; + + if (!is_hpet_capable()) + return 0; + + hpet_set_mapping(); + + /* + * Read the period and check for a sane value: + */ + hpet_period = hpet_readl(HPET_PERIOD); + if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) + goto out_nohpet; + + /* + * The period is a femto seconds value. We need to calculate the + * scaled math multiplication factor for nanosecond to hpet tick + * conversion. + */ + hpet_freq = 1000000000000000ULL; + do_div(hpet_freq, hpet_period); + hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, + NSEC_PER_SEC, 32); + /* Calculate the min / max delta */ + hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, + &hpet_clockevent); + hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, + &hpet_clockevent); + + /* + * Read the HPET ID register to retrieve the IRQ routing + * information and the number of channels + */ + id = hpet_readl(HPET_ID); + +#ifdef CONFIG_HPET_EMULATE_RTC + /* + * The legacy routing mode needs at least two channels, tick timer + * and the rtc emulation channel. + */ + if (!(id & HPET_ID_NUMBER)) + goto out_nohpet; +#endif + + /* Start the counter */ + hpet_start_counter(); + + /* Verify whether hpet counter works */ + t1 = read_hpet(); + rdtscll(start); + + /* + * We don't know the TSC frequency yet, but waiting for + * 200000 TSC cycles is safe: + * 4 GHz == 50us + * 1 GHz == 200us + */ + do { + rep_nop(); + rdtscll(now); + } while ((now - start) < 200000UL); + + if (t1 == read_hpet()) { + printk(KERN_WARNING + "HPET counter not counting. HPET disabled\n"); + goto out_nohpet; + } + + /* Initialize and register HPET clocksource + * + * hpet period is in femto seconds per cycle + * so we need to convert this to ns/cyc units + * aproximated by mult/2^shift + * + * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift + * fsec/cyc * 1ns/1000000fsec * 2^shift = mult + * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult + * (fsec/cyc << shift)/1000000 = mult + * (hpet_period << shift)/FSEC_PER_NSEC = mult + */ + tmp = (u64)hpet_period << HPET_SHIFT; + do_div(tmp, FSEC_PER_NSEC); + clocksource_hpet.mult = (u32)tmp; + + clocksource_register(&clocksource_hpet); + + if (id & HPET_ID_LEGSUP) { + hpet_enable_int(); + /* + * Start hpet with the boot cpu mask and make it + * global after the IO_APIC has been initialized. + */ + hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + clockevents_register_device(&hpet_clockevent); + global_clock_event = &hpet_clockevent; + return 1; + } + return 0; + +out_nohpet: + hpet_clear_mapping(); + boot_hpet_disable = 1; + return 0; +} + +/* + * Needs to be late, as the reserve_timer code calls kalloc ! + * + * Not a problem on i386 as hpet_enable is called from late_time_init, + * but on x86_64 it is necessary ! + */ +static __init int hpet_late_init(void) +{ + if (!is_hpet_capable()) + return -ENODEV; + + hpet_reserve_platform_timers(hpet_readl(HPET_ID)); + return 0; +} +fs_initcall(hpet_late_init); + +#ifdef CONFIG_HPET_EMULATE_RTC + +/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET + * is enabled, we support RTC interrupt functionality in software. + * RTC has 3 kinds of interrupts: + * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock + * is updated + * 2) Alarm Interrupt - generate an interrupt at a specific time of day + * 3) Periodic Interrupt - generate periodic interrupt, with frequencies + * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) + * (1) and (2) above are implemented using polling at a frequency of + * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt + * overhead. (DEFAULT_RTC_INT_FREQ) + * For (3), we use interrupts at 64Hz or user specified periodic + * frequency, whichever is higher. + */ +#include +#include + +#define DEFAULT_RTC_INT_FREQ 64 +#define DEFAULT_RTC_SHIFT 6 +#define RTC_NUM_INTS 1 + +static unsigned long hpet_rtc_flags; +static unsigned long hpet_prev_update_sec; +static struct rtc_time hpet_alarm_time; +static unsigned long hpet_pie_count; +static unsigned long hpet_t1_cmp; +static unsigned long hpet_default_delta; +static unsigned long hpet_pie_delta; +static unsigned long hpet_pie_limit; + +/* + * Timer 1 for RTC emulation. We use one shot mode, as periodic mode + * is not supported by all HPET implementations for timer 1. + * + * hpet_rtc_timer_init() is called when the rtc is initialized. + */ +int hpet_rtc_timer_init(void) +{ + unsigned long cfg, cnt, delta, flags; + + if (!is_hpet_enabled()) + return 0; + + if (!hpet_default_delta) { + uint64_t clc; + + clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; + clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; + hpet_default_delta = (unsigned long) clc; + } + + if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) + delta = hpet_default_delta; + else + delta = hpet_pie_delta; + + local_irq_save(flags); + + cnt = delta + hpet_readl(HPET_COUNTER); + hpet_writel(cnt, HPET_T1_CMP); + hpet_t1_cmp = cnt; + + cfg = hpet_readl(HPET_T1_CFG); + cfg &= ~HPET_TN_PERIODIC; + cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; + hpet_writel(cfg, HPET_T1_CFG); + + local_irq_restore(flags); + + return 1; +} + +/* + * The functions below are called from rtc driver. + * Return 0 if HPET is not being used. + * Otherwise do the necessary changes and return 1. + */ +int hpet_mask_rtc_irq_bit(unsigned long bit_mask) +{ + if (!is_hpet_enabled()) + return 0; + + hpet_rtc_flags &= ~bit_mask; + return 1; +} + +int hpet_set_rtc_irq_bit(unsigned long bit_mask) +{ + unsigned long oldbits = hpet_rtc_flags; + + if (!is_hpet_enabled()) + return 0; + + hpet_rtc_flags |= bit_mask; + + if (!oldbits) + hpet_rtc_timer_init(); + + return 1; +} + +int hpet_set_alarm_time(unsigned char hrs, unsigned char min, + unsigned char sec) +{ + if (!is_hpet_enabled()) + return 0; + + hpet_alarm_time.tm_hour = hrs; + hpet_alarm_time.tm_min = min; + hpet_alarm_time.tm_sec = sec; + + return 1; +} + +int hpet_set_periodic_freq(unsigned long freq) +{ + uint64_t clc; + + if (!is_hpet_enabled()) + return 0; + + if (freq <= DEFAULT_RTC_INT_FREQ) + hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; + else { + clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; + do_div(clc, freq); + clc >>= hpet_clockevent.shift; + hpet_pie_delta = (unsigned long) clc; + } + return 1; +} + +int hpet_rtc_dropped_irq(void) +{ + return is_hpet_enabled(); +} + +static void hpet_rtc_timer_reinit(void) +{ + unsigned long cfg, delta; + int lost_ints = -1; + + if (unlikely(!hpet_rtc_flags)) { + cfg = hpet_readl(HPET_T1_CFG); + cfg &= ~HPET_TN_ENABLE; + hpet_writel(cfg, HPET_T1_CFG); + return; + } + + if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) + delta = hpet_default_delta; + else + delta = hpet_pie_delta; + + /* + * Increment the comparator value until we are ahead of the + * current count. + */ + do { + hpet_t1_cmp += delta; + hpet_writel(hpet_t1_cmp, HPET_T1_CMP); + lost_ints++; + } while ((long)(hpet_readl(HPET_COUNTER) - hpet_t1_cmp) > 0); + + if (lost_ints) { + if (hpet_rtc_flags & RTC_PIE) + hpet_pie_count += lost_ints; + if (printk_ratelimit()) + printk(KERN_WARNING "rtc: lost %d interrupts\n", + lost_ints); + } +} + +irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) +{ + struct rtc_time curr_time; + unsigned long rtc_int_flag = 0; + + hpet_rtc_timer_reinit(); + + if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) + rtc_get_rtc_time(&curr_time); + + if (hpet_rtc_flags & RTC_UIE && + curr_time.tm_sec != hpet_prev_update_sec) { + rtc_int_flag = RTC_UF; + hpet_prev_update_sec = curr_time.tm_sec; + } + + if (hpet_rtc_flags & RTC_PIE && + ++hpet_pie_count >= hpet_pie_limit) { + rtc_int_flag |= RTC_PF; + hpet_pie_count = 0; + } + + if (hpet_rtc_flags & RTC_PIE && + (curr_time.tm_sec == hpet_alarm_time.tm_sec) && + (curr_time.tm_min == hpet_alarm_time.tm_min) && + (curr_time.tm_hour == hpet_alarm_time.tm_hour)) + rtc_int_flag |= RTC_AF; + + if (rtc_int_flag) { + rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); + rtc_interrupt(rtc_int_flag, dev_id); + } + return IRQ_HANDLED; +} +#endif diff --git a/arch/x86/kernel/hpet_32.c b/arch/x86/kernel/hpet_32.c deleted file mode 100644 index dbe0e1d44113..000000000000 --- a/arch/x86/kernel/hpet_32.c +++ /dev/null @@ -1,618 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define HPET_MASK CLOCKSOURCE_MASK(32) -#define HPET_SHIFT 22 - -/* FSEC = 10^-15 NSEC = 10^-9 */ -#define FSEC_PER_NSEC 1000000 - -/* - * HPET address is set in acpi/boot.c, when an ACPI entry exists - */ -unsigned long hpet_address; -static void __iomem *hpet_virt_address; - -/* Temporary hack. Cleanup after x86_64 clock events conversion */ -#undef hpet_readl -#undef hpet_writel - -static inline unsigned long hpet_readl(unsigned long a) -{ - return readl(hpet_virt_address + a); -} - -static inline void hpet_writel(unsigned long d, unsigned long a) -{ - writel(d, hpet_virt_address + a); -} - -#ifdef CONFIG_X86_64 - -#include - -static inline void hpet_set_mapping(void) -{ - set_fixmap_nocache(FIX_HPET_BASE, hpet_address); - __set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); - hpet_virt_address = (void __iomem *)fix_to_virt(FIX_HPET_BASE); -} - -static inline void hpet_clear_mapping(void) -{ - hpet_virt_address = NULL; -} - -#else - -static inline void hpet_set_mapping(void) -{ - hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); -} - -static inline void hpet_clear_mapping(void) -{ - iounmap(hpet_virt_address); - hpet_virt_address = NULL; -} -#endif - -/* - * HPET command line enable / disable - */ -static int boot_hpet_disable; - -static int __init hpet_setup(char* str) -{ - if (str) { - if (!strncmp("disable", str, 7)) - boot_hpet_disable = 1; - } - return 1; -} -__setup("hpet=", hpet_setup); - -static int __init disable_hpet(char *str) -{ - boot_hpet_disable = 1; - return 1; -} -__setup("nohpet", disable_hpet); - -static inline int is_hpet_capable(void) -{ - return (!boot_hpet_disable && hpet_address); -} - -/* - * HPET timer interrupt enable / disable - */ -static int hpet_legacy_int_enabled; - -/** - * is_hpet_enabled - check whether the hpet timer interrupt is enabled - */ -int is_hpet_enabled(void) -{ - return is_hpet_capable() && hpet_legacy_int_enabled; -} - -/* - * When the hpet driver (/dev/hpet) is enabled, we need to reserve - * timer 0 and timer 1 in case of RTC emulation. - */ -#ifdef CONFIG_HPET -static void hpet_reserve_platform_timers(unsigned long id) -{ - struct hpet __iomem *hpet = hpet_virt_address; - struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; - unsigned int nrtimers, i; - struct hpet_data hd; - - nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; - - memset(&hd, 0, sizeof (hd)); - hd.hd_phys_address = hpet_address; - hd.hd_address = hpet; - hd.hd_nirqs = nrtimers; - hd.hd_flags = HPET_DATA_PLATFORM; - hpet_reserve_timer(&hd, 0); - -#ifdef CONFIG_HPET_EMULATE_RTC - hpet_reserve_timer(&hd, 1); -#endif - - hd.hd_irq[0] = HPET_LEGACY_8254; - hd.hd_irq[1] = HPET_LEGACY_RTC; - - for (i = 2; i < nrtimers; timer++, i++) - hd.hd_irq[i] = (timer->hpet_config & Tn_INT_ROUTE_CNF_MASK) >> - Tn_INT_ROUTE_CNF_SHIFT; - - hpet_alloc(&hd); - -} -#else -static void hpet_reserve_platform_timers(unsigned long id) { } -#endif - -/* - * Common hpet info - */ -static unsigned long hpet_period; - -static void hpet_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt); -static int hpet_next_event(unsigned long delta, - struct clock_event_device *evt); - -/* - * The hpet clock event device - */ -static struct clock_event_device hpet_clockevent = { - .name = "hpet", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = hpet_set_mode, - .set_next_event = hpet_next_event, - .shift = 32, - .irq = 0, -}; - -static void hpet_start_counter(void) -{ - unsigned long cfg = hpet_readl(HPET_CFG); - - cfg &= ~HPET_CFG_ENABLE; - hpet_writel(cfg, HPET_CFG); - hpet_writel(0, HPET_COUNTER); - hpet_writel(0, HPET_COUNTER + 4); - cfg |= HPET_CFG_ENABLE; - hpet_writel(cfg, HPET_CFG); -} - -static void hpet_enable_int(void) -{ - unsigned long cfg = hpet_readl(HPET_CFG); - - cfg |= HPET_CFG_LEGACY; - hpet_writel(cfg, HPET_CFG); - hpet_legacy_int_enabled = 1; -} - -static void hpet_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) -{ - unsigned long cfg, cmp, now; - uint64_t delta; - - switch(mode) { - case CLOCK_EVT_MODE_PERIODIC: - delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult; - delta >>= hpet_clockevent.shift; - now = hpet_readl(HPET_COUNTER); - cmp = now + (unsigned long) delta; - cfg = hpet_readl(HPET_T0_CFG); - cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | - HPET_TN_SETVAL | HPET_TN_32BIT; - hpet_writel(cfg, HPET_T0_CFG); - /* - * The first write after writing TN_SETVAL to the - * config register sets the counter value, the second - * write sets the period. - */ - hpet_writel(cmp, HPET_T0_CMP); - udelay(1); - hpet_writel((unsigned long) delta, HPET_T0_CMP); - break; - - case CLOCK_EVT_MODE_ONESHOT: - cfg = hpet_readl(HPET_T0_CFG); - cfg &= ~HPET_TN_PERIODIC; - cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; - hpet_writel(cfg, HPET_T0_CFG); - break; - - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - cfg = hpet_readl(HPET_T0_CFG); - cfg &= ~HPET_TN_ENABLE; - hpet_writel(cfg, HPET_T0_CFG); - break; - - case CLOCK_EVT_MODE_RESUME: - hpet_enable_int(); - break; - } -} - -static int hpet_next_event(unsigned long delta, - struct clock_event_device *evt) -{ - unsigned long cnt; - - cnt = hpet_readl(HPET_COUNTER); - cnt += delta; - hpet_writel(cnt, HPET_T0_CMP); - - return ((long)(hpet_readl(HPET_COUNTER) - cnt ) > 0) ? -ETIME : 0; -} - -/* - * Clock source related code - */ -static cycle_t read_hpet(void) -{ - return (cycle_t)hpet_readl(HPET_COUNTER); -} - -#ifdef CONFIG_X86_64 -static cycle_t __vsyscall_fn vread_hpet(void) -{ - return readl((const void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); -} -#endif - -static struct clocksource clocksource_hpet = { - .name = "hpet", - .rating = 250, - .read = read_hpet, - .mask = HPET_MASK, - .shift = HPET_SHIFT, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .resume = hpet_start_counter, -#ifdef CONFIG_X86_64 - .vread = vread_hpet, -#endif -}; - -/* - * Try to setup the HPET timer - */ -int __init hpet_enable(void) -{ - unsigned long id; - uint64_t hpet_freq; - u64 tmp, start, now; - cycle_t t1; - - if (!is_hpet_capable()) - return 0; - - hpet_set_mapping(); - - /* - * Read the period and check for a sane value: - */ - hpet_period = hpet_readl(HPET_PERIOD); - if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) - goto out_nohpet; - - /* - * The period is a femto seconds value. We need to calculate the - * scaled math multiplication factor for nanosecond to hpet tick - * conversion. - */ - hpet_freq = 1000000000000000ULL; - do_div(hpet_freq, hpet_period); - hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, - NSEC_PER_SEC, 32); - /* Calculate the min / max delta */ - hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, - &hpet_clockevent); - hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, - &hpet_clockevent); - - /* - * Read the HPET ID register to retrieve the IRQ routing - * information and the number of channels - */ - id = hpet_readl(HPET_ID); - -#ifdef CONFIG_HPET_EMULATE_RTC - /* - * The legacy routing mode needs at least two channels, tick timer - * and the rtc emulation channel. - */ - if (!(id & HPET_ID_NUMBER)) - goto out_nohpet; -#endif - - /* Start the counter */ - hpet_start_counter(); - - /* Verify whether hpet counter works */ - t1 = read_hpet(); - rdtscll(start); - - /* - * We don't know the TSC frequency yet, but waiting for - * 200000 TSC cycles is safe: - * 4 GHz == 50us - * 1 GHz == 200us - */ - do { - rep_nop(); - rdtscll(now); - } while ((now - start) < 200000UL); - - if (t1 == read_hpet()) { - printk(KERN_WARNING - "HPET counter not counting. HPET disabled\n"); - goto out_nohpet; - } - - /* Initialize and register HPET clocksource - * - * hpet period is in femto seconds per cycle - * so we need to convert this to ns/cyc units - * aproximated by mult/2^shift - * - * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift - * fsec/cyc * 1ns/1000000fsec * 2^shift = mult - * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult - * (fsec/cyc << shift)/1000000 = mult - * (hpet_period << shift)/FSEC_PER_NSEC = mult - */ - tmp = (u64)hpet_period << HPET_SHIFT; - do_div(tmp, FSEC_PER_NSEC); - clocksource_hpet.mult = (u32)tmp; - - clocksource_register(&clocksource_hpet); - - if (id & HPET_ID_LEGSUP) { - hpet_enable_int(); - /* - * Start hpet with the boot cpu mask and make it - * global after the IO_APIC has been initialized. - */ - hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); - clockevents_register_device(&hpet_clockevent); - global_clock_event = &hpet_clockevent; - return 1; - } - return 0; - -out_nohpet: - hpet_clear_mapping(); - boot_hpet_disable = 1; - return 0; -} - -/* - * Needs to be late, as the reserve_timer code calls kalloc ! - * - * Not a problem on i386 as hpet_enable is called from late_time_init, - * but on x86_64 it is necessary ! - */ -static __init int hpet_late_init(void) -{ - if (!is_hpet_capable()) - return -ENODEV; - - hpet_reserve_platform_timers(hpet_readl(HPET_ID)); - return 0; -} -fs_initcall(hpet_late_init); - -#ifdef CONFIG_HPET_EMULATE_RTC - -/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET - * is enabled, we support RTC interrupt functionality in software. - * RTC has 3 kinds of interrupts: - * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock - * is updated - * 2) Alarm Interrupt - generate an interrupt at a specific time of day - * 3) Periodic Interrupt - generate periodic interrupt, with frequencies - * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) - * (1) and (2) above are implemented using polling at a frequency of - * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt - * overhead. (DEFAULT_RTC_INT_FREQ) - * For (3), we use interrupts at 64Hz or user specified periodic - * frequency, whichever is higher. - */ -#include -#include - -#define DEFAULT_RTC_INT_FREQ 64 -#define DEFAULT_RTC_SHIFT 6 -#define RTC_NUM_INTS 1 - -static unsigned long hpet_rtc_flags; -static unsigned long hpet_prev_update_sec; -static struct rtc_time hpet_alarm_time; -static unsigned long hpet_pie_count; -static unsigned long hpet_t1_cmp; -static unsigned long hpet_default_delta; -static unsigned long hpet_pie_delta; -static unsigned long hpet_pie_limit; - -/* - * Timer 1 for RTC emulation. We use one shot mode, as periodic mode - * is not supported by all HPET implementations for timer 1. - * - * hpet_rtc_timer_init() is called when the rtc is initialized. - */ -int hpet_rtc_timer_init(void) -{ - unsigned long cfg, cnt, delta, flags; - - if (!is_hpet_enabled()) - return 0; - - if (!hpet_default_delta) { - uint64_t clc; - - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; - clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; - hpet_default_delta = (unsigned long) clc; - } - - if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) - delta = hpet_default_delta; - else - delta = hpet_pie_delta; - - local_irq_save(flags); - - cnt = delta + hpet_readl(HPET_COUNTER); - hpet_writel(cnt, HPET_T1_CMP); - hpet_t1_cmp = cnt; - - cfg = hpet_readl(HPET_T1_CFG); - cfg &= ~HPET_TN_PERIODIC; - cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; - hpet_writel(cfg, HPET_T1_CFG); - - local_irq_restore(flags); - - return 1; -} - -/* - * The functions below are called from rtc driver. - * Return 0 if HPET is not being used. - * Otherwise do the necessary changes and return 1. - */ -int hpet_mask_rtc_irq_bit(unsigned long bit_mask) -{ - if (!is_hpet_enabled()) - return 0; - - hpet_rtc_flags &= ~bit_mask; - return 1; -} - -int hpet_set_rtc_irq_bit(unsigned long bit_mask) -{ - unsigned long oldbits = hpet_rtc_flags; - - if (!is_hpet_enabled()) - return 0; - - hpet_rtc_flags |= bit_mask; - - if (!oldbits) - hpet_rtc_timer_init(); - - return 1; -} - -int hpet_set_alarm_time(unsigned char hrs, unsigned char min, - unsigned char sec) -{ - if (!is_hpet_enabled()) - return 0; - - hpet_alarm_time.tm_hour = hrs; - hpet_alarm_time.tm_min = min; - hpet_alarm_time.tm_sec = sec; - - return 1; -} - -int hpet_set_periodic_freq(unsigned long freq) -{ - uint64_t clc; - - if (!is_hpet_enabled()) - return 0; - - if (freq <= DEFAULT_RTC_INT_FREQ) - hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; - else { - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; - do_div(clc, freq); - clc >>= hpet_clockevent.shift; - hpet_pie_delta = (unsigned long) clc; - } - return 1; -} - -int hpet_rtc_dropped_irq(void) -{ - return is_hpet_enabled(); -} - -static void hpet_rtc_timer_reinit(void) -{ - unsigned long cfg, delta; - int lost_ints = -1; - - if (unlikely(!hpet_rtc_flags)) { - cfg = hpet_readl(HPET_T1_CFG); - cfg &= ~HPET_TN_ENABLE; - hpet_writel(cfg, HPET_T1_CFG); - return; - } - - if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit) - delta = hpet_default_delta; - else - delta = hpet_pie_delta; - - /* - * Increment the comparator value until we are ahead of the - * current count. - */ - do { - hpet_t1_cmp += delta; - hpet_writel(hpet_t1_cmp, HPET_T1_CMP); - lost_ints++; - } while ((long)(hpet_readl(HPET_COUNTER) - hpet_t1_cmp) > 0); - - if (lost_ints) { - if (hpet_rtc_flags & RTC_PIE) - hpet_pie_count += lost_ints; - if (printk_ratelimit()) - printk(KERN_WARNING "rtc: lost %d interrupts\n", - lost_ints); - } -} - -irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) -{ - struct rtc_time curr_time; - unsigned long rtc_int_flag = 0; - - hpet_rtc_timer_reinit(); - - if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) - rtc_get_rtc_time(&curr_time); - - if (hpet_rtc_flags & RTC_UIE && - curr_time.tm_sec != hpet_prev_update_sec) { - rtc_int_flag = RTC_UF; - hpet_prev_update_sec = curr_time.tm_sec; - } - - if (hpet_rtc_flags & RTC_PIE && - ++hpet_pie_count >= hpet_pie_limit) { - rtc_int_flag |= RTC_PF; - hpet_pie_count = 0; - } - - if (hpet_rtc_flags & RTC_PIE && - (curr_time.tm_sec == hpet_alarm_time.tm_sec) && - (curr_time.tm_min == hpet_alarm_time.tm_min) && - (curr_time.tm_hour == hpet_alarm_time.tm_hour)) - rtc_int_flag |= RTC_AF; - - if (rtc_int_flag) { - rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); - rtc_interrupt(rtc_int_flag, dev_id); - } - return IRQ_HANDLED; -} -#endif diff --git a/arch/x86/kernel/hpet_64.c b/arch/x86/kernel/hpet_64.c deleted file mode 100644 index 1ebce2ec7eaf..000000000000 --- a/arch/x86/kernel/hpet_64.c +++ /dev/null @@ -1,444 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HPET_MASK 0xFFFFFFFF -#define HPET_SHIFT 22 - -/* FSEC = 10^-15 NSEC = 10^-9 */ -#define FSEC_PER_NSEC 1000000 - -int nohpet __initdata; - -unsigned long hpet_address; -unsigned long hpet_period; /* fsecs / HPET clock */ -unsigned long hpet_tick; /* HPET clocks / interrupt */ - -int hpet_use_timer; /* Use counter of hpet for time keeping, - * otherwise PIT - */ - -#ifdef CONFIG_HPET -static __init int late_hpet_init(void) -{ - struct hpet_data hd; - unsigned int ntimer; - - if (!hpet_address) - return 0; - - memset(&hd, 0, sizeof(hd)); - - ntimer = hpet_readl(HPET_ID); - ntimer = (ntimer & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; - ntimer++; - - /* - * Register with driver. - * Timer0 and Timer1 is used by platform. - */ - hd.hd_phys_address = hpet_address; - hd.hd_address = (void __iomem *)fix_to_virt(FIX_HPET_BASE); - hd.hd_nirqs = ntimer; - hd.hd_flags = HPET_DATA_PLATFORM; - hpet_reserve_timer(&hd, 0); -#ifdef CONFIG_HPET_EMULATE_RTC - hpet_reserve_timer(&hd, 1); -#endif - hd.hd_irq[0] = HPET_LEGACY_8254; - hd.hd_irq[1] = HPET_LEGACY_RTC; - if (ntimer > 2) { - struct hpet *hpet; - struct hpet_timer *timer; - int i; - - hpet = (struct hpet *) fix_to_virt(FIX_HPET_BASE); - timer = &hpet->hpet_timers[2]; - for (i = 2; i < ntimer; timer++, i++) - hd.hd_irq[i] = (timer->hpet_config & - Tn_INT_ROUTE_CNF_MASK) >> - Tn_INT_ROUTE_CNF_SHIFT; - - } - - hpet_alloc(&hd); - return 0; -} -fs_initcall(late_hpet_init); -#endif - -int hpet_timer_stop_set_go(unsigned long tick) -{ - unsigned int cfg; - -/* - * Stop the timers and reset the main counter. - */ - - cfg = hpet_readl(HPET_CFG); - cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); - hpet_writel(cfg, HPET_CFG); - hpet_writel(0, HPET_COUNTER); - hpet_writel(0, HPET_COUNTER + 4); - -/* - * Set up timer 0, as periodic with first interrupt to happen at hpet_tick, - * and period also hpet_tick. - */ - if (hpet_use_timer) { - hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | - HPET_TN_32BIT, HPET_T0_CFG); - hpet_writel(hpet_tick, HPET_T0_CMP); /* next interrupt */ - hpet_writel(hpet_tick, HPET_T0_CMP); /* period */ - cfg |= HPET_CFG_LEGACY; - } -/* - * Go! - */ - - cfg |= HPET_CFG_ENABLE; - hpet_writel(cfg, HPET_CFG); - - return 0; -} - -static cycle_t read_hpet(void) -{ - return (cycle_t)hpet_readl(HPET_COUNTER); -} - -static cycle_t __vsyscall_fn vread_hpet(void) -{ - return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); -} - -struct clocksource clocksource_hpet = { - .name = "hpet", - .rating = 250, - .read = read_hpet, - .mask = (cycle_t)HPET_MASK, - .mult = 0, /* set below */ - .shift = HPET_SHIFT, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .vread = vread_hpet, -}; - -int __init hpet_arch_init(void) -{ - unsigned int id; - u64 tmp; - - if (!hpet_address) - return -1; - set_fixmap_nocache(FIX_HPET_BASE, hpet_address); - __set_fixmap(VSYSCALL_HPET, hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); - -/* - * Read the period, compute tick and quotient. - */ - - id = hpet_readl(HPET_ID); - - if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER)) - return -1; - - hpet_period = hpet_readl(HPET_PERIOD); - if (hpet_period < 100000 || hpet_period > 100000000) - return -1; - - hpet_tick = (FSEC_PER_TICK + hpet_period / 2) / hpet_period; - - hpet_use_timer = (id & HPET_ID_LEGSUP); - - /* - * hpet period is in femto seconds per cycle - * so we need to convert this to ns/cyc units - * aproximated by mult/2^shift - * - * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift - * fsec/cyc * 1ns/1000000fsec * 2^shift = mult - * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult - * (fsec/cyc << shift)/1000000 = mult - * (hpet_period << shift)/FSEC_PER_NSEC = mult - */ - tmp = (u64)hpet_period << HPET_SHIFT; - do_div(tmp, FSEC_PER_NSEC); - clocksource_hpet.mult = (u32)tmp; - clocksource_register(&clocksource_hpet); - - return hpet_timer_stop_set_go(hpet_tick); -} - -int hpet_reenable(void) -{ - return hpet_timer_stop_set_go(hpet_tick); -} - -#ifdef CONFIG_HPET_EMULATE_RTC -/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET - * is enabled, we support RTC interrupt functionality in software. - * RTC has 3 kinds of interrupts: - * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock - * is updated - * 2) Alarm Interrupt - generate an interrupt at a specific time of day - * 3) Periodic Interrupt - generate periodic interrupt, with frequencies - * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) - * (1) and (2) above are implemented using polling at a frequency of - * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt - * overhead. (DEFAULT_RTC_INT_FREQ) - * For (3), we use interrupts at 64Hz or user specified periodic - * frequency, whichever is higher. - */ -#include - -#define DEFAULT_RTC_INT_FREQ 64 -#define RTC_NUM_INTS 1 - -static unsigned long UIE_on; -static unsigned long prev_update_sec; - -static unsigned long AIE_on; -static struct rtc_time alarm_time; - -static unsigned long PIE_on; -static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ; -static unsigned long PIE_count; - -static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */ -static unsigned int hpet_t1_cmp; /* cached comparator register */ - -int is_hpet_enabled(void) -{ - return hpet_address != 0; -} - -/* - * Timer 1 for RTC, we do not use periodic interrupt feature, - * even if HPET supports periodic interrupts on Timer 1. - * The reason being, to set up a periodic interrupt in HPET, we need to - * stop the main counter. And if we do that everytime someone diables/enables - * RTC, we will have adverse effect on main kernel timer running on Timer 0. - * So, for the time being, simulate the periodic interrupt in software. - * - * hpet_rtc_timer_init() is called for the first time and during subsequent - * interuppts reinit happens through hpet_rtc_timer_reinit(). - */ -int hpet_rtc_timer_init(void) -{ - unsigned int cfg, cnt; - unsigned long flags; - - if (!is_hpet_enabled()) - return 0; - /* - * Set the counter 1 and enable the interrupts. - */ - if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ)) - hpet_rtc_int_freq = PIE_freq; - else - hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; - - local_irq_save(flags); - - cnt = hpet_readl(HPET_COUNTER); - cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq); - hpet_writel(cnt, HPET_T1_CMP); - hpet_t1_cmp = cnt; - - cfg = hpet_readl(HPET_T1_CFG); - cfg &= ~HPET_TN_PERIODIC; - cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; - hpet_writel(cfg, HPET_T1_CFG); - - local_irq_restore(flags); - - return 1; -} - -static void hpet_rtc_timer_reinit(void) -{ - unsigned int cfg, cnt, ticks_per_int, lost_ints; - - if (unlikely(!(PIE_on | AIE_on | UIE_on))) { - cfg = hpet_readl(HPET_T1_CFG); - cfg &= ~HPET_TN_ENABLE; - hpet_writel(cfg, HPET_T1_CFG); - return; - } - - if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ)) - hpet_rtc_int_freq = PIE_freq; - else - hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; - - /* It is more accurate to use the comparator value than current count.*/ - ticks_per_int = hpet_tick * HZ / hpet_rtc_int_freq; - hpet_t1_cmp += ticks_per_int; - hpet_writel(hpet_t1_cmp, HPET_T1_CMP); - - /* - * If the interrupt handler was delayed too long, the write above tries - * to schedule the next interrupt in the past and the hardware would - * not interrupt until the counter had wrapped around. - * So we have to check that the comparator wasn't set to a past time. - */ - cnt = hpet_readl(HPET_COUNTER); - if (unlikely((int)(cnt - hpet_t1_cmp) > 0)) { - lost_ints = (cnt - hpet_t1_cmp) / ticks_per_int + 1; - /* Make sure that, even with the time needed to execute - * this code, the next scheduled interrupt has been moved - * back to the future: */ - lost_ints++; - - hpet_t1_cmp += lost_ints * ticks_per_int; - hpet_writel(hpet_t1_cmp, HPET_T1_CMP); - - if (PIE_on) - PIE_count += lost_ints; - - if (printk_ratelimit()) - printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", - hpet_rtc_int_freq); - } -} - -/* - * The functions below are called from rtc driver. - * Return 0 if HPET is not being used. - * Otherwise do the necessary changes and return 1. - */ -int hpet_mask_rtc_irq_bit(unsigned long bit_mask) -{ - if (!is_hpet_enabled()) - return 0; - - if (bit_mask & RTC_UIE) - UIE_on = 0; - if (bit_mask & RTC_PIE) - PIE_on = 0; - if (bit_mask & RTC_AIE) - AIE_on = 0; - - return 1; -} - -int hpet_set_rtc_irq_bit(unsigned long bit_mask) -{ - int timer_init_reqd = 0; - - if (!is_hpet_enabled()) - return 0; - - if (!(PIE_on | AIE_on | UIE_on)) - timer_init_reqd = 1; - - if (bit_mask & RTC_UIE) { - UIE_on = 1; - } - if (bit_mask & RTC_PIE) { - PIE_on = 1; - PIE_count = 0; - } - if (bit_mask & RTC_AIE) { - AIE_on = 1; - } - - if (timer_init_reqd) - hpet_rtc_timer_init(); - - return 1; -} - -int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) -{ - if (!is_hpet_enabled()) - return 0; - - alarm_time.tm_hour = hrs; - alarm_time.tm_min = min; - alarm_time.tm_sec = sec; - - return 1; -} - -int hpet_set_periodic_freq(unsigned long freq) -{ - if (!is_hpet_enabled()) - return 0; - - PIE_freq = freq; - PIE_count = 0; - - return 1; -} - -int hpet_rtc_dropped_irq(void) -{ - if (!is_hpet_enabled()) - return 0; - - return 1; -} - -irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id) -{ - struct rtc_time curr_time; - unsigned long rtc_int_flag = 0; - int call_rtc_interrupt = 0; - - hpet_rtc_timer_reinit(); - - if (UIE_on | AIE_on) { - rtc_get_rtc_time(&curr_time); - } - if (UIE_on) { - if (curr_time.tm_sec != prev_update_sec) { - /* Set update int info, call real rtc int routine */ - call_rtc_interrupt = 1; - rtc_int_flag = RTC_UF; - prev_update_sec = curr_time.tm_sec; - } - } - if (PIE_on) { - PIE_count++; - if (PIE_count >= hpet_rtc_int_freq/PIE_freq) { - /* Set periodic int info, call real rtc int routine */ - call_rtc_interrupt = 1; - rtc_int_flag |= RTC_PF; - PIE_count = 0; - } - } - if (AIE_on) { - if ((curr_time.tm_sec == alarm_time.tm_sec) && - (curr_time.tm_min == alarm_time.tm_min) && - (curr_time.tm_hour == alarm_time.tm_hour)) { - /* Set alarm int info, call real rtc int routine */ - call_rtc_interrupt = 1; - rtc_int_flag |= RTC_AF; - } - } - if (call_rtc_interrupt) { - rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); - rtc_interrupt(rtc_int_flag, dev_id); - } - return IRQ_HANDLED; -} -#endif - -static int __init nohpet_setup(char *s) -{ - nohpet = 1; - return 1; -} - -__setup("nohpet", nohpet_setup); diff --git a/arch/x86/kernel/i8253.c b/arch/x86/kernel/i8253.c new file mode 100644 index 000000000000..ac15e4cbd9c1 --- /dev/null +++ b/arch/x86/kernel/i8253.c @@ -0,0 +1,208 @@ +/* + * i8253.c 8253/PIT functions + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +DEFINE_SPINLOCK(i8253_lock); +EXPORT_SYMBOL(i8253_lock); + +/* + * HPET replaces the PIT, when enabled. So we need to know, which of + * the two timers is used + */ +struct clock_event_device *global_clock_event; + +/* + * Initialize the PIT timer. + * + * This is also called after resume to bring the PIT into operation again. + */ +static void init_pit_timer(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + unsigned long flags; + + spin_lock_irqsave(&i8253_lock, flags); + + switch(mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(0x34, PIT_MODE); + outb_p(LATCH & 0xff , PIT_CH0); /* LSB */ + outb(LATCH >> 8 , PIT_CH0); /* MSB */ + break; + + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + if (evt->mode == CLOCK_EVT_MODE_PERIODIC || + evt->mode == CLOCK_EVT_MODE_ONESHOT) { + outb_p(0x30, PIT_MODE); + outb_p(0, PIT_CH0); + outb_p(0, PIT_CH0); + } + break; + + case CLOCK_EVT_MODE_ONESHOT: + /* One shot setup */ + outb_p(0x38, PIT_MODE); + break; + + case CLOCK_EVT_MODE_RESUME: + /* Nothing to do here */ + break; + } + spin_unlock_irqrestore(&i8253_lock, flags); +} + +/* + * Program the next event in oneshot mode + * + * Delta is given in PIT ticks + */ +static int pit_next_event(unsigned long delta, struct clock_event_device *evt) +{ + unsigned long flags; + + spin_lock_irqsave(&i8253_lock, flags); + outb_p(delta & 0xff , PIT_CH0); /* LSB */ + outb(delta >> 8 , PIT_CH0); /* MSB */ + spin_unlock_irqrestore(&i8253_lock, flags); + + return 0; +} + +/* + * On UP the PIT can serve all of the possible timer functions. On SMP systems + * it can be solely used for the global tick. + * + * The profiling and update capabilites are switched off once the local apic is + * registered. This mechanism replaces the previous #ifdef LOCAL_APIC - + * !using_apic_timer decisions in do_timer_interrupt_hook() + */ +struct clock_event_device pit_clockevent = { + .name = "pit", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = init_pit_timer, + .set_next_event = pit_next_event, + .shift = 32, + .irq = 0, +}; + +/* + * Initialize the conversion factor and the min/max deltas of the clock event + * structure and register the clock event source with the framework. + */ +void __init setup_pit_timer(void) +{ + /* + * Start pit with the boot cpu mask and make it global after the + * IO_APIC has been initialized. + */ + pit_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + pit_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, 32); + pit_clockevent.max_delta_ns = + clockevent_delta2ns(0x7FFF, &pit_clockevent); + pit_clockevent.min_delta_ns = + clockevent_delta2ns(0xF, &pit_clockevent); + clockevents_register_device(&pit_clockevent); + global_clock_event = &pit_clockevent; +} + +#ifndef CONFIG_X86_64 +/* + * Since the PIT overflows every tick, its not very useful + * to just read by itself. So use jiffies to emulate a free + * running counter: + */ +static cycle_t pit_read(void) +{ + unsigned long flags; + int count; + u32 jifs; + static int old_count; + static u32 old_jifs; + + spin_lock_irqsave(&i8253_lock, flags); + /* + * Although our caller may have the read side of xtime_lock, + * this is now a seqlock, and we are cheating in this routine + * by having side effects on state that we cannot undo if + * there is a collision on the seqlock and our caller has to + * retry. (Namely, old_jifs and old_count.) So we must treat + * jiffies as volatile despite the lock. We read jiffies + * before latching the timer count to guarantee that although + * the jiffies value might be older than the count (that is, + * the counter may underflow between the last point where + * jiffies was incremented and the point where we latch the + * count), it cannot be newer. + */ + jifs = jiffies; + outb_p(0x00, PIT_MODE); /* latch the count ASAP */ + count = inb_p(PIT_CH0); /* read the latched count */ + count |= inb_p(PIT_CH0) << 8; + + /* VIA686a test code... reset the latch if count > max + 1 */ + if (count > LATCH) { + outb_p(0x34, PIT_MODE); + outb_p(LATCH & 0xff, PIT_CH0); + outb(LATCH >> 8, PIT_CH0); + count = LATCH - 1; + } + + /* + * It's possible for count to appear to go the wrong way for a + * couple of reasons: + * + * 1. The timer counter underflows, but we haven't handled the + * resulting interrupt and incremented jiffies yet. + * 2. Hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * (see c't 95/10 page 335 for Neptun bug.) + * + * Previous attempts to handle these cases intelligently were + * buggy, so we just do the simple thing now. + */ + if (count > old_count && jifs == old_jifs) { + count = old_count; + } + old_count = count; + old_jifs = jifs; + + spin_unlock_irqrestore(&i8253_lock, flags); + + count = (LATCH - 1) - count; + + return (cycle_t)(jifs * LATCH) + count; +} + +static struct clocksource clocksource_pit = { + .name = "pit", + .rating = 110, + .read = pit_read, + .mask = CLOCKSOURCE_MASK(32), + .mult = 0, + .shift = 20, +}; + +static int __init init_pit_clocksource(void) +{ + if (num_possible_cpus() > 1) /* PIT does not scale! */ + return 0; + + clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20); + return clocksource_register(&clocksource_pit); +} +arch_initcall(init_pit_clocksource); + +#endif diff --git a/arch/x86/kernel/i8253_32.c b/arch/x86/kernel/i8253_32.c deleted file mode 100644 index ac15e4cbd9c1..000000000000 --- a/arch/x86/kernel/i8253_32.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * i8253.c 8253/PIT functions - * - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -DEFINE_SPINLOCK(i8253_lock); -EXPORT_SYMBOL(i8253_lock); - -/* - * HPET replaces the PIT, when enabled. So we need to know, which of - * the two timers is used - */ -struct clock_event_device *global_clock_event; - -/* - * Initialize the PIT timer. - * - * This is also called after resume to bring the PIT into operation again. - */ -static void init_pit_timer(enum clock_event_mode mode, - struct clock_event_device *evt) -{ - unsigned long flags; - - spin_lock_irqsave(&i8253_lock, flags); - - switch(mode) { - case CLOCK_EVT_MODE_PERIODIC: - /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(0x34, PIT_MODE); - outb_p(LATCH & 0xff , PIT_CH0); /* LSB */ - outb(LATCH >> 8 , PIT_CH0); /* MSB */ - break; - - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: - if (evt->mode == CLOCK_EVT_MODE_PERIODIC || - evt->mode == CLOCK_EVT_MODE_ONESHOT) { - outb_p(0x30, PIT_MODE); - outb_p(0, PIT_CH0); - outb_p(0, PIT_CH0); - } - break; - - case CLOCK_EVT_MODE_ONESHOT: - /* One shot setup */ - outb_p(0x38, PIT_MODE); - break; - - case CLOCK_EVT_MODE_RESUME: - /* Nothing to do here */ - break; - } - spin_unlock_irqrestore(&i8253_lock, flags); -} - -/* - * Program the next event in oneshot mode - * - * Delta is given in PIT ticks - */ -static int pit_next_event(unsigned long delta, struct clock_event_device *evt) -{ - unsigned long flags; - - spin_lock_irqsave(&i8253_lock, flags); - outb_p(delta & 0xff , PIT_CH0); /* LSB */ - outb(delta >> 8 , PIT_CH0); /* MSB */ - spin_unlock_irqrestore(&i8253_lock, flags); - - return 0; -} - -/* - * On UP the PIT can serve all of the possible timer functions. On SMP systems - * it can be solely used for the global tick. - * - * The profiling and update capabilites are switched off once the local apic is - * registered. This mechanism replaces the previous #ifdef LOCAL_APIC - - * !using_apic_timer decisions in do_timer_interrupt_hook() - */ -struct clock_event_device pit_clockevent = { - .name = "pit", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = init_pit_timer, - .set_next_event = pit_next_event, - .shift = 32, - .irq = 0, -}; - -/* - * Initialize the conversion factor and the min/max deltas of the clock event - * structure and register the clock event source with the framework. - */ -void __init setup_pit_timer(void) -{ - /* - * Start pit with the boot cpu mask and make it global after the - * IO_APIC has been initialized. - */ - pit_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); - pit_clockevent.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, 32); - pit_clockevent.max_delta_ns = - clockevent_delta2ns(0x7FFF, &pit_clockevent); - pit_clockevent.min_delta_ns = - clockevent_delta2ns(0xF, &pit_clockevent); - clockevents_register_device(&pit_clockevent); - global_clock_event = &pit_clockevent; -} - -#ifndef CONFIG_X86_64 -/* - * Since the PIT overflows every tick, its not very useful - * to just read by itself. So use jiffies to emulate a free - * running counter: - */ -static cycle_t pit_read(void) -{ - unsigned long flags; - int count; - u32 jifs; - static int old_count; - static u32 old_jifs; - - spin_lock_irqsave(&i8253_lock, flags); - /* - * Although our caller may have the read side of xtime_lock, - * this is now a seqlock, and we are cheating in this routine - * by having side effects on state that we cannot undo if - * there is a collision on the seqlock and our caller has to - * retry. (Namely, old_jifs and old_count.) So we must treat - * jiffies as volatile despite the lock. We read jiffies - * before latching the timer count to guarantee that although - * the jiffies value might be older than the count (that is, - * the counter may underflow between the last point where - * jiffies was incremented and the point where we latch the - * count), it cannot be newer. - */ - jifs = jiffies; - outb_p(0x00, PIT_MODE); /* latch the count ASAP */ - count = inb_p(PIT_CH0); /* read the latched count */ - count |= inb_p(PIT_CH0) << 8; - - /* VIA686a test code... reset the latch if count > max + 1 */ - if (count > LATCH) { - outb_p(0x34, PIT_MODE); - outb_p(LATCH & 0xff, PIT_CH0); - outb(LATCH >> 8, PIT_CH0); - count = LATCH - 1; - } - - /* - * It's possible for count to appear to go the wrong way for a - * couple of reasons: - * - * 1. The timer counter underflows, but we haven't handled the - * resulting interrupt and incremented jiffies yet. - * 2. Hardware problem with the timer, not giving us continuous time, - * the counter does small "jumps" upwards on some Pentium systems, - * (see c't 95/10 page 335 for Neptun bug.) - * - * Previous attempts to handle these cases intelligently were - * buggy, so we just do the simple thing now. - */ - if (count > old_count && jifs == old_jifs) { - count = old_count; - } - old_count = count; - old_jifs = jifs; - - spin_unlock_irqrestore(&i8253_lock, flags); - - count = (LATCH - 1) - count; - - return (cycle_t)(jifs * LATCH) + count; -} - -static struct clocksource clocksource_pit = { - .name = "pit", - .rating = 110, - .read = pit_read, - .mask = CLOCKSOURCE_MASK(32), - .mult = 0, - .shift = 20, -}; - -static int __init init_pit_clocksource(void) -{ - if (num_possible_cpus() > 1) /* PIT does not scale! */ - return 0; - - clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20); - return clocksource_register(&clocksource_pit); -} -arch_initcall(init_pit_clocksource); - -#endif diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index aca081c37884..e0134d6c88da 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -150,48 +150,6 @@ int update_persistent_clock(struct timespec now) return set_rtc_mmss(now.tv_sec); } -void main_timer_handler(void) -{ -/* - * Here we are in the timer irq handler. We have irqs locally disabled (so we - * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running - * on the other CPU, so we need a lock. We also need to lock the vsyscall - * variables, because both do_timer() and us change them -arca+vojtech - */ - - write_seqlock(&xtime_lock); - -/* - * Do the timer stuff. - */ - - do_timer(1); -#ifndef CONFIG_SMP - update_process_times(user_mode(get_irq_regs())); -#endif - -/* - * In the SMP case we use the local APIC timer interrupt to do the profiling, - * except when we simulate SMP mode on a uniprocessor system, in that case we - * have to call the local interrupt handler. - */ - - if (!using_apic_timer) - smp_local_timer_interrupt(); - - write_sequnlock(&xtime_lock); -} - -static irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - if (apic_runs_main_timer > 1) - return IRQ_HANDLED; - main_timer_handler(); - if (using_apic_timer) - smp_send_timer_broadcast_ipi(); - return IRQ_HANDLED; -} - static irqreturn_t timer_event_interrupt(int irq, void *dev_id) { add_pda(irq0_irqs, 1); diff --git a/include/asm-x86/apic_64.h b/include/asm-x86/apic_64.h index f32f654d1f96..3c8f21eef0be 100644 --- a/include/asm-x86/apic_64.h +++ b/include/asm-x86/apic_64.h @@ -79,8 +79,6 @@ extern void smp_local_timer_interrupt (void); extern void setup_boot_APIC_clock (void); extern void setup_secondary_APIC_clock (void); extern int APIC_init_uniprocessor (void); -extern void disable_APIC_timer(void); -extern void enable_APIC_timer(void); extern void setup_apic_routing(void); extern void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector, @@ -95,10 +93,6 @@ extern int apic_is_clustered_box(void); #define K8_APIC_EXT_INT_MSG_EXT 0x7 #define K8_APIC_EXT_LVT_ENTRY_THRESHOLD 0 -void smp_send_timer_broadcast_ipi(void); -void switch_APIC_timer_to_ipi(void *cpumask); -void switch_ipi_to_APIC_timer(void *cpumask); - #define ARCH_APICTIMER_STOPS_ON_C3 1 extern unsigned boot_cpu_id; diff --git a/include/asm-x86/proto.h b/include/asm-x86/proto.h index 31f20ad65876..c44a3a93b5a4 100644 --- a/include/asm-x86/proto.h +++ b/include/asm-x86/proto.h @@ -51,9 +51,6 @@ extern void reserve_bootmem_generic(unsigned long phys, unsigned len); extern void load_gs_index(unsigned gs); -extern void stop_timer_interrupt(void); -extern void main_timer_handler(void); - extern unsigned long end_pfn_map; extern void show_trace(struct task_struct *, struct pt_regs *, unsigned long * rsp); @@ -90,14 +87,10 @@ extern int timer_over_8254; extern int gsi_irq_sharing(int gsi); -extern void smp_local_timer_interrupt(void); - extern int force_mwait; long do_arch_prctl(struct task_struct *task, int code, unsigned long addr); -void i8254_timer_resume(void); - #define round_up(x,y) (((x) + (y) - 1) & ~((y)-1)) #define round_down(x,y) ((x) & ~((y)-1)) -- cgit v1.2.3 From bc1d99c1de85e826765318738e00e6c4e9a0cd12 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86_64: cleanup apic.c after clock events switch Make variables static. Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/apic_64.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 1231365404c4..395928de28ea 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -41,9 +41,8 @@ #include int apic_verbosity; -int apic_calibrate_pmtmr __initdata; - int disable_apic_timer __cpuinitdata; +static int apic_calibrate_pmtmr __initdata; /* Local APIC timer works in C2? */ int local_apic_timer_c2_ok; @@ -247,7 +246,10 @@ void disconnect_bsp_APIC(int virt_wire_setup) apic_write(APIC_SPIV, value); if (!virt_wire_setup) { - /* For LVT0 make it edge triggered, active high, external and enabled */ + /* + * For LVT0 make it edge triggered, active high, + * external and enabled + */ value = apic_read(APIC_LVT0); value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | @@ -483,10 +485,12 @@ void __cpuinit setup_local_APIC (void) value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; if (!smp_processor_id() && !value) { value = APIC_DM_EXTINT; - apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", smp_processor_id()); + apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", + smp_processor_id()); } else { value = APIC_DM_EXTINT | APIC_LVT_MASKED; - apic_printk(APIC_VERBOSE, "masked ExtINT on CPU#%d\n", smp_processor_id()); + apic_printk(APIC_VERBOSE, "masked ExtINT on CPU#%d\n", + smp_processor_id()); } apic_write(APIC_LVT0, value); @@ -793,12 +797,14 @@ void __init init_apic_mappings(void) if (smp_found_config) { ioapic_phys = mp_ioapics[i].mpc_apicaddr; } else { - ioapic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); + ioapic_phys = (unsigned long) + alloc_bootmem_pages(PAGE_SIZE); ioapic_phys = __pa(ioapic_phys); } set_fixmap_nocache(idx, ioapic_phys); - apic_printk(APIC_VERBOSE,"mapped IOAPIC to %016lx (%016lx)\n", - __fix_to_virt(idx), ioapic_phys); + apic_printk(APIC_VERBOSE, + "mapped IOAPIC to %016lx (%016lx)\n", + __fix_to_virt(idx), ioapic_phys); idx++; if (ioapic_res != NULL) { -- cgit v1.2.3 From 5b94664a3a2a4f3c516a4a2f454f3c811cb25959 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: jiffies: remove unused macros The x86 hpet cleanups allow removal of some unused macros. Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- include/linux/jiffies.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index c080f61fb024..d7a5e034c3a2 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -36,8 +36,6 @@ /* LATCH is used in the interval timer and ftape setup. */ #define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ -#define LATCH_HPET ((HPET_TICK_RATE + HZ/2) / HZ) - /* Suppose we want to devide two numbers NOM and DEN: NOM/DEN, the we can * improve accuracy by shifting LSH bits, hence calculating: * (NOM << LSH) / DEN @@ -53,13 +51,9 @@ /* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */ #define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8)) -#define ACTHZ_HPET (SH_DIV (HPET_TICK_RATE, LATCH_HPET, 8)) - /* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */ #define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8)) -#define TICK_NSEC_HPET (SH_DIV(1000000UL * 1000, ACTHZ_HPET, 8)) - /* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */ #define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ) -- cgit v1.2.3 From 43ca7ec96f01bd921e772112729005d4474fdbf4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: ACPI: remove the now unused ifdef code The conversion of x86-64 to clock events makes the #ifdef CONFIG_GENERIC_CLOCKEVENTS n the timer broadcast functions useless. Remove it. Signed-off-by: Thomas Gleixner Cc: john stultz Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- drivers/acpi/processor_idle.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 1e8287b4f40c..1f6fb38de017 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -276,21 +276,12 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { -#ifdef CONFIG_GENERIC_CLOCKEVENTS unsigned long reason; reason = pr->power.timer_broadcast_on_state < INT_MAX ? CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; clockevents_notify(reason, &pr->id); -#else - cpumask_t mask = cpumask_of_cpu(pr->id); - - if (pr->power.timer_broadcast_on_state < INT_MAX) - on_each_cpu(switch_APIC_timer_to_ipi, &mask, 1, 1); - else - on_each_cpu(switch_ipi_to_APIC_timer, &mask, 1, 1); -#endif } /* Power(C) State timer broadcast control */ @@ -298,8 +289,6 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, int broadcast) { -#ifdef CONFIG_GENERIC_CLOCKEVENTS - int state = cx - pr->power.states; if (state >= pr->power.timer_broadcast_on_state) { @@ -309,7 +298,6 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr, CLOCK_EVT_NOTIFY_BROADCAST_EXIT; clockevents_notify(reason, &pr->id); } -#endif } #else -- cgit v1.2.3 From 39d0b7ba7b9c0a2c594073400cde1d0beffa0bea Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: i386: Remove the useless #ifdef in i8253.h Signed-off-by: Thomas Gleixner Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven --- include/asm-x86/i8253.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/asm-x86/i8253.h b/include/asm-x86/i8253.h index 28cf67da59a3..747548ec5d1d 100644 --- a/include/asm-x86/i8253.h +++ b/include/asm-x86/i8253.h @@ -8,12 +8,8 @@ extern spinlock_t i8253_lock; -#ifdef CONFIG_GENERIC_CLOCKEVENTS - extern struct clock_event_device *global_clock_event; extern void setup_pit_timer(void); -#endif - #endif /* __ASM_I8253_H__ */ -- cgit v1.2.3 From 31c435d75e7d15a5f965c5eb0e33fe0e236f49a4 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: i386/x8664: cleanup the shared hpet code Remove hpet_readl/writel from vsyscall.h, where it does not belong anyway. Use the hpet code itself. Signed-off-by: Chris Wright Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Arjan van de Ven --- arch/x86/kernel/hpet.c | 6 +----- include/asm-x86/hpet.h | 2 ++ include/asm-x86/vsyscall.h | 3 --- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index dbe0e1d44113..078dbc6d80ec 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -25,11 +25,7 @@ unsigned long hpet_address; static void __iomem *hpet_virt_address; -/* Temporary hack. Cleanup after x86_64 clock events conversion */ -#undef hpet_readl -#undef hpet_writel - -static inline unsigned long hpet_readl(unsigned long a) +unsigned long hpet_readl(unsigned long a) { return readl(hpet_virt_address + a); } diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h index e2976ed30b70..64446a9577dc 100644 --- a/include/asm-x86/hpet.h +++ b/include/asm-x86/hpet.h @@ -65,6 +65,7 @@ extern unsigned long hpet_address; extern int is_hpet_enabled(void); extern int hpet_enable(void); +extern unsigned long hpet_readl(unsigned long a); #ifdef CONFIG_HPET_EMULATE_RTC @@ -84,6 +85,7 @@ extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id); #else static inline int hpet_enable(void) { return 0; } +static inline unsigned long hpet_readl(unsigned long a) { return 0; } #endif /* CONFIG_HPET_TIMER */ #endif /* ASM_X86_HPET_H */ diff --git a/include/asm-x86/vsyscall.h b/include/asm-x86/vsyscall.h index 3b8ceb4af2cf..f01c49f5d108 100644 --- a/include/asm-x86/vsyscall.h +++ b/include/asm-x86/vsyscall.h @@ -29,9 +29,6 @@ enum vsyscall_num { #define VGETCPU_RDTSCP 1 #define VGETCPU_LSL 2 -#define hpet_readl(a) readl((const void __iomem *)fix_to_virt(FIX_HPET_BASE) + a) -#define hpet_writel(d,a) writel(d, (void __iomem *)fix_to_virt(FIX_HPET_BASE) + a) - extern int __vgetcpu_mode; extern volatile unsigned long __jiffies; -- cgit v1.2.3 From 4a93232dab0a07074bcc5291a0f1f39919916f31 Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: clock events: allow replacement of broadcast timer Change the broadcast timer, if a timer with higher rating becomes available. Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- kernel/time/tick-broadcast.c | 13 ++++++------- kernel/time/tick-common.c | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index acf15b49e55b..298bc7c6f09f 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -64,8 +64,9 @@ static void tick_broadcast_start_periodic(struct clock_event_device *bc) */ int tick_check_broadcast_device(struct clock_event_device *dev) { - if (tick_broadcast_device.evtdev || - (dev->features & CLOCK_EVT_FEAT_C3STOP)) + if ((tick_broadcast_device.evtdev && + tick_broadcast_device.evtdev->rating >= dev->rating) || + (dev->features & CLOCK_EVT_FEAT_C3STOP)) return 0; clockevents_exchange_device(NULL, dev); @@ -513,11 +514,9 @@ static void tick_broadcast_clear_oneshot(int cpu) */ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { - if (bc->mode != CLOCK_EVT_MODE_ONESHOT) { - bc->event_handler = tick_handle_oneshot_broadcast; - clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); - bc->next_event.tv64 = KTIME_MAX; - } + bc->event_handler = tick_handle_oneshot_broadcast; + clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); + bc->next_event.tv64 = KTIME_MAX; } /* diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 77a21abc8716..3f3ae3907830 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -200,7 +200,7 @@ static int tick_check_new_device(struct clock_event_device *newdev) cpu = smp_processor_id(); if (!cpu_isset(cpu, newdev->cpumask)) - goto out; + goto out_bc; td = &per_cpu(tick_cpu_device, cpu); curdev = td->evtdev; @@ -265,7 +265,7 @@ out_bc: */ if (tick_check_broadcast_device(newdev)) ret = NOTIFY_STOP; -out: + spin_unlock_irqrestore(&tick_device_lock, flags); return ret; -- cgit v1.2.3 From 610bf2f143b9c5cda768a2d428d66d3a16769930 Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86: HPET restructure hpet code for hpet force enable Restructure and rename legacy replacement mode HPET timer support. Just the code structural changes and should be zero functionality change. Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/hpet.c | 148 +++++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 65 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 078dbc6d80ec..ce3bcc4334d5 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -149,9 +149,9 @@ static void hpet_reserve_platform_timers(unsigned long id) { } */ static unsigned long hpet_period; -static void hpet_set_mode(enum clock_event_mode mode, +static void hpet_legacy_set_mode(enum clock_event_mode mode, struct clock_event_device *evt); -static int hpet_next_event(unsigned long delta, +static int hpet_legacy_next_event(unsigned long delta, struct clock_event_device *evt); /* @@ -160,8 +160,8 @@ static int hpet_next_event(unsigned long delta, static struct clock_event_device hpet_clockevent = { .name = "hpet", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = hpet_set_mode, - .set_next_event = hpet_next_event, + .set_mode = hpet_legacy_set_mode, + .set_next_event = hpet_legacy_next_event, .shift = 32, .irq = 0, }; @@ -178,7 +178,7 @@ static void hpet_start_counter(void) hpet_writel(cfg, HPET_CFG); } -static void hpet_enable_int(void) +static void hpet_enable_legacy_int(void) { unsigned long cfg = hpet_readl(HPET_CFG); @@ -187,7 +187,39 @@ static void hpet_enable_int(void) hpet_legacy_int_enabled = 1; } -static void hpet_set_mode(enum clock_event_mode mode, +static void hpet_legacy_clockevent_register(void) +{ + uint64_t hpet_freq; + + /* Start HPET legacy interrupts */ + hpet_enable_legacy_int(); + + /* + * The period is a femto seconds value. We need to calculate the + * scaled math multiplication factor for nanosecond to hpet tick + * conversion. + */ + hpet_freq = 1000000000000000ULL; + do_div(hpet_freq, hpet_period); + hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, + NSEC_PER_SEC, 32); + /* Calculate the min / max delta */ + hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, + &hpet_clockevent); + hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, + &hpet_clockevent); + + /* + * Start hpet with the boot cpu mask and make it + * global after the IO_APIC has been initialized. + */ + hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); + clockevents_register_device(&hpet_clockevent); + global_clock_event = &hpet_clockevent; + printk(KERN_DEBUG "hpet clockevent registered\n"); +} + +static void hpet_legacy_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { unsigned long cfg, cmp, now; @@ -228,12 +260,12 @@ static void hpet_set_mode(enum clock_event_mode mode, break; case CLOCK_EVT_MODE_RESUME: - hpet_enable_int(); + hpet_enable_legacy_int(); break; } } -static int hpet_next_event(unsigned long delta, +static int hpet_legacy_next_event(unsigned long delta, struct clock_event_device *evt) { unsigned long cnt; @@ -273,58 +305,11 @@ static struct clocksource clocksource_hpet = { #endif }; -/* - * Try to setup the HPET timer - */ -int __init hpet_enable(void) +static int hpet_clocksource_register(void) { - unsigned long id; - uint64_t hpet_freq; u64 tmp, start, now; cycle_t t1; - if (!is_hpet_capable()) - return 0; - - hpet_set_mapping(); - - /* - * Read the period and check for a sane value: - */ - hpet_period = hpet_readl(HPET_PERIOD); - if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) - goto out_nohpet; - - /* - * The period is a femto seconds value. We need to calculate the - * scaled math multiplication factor for nanosecond to hpet tick - * conversion. - */ - hpet_freq = 1000000000000000ULL; - do_div(hpet_freq, hpet_period); - hpet_clockevent.mult = div_sc((unsigned long) hpet_freq, - NSEC_PER_SEC, 32); - /* Calculate the min / max delta */ - hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, - &hpet_clockevent); - hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30, - &hpet_clockevent); - - /* - * Read the HPET ID register to retrieve the IRQ routing - * information and the number of channels - */ - id = hpet_readl(HPET_ID); - -#ifdef CONFIG_HPET_EMULATE_RTC - /* - * The legacy routing mode needs at least two channels, tick timer - * and the rtc emulation channel. - */ - if (!(id & HPET_ID_NUMBER)) - goto out_nohpet; -#endif - /* Start the counter */ hpet_start_counter(); @@ -346,7 +331,7 @@ int __init hpet_enable(void) if (t1 == read_hpet()) { printk(KERN_WARNING "HPET counter not counting. HPET disabled\n"); - goto out_nohpet; + return -ENODEV; } /* Initialize and register HPET clocksource @@ -367,15 +352,48 @@ int __init hpet_enable(void) clocksource_register(&clocksource_hpet); + return 0; +} + +/* + * Try to setup the HPET timer + */ +int __init hpet_enable(void) +{ + unsigned long id; + + if (!is_hpet_capable()) + return 0; + + hpet_set_mapping(); + + /* + * Read the period and check for a sane value: + */ + hpet_period = hpet_readl(HPET_PERIOD); + if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) + goto out_nohpet; + + /* + * Read the HPET ID register to retrieve the IRQ routing + * information and the number of channels + */ + id = hpet_readl(HPET_ID); + +#ifdef CONFIG_HPET_EMULATE_RTC + /* + * The legacy routing mode needs at least two channels, tick timer + * and the rtc emulation channel. + */ + if (!(id & HPET_ID_NUMBER)) + goto out_nohpet; +#endif + + if (hpet_clocksource_register()) + goto out_nohpet; + if (id & HPET_ID_LEGSUP) { - hpet_enable_int(); - /* - * Start hpet with the boot cpu mask and make it - * global after the IO_APIC has been initialized. - */ - hpet_clockevent.cpumask = cpumask_of_cpu(smp_processor_id()); - clockevents_register_device(&hpet_clockevent); - global_clock_event = &hpet_clockevent; + hpet_legacy_clockevent_register(); return 1; } return 0; -- cgit v1.2.3 From d54bd57d6580250e6551261f3b15c45a9d90c77b Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86: HPET force enable o ICH7 and later Force detect and/or enable HPET on ICH chipsets. This patch just handles the detection part and following patches use this information. Adds a function to repeat the force enabling during resume time. Using HPET this way, instead of PIT increases the time CPUs can reside in C-state when system is totally idle. On my test system with Core 2 Duo, average C-state residency goes up from ~20mS to ~80mS. [ Build fixed from Andrew Morton ] Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/quirks.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ include/asm-x86/hpet.h | 1 + 2 files changed, 103 insertions(+) diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index 6722469c2633..d3ac703867d6 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -4,6 +4,8 @@ #include #include +#include + #if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) && defined(CONFIG_PCI) static void __devinit quirk_intel_irqbalance(struct pci_dev *dev) @@ -47,3 +49,103 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quir DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_intel_irqbalance); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_intel_irqbalance); #endif + +#if defined(CONFIG_HPET_TIMER) +unsigned long force_hpet_address; + +static void __iomem *rcba_base; + +void ich_force_hpet_resume(void) +{ + u32 val; + + if (!force_hpet_address) + return; + + if (rcba_base == NULL) + BUG(); + + /* read the Function Disable register, dword mode only */ + val = readl(rcba_base + 0x3404); + if (!(val & 0x80)) { + /* HPET disabled in HPTC. Trying to enable */ + writel(val | 0x80, rcba_base + 0x3404); + } + + val = readl(rcba_base + 0x3404); + if (!(val & 0x80)) + BUG(); + else + printk(KERN_DEBUG "Force enabled HPET at resume\n"); + + return; +} + +static void ich_force_enable_hpet(struct pci_dev *dev) +{ + u32 val; + u32 uninitialized_var(rcba); + int err = 0; + + if (hpet_address || force_hpet_address) + return; + + pci_read_config_dword(dev, 0xF0, &rcba); + rcba &= 0xFFFFC000; + if (rcba == 0) { + printk(KERN_DEBUG "RCBA disabled. Cannot force enable HPET\n"); + return; + } + + /* use bits 31:14, 16 kB aligned */ + rcba_base = ioremap_nocache(rcba, 0x4000); + if (rcba_base == NULL) { + printk(KERN_DEBUG "ioremap failed. Cannot force enable HPET\n"); + return; + } + + /* read the Function Disable register, dword mode only */ + val = readl(rcba_base + 0x3404); + + if (val & 0x80) { + /* HPET is enabled in HPTC. Just not reported by BIOS */ + val = val & 0x3; + force_hpet_address = 0xFED00000 | (val << 12); + printk(KERN_DEBUG "Force enabled HPET at base address 0x%lx\n", + force_hpet_address); + iounmap(rcba_base); + return; + } + + /* HPET disabled in HPTC. Trying to enable */ + writel(val | 0x80, rcba_base + 0x3404); + + val = readl(rcba_base + 0x3404); + if (!(val & 0x80)) { + err = 1; + } else { + val = val & 0x3; + force_hpet_address = 0xFED00000 | (val << 12); + } + + if (err) { + force_hpet_address = 0; + iounmap(rcba_base); + printk(KERN_DEBUG "Failed to force enable HPET\n"); + } else { + printk(KERN_DEBUG "Force enabled HPET at base address 0x%lx\n", + force_hpet_address); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, + ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, + ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, + ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, + ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, + ich_force_enable_hpet); +#endif diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h index 64446a9577dc..7c933242365f 100644 --- a/include/asm-x86/hpet.h +++ b/include/asm-x86/hpet.h @@ -66,6 +66,7 @@ extern unsigned long hpet_address; extern int is_hpet_enabled(void); extern int hpet_enable(void); extern unsigned long hpet_readl(unsigned long a); +extern void ich_force_hpet_resume(void); #ifdef CONFIG_HPET_EMULATE_RTC -- cgit v1.2.3 From 59c69f2a51b41e9886b85f61c04e8d0d2a35f37b Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:23 +0200 Subject: x86: HPET try to activate force detected hpet Enable HPET later during boot, after the force detect in PCI quirks. Also add a call to repeat the force enabling at resume time. Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/hpet.c | 27 +++++++++++++++++++++++++-- include/asm-x86/hpet.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index ce3bcc4334d5..a6c68915d2a9 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -164,6 +164,7 @@ static struct clock_event_device hpet_clockevent = { .set_next_event = hpet_legacy_next_event, .shift = 32, .irq = 0, + .rating = 50, }; static void hpet_start_counter(void) @@ -178,6 +179,17 @@ static void hpet_start_counter(void) hpet_writel(cfg, HPET_CFG); } +static void hpet_resume_device(void) +{ + ich_force_hpet_resume(); +} + +static void hpet_restart_counter(void) +{ + hpet_resume_device(); + hpet_start_counter(); +} + static void hpet_enable_legacy_int(void) { unsigned long cfg = hpet_readl(HPET_CFG); @@ -299,7 +311,7 @@ static struct clocksource clocksource_hpet = { .mask = HPET_MASK, .shift = HPET_SHIFT, .flags = CLOCK_SOURCE_IS_CONTINUOUS, - .resume = hpet_start_counter, + .resume = hpet_restart_counter, #ifdef CONFIG_X86_64 .vread = vread_hpet, #endif @@ -412,10 +424,21 @@ out_nohpet: */ static __init int hpet_late_init(void) { - if (!is_hpet_capable()) + if (boot_hpet_disable) return -ENODEV; + if (!hpet_address) { + if (!force_hpet_address) + return -ENODEV; + + hpet_address = force_hpet_address; + hpet_enable(); + if (!hpet_virt_address) + return -ENODEV; + } + hpet_reserve_platform_timers(hpet_readl(HPET_ID)); + return 0; } fs_initcall(hpet_late_init); diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h index 7c933242365f..47de4b8cdda9 100644 --- a/include/asm-x86/hpet.h +++ b/include/asm-x86/hpet.h @@ -63,6 +63,7 @@ /* hpet memory map physical address */ extern unsigned long hpet_address; +extern unsigned long force_hpet_address; extern int is_hpet_enabled(void); extern int hpet_enable(void); extern unsigned long hpet_readl(unsigned long a); -- cgit v1.2.3 From bfe0c1cc6456bba1f4e3cc1fe29c0ea578ac763a Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:24 +0200 Subject: x86: HPET force enable for ICH5 force_enable hpet for ICH5. [ Build fixes from Andrew Morton ] Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/hpet.c | 2 +- arch/x86/kernel/quirks.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++- include/asm-x86/hpet.h | 2 +- include/linux/pci_ids.h | 1 + 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index a6c68915d2a9..f8367074da0d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -181,7 +181,7 @@ static void hpet_start_counter(void) static void hpet_resume_device(void) { - ich_force_hpet_resume(); + force_hpet_resume(); } static void hpet_restart_counter(void) diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index d3ac703867d6..79133b1c2595 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -53,9 +53,15 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quir #if defined(CONFIG_HPET_TIMER) unsigned long force_hpet_address; +static enum { + NONE_FORCE_HPET_RESUME, + OLD_ICH_FORCE_HPET_RESUME, + ICH_FORCE_HPET_RESUME +} force_hpet_resume_type; + static void __iomem *rcba_base; -void ich_force_hpet_resume(void) +static void ich_force_hpet_resume(void) { u32 val; @@ -133,6 +139,7 @@ static void ich_force_enable_hpet(struct pci_dev *dev) iounmap(rcba_base); printk(KERN_DEBUG "Failed to force enable HPET\n"); } else { + force_hpet_resume_type = ICH_FORCE_HPET_RESUME; printk(KERN_DEBUG "Force enabled HPET at base address 0x%lx\n", force_hpet_address); } @@ -148,4 +155,98 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, ich_force_enable_hpet); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, ich_force_enable_hpet); + + +static struct pci_dev *cached_dev; + +static void old_ich_force_hpet_resume(void) +{ + u32 val; + u32 uninitialized_var(gen_cntl); + + if (!force_hpet_address || !cached_dev) + return; + + pci_read_config_dword(cached_dev, 0xD0, &gen_cntl); + gen_cntl &= (~(0x7 << 15)); + gen_cntl |= (0x4 << 15); + + pci_write_config_dword(cached_dev, 0xD0, gen_cntl); + pci_read_config_dword(cached_dev, 0xD0, &gen_cntl); + val = gen_cntl >> 15; + val &= 0x7; + if (val == 0x4) + printk(KERN_DEBUG "Force enabled HPET at resume\n"); + else + BUG(); +} + +static void old_ich_force_enable_hpet(struct pci_dev *dev) +{ + u32 val; + u32 uninitialized_var(gen_cntl); + + if (hpet_address || force_hpet_address) + return; + + pci_read_config_dword(dev, 0xD0, &gen_cntl); + /* + * Bit 17 is HPET enable bit. + * Bit 16:15 control the HPET base address. + */ + val = gen_cntl >> 15; + val &= 0x7; + if (val & 0x4) { + val &= 0x3; + force_hpet_address = 0xFED00000 | (val << 12); + printk(KERN_DEBUG "HPET at base address 0x%lx\n", + force_hpet_address); + cached_dev = dev; + return; + } + + /* + * HPET is disabled. Trying enabling at FED00000 and check + * whether it sticks + */ + gen_cntl &= (~(0x7 << 15)); + gen_cntl |= (0x4 << 15); + pci_write_config_dword(dev, 0xD0, gen_cntl); + + pci_read_config_dword(dev, 0xD0, &gen_cntl); + + val = gen_cntl >> 15; + val &= 0x7; + if (val & 0x4) { + /* HPET is enabled in HPTC. Just not reported by BIOS */ + val &= 0x3; + force_hpet_address = 0xFED00000 | (val << 12); + printk(KERN_DEBUG "Force enabled HPET at base address 0x%lx\n", + force_hpet_address); + force_hpet_resume_type = OLD_ICH_FORCE_HPET_RESUME; + return; + } + + printk(KERN_DEBUG "Failed to force enable HPET\n"); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, + old_ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_12, + old_ich_force_enable_hpet); + +void force_hpet_resume(void) +{ + switch (force_hpet_resume_type) { + case ICH_FORCE_HPET_RESUME: + return ich_force_hpet_resume(); + + case OLD_ICH_FORCE_HPET_RESUME: + return old_ich_force_hpet_resume(); + + default: + break; + } +} + #endif diff --git a/include/asm-x86/hpet.h b/include/asm-x86/hpet.h index 47de4b8cdda9..d4ab6db050b6 100644 --- a/include/asm-x86/hpet.h +++ b/include/asm-x86/hpet.h @@ -67,7 +67,7 @@ extern unsigned long force_hpet_address; extern int is_hpet_enabled(void); extern int hpet_enable(void); extern unsigned long hpet_readl(unsigned long a); -extern void ich_force_hpet_resume(void); +extern void force_hpet_resume(void); #ifdef CONFIG_HPET_EMULATE_RTC diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3948708c42ca..584741bb73b3 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2242,6 +2242,7 @@ #define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 #define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 #define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db +#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc #define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd #define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 #define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 -- cgit v1.2.3 From 32a2da64c26d3c886c35f60299259a3497ff96bb Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:24 +0200 Subject: x86: HPET force enable ICH5 suspend/resume fix A bugfix in ich5 hpet force detect which caused resumes to fail. Thanks to Udo A Steinberg for reporting the problem. Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Arjan van de Ven Signed-off-by: Thomas Gleixner --- arch/x86/kernel/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index 79133b1c2595..44e36c59cbde 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -201,7 +201,6 @@ static void old_ich_force_enable_hpet(struct pci_dev *dev) force_hpet_address = 0xFED00000 | (val << 12); printk(KERN_DEBUG "HPET at base address 0x%lx\n", force_hpet_address); - cached_dev = dev; return; } @@ -223,6 +222,7 @@ static void old_ich_force_enable_hpet(struct pci_dev *dev) force_hpet_address = 0xFED00000 | (val << 12); printk(KERN_DEBUG "Force enabled HPET at base address 0x%lx\n", force_hpet_address); + cached_dev = dev; force_hpet_resume_type = OLD_ICH_FORCE_HPET_RESUME; return; } -- cgit v1.2.3 From ed6fb174eea8869e88d8bc506a55f3ef76fcb7ed Mon Sep 17 00:00:00 2001 From: Venki Pallipadi Date: Fri, 12 Oct 2007 23:04:24 +0200 Subject: x86: HPET add another ICH7 PCI id Add another PCI ID for ICH7 force hpet. Signed-off-by: Venkatesh Pallipadi Cc: Andi Kleen Cc: john stultz Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Signed-off-by: Arjan van de Ven --- arch/x86/kernel/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index 44e36c59cbde..d769e204f942 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -149,6 +149,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0, ich_force_enable_hpet); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, ich_force_enable_hpet); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, + ich_force_enable_hpet); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, ich_force_enable_hpet); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, -- cgit v1.2.3