aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel
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 /arch/sh/kernel
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>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r--arch/sh/kernel/time.c124
1 files changed, 123 insertions, 1 deletions
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