diff options
Diffstat (limited to 'arch/sh/kernel/time.c')
-rw-r--r-- | arch/sh/kernel/time.c | 124 |
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 | ||
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 |