diff options
Diffstat (limited to 'arch/sh/kernel/time.c')
| -rw-r--r-- | arch/sh/kernel/time.c | 172 |
1 files changed, 55 insertions, 117 deletions
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index d47e775962e9..a3a67d151e52 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | 4 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka |
| 5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | 5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> |
| 6 | * Copyright (C) 2002 - 2006 Paul Mundt | 6 | * Copyright (C) 2002 - 2007 Paul Mundt |
| 7 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | 7 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> |
| 8 | * | 8 | * |
| 9 | * Some code taken from i386 version. | 9 | * Some code taken from i386 version. |
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/profile.h> | 15 | #include <linux/profile.h> |
| 16 | #include <linux/timex.h> | 16 | #include <linux/timex.h> |
| 17 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
| 18 | #include <linux/clockchips.h> | ||
| 18 | #include <asm/clock.h> | 19 | #include <asm/clock.h> |
| 19 | #include <asm/rtc.h> | 20 | #include <asm/rtc.h> |
| 20 | #include <asm/timer.h> | 21 | #include <asm/timer.h> |
| @@ -38,6 +39,14 @@ static int null_rtc_set_time(const time_t secs) | |||
| 38 | return 0; | 39 | return 0; |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 42 | /* | ||
| 43 | * Null high precision timer functions for systems lacking one. | ||
| 44 | */ | ||
| 45 | static cycle_t null_hpt_read(void) | ||
| 46 | { | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 41 | void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; | 50 | void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; |
| 42 | int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; | 51 | int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; |
| 43 | 52 | ||
| @@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv) | |||
| 101 | EXPORT_SYMBOL(do_settimeofday); | 110 | EXPORT_SYMBOL(do_settimeofday); |
| 102 | #endif /* !CONFIG_GENERIC_TIME */ | 111 | #endif /* !CONFIG_GENERIC_TIME */ |
| 103 | 112 | ||
| 113 | #ifndef CONFIG_GENERIC_CLOCKEVENTS | ||
| 104 | /* last time the RTC clock got updated */ | 114 | /* last time the RTC clock got updated */ |
| 105 | static long last_rtc_update; | 115 | static long last_rtc_update; |
| 106 | 116 | ||
| @@ -138,6 +148,7 @@ void handle_timer_tick(void) | |||
| 138 | last_rtc_update = xtime.tv_sec - 600; | 148 | last_rtc_update = xtime.tv_sec - 600; |
| 139 | } | 149 | } |
| 140 | } | 150 | } |
| 151 | #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ | ||
| 141 | 152 | ||
| 142 | #ifdef CONFIG_PM | 153 | #ifdef CONFIG_PM |
| 143 | int timer_suspend(struct sys_device *dev, pm_message_t state) | 154 | int timer_suspend(struct sys_device *dev, pm_message_t state) |
| @@ -168,136 +179,58 @@ static struct sysdev_class timer_sysclass = { | |||
| 168 | .resume = timer_resume, | 179 | .resume = timer_resume, |
| 169 | }; | 180 | }; |
| 170 | 181 | ||
| 171 | #ifdef CONFIG_NO_IDLE_HZ | 182 | static int __init timer_init_sysfs(void) |
| 172 | static int timer_dyn_tick_enable(void) | ||
| 173 | { | 183 | { |
| 174 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | 184 | int ret = sysdev_class_register(&timer_sysclass); |
| 175 | unsigned long flags; | 185 | if (ret != 0) |
| 176 | int ret = -ENODEV; | 186 | return ret; |
| 177 | |||
| 178 | if (dyn_tick) { | ||
| 179 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
| 180 | ret = 0; | ||
| 181 | if (!(dyn_tick->state & DYN_TICK_ENABLED)) { | ||
| 182 | ret = dyn_tick->enable(); | ||
| 183 | |||
| 184 | if (ret == 0) | ||
| 185 | dyn_tick->state |= DYN_TICK_ENABLED; | ||
| 186 | } | ||
| 187 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 188 | } | ||
| 189 | 187 | ||
| 190 | return ret; | 188 | sys_timer->dev.cls = &timer_sysclass; |
| 189 | return sysdev_register(&sys_timer->dev); | ||
| 191 | } | 190 | } |
| 191 | device_initcall(timer_init_sysfs); | ||
| 192 | 192 | ||
| 193 | static int timer_dyn_tick_disable(void) | 193 | void (*board_time_init)(void); |
| 194 | { | ||
| 195 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
| 196 | unsigned long flags; | ||
| 197 | int ret = -ENODEV; | ||
| 198 | |||
| 199 | if (dyn_tick) { | ||
| 200 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
| 201 | ret = 0; | ||
| 202 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
| 203 | ret = dyn_tick->disable(); | ||
| 204 | |||
| 205 | if (ret == 0) | ||
| 206 | dyn_tick->state &= ~DYN_TICK_ENABLED; | ||
| 207 | } | ||
| 208 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 209 | } | ||
| 210 | |||
| 211 | return ret; | ||
| 212 | } | ||
| 213 | 194 | ||
| 214 | /* | 195 | /* |
| 215 | * Reprogram the system timer for at least the calculated time interval. | 196 | * Shamelessly based on the MIPS and Sparc64 work. |
| 216 | * This function should be called from the idle thread with IRQs disabled, | ||
| 217 | * immediately before sleeping. | ||
| 218 | */ | 197 | */ |
| 219 | void timer_dyn_reprogram(void) | 198 | static unsigned long timer_ticks_per_nsec_quotient __read_mostly; |
| 220 | { | 199 | unsigned long sh_hpt_frequency = 0; |
| 221 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | 200 | |
| 222 | unsigned long next, seq, flags; | 201 | #define NSEC_PER_CYC_SHIFT 10 |
| 223 | 202 | ||
| 224 | if (!dyn_tick) | 203 | struct clocksource clocksource_sh = { |
| 225 | return; | 204 | .name = "SuperH", |
| 226 | 205 | .rating = 200, | |
| 227 | spin_lock_irqsave(&dyn_tick->lock, flags); | 206 | .mask = CLOCKSOURCE_MASK(32), |
| 228 | if (dyn_tick->state & DYN_TICK_ENABLED) { | 207 | .read = null_hpt_read, |
| 229 | next = next_timer_interrupt(); | 208 | .shift = 16, |
| 230 | do { | 209 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
| 231 | seq = read_seqbegin(&xtime_lock); | 210 | }; |
| 232 | dyn_tick->reprogram(next - jiffies); | ||
| 233 | } while (read_seqretry(&xtime_lock, seq)); | ||
| 234 | } | ||
| 235 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 236 | } | ||
| 237 | 211 | ||
| 238 | static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) | 212 | static void __init init_sh_clocksource(void) |
| 239 | { | 213 | { |
| 240 | return sprintf(buf, "%i\n", | 214 | if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read) |
| 241 | (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); | 215 | return; |
| 242 | } | ||
| 243 | 216 | ||
| 244 | static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, | 217 | clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency, |
| 245 | size_t count) | 218 | clocksource_sh.shift); |
| 246 | { | ||
| 247 | unsigned int enable = simple_strtoul(buf, NULL, 2); | ||
| 248 | 219 | ||
| 249 | if (enable) | 220 | timer_ticks_per_nsec_quotient = |
| 250 | timer_dyn_tick_enable(); | 221 | clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT); |
| 251 | else | ||
| 252 | timer_dyn_tick_disable(); | ||
| 253 | 222 | ||
| 254 | return count; | 223 | clocksource_register(&clocksource_sh); |
| 255 | } | 224 | } |
| 256 | static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); | ||
| 257 | 225 | ||
| 258 | /* | 226 | #ifdef CONFIG_GENERIC_TIME |
| 259 | * dyntick=enable|disable | 227 | unsigned long long sched_clock(void) |
| 260 | */ | ||
| 261 | static char dyntick_str[4] __initdata = ""; | ||
| 262 | |||
| 263 | static int __init dyntick_setup(char *str) | ||
| 264 | { | 228 | { |
| 265 | if (str) | 229 | unsigned long long ticks = clocksource_sh.read(); |
| 266 | strlcpy(dyntick_str, str, sizeof(dyntick_str)); | 230 | return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT; |
| 267 | return 1; | ||
| 268 | } | 231 | } |
| 269 | |||
| 270 | __setup("dyntick=", dyntick_setup); | ||
| 271 | #endif | ||
| 272 | |||
| 273 | static int __init timer_init_sysfs(void) | ||
| 274 | { | ||
| 275 | int ret = sysdev_class_register(&timer_sysclass); | ||
| 276 | if (ret != 0) | ||
| 277 | return ret; | ||
| 278 | |||
| 279 | sys_timer->dev.cls = &timer_sysclass; | ||
| 280 | ret = sysdev_register(&sys_timer->dev); | ||
| 281 | |||
| 282 | #ifdef CONFIG_NO_IDLE_HZ | ||
| 283 | if (ret == 0 && sys_timer->dyn_tick) { | ||
| 284 | ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); | ||
| 285 | |||
| 286 | /* | ||
| 287 | * Turn on dynamic tick after calibrate delay | ||
| 288 | * for correct bogomips | ||
| 289 | */ | ||
| 290 | if (ret == 0 && dyntick_str[0] == 'e') | ||
| 291 | ret = timer_dyn_tick_enable(); | ||
| 292 | } | ||
| 293 | #endif | 232 | #endif |
| 294 | 233 | ||
| 295 | return ret; | ||
| 296 | } | ||
| 297 | device_initcall(timer_init_sysfs); | ||
| 298 | |||
| 299 | void (*board_time_init)(void); | ||
| 300 | |||
| 301 | void __init time_init(void) | 234 | void __init time_init(void) |
| 302 | { | 235 | { |
| 303 | if (board_time_init) | 236 | if (board_time_init) |
| @@ -316,10 +249,15 @@ void __init time_init(void) | |||
| 316 | sys_timer = get_sys_timer(); | 249 | sys_timer = get_sys_timer(); |
| 317 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); | 250 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); |
| 318 | 251 | ||
| 319 | #ifdef CONFIG_NO_IDLE_HZ | 252 | if (sys_timer->ops->read) |
| 320 | if (sys_timer->dyn_tick) | 253 | clocksource_sh.read = sys_timer->ops->read; |
| 321 | spin_lock_init(&sys_timer->dyn_tick->lock); | 254 | |
| 322 | #endif | 255 | init_sh_clocksource(); |
| 256 | |||
| 257 | if (sh_hpt_frequency) | ||
| 258 | printk("Using %lu.%03lu MHz high precision timer.\n", | ||
| 259 | ((sh_hpt_frequency + 500) / 1000) / 1000, | ||
| 260 | ((sh_hpt_frequency + 500) / 1000) % 1000); | ||
| 323 | 261 | ||
| 324 | #if defined(CONFIG_SH_KGDB) | 262 | #if defined(CONFIG_SH_KGDB) |
| 325 | /* | 263 | /* |
