aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-12 15:39:39 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-12 15:39:39 -0700
commit57c5b9998ea05a90ebacaa13c45f985ffe09dbe9 (patch)
tree89ce5149dbba2d9f827f77d35b3b7f8609f85ea9 /arch
parenta6e3d7dba92e19acffaa36aad962741a762aa8c5 (diff)
parented6fb174eea8869e88d8bc506a55f3ef76fcb7ed (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/tglx/linux-2.6-x86
* git://git.kernel.org/pub/scm/linux/kernel/git/tglx/linux-2.6-x86: (40 commits) x86: HPET add another ICH7 PCI id x86: HPET force enable ICH5 suspend/resume fix x86: HPET force enable for ICH5 x86: HPET try to activate force detected hpet x86: HPET force enable o ICH7 and later x86: HPET restructure hpet code for hpet force enable clock events: allow replacement of broadcast timer i386/x8664: cleanup the shared hpet code i386: Remove the useless #ifdef in i8253.h ACPI: remove the now unused ifdef code jiffies: remove unused macros x86_64: cleanup apic.c after clock events switch x86_64: remove now unused code x86: unify timex.h variants x86: kill 8253pit.h x86: disable apic timer for AMD C1E enabled CPUs x86: Fix irq0 / local apic timer accounting x86_64: convert to clock events x86_64: Add (not yet used) clock event functions x86_64: prepare idle loop for dynamic ticks ...
Diffstat (limited to 'arch')
-rw-r--r--arch/i386/Kconfig10
-rw-r--r--arch/x86/kernel/Makefile_326
-rw-r--r--arch/x86/kernel/Makefile_644
-rw-r--r--arch/x86/kernel/apic_64.c356
-rw-r--r--arch/x86/kernel/geode_32.c4
-rw-r--r--arch/x86/kernel/hpet.c (renamed from arch/x86/kernel/hpet_32.c)250
-rw-r--r--arch/x86/kernel/hpet_64.c493
-rw-r--r--arch/x86/kernel/i8253.c (renamed from arch/x86/kernel/i8253_32.c)4
-rw-r--r--arch/x86/kernel/i8259_32.c1
-rw-r--r--arch/x86/kernel/i8259_64.c46
-rw-r--r--arch/x86/kernel/mfgpt_32.c362
-rw-r--r--arch/x86/kernel/nmi_32.c3
-rw-r--r--arch/x86/kernel/nmi_64.c2
-rw-r--r--arch/x86/kernel/process_64.c4
-rw-r--r--arch/x86/kernel/quirks.c205
-rw-r--r--arch/x86/kernel/setup_64.c34
-rw-r--r--arch/x86/kernel/smpboot_64.c4
-rw-r--r--arch/x86/kernel/time_32.c3
-rw-r--r--arch/x86/kernel/time_64.c177
-rw-r--r--arch/x86/kernel/tsc_64.c93
-rw-r--r--arch/x86_64/Kconfig14
21 files changed, 1091 insertions, 984 deletions
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index 2d85e4b8730..6bbbc2755e4 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/Makefile_32 b/arch/x86/kernel/Makefile_32
index c624193740f..7ff02063b85 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,9 +37,9 @@ 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
+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/Makefile_64 b/arch/x86/kernel/Makefile_64
index 3ab017a0a3b..43da66213a4 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.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 925758dbca0..395928de28e 100644
--- a/arch/x86/kernel/apic_64.c
+++ b/arch/x86/kernel/apic_64.c
@@ -25,6 +25,7 @@
#include <linux/sysdev.h>
#include <linux/module.h>
#include <linux/ioport.h>
+#include <linux/clockchips.h>
#include <asm/atomic.h>
#include <asm/smp.h>
@@ -39,12 +40,9 @@
#include <asm/hpet.h>
#include <asm/apic.h>
-int apic_mapped;
int apic_verbosity;
-int apic_runs_main_timer;
-int apic_calibrate_pmtmr __initdata;
-
-int disable_apic_timer __initdata;
+int disable_apic_timer __cpuinitdata;
+static int apic_calibrate_pmtmr __initdata;
/* Local APIC timer works in C2? */
int local_apic_timer_c2_ok;
@@ -56,14 +54,78 @@ static struct resource lapic_resource = {
.flags = IORESOURCE_MEM | IORESOURCE_BUSY,
};
+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);
+}
+
/*
- * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
- * IPIs in place of local APIC timers
+ * Local APIC timer broadcast function
*/
-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 lapic_timer_broadcast(cpumask_t mask)
+{
+#ifdef CONFIG_SMP
+ send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
+#endif
+}
static void apic_pm_activate(void);
@@ -184,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 |
@@ -420,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);
@@ -706,8 +773,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;
@@ -730,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) {
@@ -758,16 +827,14 @@ void __init init_apic_mappings(void)
* P5 APIC double write bug.
*/
-#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);
@@ -780,44 +847,18 @@ 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);
}
-static void setup_APIC_timer(unsigned int clocks)
+static void setup_APIC_timer(void)
{
- unsigned long flags;
+ 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());
- /* 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);
- }
- __setup_APIC_LVTT(clocks);
- /* 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);
}
/*
@@ -835,17 +876,22 @@ 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;
int result;
+
+ local_irq_disable();
+
/*
* 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(250000000, 0, 0);
apic_start = apic_read(APIC_TMCCT);
#ifdef CONFIG_X86_PM_TIMER
@@ -867,123 +913,62 @@ static int __init calibrate_APIC_clock(void)
result = (apic_start - apic) * 1000L * tsc_khz /
(tsc - tsc_start);
}
- printk("result %d\n", result);
+ local_irq_enable();
+
+ 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;
-}
+ /* 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);
-static unsigned int calibration_result;
+ calibration_result = result / HZ;
+}
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;
-
- local_irq_disable();
+ calibrate_APIC_clock();
- calibration_result = 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.
*/
- setup_APIC_timer(calibration_result);
+ 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");
- local_irq_enable();
+ setup_APIC_timer();
}
void __cpuinit setup_secondary_APIC_clock(void)
{
- local_irq_disable(); /* FIXME: Do we need this? --RR */
- setup_APIC_timer(calibration_result);
- local_irq_enable();
+ 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;
@@ -997,8 +982,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.
@@ -1011,22 +994,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).
+ * 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.
*
- * We might want to decouple profiling from the 'long path',
- * and do the profiling totally in assembly.
- *
- * 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);
}
/*
@@ -1042,11 +1037,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.
*/
@@ -1225,29 +1215,13 @@ 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;
- nohpet = 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)
{
apic_calibrate_pmtmr = 1;
notsc_setup(NULL);
- return setup_apicmaintimer(NULL);
+ return 0;
}
__setup("apicpmtimer", setup_apicpmtimer);
-__setup("noapictimer", setup_noapictimer);
-
diff --git a/arch/x86/kernel/geode_32.c b/arch/x86/kernel/geode_32.c
index 41e8aec4c61..f12d8c5d980 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/hpet_32.c b/arch/x86/kernel/hpet.c
index 533d4932bc7..f8367074da0 100644
--- a/arch/x86/kernel/hpet_32.c
+++ b/arch/x86/kernel/hpet.c
@@ -1,5 +1,6 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/hpet.h>
#include <linux/init.h>
@@ -7,11 +8,11 @@
#include <linux/pm.h>
#include <linux/delay.h>
+#include <asm/fixmap.h>
#include <asm/hpet.h>
+#include <asm/i8253.h>
#include <asm/io.h>
-extern struct clock_event_device *global_clock_event;
-
#define HPET_MASK CLOCKSOURCE_MASK(32)
#define HPET_SHIFT 22
@@ -22,9 +23,9 @@ 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)
+unsigned long hpet_readl(unsigned long a)
{
return readl(hpet_virt_address + a);
}
@@ -34,6 +35,36 @@ static inline void hpet_writel(unsigned long d, unsigned long a)
writel(d, hpet_virt_address + a);
}
+#ifdef CONFIG_X86_64
+
+#include <asm/pgtable.h>
+
+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
*/
@@ -49,6 +80,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);
@@ -83,7 +121,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);
@@ -111,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);
/*
@@ -122,10 +160,11 @@ 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,
+ .rating = 50,
};
static void hpet_start_counter(void)
@@ -140,7 +179,18 @@ static void hpet_start_counter(void)
hpet_writel(cfg, HPET_CFG);
}
-static void hpet_enable_int(void)
+static void hpet_resume_device(void)
+{
+ 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);
@@ -149,7 +199,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;
@@ -190,12 +272,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;
@@ -215,6 +297,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,
@@ -222,61 +311,17 @@ 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
};
-/*
- * 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_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
-
- /*
- * 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();
@@ -298,7 +343,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
@@ -319,27 +364,84 @@ 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();
- hpet_reserve_platform_timers(id);
- /*
- * 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;
out_nohpet:
- iounmap(hpet_virt_address);
- hpet_virt_address = NULL;
+ hpet_clear_mapping();
boot_hpet_disable = 1;