Skip to content

Commit

Permalink
cpuidle: Single/Global registration of idle states
Browse files Browse the repository at this point in the history
This patch makes the cpuidle_states structure global (single copy)
instead of per-cpu. The statistics needed on per-cpu basis
by the governor are kept per-cpu. This simplifies the cpuidle
subsystem as state registration is done by single cpu only.
Having single copy of cpuidle_states saves memory. Rare case
of asymmetric C-states can be handled within the cpuidle driver
and architectures such as POWER do not have asymmetric C-states.

Having single/global registration of all the idle states,
dynamic C-state transitions on x86 are handled by
the boot cpu. Here, the boot cpu  would disable all the devices,
re-populate the states and later enable all the devices,
irrespective of the cpu that would receive the notification first.

Reference:
https://lkml.org/lkml/2011/4/25/83

Signed-off-by: Deepthi Dharwar <[email protected]>
Signed-off-by: Trinabh Gupta <[email protected]>
Tested-by: Jean Pihet <[email protected]>
Reviewed-by: Kevin Hilman <[email protected]>
Acked-by: Arjan van de Ven <[email protected]>
Acked-by: Kevin Hilman <[email protected]>
Signed-off-by: Len Brown <[email protected]>
  • Loading branch information
Deepthi Dharwar authored and lenb committed Nov 7, 2011
1 parent 4202735 commit 46bcfad
Show file tree
Hide file tree
Showing 16 changed files with 439 additions and 207 deletions.
31 changes: 17 additions & 14 deletions arch/arm/mach-at91/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static struct cpuidle_driver at91_idle_driver = {

/* Actual code that puts the SoC in different idle states */
static int at91_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct timeval before, after;
Expand Down Expand Up @@ -64,27 +65,29 @@ static int at91_enter_idle(struct cpuidle_device *dev,
static int at91_init_cpuidle(void)
{
struct cpuidle_device *device;

cpuidle_register_driver(&at91_idle_driver);
struct cpuidle_driver *driver = &at91_idle_driver;

device = &per_cpu(at91_cpuidle_device, smp_processor_id());
device->state_count = AT91_MAX_STATES;
driver->state_count = AT91_MAX_STATES;

/* Wait for interrupt state */
device->states[0].enter = at91_enter_idle;
device->states[0].exit_latency = 1;
device->states[0].target_residency = 10000;
device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[0].name, "WFI");
strcpy(device->states[0].desc, "Wait for interrupt");
driver->states[0].enter = at91_enter_idle;
driver->states[0].exit_latency = 1;
driver->states[0].target_residency = 10000;
driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[0].name, "WFI");
strcpy(driver->states[0].desc, "Wait for interrupt");

/* Wait for interrupt and RAM self refresh state */
device->states[1].enter = at91_enter_idle;
device->states[1].exit_latency = 10;
device->states[1].target_residency = 10000;
device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[1].name, "RAM_SR");
strcpy(device->states[1].desc, "WFI and RAM Self Refresh");
driver->states[1].enter = at91_enter_idle;
driver->states[1].exit_latency = 10;
driver->states[1].target_residency = 10000;
driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[1].name, "RAM_SR");
strcpy(driver->states[1].desc, "WFI and RAM Self Refresh");

cpuidle_register_driver(&at91_idle_driver);

if (cpuidle_register_device(device)) {
printk(KERN_ERR "at91_init_cpuidle: Failed registering\n");
Expand Down
39 changes: 21 additions & 18 deletions arch/arm/mach-davinci/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {

/* Actual code that puts the SoC in different idle states */
static int davinci_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
Expand Down Expand Up @@ -109,6 +110,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
{
int ret;
struct cpuidle_device *device;
struct cpuidle_driver *driver = &davinci_idle_driver;
struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;

device = &per_cpu(davinci_cpuidle_device, smp_processor_id());
Expand All @@ -120,32 +122,33 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)

ddr2_reg_base = pdata->ddr2_ctlr_base;

ret = cpuidle_register_driver(&davinci_idle_driver);
if (ret) {
dev_err(&pdev->dev, "failed to register driver\n");
return ret;
}

/* Wait for interrupt state */
device->states[0].enter = davinci_enter_idle;
device->states[0].exit_latency = 1;
device->states[0].target_residency = 10000;
device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[0].name, "WFI");
strcpy(device->states[0].desc, "Wait for interrupt");
driver->states[0].enter = davinci_enter_idle;
driver->states[0].exit_latency = 1;
driver->states[0].target_residency = 10000;
driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[0].name, "WFI");
strcpy(driver->states[0].desc, "Wait for interrupt");

