diff options
-rw-r--r-- | arch/sh/Kconfig | 18 | ||||
-rw-r--r-- | arch/sh/kernel/time.c | 124 | ||||
-rw-r--r-- | include/asm-sh/timer.h | 21 |
3 files changed, 162 insertions, 1 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 48308dc86e33..aa1ebc561b84 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig | |||
@@ -423,6 +423,24 @@ config SH_TIMER_IRQ | |||
423 | default "140" if CPU_SUBTYPE_SH7206 | 423 | default "140" if CPU_SUBTYPE_SH7206 |
424 | default "16" | 424 | default "16" |
425 | 425 | ||
426 | config NO_IDLE_HZ | ||
427 | bool "Dynamic tick timer" | ||
428 | help | ||
429 | Select this option if you want to disable continuous timer ticks | ||
430 | and have them programmed to occur as required. This option saves | ||
431 | power as the system can remain in idle state for longer. | ||
432 | |||
433 | By default dynamic tick is disabled during the boot, and can be | ||
434 | manually enabled with: | ||
435 | |||
436 | echo 1 > /sys/devices/system/timer/timer0/dyn_tick | ||
437 | |||
438 | Alternatively, if you want dynamic tick automatically enabled | ||
439 | during boot, pass "dyntick=enable" via the kernel command string. | ||
440 | |||
441 | Please note that dynamic tick may affect the accuracy of | ||
442 | timekeeping on some platforms depending on the implementation. | ||
443 | |||
426 | config SH_PCLK_FREQ | 444 | config SH_PCLK_FREQ |
427 | int "Peripheral clock frequency (in Hz)" | 445 | int "Peripheral clock frequency (in Hz)" |
428 | default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343 | 446 | default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343 |
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index c55d6f217a46..1b91c7214979 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c | |||
@@ -174,6 +174,108 @@ static struct sysdev_class timer_sysclass = { | |||
174 | .resume = timer_resume, | 174 | .resume = timer_resume, |
175 | }; | 175 | }; |
176 | 176 | ||
177 | #ifdef CONFIG_NO_IDLE_HZ | ||
178 | static int timer_dyn_tick_enable(void) | ||
179 | { | ||
180 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
181 | unsigned long flags; | ||
182 | int ret = -ENODEV; | ||
183 | |||
184 | if (dyn_tick) { | ||
185 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
186 | ret = 0; | ||
187 | if (!(dyn_tick->state & DYN_TICK_ENABLED)) { | ||
188 | ret = dyn_tick->enable(); | ||
189 | |||
190 | if (ret == 0) | ||
191 | dyn_tick->state |= DYN_TICK_ENABLED; | ||
192 | } | ||
193 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
194 | } | ||
195 | |||
196 | return ret; | ||
197 | } | ||
198 | |||
199 | static int timer_dyn_tick_disable(void) | ||
200 | { | ||
201 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
202 | unsigned long flags; | ||
203 | int ret = -ENODEV; | ||
204 | |||
205 | if (dyn_tick) { | ||
206 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
207 | ret = 0; | ||
208 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
209 | ret = dyn_tick->disable(); | ||
210 | |||
211 | if (ret == 0) | ||
212 | dyn_tick->state &= ~DYN_TICK_ENABLED; | ||
213 | } | ||
214 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
215 | } | ||
216 | |||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Reprogram the system timer for at least the calculated time interval. | ||
222 | * This function should be called from the idle thread with IRQs disabled, | ||
223 | * immediately before sleeping. | ||
224 | */ | ||
225 | void timer_dyn_reprogram(void) | ||
226 | { | ||
227 | struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick; | ||
228 | unsigned long next, seq, flags; | ||
229 | |||
230 | if (!dyn_tick) | ||
231 | return; | ||
232 | |||
233 | spin_lock_irqsave(&dyn_tick->lock, flags); | ||
234 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
235 | next = next_timer_interrupt(); | ||
236 | do { | ||
237 | seq = read_seqbegin(&xtime_lock); | ||
238 | dyn_tick->reprogram(next - jiffies); | ||
239 | } while (read_seqretry(&xtime_lock, seq)); | ||
240 | } | ||
241 | spin_unlock_irqrestore(&dyn_tick->lock, flags); | ||
242 | } | ||
243 | |||
244 | static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) | ||
245 | { | ||
246 | return sprintf(buf, "%i\n", | ||
247 | (sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); | ||
248 | } | ||
249 | |||
250 | static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, | ||
251 | size_t count) | ||
252 | { | ||
253 | unsigned int enable = simple_strtoul(buf, NULL, 2); | ||
254 | |||
255 | if (enable) | ||
256 | timer_dyn_tick_enable(); | ||
257 | else | ||
258 | timer_dyn_tick_disable(); | ||
259 | |||
260 | return count; | ||
261 | } | ||
262 | static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); | ||
263 | |||
264 | /* | ||
265 | * dyntick=enable|disable | ||
266 | */ | ||
267 | static char dyntick_str[4] __initdata = ""; | ||
268 | |||
269 | static int __init dyntick_setup(char *str) | ||
270 | { | ||
271 | if (str) | ||
272 | strlcpy(dyntick_str, str, sizeof(dyntick_str)); | ||
273 | return 1; | ||
274 | } | ||
275 | |||
276 | __setup("dyntick=", dyntick_setup); | ||
277 | #endif | ||
278 | |||
177 | static int __init timer_init_sysfs(void) | 279 | static int __init timer_init_sysfs(void) |
178 | { | 280 | { |
179 | int ret = sysdev_class_register(&timer_sysclass); | 281 | int ret = sysdev_class_register(&timer_sysclass); |
@@ -181,7 +283,22 @@ static int __init timer_init_sysfs(void) | |||
181 | return ret; | 283 | return ret; |
182 | 284 | ||
183 | sys_timer->dev.cls = &timer_sysclass; | 285 | sys_timer->dev.cls = &timer_sysclass; |
184 | return sysdev_register(&sys_timer->dev); | 286 | ret = sysdev_register(&sys_timer->dev); |
287 | |||
288 | #ifdef CONFIG_NO_IDLE_HZ | ||
289 | if (ret == 0 && sys_timer->dyn_tick) { | ||
290 | ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); | ||
291 | |||
292 | /* | ||
293 | * Turn on dynamic tick after calibrate delay | ||
294 | * for correct bogomips | ||
295 | */ | ||
296 | if (ret == 0 && dyntick_str[0] == 'e') | ||
297 | ret = timer_dyn_tick_enable(); | ||
298 | } | ||
299 | #endif | ||
300 | |||
301 | return ret; | ||
185 | } | 302 | } |
186 | device_initcall(timer_init_sysfs); | 303 | device_initcall(timer_init_sysfs); |
187 | 304 | ||
@@ -205,6 +322,11 @@ void __init time_init(void) | |||
205 | sys_timer = get_sys_timer(); | 322 | sys_timer = get_sys_timer(); |
206 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); | 323 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); |
207 | 324 | ||
325 | #ifdef CONFIG_NO_IDLE_HZ | ||
326 | if (sys_timer->dyn_tick) | ||
327 | spin_lock_init(&sys_timer->dyn_tick->lock); | ||
328 | #endif | ||
329 | |||
208 | #if defined(CONFIG_SH_KGDB) | 330 | #if defined(CONFIG_SH_KGDB) |
209 | /* | 331 | /* |
210 | * Set up kgdb as requested. We do it here because the serial | 332 | * Set up kgdb as requested. We do it here because the serial |
diff --git a/include/asm-sh/timer.h b/include/asm-sh/timer.h index 5a014bca9d58..17b5e76a4c31 100644 --- a/include/asm-sh/timer.h +++ b/include/asm-sh/timer.h | |||
@@ -18,8 +18,29 @@ struct sys_timer { | |||
18 | 18 | ||
19 | struct sys_device dev; | 19 | struct sys_device dev; |
20 | struct sys_timer_ops *ops; | 20 | struct sys_timer_ops *ops; |
21 | |||
22 | #ifdef CONFIG_NO_IDLE_HZ | ||
23 | struct dyn_tick_timer *dyn_tick; | ||
24 | #endif | ||
21 | }; | 25 | }; |
22 | 26 | ||
27 | #ifdef CONFIG_NO_IDLE_HZ | ||
28 | #define DYN_TICK_ENABLED (1 << 1) | ||
29 | |||
30 | struct dyn_tick_timer { | ||
31 | spinlock_t lock; | ||
32 | unsigned int state; /* Current state */ | ||
33 | int (*enable)(void); /* Enables dynamic tick */ | ||
34 | int (*disable)(void); /* Disables dynamic tick */ | ||
35 | void (*reprogram)(unsigned long); /* Reprograms the timer */ | ||
36 | int (*handler)(int, void *); | ||
37 | }; | ||
38 | |||
39 | void timer_dyn_reprogram(void); | ||
40 | #else | ||
41 | #define timer_dyn_reprogram() do { } while (0) | ||
42 | #endif | ||
43 | |||
23 | #define TICK_SIZE (tick_nsec / 1000) | 44 | #define TICK_SIZE (tick_nsec / 1000) |
24 | 45 | ||
25 | extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer; | 46 | extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer; |