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 | /* |