Cpuidle governor only suggests the state to enter using the governor->select() interface, but allows the low level driver to override the recommended state. The actual entered state may be different because of software or hardware demotion. Software demotion is done by the back-end cpuidle driver and can be accounted correctly. Current cpuidle code uses last_state field to capture the actual state entered and based on that updates the statistics for the state entered. Ideally the driver enter routine should update the counters, and it should return the state actually entered rather than the time spent there. The generic cpuidle code should simply handle where the counters live in the sysfs namespace, not updating the counters. Reference: https://lkml.org/lkml/2011/3/25/52 Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com> Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com> Tested-by: Jean Pihet <j-pihet@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Acked-by: Arjan van de Ven <arjan@linux.intel.com> Acked-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Len Brown <len.brown@intel.com>
88 lines
2.0 KiB
C
88 lines
2.0 KiB
C
/* linux/arch/arm/mach-exynos4/cpuidle.c
|
|
*
|
|
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/proc-fns.h>
|
|
|
|
static int exynos4_enter_idle(struct cpuidle_device *dev,
|
|
int index);
|
|
|
|
static struct cpuidle_state exynos4_cpuidle_set[] = {
|
|
[0] = {
|
|
.enter = exynos4_enter_idle,
|
|
.exit_latency = 1,
|
|
.target_residency = 100000,
|
|
.flags = CPUIDLE_FLAG_TIME_VALID,
|
|
.name = "IDLE",
|
|
.desc = "ARM clock gating(WFI)",
|
|
},
|
|
};
|
|
|
|
static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device);
|
|
|
|
static struct cpuidle_driver exynos4_idle_driver = {
|
|
.name = "exynos4_idle",
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static int exynos4_enter_idle(struct cpuidle_device *dev,
|
|
int index)
|
|
{
|
|
struct timeval before, after;
|
|
int idle_time;
|
|
|
|
local_irq_disable();
|
|
do_gettimeofday(&before);
|
|
|
|
cpu_do_idle();
|
|
|
|
do_gettimeofday(&after);
|
|
local_irq_enable();
|
|
idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
|
|
(after.tv_usec - before.tv_usec);
|
|
|
|
dev->last_residency = idle_time;
|
|
return index;
|
|
}
|
|
|
|
static int __init exynos4_init_cpuidle(void)
|
|
{
|
|
int i, max_cpuidle_state, cpu_id;
|
|
struct cpuidle_device *device;
|
|
|
|
cpuidle_register_driver(&exynos4_idle_driver);
|
|
|
|
for_each_cpu(cpu_id, cpu_online_mask) {
|
|
device = &per_cpu(exynos4_cpuidle_device, cpu_id);
|
|
device->cpu = cpu_id;
|
|
|
|
device->state_count = (sizeof(exynos4_cpuidle_set) /
|
|
sizeof(struct cpuidle_state));
|
|
|
|
max_cpuidle_state = device->state_count;
|
|
|
|
for (i = 0; i < max_cpuidle_state; i++) {
|
|
memcpy(&device->states[i], &exynos4_cpuidle_set[i],
|
|
sizeof(struct cpuidle_state));
|
|
}
|
|
|
|
if (cpuidle_register_device(device)) {
|
|
printk(KERN_ERR "CPUidle register device failed\n,");
|
|
return -EIO;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
device_initcall(exynos4_init_cpuidle);
|