aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/kernel/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/kernel/time.c')
-rw-r--r--arch/sh/kernel/time.c172
1 files changed, 55 insertions, 117 deletions
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c
index d47e775962e..a3a67d151e5 100644
--- a/arch/sh/kernel/time.c
+++ b/arch/sh/kernel/time.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
- * Copyright (C) 2002 - 2006 Paul Mundt
+ * Copyright (C) 2002 - 2007 Paul Mundt
* Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
*
* Some code taken from i386 version.
@@ -15,6 +15,7 @@
#include <linux/profile.h>
#include <linux/timex.h>
#include <linux/sched.h>
+#include <linux/clockchips.h>
#include <asm/clock.h>
#include <asm/rtc.h>
#include <asm/timer.h>
@@ -38,6 +39,14 @@ static int null_rtc_set_time(const time_t secs)
return 0;
}
+/*
+ * Null high precision timer functions for systems lacking one.
+ */
+static cycle_t null_hpt_read(void)
+{
+ return 0;
+}
+
void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
@@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv)
EXPORT_SYMBOL(do_settimeofday);
#endif /* !CONFIG_GENERIC_TIME */
+#ifndef CONFIG_GENERIC_CLOCKEVENTS
/* last time the RTC clock got updated */
static long last_rtc_update;
@@ -138,6 +148,7 @@ void handle_timer_tick(void)
last_rtc_update = xtime.tv_sec - 600;
}
}
+#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
#ifdef CONFIG_PM
int timer_suspend(struct sys_device *dev, pm_message_t state)
@@ -168,136 +179,58 @@ static struct sysdev_class timer_sysclass = {
.resume = timer_resume,
};
-#ifdef CONFIG_NO_IDLE_HZ
-static int timer_dyn_tick_enable(void)
+static int __init timer_init_sysfs(void)
{
- struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
- unsigned long flags;
- int ret = -ENODEV;
-
- if (dyn_tick) {
- spin_lock_irqsave(&dyn_tick->lock, flags);
- ret = 0;
- if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
- ret = dyn_tick->enable();
-
- if (ret == 0)
- dyn_tick->state |= DYN_TICK_ENABLED;
- }
- spin_unlock_irqrestore(&dyn_tick->lock, flags);
- }
+ int ret = sysdev_class_register(&timer_sysclass);
+ if (ret != 0)
+ return ret;
- return ret;
+ sys_timer->dev.cls = &timer_sysclass;
+ return sysdev_register(&sys_timer->dev);
}
+device_initcall(timer_init_sysfs);
-static int timer_dyn_tick_disable(void)
-{
- struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
- unsigned long flags;
- int ret = -ENODEV;
-
- if (dyn_tick) {
- spin_lock_irqsave(&dyn_tick->lock, flags);
- ret = 0;
- if (dyn_tick->state & DYN_TICK_ENABLED) {
- ret = dyn_tick->disable();
-
- if (ret == 0)
- dyn_tick->state &= ~DYN_TICK_ENABLED;
- }
- spin_unlock_irqrestore(&dyn_tick->lock, flags);
- }
-
- return ret;
-}
+void (*board_time_init)(void);
/*
- * Reprogram the system timer for at least the calculated time interval.
- * This function should be called from the idle thread with IRQs disabled,
- * immediately before sleeping.
+ * Shamelessly based on the MIPS and Sparc64 work.
*/
-void timer_dyn_reprogram(void)
-{
- struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
- unsigned long next, seq, flags;
-
- if (!dyn_tick)
- return;
-
- spin_lock_irqsave(&dyn_tick->lock, flags);
- if (dyn_tick->state & DYN_TICK_ENABLED) {
- next = next_timer_interrupt();
- do {
- seq = read_seqbegin(&xtime_lock);
- dyn_tick->reprogram(next - jiffies);
- } while (read_seqretry(&xtime_lock, seq));
- }
- spin_unlock_irqrestore(&dyn_tick->lock, flags);
-}
+static unsigned long timer_ticks_per_nsec_quotient __read_mostly;
+unsigned long sh_hpt_frequency = 0;
+
+#define NSEC_PER_CYC_SHIFT 10
+
+struct clocksource clocksource_sh = {
+ .name = "SuperH",
+ .rating = 200,
+ .mask = CLOCKSOURCE_MASK(32),
+ .read = null_hpt_read,
+ .shift = 16,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
-static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
+static void __init init_sh_clocksource(void)
{
- return sprintf(buf, "%i\n",
- (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
-}
+ if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read)
+ return;
-static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
- size_t count)
-{
- unsigned int enable = simple_strtoul(buf, NULL, 2);
+ clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency,
+ clocksource_sh.shift);
- if (enable)
- timer_dyn_tick_enable();
- else
- timer_dyn_tick_disable();
+ timer_ticks_per_nsec_quotient =
+ clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT);
- return count;
+ clocksource_register(&clocksource_sh);
}
-static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
-/*
- * dyntick=enable|disable
- */
-static char dyntick_str[4] __initdata = "";
-
-static int __init dyntick_setup(char *str)
+#ifdef CONFIG_GENERIC_TIME
+unsigned long long sched_clock(void)
{
- if (str)
- strlcpy(dyntick_str, str, sizeof(dyntick_str));
- return 1;
+ unsigned long long ticks = clocksource_sh.read();
+ return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT;
}
-
-__setup("dyntick=", dyntick_setup);
-#endif
-
-static int __init timer_init_sysfs(void)
-{
- int ret = sysdev_class_register(&timer_sysclass);
- if (ret != 0)
- return ret;
-
- sys_timer->dev.cls = &timer_sysclass;
- ret = sysdev_register(&sys_timer->dev);
-
-#ifdef CONFIG_NO_IDLE_HZ
- if (ret == 0 && sys_timer->dyn_tick) {
- ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick);
-
- /*
- * Turn on dynamic tick after calibrate delay
- * for correct bogomips
- */
- if (ret == 0 && dyntick_str[0] == 'e')
- ret = timer_dyn_tick_enable();
- }
#endif
- return ret;
-}
-device_initcall(timer_init_sysfs);
-
-void (*board_time_init)(void);
-
void __init time_init(void)
{
if (board_time_init)
@@ -316,10 +249,15 @@ void __init time_init(void)
sys_timer = get_sys_timer();
printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);
-#ifdef CONFIG_NO_IDLE_HZ
- if (sys_timer->dyn_tick)
- spin_lock_init(&sys_timer->dyn_tick->lock);
-#endif
+ if (sys_timer->ops->read)
+ clocksource_sh.read = sys_timer->ops->read;
+
+ init_sh_clocksource();
+
+ if (sh_hpt_frequency)
+ printk("Using %lu.%03lu MHz high precision timer.\n",
+ ((sh_hpt_frequency + 500) / 1000) / 1000,
+ ((sh_hpt_frequency + 500) / 1000) % 1000);
#if defined(CONFIG_SH_KGDB)
/*