aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2006-11-30 23:23:47 -0500
committerPaul Mundt <lethal@linux-sh.org>2006-12-05 20:45:40 -0500
commitbd156147eb63ae525e0ac67868e41a808f03c532 (patch)
tree8774cf4f3297c94c10583f6331b5b17e0322f0db
parent1d118562c2067a42d0e8f70671a4ce27d7c6ffee (diff)
sh: dyntick infrastructure.
This adds basic NO_IDLE_HZ support to the SH timer API so timers are able to wire it up. Taken from the ARM version, as it fit in to our API with very few changes needed. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--arch/sh/Kconfig18
-rw-r--r--arch/sh/kernel/time.c124
-rw-r--r--include/asm-sh/timer.h21
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
426config 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
426config SH_PCLK_FREQ 444config 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
178static 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
199static 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 */
225void 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
244static 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
250static 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}
262static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
263
264/*
265 * dyntick=enable|disable
266 */
267static char dyntick_str[4] __initdata = "";
268
269static 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
177static int __init timer_init_sysfs(void) 279static 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}
186device_initcall(timer_init_sysfs); 303device_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
30struct 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
39void 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
25extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer; 46extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer;