Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+162 -1
+18
arch/sh/Kconfig
··· 423 423 default "140" if CPU_SUBTYPE_SH7206 424 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 444 config SH_PCLK_FREQ 427 445 int "Peripheral clock frequency (in Hz)" 428 446 default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343
+123 -1
arch/sh/kernel/time.c
··· 174 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 279 static int __init timer_init_sysfs(void) 178 280 { 179 281 int ret = sysdev_class_register(&timer_sysclass); ··· 283 181 return ret; 284 182 285 183 sys_timer->dev.cls = &timer_sysclass; 286 - return sysdev_register(&sys_timer->dev); 184 + ret = sysdev_register(&sys_timer->dev); 185 + 186 + #ifdef CONFIG_NO_IDLE_HZ 187 + if (ret == 0 && sys_timer->dyn_tick) { 188 + ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick); 189 + 190 + /* 191 + * Turn on dynamic tick after calibrate delay 192 + * for correct bogomips 193 + */ 194 + if (ret == 0 && dyntick_str[0] == 'e') 195 + ret = timer_dyn_tick_enable(); 196 + } 197 + #endif 198 + 199 + return ret; 287 200 } 288 201 device_initcall(timer_init_sysfs); 289 202 ··· 321 204 */ 322 205 sys_timer = get_sys_timer(); 323 206 printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); 207 + 208 + #ifdef CONFIG_NO_IDLE_HZ 209 + if (sys_timer->dyn_tick) 210 + spin_lock_init(&sys_timer->dyn_tick->lock); 211 + #endif 324 212 325 213 #if defined(CONFIG_SH_KGDB) 326 214 /*
+21
include/asm-sh/timer.h
··· 18 18 19 19 struct sys_device dev; 20 20 struct sys_timer_ops *ops; 21 + 22 + #ifdef CONFIG_NO_IDLE_HZ 23 + struct dyn_tick_timer *dyn_tick; 24 + #endif 21 25 }; 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 22 43 23 44 #define TICK_SIZE (tick_nsec / 1000) 24 45