aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/time.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2007-05-09 04:33:24 -0400
committerPaul Mundt <lethal@linux-sh.org>2007-05-09 04:33:24 -0400
commit57be2b484a417bffae66359b9b89e7239480b729 (patch)
treed517f5b449b4f6b629790476082a4a7c478112bb /arch/sh/kernel/time.c
parent1ce7ddd5f4cc754b6afe9eec5cee89ede75348ea (diff)
sh: clockevent/clocksource/hrtimers/nohz TMU support.
This adds basic support for clockevents and clocksources, presently only implemented for TMU-based systems (which are the majority of SH-3 and SH-4 systems). The old NO_IDLE_HZ implementation is also dropped completely, the only users of this were on TMU-based systems anyways. More work needs to be done to generalize the TMU handling, in that the current implementation is rather tied to the notion of TMU0 and TMU1 utilization. Additionally, as more SH timers switch over to this scheme, we'll be able to gut most of the remaining system timer infrastructure that existed before. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
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 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 */
45static cycle_t null_hpt_read(void)
46{
47 return 0;
48}
49
41void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; 50void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
42int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; 51int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
43 52
@@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv)
101EXPORT_SYMBOL(do_settimeofday); 110EXPORT_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 */
105static long last_rtc_update; 115static 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
143int timer_suspend(struct sys_device *dev, pm_message_t state) 154int 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 182static int __init timer_init_sysfs(void)
172static 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}
191device_initcall(timer_init_sysfs);
192 192
193static int timer_dyn_tick_disable(void) 193void (*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 */
219void timer_dyn_reprogram(void) 198static unsigned long timer_ticks_per_nsec_quotient __read_mostly;
220{ 199unsigned 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) 203struct 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
238static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) 212static 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
244static 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}
256static 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 227unsigned long long sched_clock(void)
260 */
261static char dyntick_str[4] __initdata = "";
262
263static 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
273static 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}
297device_initcall(timer_init_sysfs);
298
299void (*board_time_init)(void);
300
301void __init time_init(void) 234void __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 /*