/* Wait for interrupt and DDR self refresh state */
device->states[1].enter = davinci_enter_idle;
device->states[1].exit_latency = 10;
device->states[1].target_residency = 10000;
device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[1].name, "DDR SR");
strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
driver->states[1].enter = davinci_enter_idle;
driver->states[1].exit_latency = 10;
driver->states[1].target_residency = 10000;
driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[1].name, "DDR SR");
strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
if (pdata->ddr2_pdown)
davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN;
cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]);

device->state_count = DAVINCI_CPUIDLE_MAX_STATES;
driver->state_count = DAVINCI_CPUIDLE_MAX_STATES;

ret = cpuidle_register_driver(&davinci_idle_driver);
if (ret) {
dev_err(&pdev->dev, "failed to register driver\n");
return ret;
}

ret = cpuidle_register_device(device);
if (ret) {
Expand Down
23 changes: 13 additions & 10 deletions arch/arm/mach-exynos4/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <asm/proc-fns.h>

static int exynos4_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index);

static struct cpuidle_state exynos4_cpuidle_set[] = {
Expand All @@ -37,6 +38,7 @@ static struct cpuidle_driver exynos4_idle_driver = {
};

static int exynos4_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct timeval before, after;
Expand All @@ -60,22 +62,23 @@ static int __init exynos4_init_cpuidle(void)
{
int i, max_cpuidle_state, cpu_id;
struct cpuidle_device *device;

struct cpuidle_driver *drv = &exynos4_idle_driver;

/* Setup cpuidle driver */
drv->state_count = (sizeof(exynos4_cpuidle_set) /
sizeof(struct cpuidle_state));
max_cpuidle_state = drv->state_count;
for (i = 0; i < max_cpuidle_state; i++) {
memcpy(&drv->states[i], &exynos4_cpuidle_set[i],
sizeof(struct cpuidle_state));
}
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));
}
device->state_count = drv->state_count;

if (cpuidle_register_device(device)) {
printk(KERN_ERR "CPUidle register device failed\n,");
Expand Down
30 changes: 16 additions & 14 deletions arch/arm/mach-kirkwood/cpuidle.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);

/* Actual code that puts the SoC in different idle states */
static int kirkwood_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct timeval before, after;
Expand Down Expand Up @@ -68,28 +69,29 @@ static int kirkwood_enter_idle(struct cpuidle_device *dev,
static int kirkwood_init_cpuidle(void)
{
struct cpuidle_device *device;

cpuidle_register_driver(&kirkwood_idle_driver);
struct cpuidle_driver *driver = &kirkwood_idle_driver;

device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id());
device->state_count = KIRKWOOD_MAX_STATES;
driver->state_count = KIRKWOOD_MAX_STATES;

/* Wait for interrupt state */
device->states[0].enter = kirkwood_enter_idle;
device->states[0].exit_latency = 1;
device->states[0].target_residency = 10000;
device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[0].name, "WFI");
strcpy(device->states[0].desc, "Wait for interrupt");
driver->states[0].enter = kirkwood_enter_idle;
driver->states[0].exit_latency = 1;
driver->states[0].target_residency = 10000;
driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[0].name, "WFI");
strcpy(driver->states[0].desc, "Wait for interrupt");

/* Wait for interrupt and DDR self refresh state */
device->states[1].enter = kirkwood_enter_idle;
device->states[1].exit_latency = 10;
device->states[1].target_residency = 10000;
device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(device->states[1].name, "DDR SR");
strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
driver->states[1].enter = kirkwood_enter_idle;
driver->states[1].exit_latency = 10;
driver->states[1].target_residency = 10000;
driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
strcpy(driver->states[1].name, "DDR SR");
strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");

cpuidle_register_driver(&kirkwood_idle_driver);
if (cpuidle_register_device(device)) {
printk(KERN_ERR "kirkwood_init_cpuidle: Failed registering\n");
return -EIO;
Expand Down
Loading

0 comments on commit 46bcfad

Please sign in to comment.