X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fcpuidle%2Fcpuidle.c;h=12fdd3987a36c2b09ab65b9d598f276e51fbf5b7;hb=729adf1b5f4562f67fe8bf6c1df97edc1128fac7;hp=2c4b2d47973e195cbb0422a9b539400a2ad5ef5e;hpb=9a0b841586c3c6c846effdbe75885c2ebc0031b0;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 2c4b2d4..12fdd39 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "cpuidle.h" @@ -27,6 +29,19 @@ static void (*pm_idle_old)(void); static int enabled_devices; +#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT) +static void cpuidle_kick_cpus(void) +{ + cpu_idle_wait(); +} +#elif defined(CONFIG_SMP) +# error "Arch needs cpu_idle_wait() equivalent here" +#else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */ +static void cpuidle_kick_cpus(void) {} +#endif + +static int __cpuidle_register_device(struct cpuidle_device *dev); + /** * cpuidle_idle_call - the main idle loop * @@ -43,25 +58,44 @@ static void cpuidle_idle_call(void) if (pm_idle_old) pm_idle_old(); else +#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE) + default_idle(); +#else local_irq_enable(); +#endif return; } +#if 0 + /* shows regressions, re-enable for 2.6.29 */ + /* + * run any timers that can be run now, at this point + * before calculating the idle duration etc. + */ + hrtimer_peek_ahead_timers(); +#endif /* ask the governor for the next state */ next_state = cpuidle_curr_governor->select(dev); - if (need_resched()) + if (need_resched()) { + local_irq_enable(); return; + } + target_state = &dev->states[next_state]; /* enter the state and update stats */ - dev->last_residency = target_state->enter(dev, target_state); dev->last_state = target_state; - target_state->time += dev->last_residency; + dev->last_residency = target_state->enter(dev, target_state); + if (dev->last_state) + target_state = dev->last_state; + + target_state->time += (unsigned long long)dev->last_residency; target_state->usage++; /* give the governor an opportunity to reflect on the outcome */ if (cpuidle_curr_governor->reflect) cpuidle_curr_governor->reflect(dev); + trace_power_end(0); } /** @@ -81,9 +115,9 @@ void cpuidle_install_idle_handler(void) */ void cpuidle_uninstall_idle_handler(void) { - if (enabled_devices && (pm_idle != pm_idle_old)) { + if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) { pm_idle = pm_idle_old; - cpu_idle_wait(); + cpuidle_kick_cpus(); } } @@ -127,6 +161,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev) if (!dev->state_count) return -EINVAL; + if (dev->registered == 0) { + ret = __cpuidle_register_device(dev); + if (ret) + return ret; + } + if ((ret = cpuidle_add_state_sysfs(dev))) return ret; @@ -208,11 +248,12 @@ static void poll_idle_init(struct cpuidle_device *dev) cpuidle_set_statedata(state, NULL); - snprintf(state->name, CPUIDLE_NAME_LEN, "C0 (poll idle)"); + snprintf(state->name, CPUIDLE_NAME_LEN, "C0"); + snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); state->exit_latency = 0; state->target_residency = 0; state->power_usage = -1; - state->flags = CPUIDLE_FLAG_POLL | CPUIDLE_FLAG_TIME_VALID; + state->flags = CPUIDLE_FLAG_POLL; state->enter = poll_idle; } #else @@ -220,10 +261,13 @@ static void poll_idle_init(struct cpuidle_device *dev) {} #endif /* CONFIG_ARCH_HAS_CPU_RELAX */ /** - * cpuidle_register_device - registers a CPU's idle PM feature + * __cpuidle_register_device - internal register function called before register + * and enable routines * @dev: the cpu + * + * cpuidle_lock mutex must be held before this is called */ -int cpuidle_register_device(struct cpuidle_device *dev) +static int __cpuidle_register_device(struct cpuidle_device *dev) { int ret; struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); @@ -235,18 +279,34 @@ int cpuidle_register_device(struct cpuidle_device *dev) init_completion(&dev->kobj_unregister); - mutex_lock(&cpuidle_lock); - poll_idle_init(dev); per_cpu(cpuidle_devices, dev->cpu) = dev; list_add(&dev->device_list, &cpuidle_detected_devices); if ((ret = cpuidle_add_sysfs(sys_dev))) { - mutex_unlock(&cpuidle_lock); module_put(cpuidle_curr_driver->owner); return ret; } + dev->registered = 1; + return 0; +} + +/** + * cpuidle_register_device - registers a CPU's idle PM feature + * @dev: the cpu + */ +int cpuidle_register_device(struct cpuidle_device *dev) +{ + int ret; + + mutex_lock(&cpuidle_lock); + + if ((ret = __cpuidle_register_device(dev))) { + mutex_unlock(&cpuidle_lock); + return ret; + } + cpuidle_enable_device(dev); cpuidle_install_idle_handler(); @@ -266,6 +326,9 @@ void cpuidle_unregister_device(struct cpuidle_device *dev) { struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); + if (dev->registered == 0) + return; + cpuidle_pause_and_lock(); cpuidle_disable_device(dev); @@ -298,7 +361,7 @@ static void smp_callback(void *v) static int cpuidle_latency_notify(struct notifier_block *b, unsigned long l, void *v) { - smp_call_function(smp_callback, NULL, 0, 1); + smp_call_function(smp_callback, NULL, 1); return NOTIFY_OK; }