diff options
Diffstat (limited to 'arch/sh/kernel/time.c')
| -rw-r--r-- | arch/sh/kernel/time.c | 139 |
1 files changed, 134 insertions, 5 deletions
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 57e708d7b52d..c206c9504c4b 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c | |||
| @@ -13,6 +13,8 @@ | |||
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
| 15 | #include <linux/profile.h> | 15 | #include <linux/profile.h> |
| 16 | #include <linux/timex.h> | ||
| 17 | #include <linux/sched.h> | ||
| 16 | #include <asm/clock.h> | 18 | #include <asm/clock.h> |
| 17 | #include <asm/rtc.h> | 19 | #include <asm/rtc.h> |
| 18 | #include <asm/timer.h> | 20 | #include <asm/timer.h> |
| @@ -50,15 +52,20 @@ unsigned long long __attribute__ ((weak)) sched_clock(void) | |||
| 50 | #ifndef CONFIG_GENERIC_TIME | 52 | #ifndef CONFIG_GENERIC_TIME |
| 51 | void do_gettimeofday(struct timeval *tv) | 53 | void do_gettimeofday(struct timeval *tv) |
| 52 | { | 54 | { |
| 55 | unsigned long flags; | ||
| 53 | unsigned long seq; | 56 | unsigned long seq; |
| 54 | unsigned long usec, sec; | 57 | unsigned long usec, sec; |
| 55 | 58 | ||
| 56 | do { | 59 | do { |
| 57 | seq = read_seqbegin(&xtime_lock); | 60 | /* |
| 61 | * Turn off IRQs when grabbing xtime_lock, so that | ||
| 62 | * the sys_timer get_offset code doesn't have to handle it. | ||
| 63 | */ | ||
| 64 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
| 58 | usec = get_timer_offset(); | 65 | usec = get_timer_offset(); |
| 59 | sec = xtime.tv_sec; | 66 | sec = xtime.tv_sec; |
| 60 | usec += xtime.tv_nsec / 1000; | 67 | usec += xtime.tv_nsec / NSEC_PER_USEC; |
| 61 | } while (read_seqretry(&xtime_lock, seq)); | 68 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); |
| 62 | 69 | ||
| 63 | while (usec >= 1000000) { | 70 | while (usec >= 1000000) { |
| 64 | usec -= 1000000; | 71 | usec -= 1000000; |
| @@ -85,7 +92,7 @@ int do_settimeofday(struct timespec *tv) | |||
| 85 | * wall time. Discover what correction gettimeofday() would have | 92 | * wall time. Discover what correction gettimeofday() would have |
| 86 | * made, and then undo it! | 93 | * made, and then undo it! |
| 87 | */ | 94 | */ |
| 88 | nsec -= 1000 * get_timer_offset(); | 95 | nsec -= get_timer_offset() * NSEC_PER_USEC; |
| 89 | 96 | ||
| 90 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | 97 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); |
| 91 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | 98 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); |
| @@ -169,6 +176,108 @@ static struct sysdev_class timer_sysclass = { | |||
| 169 | .resume = timer_resume, | 176 | .resume = timer_resume, |
| 170 | }; | 177 | }; |
| 171 | 178 | ||
| 179 | #ifdef CONFIG_NO_IDLE_HZ | ||
| 180 | static int timer_dyn_tick_enable(void) | ||
| 181 | { | ||
| 182 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
| 183 | unsigned long flags; | ||
| 184 | int ret = -ENODEV; | ||
| 185 | |||
| 186 | if (dyn_tick) { | ||
| 187 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
| 188 | ret = 0; | ||
| 189 | if (!(dyn_tick->state & DYN_TICK_ENABLED)) { | ||
| 190 | ret = dyn_tick->enable(); | ||
| 191 | |||
| 192 | if (ret == 0) | ||
| 193 | dyn_tick->state |= DYN_TICK_ENABLED; | ||
| 194 | } | ||
| 195 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 196 | } | ||
| 197 | |||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | static int timer_dyn_tick_disable(void) | ||
| 202 | { | ||
| 203 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
| 204 | unsigned long flags; | ||
| 205 | int ret = -ENODEV; | ||
| 206 | |||
| 207 | if (dyn_tick) { | ||
| 208 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
| 209 | ret = 0; | ||
| 210 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
| 211 | ret = dyn_tick->disable(); | ||
| 212 | |||
| 213 | if (ret == 0) | ||
| 214 | dyn_tick->state &= ~DYN_TICK_ENABLED; | ||
| 215 | } | ||
| 216 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 217 | } | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | /* | ||
| 223 | * Reprogram the system timer for at least the calculated time interval. | ||
| 224 | * This function should be called from the idle thread with IRQs disabled, | ||
| 225 | * immediately before sleeping. | ||
| 226 | */ | ||
| 227 | void timer_dyn_reprogram(void) | ||
| 228 | { | ||
| 229 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
| 230 | unsigned long next, seq, flags; | ||
| 231 | |||
| 232 | if (!dyn_tick) | ||
| 233 | return; | ||
| 234 | |||
| 235 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
| 236 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
| 237 | next = next_timer_interrupt(); | ||
| 238 | do { | ||
| 239 | seq = read_seqbegin(&xtime_lock); | ||
| 240 | dyn_tick->reprogram(next - jiffies); | ||
| 241 | } while (read_seqretry(&xtime_lock, seq)); | ||
| 242 | } | ||
| 243 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
| 244 | } | ||
| 245 | |||
| 246 | static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) | ||
| 247 | { | ||
| 248 | return sprintf(buf, "%i\n", | ||
| 249 | (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); | ||
| 250 | } | ||
| 251 | |||
| 252 | static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, | ||
| 253 | size_t count) | ||
| 254 | { | ||
| 255 | unsigned int enable = simple_strtoul(buf, NULL, 2); | ||
| 256 | |||
| 257 | if (enable) | ||
| 258 | timer_dyn_tick_enable(); | ||
| 259 | else | ||
| 260 | timer_dyn_tick_disable(); | ||
| 261 | |||
| 262 | return count; | ||
| 263 | } | ||
| 264 | static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); | ||
| 265 | |||
| 266 | /* | ||
| 267 | * dyntick=enable|disable | ||
| 268 | */ | ||
| 269 | static char dyntick_str[4] __initdata = ""; | ||
| 270 | |||
| 271 | static int __init dyntick_setup(char *str) | ||
| 272 | { | ||
| 273 | if (str) | ||
| 274 | strlcpy(dyntick_str, str, sizeof(dyntick_str)); | ||
| 275 | return 1; | ||
| 276 | } | ||
| 277 | |||
| 278 | __setup("dyntick=", dyntick_setup); | ||
| 279 | #endif | ||
| 280 | |||
| 172 | static int __init timer_init_sysfs(void) | 281 | static int __init timer_init_sysfs(void) |
| 173 | { | 282 | { |
| 174 | int ret = sysdev_class_register(&timer_sysclass); | 283 | int ret = sysdev_class_register(&timer_sysclass); |
| @@ -176,7 +285,22 @@ static int __init timer_init_sysfs(void) | |||
| 176 | return ret; | 285 | return ret; |
| 177 | 286 | ||
| 178 | sys_timer->dev.cls = &timer_sysclass; | 287 | sys_timer->dev.cls = &timer_sysclass; |
| 179 | return sysdev_register(&sys_timer->dev); | 288 | ret = sysdev_register(&sys_timer->dev); |
| 289 | |||
| 290 | #ifdef CONFIG_NO_IDLE_HZ | ||
| 291 | if (ret == 0 && sys_timer->dyn_tick) { | ||
| 292 | ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); | ||
| 293 | |||
| 294 | /* | ||
| 295 | * Turn on dynamic tick after calibrate delay | ||
| 296 | * for correct bogomips | ||
| 297 | */ | ||
| 298 | if (ret == 0 && dyntick_str[0] == 'e') | ||
| 299 | ret = timer_dyn_tick_enable(); | ||
| 300 | } | ||
| 301 | #endif | ||
| 302 | |||
| 303 | return ret; | ||
| 180 | } | 304 | } |
| 181 | device_initcall(timer_init_sysfs); | 305 | device_initcall(timer_init_sysfs); |
| 182 | 306 | ||
| @@ -200,6 +324,11 @@ void __init time_init(void) | |||
| 200 | sys_timer = get_sys_timer(); | 324 | sys_timer = get_sys_timer(); |
| 201 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); | 325 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); |
| 202 | 326 | ||
| 327 | #ifdef CONFIG_NO_IDLE_HZ | ||
| 328 | if (sys_timer->dyn_tick) | ||
| 329 | spin_lock_init(&sys_timer->dyn_tick->lock); | ||
| 330 | #endif | ||
| 331 | |||
| 203 | #if defined(CONFIG_SH_KGDB) | 332 | #if defined(CONFIG_SH_KGDB) |
| 204 | /* | 333 | /* |
| 205 | * Set up kgdb as requested. We do it here because the serial | 334 | * Set up kgdb as requested. We do it here because the serial |
