Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 42607
b: refs/heads/master
c: bd15614
h: refs/heads/master
i:
  42605: 364e0a1
  42603: 84fa6b1
  42599: 4de98b7
  42591: 884998a
v: v3
  • Loading branch information
Paul Mundt committed Dec 6, 2006
1 parent fa41550 commit a71b96f
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1d118562c2067a42d0e8f70671a4ce27d7c6ffee
refs/heads/master: bd156147eb63ae525e0ac67868e41a808f03c532
18 changes: 18 additions & 0 deletions trunk/arch/sh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,24 @@ config SH_TIMER_IRQ
default "140" if CPU_SUBTYPE_SH7206
default "16"

config NO_IDLE_HZ
bool "Dynamic tick timer"
help
Select this option if you want to disable continuous timer ticks
and have them programmed to occur as required. This option saves
power as the system can remain in idle state for longer.

By default dynamic tick is disabled during the boot, and can be
manually enabled with:

echo 1 > /sys/devices/system/timer/timer0/dyn_tick

Alternatively, if you want dynamic tick automatically enabled
during boot, pass "dyntick=enable" via the kernel command string.

Please note that dynamic tick may affect the accuracy of
timekeeping on some platforms depending on the implementation.

config SH_PCLK_FREQ
int "Peripheral clock frequency (in Hz)"
default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343
Expand Down
124 changes: 123 additions & 1 deletion trunk/arch/sh/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,131 @@ static struct sysdev_class timer_sysclass = {
.resume = timer_resume,
};

#ifdef CONFIG_NO_IDLE_HZ
static int timer_dyn_tick_enable(void)
{
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
unsigned long flags;
int ret = -ENODEV;

if (dyn_tick) {
spin_lock_irqsave(&dyn_tick->lock, flags);
ret = 0;
if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
ret = dyn_tick->enable();

if (ret == 0)
dyn_tick->state |= DYN_TICK_ENABLED;
}
spin_unlock_irqrestore(&dyn_tick->lock, flags);
}

return ret;
}

static int timer_dyn_tick_disable(void)
{
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
unsigned long flags;
int ret = -ENODEV;

if (dyn_tick) {
spin_lock_irqsave(&dyn_tick->lock, flags);
ret = 0;
if (dyn_tick->state & DYN_TICK_ENABLED) {
ret = dyn_tick->disable();

if (ret == 0)
dyn_tick->state &= ~DYN_TICK_ENABLED;
}
spin_unlock_irqrestore(&dyn_tick->lock, flags);
}

return ret;
}

/*
* Reprogram the system timer for at least the calculated time interval.
* This function should be called from the idle thread with IRQs disabled,
* immediately before sleeping.
*/
void timer_dyn_reprogram(void)
{
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
unsigned long next, seq, flags;

if (!dyn_tick)
return;

spin_lock_irqsave(&dyn_tick->lock, flags);
if (dyn_tick->state & DYN_TICK_ENABLED) {
next = next_timer_interrupt();
do {
seq = read_seqbegin(&xtime_lock);
dyn_tick->reprogram(next - jiffies);
} while (read_seqretry(&xtime_lock, seq));
}
spin_unlock_irqrestore(&dyn_tick->lock, flags);
}

static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
{
return sprintf(buf, "%i\n",
(sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
}

static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
size_t count)
{
unsigned int enable = simple_strtoul(buf, NULL, 2);

if (enable)
timer_dyn_tick_enable();
else
timer_dyn_tick_disable();

return count;
}
static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);

/*
* dyntick=enable|disable
*/
static char dyntick_str[4] __initdata = "";

static int __init dyntick_setup(char *str)
{
if (str)
strlcpy(dyntick_str, str, sizeof(dyntick_str));
return 1;
}

__setup("dyntick=", dyntick_setup);
#endif

static int __init timer_init_sysfs(void)
{
int ret = sysdev_class_register(&timer_sysclass);
if (ret != 0)
return ret;

sys_timer->dev.cls = &timer_sysclass;
return sysdev_register(&sys_timer->dev);
ret = sysdev_register(&sys_timer->dev);

#ifdef CONFIG_NO_IDLE_HZ
if (ret == 0 && sys_timer->dyn_tick) {
ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick);

/*
* Turn on dynamic tick after calibrate delay
* for correct bogomips
*/
if (ret == 0 && dyntick_str[0] == 'e')
ret = timer_dyn_tick_enable();
}
#endif

return ret;
}
device_initcall(timer_init_sysfs);

Expand All @@ -205,6 +322,11 @@ void __init time_init(void)
sys_timer = get_sys_timer();
printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);

#ifdef CONFIG_NO_IDLE_HZ
if (sys_timer->dyn_tick)
spin_lock_init(&sys_timer->dyn_tick->lock);
#endif

#if defined(CONFIG_SH_KGDB)
/*
* Set up kgdb as requested. We do it here because the serial
Expand Down
21 changes: 21 additions & 0 deletions trunk/include/asm-sh/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,29 @@ struct sys_timer {

struct sys_device dev;
struct sys_timer_ops *ops;

#ifdef CONFIG_NO_IDLE_HZ
struct dyn_tick_timer *dyn_tick;
#endif
};

#ifdef CONFIG_NO_IDLE_HZ
#define DYN_TICK_ENABLED (1 << 1)

struct dyn_tick_timer {
spinlock_t lock;
unsigned int state; /* Current state */
int (*enable)(void); /* Enables dynamic tick */
int (*disable)(void); /* Disables dynamic tick */
void (*reprogram)(unsigned long); /* Reprograms the timer */
int (*handler)(int, void *);
};

void timer_dyn_reprogram(void);
#else
#define timer_dyn_reprogram() do { } while (0)
#endif

#define TICK_SIZE (tick_nsec / 1000)

extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer;
Expand Down

0 comments on commit a71b96f

Please sign in to comment.