| From a50a3085d47712f6a11024570ffc8bf6b90fb126 Mon Sep 17 00:00:00 2001 |
| From: Deepthi Dharwar <deepthi@linux.vnet.ibm.com> |
| Date: Fri, 28 Oct 2011 16:20:42 +0530 |
| Subject: cpuidle: Single/Global registration of idle states |
| |
| 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 <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> |
| (cherry picked from commit 46bcfad7a819bd17ac4e831b04405152d59784ab) |
| |
| Conflicts: |
| |
| drivers/idle/intel_idle.c |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| arch/arm/mach-at91/cpuidle.c | 31 +++-- |
| arch/arm/mach-davinci/cpuidle.c | 39 +++--- |
| arch/arm/mach-exynos4/cpuidle.c | 21 ++- |
| arch/arm/mach-kirkwood/cpuidle.c | 30 ++--- |
| arch/arm/mach-omap2/cpuidle34xx.c | 73 ++++++++---- |
| arch/sh/kernel/cpu/shmobile/cpuidle.c | 18 +-- |
| drivers/acpi/processor_driver.c | 20 --- |
| drivers/acpi/processor_idle.c | 191 +++++++++++++++++++++++++++++----- |
| drivers/cpuidle/cpuidle.c | 45 ++------ |
| drivers/cpuidle/driver.c | 25 ++++ |
| drivers/cpuidle/governors/ladder.c | 28 +++- |
| drivers/cpuidle/governors/menu.c | 20 ++- |
| drivers/cpuidle/sysfs.c | 3 |
| drivers/idle/intel_idle.c | 80 ++++++++++---- |
| include/acpi/processor.h | 1 |
| include/linux/cpuidle.h | 19 ++- |
| 16 files changed, 438 insertions(+), 206 deletions(-) |
| |
| --- a/arch/arm/mach-at91/cpuidle.c |
| +++ b/arch/arm/mach-at91/cpuidle.c |
| @@ -34,6 +34,7 @@ static struct cpuidle_driver at91_idle_d |
| |
| /* 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; |
| @@ -65,27 +66,29 @@ static int at91_enter_idle(struct cpuidl |
| 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"); |
| --- a/arch/arm/mach-davinci/cpuidle.c |
| +++ b/arch/arm/mach-davinci/cpuidle.c |
| @@ -79,6 +79,7 @@ static struct davinci_ops davinci_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]; |
| @@ -110,6 +111,7 @@ static int __init davinci_cpuidle_probe( |
| { |
| 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()); |
| @@ -121,32 +123,33 @@ static int __init davinci_cpuidle_probe( |
| |
| 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) { |
| --- a/arch/arm/mach-exynos4/cpuidle.c |
| +++ b/arch/arm/mach-exynos4/cpuidle.c |
| @@ -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[] = { |
| @@ -37,6 +38,7 @@ static struct cpuidle_driver exynos4_idl |
| }; |
| |
| static int exynos4_enter_idle(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index) |
| { |
| struct timeval before, after; |
| @@ -60,22 +62,23 @@ static int __init exynos4_init_cpuidle(v |
| { |
| 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,"); |
| --- a/arch/arm/mach-kirkwood/cpuidle.c |
| +++ b/arch/arm/mach-kirkwood/cpuidle.c |
| @@ -33,6 +33,7 @@ static DEFINE_PER_CPU(struct cpuidle_dev |
| |
| /* 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; |
| @@ -69,28 +70,29 @@ static int kirkwood_enter_idle(struct cp |
| 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; |
| --- a/arch/arm/mach-omap2/cpuidle34xx.c |
| +++ b/arch/arm/mach-omap2/cpuidle34xx.c |
| @@ -88,12 +88,14 @@ static int _cpuidle_deny_idle(struct pow |
| /** |
| * omap3_enter_idle - Programs OMAP3 to enter the specified state |
| * @dev: cpuidle device |
| + * @drv: cpuidle driver |
| * @index: the index of state to be entered |
| * |
| * Called from the CPUidle framework to program the device to the |
| * specified target state selected by the governor. |
| */ |
| static int omap3_enter_idle(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index) |
| { |
| struct omap3_idle_statedata *cx = |
| @@ -148,6 +150,7 @@ return_sleep_time: |
| /** |
| * next_valid_state - Find next valid C-state |
| * @dev: cpuidle device |
| + * @drv: cpuidle driver |
| * @index: Index of currently selected c-state |
| * |
| * If the state corresponding to index is valid, index is returned back |
| @@ -158,10 +161,11 @@ return_sleep_time: |
| * if it satisfies the enable_off_mode condition. |
| */ |
| static int next_valid_state(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index) |
| { |
| struct cpuidle_state_usage *curr_usage = &dev->states_usage[index]; |
| - struct cpuidle_state *curr = &dev->states[index]; |
| + struct cpuidle_state *curr = &drv->states[index]; |
| struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage); |
| u32 mpu_deepest_state = PWRDM_POWER_RET; |
| u32 core_deepest_state = PWRDM_POWER_RET; |
| @@ -188,7 +192,7 @@ static int next_valid_state(struct cpuid |
| |
| /* Reach the current state starting at highest C-state */ |
| for (; idx >= 0; idx--) { |
| - if (&dev->states[idx] == curr) { |
| + if (&drv->states[idx] == curr) { |
| next_index = idx; |
| break; |
| } |
| @@ -224,12 +228,14 @@ static int next_valid_state(struct cpuid |
| /** |
| * omap3_enter_idle_bm - Checks for any bus activity |
| * @dev: cpuidle device |
| + * @drv: cpuidle driver |
| * @index: array index of target state to be programmed |
| * |
| * This function checks for any pending activity and then programs |
| * the device to the specified or a safer state. |
| */ |
| static int omap3_enter_idle_bm(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index) |
| { |
| int new_state_idx; |
| @@ -238,7 +244,7 @@ static int omap3_enter_idle_bm(struct cp |
| int ret; |
| |
| if (!omap3_can_sleep()) { |
| - new_state_idx = dev->safe_state_index; |
| + new_state_idx = drv->safe_state_index; |
| goto select_state; |
| } |
| |
| @@ -248,7 +254,7 @@ static int omap3_enter_idle_bm(struct cp |
| */ |
| cam_state = pwrdm_read_pwrst(cam_pd); |
| if (cam_state == PWRDM_POWER_ON) { |
| - new_state_idx = dev->safe_state_index; |
| + new_state_idx = drv->safe_state_index; |
| goto select_state; |
| } |
| |
| @@ -275,10 +281,10 @@ static int omap3_enter_idle_bm(struct cp |
| if (per_next_state != per_saved_state) |
| pwrdm_set_next_pwrst(per_pd, per_next_state); |
| |
| - new_state_idx = next_valid_state(dev, index); |
| + new_state_idx = next_valid_state(dev, drv, index); |
| |
| select_state: |
| - ret = omap3_enter_idle(dev, new_state_idx); |
| + ret = omap3_enter_idle(dev, drv, new_state_idx); |
| |
| /* Restore original PER state if it was modified */ |
| if (per_next_state != per_saved_state) |
| @@ -311,22 +317,30 @@ struct cpuidle_driver omap3_idle_driver |
| .owner = THIS_MODULE, |
| }; |
| |
| -/* Helper to fill the C-state common data and register the driver_data */ |
| -static inline struct omap3_idle_statedata *_fill_cstate( |
| - struct cpuidle_device *dev, |
| +/* Helper to fill the C-state common data*/ |
| +static inline void _fill_cstate(struct cpuidle_driver *drv, |
| int idx, const char *descr) |
| { |
| - struct omap3_idle_statedata *cx = &omap3_idle_data[idx]; |
| - struct cpuidle_state *state = &dev->states[idx]; |
| - struct cpuidle_state_usage *state_usage = &dev->states_usage[idx]; |
| + struct cpuidle_state *state = &drv->states[idx]; |
| |
| state->exit_latency = cpuidle_params_table[idx].exit_latency; |
| state->target_residency = cpuidle_params_table[idx].target_residency; |
| state->flags = CPUIDLE_FLAG_TIME_VALID; |
| state->enter = omap3_enter_idle_bm; |
| - cx->valid = cpuidle_params_table[idx].valid; |
| sprintf(state->name, "C%d", idx + 1); |
| strncpy(state->desc, descr, CPUIDLE_DESC_LEN); |
| + |
| +} |
| + |
| +/* Helper to register the driver_data */ |
| +static inline struct omap3_idle_statedata *_fill_cstate_usage( |
| + struct cpuidle_device *dev, |
| + int idx) |
| +{ |
| + struct omap3_idle_statedata *cx = &omap3_idle_data[idx]; |
| + struct cpuidle_state_usage *state_usage = &dev->states_usage[idx]; |
| + |
| + cx->valid = cpuidle_params_table[idx].valid; |
| cpuidle_set_statedata(state_usage, cx); |
| |
| return cx; |
| @@ -341,6 +355,7 @@ static inline struct omap3_idle_statedat |
| int __init omap3_idle_init(void) |
| { |
| struct cpuidle_device *dev; |
| + struct cpuidle_driver *drv = &omap3_idle_driver; |
| struct omap3_idle_statedata *cx; |
| |
| mpu_pd = pwrdm_lookup("mpu_pwrdm"); |
| @@ -348,45 +363,52 @@ int __init omap3_idle_init(void) |
| per_pd = pwrdm_lookup("per_pwrdm"); |
| cam_pd = pwrdm_lookup("cam_pwrdm"); |
| |
| - cpuidle_register_driver(&omap3_idle_driver); |
| + |
| + drv->safe_state_index = -1; |
| dev = &per_cpu(omap3_idle_dev, smp_processor_id()); |
| - dev->safe_state_index = -1; |
| |
| /* C1 . MPU WFI + Core active */ |
| - cx = _fill_cstate(dev, 0, "MPU ON + CORE ON"); |
| - (&dev->states[0])->enter = omap3_enter_idle; |
| - dev->safe_state_index = 0; |
| + _fill_cstate(drv, 0, "MPU ON + CORE ON"); |
| + (&drv->states[0])->enter = omap3_enter_idle; |
| + drv->safe_state_index = 0; |
| + cx = _fill_cstate_usage(dev, 0); |
| cx->valid = 1; /* C1 is always valid */ |
| cx->mpu_state = PWRDM_POWER_ON; |
| cx->core_state = PWRDM_POWER_ON; |
| |
| /* C2 . MPU WFI + Core inactive */ |
| - cx = _fill_cstate(dev, 1, "MPU ON + CORE ON"); |
| + _fill_cstate(drv, 1, "MPU ON + CORE ON"); |
| + cx = _fill_cstate_usage(dev, 1); |
| cx->mpu_state = PWRDM_POWER_ON; |
| cx->core_state = PWRDM_POWER_ON; |
| |
| /* C3 . MPU CSWR + Core inactive */ |
| - cx = _fill_cstate(dev, 2, "MPU RET + CORE ON"); |
| + _fill_cstate(drv, 2, "MPU RET + CORE ON"); |
| + cx = _fill_cstate_usage(dev, 2); |
| cx->mpu_state = PWRDM_POWER_RET; |
| cx->core_state = PWRDM_POWER_ON; |
| |
| /* C4 . MPU OFF + Core inactive */ |
| - cx = _fill_cstate(dev, 3, "MPU OFF + CORE ON"); |
| + _fill_cstate(drv, 3, "MPU OFF + CORE ON"); |
| + cx = _fill_cstate_usage(dev, 3); |
| cx->mpu_state = PWRDM_POWER_OFF; |
| cx->core_state = PWRDM_POWER_ON; |
| |
| /* C5 . MPU RET + Core RET */ |
| - cx = _fill_cstate(dev, 4, "MPU RET + CORE RET"); |
| + _fill_cstate(drv, 4, "MPU RET + CORE RET"); |
| + cx = _fill_cstate_usage(dev, 4); |
| cx->mpu_state = PWRDM_POWER_RET; |
| cx->core_state = PWRDM_POWER_RET; |
| |
| /* C6 . MPU OFF + Core RET */ |
| - cx = _fill_cstate(dev, 5, "MPU OFF + CORE RET"); |
| + _fill_cstate(drv, 5, "MPU OFF + CORE RET"); |
| + cx = _fill_cstate_usage(dev, 5); |
| cx->mpu_state = PWRDM_POWER_OFF; |
| cx->core_state = PWRDM_POWER_RET; |
| |
| /* C7 . MPU OFF + Core OFF */ |
| - cx = _fill_cstate(dev, 6, "MPU OFF + CORE OFF"); |
| + _fill_cstate(drv, 6, "MPU OFF + CORE OFF"); |
| + cx = _fill_cstate_usage(dev, 6); |
| /* |
| * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot |
| * enable OFF mode in a stable form for previous revisions. |
| @@ -400,6 +422,9 @@ int __init omap3_idle_init(void) |
| cx->mpu_state = PWRDM_POWER_OFF; |
| cx->core_state = PWRDM_POWER_OFF; |
| |
| + drv->state_count = OMAP3_NUM_STATES; |
| + cpuidle_register_driver(&omap3_idle_driver); |
| + |
| dev->state_count = OMAP3_NUM_STATES; |
| if (cpuidle_register_device(dev)) { |
| printk(KERN_ERR "%s: CPUidle register device failed\n", |
| --- a/arch/sh/kernel/cpu/shmobile/cpuidle.c |
| +++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c |
| @@ -25,6 +25,7 @@ static unsigned long cpuidle_mode[] = { |
| }; |
| |
| static int cpuidle_sleep_enter(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index) |
| { |
| unsigned long allowed_mode = arch_hwblk_sleep_mode(); |
| @@ -64,19 +65,19 @@ static struct cpuidle_driver cpuidle_dri |
| void sh_mobile_setup_cpuidle(void) |
| { |
| struct cpuidle_device *dev = &cpuidle_dev; |
| + struct cpuidle_driver *drv = &cpuidle_driver; |
| struct cpuidle_state *state; |
| int i; |
| |
| - cpuidle_register_driver(&cpuidle_driver); |
| |
| for (i = 0; i < CPUIDLE_STATE_MAX; i++) { |
| - dev->states[i].name[0] = '\0'; |
| - dev->states[i].desc[0] = '\0'; |
| + drv->states[i].name[0] = '\0'; |
| + drv->states[i].desc[0] = '\0'; |
| } |
| |
| i = CPUIDLE_DRIVER_STATE_START; |
| |
| - state = &dev->states[i++]; |
| + state = &drv->states[i++]; |
| snprintf(state->name, CPUIDLE_NAME_LEN, "C1"); |
| strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN); |
| state->exit_latency = 1; |
| @@ -86,10 +87,10 @@ void sh_mobile_setup_cpuidle(void) |
| state->flags |= CPUIDLE_FLAG_TIME_VALID; |
| state->enter = cpuidle_sleep_enter; |
| |
| - dev->safe_state_index = i-1; |
| + drv->safe_state_index = i-1; |
| |
| if (sh_mobile_sleep_supported & SUSP_SH_SF) { |
| - state = &dev->states[i++]; |
| + state = &drv->states[i++]; |
| snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); |
| strncpy(state->desc, "SuperH Sleep Mode [SF]", |
| CPUIDLE_DESC_LEN); |
| @@ -102,7 +103,7 @@ void sh_mobile_setup_cpuidle(void) |
| } |
| |
| if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) { |
| - state = &dev->states[i++]; |
| + state = &drv->states[i++]; |
| snprintf(state->name, CPUIDLE_NAME_LEN, "C3"); |
| strncpy(state->desc, "SuperH Mobile Standby Mode [SF]", |
| CPUIDLE_DESC_LEN); |
| @@ -114,7 +115,10 @@ void sh_mobile_setup_cpuidle(void) |
| state->enter = cpuidle_sleep_enter; |
| } |
| |
| + drv->state_count = i; |
| dev->state_count = i; |
| |
| + cpuidle_register_driver(&cpuidle_driver); |
| + |
| cpuidle_register_device(dev); |
| } |
| --- a/drivers/acpi/processor_driver.c |
| +++ b/drivers/acpi/processor_driver.c |
| @@ -427,7 +427,7 @@ static int acpi_cpu_soft_notify(struct n |
| |
| if (action == CPU_ONLINE && pr) { |
| acpi_processor_ppc_has_changed(pr, 0); |
| - acpi_processor_cst_has_changed(pr); |
| + acpi_processor_hotplug(pr); |
| acpi_processor_reevaluate_tstate(pr, action); |
| acpi_processor_tstate_has_changed(pr); |
| } |
| @@ -504,8 +504,7 @@ static int __cpuinit acpi_processor_add( |
| acpi_processor_get_throttling_info(pr); |
| acpi_processor_get_limit_info(pr); |
| |
| - |
| - if (cpuidle_get_driver() == &acpi_idle_driver) |
| + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) |
| acpi_processor_power_init(pr, device); |
| |
| pr->cdev = thermal_cooling_device_register("Processor", device, |
| @@ -801,17 +800,9 @@ static int __init acpi_processor_init(vo |
| |
| memset(&errata, 0, sizeof(errata)); |
| |
| - if (!cpuidle_register_driver(&acpi_idle_driver)) { |
| - printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", |
| - acpi_idle_driver.name); |
| - } else { |
| - printk(KERN_DEBUG "ACPI: acpi_idle yielding to %s\n", |
| - cpuidle_get_driver()->name); |
| - } |
| - |
| result = acpi_bus_register_driver(&acpi_processor_driver); |
| if (result < 0) |
| - goto out_cpuidle; |
| + return result; |
| |
| acpi_processor_install_hotplug_notify(); |
| |
| @@ -822,11 +813,6 @@ static int __init acpi_processor_init(vo |
| acpi_processor_throttling_init(); |
| |
| return 0; |
| - |
| -out_cpuidle: |
| - cpuidle_unregister_driver(&acpi_idle_driver); |
| - |
| - return result; |
| } |
| |
| static void __exit acpi_processor_exit(void) |
| --- a/drivers/acpi/processor_idle.c |
| +++ b/drivers/acpi/processor_idle.c |
| @@ -741,11 +741,13 @@ static inline void acpi_idle_do_entry(st |
| /** |
| * acpi_idle_enter_c1 - enters an ACPI C1 state-type |
| * @dev: the target CPU |
| + * @drv: cpuidle driver containing cpuidle state info |
| * @index: index of target state |
| * |
| * This is equivalent to the HALT instruction. |
| */ |
| -static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index) |
| +static int acpi_idle_enter_c1(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index) |
| { |
| ktime_t kt1, kt2; |
| s64 idle_time; |
| @@ -787,9 +789,11 @@ static int acpi_idle_enter_c1(struct cpu |
| /** |
| * acpi_idle_enter_simple - enters an ACPI state without BM handling |
| * @dev: the target CPU |
| + * @drv: cpuidle driver with cpuidle state information |
| * @index: the index of suggested state |
| */ |
| -static int acpi_idle_enter_simple(struct cpuidle_device *dev, int index) |
| +static int acpi_idle_enter_simple(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index) |
| { |
| struct acpi_processor *pr; |
| struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; |
| @@ -869,11 +873,13 @@ static DEFINE_SPINLOCK(c3_lock); |
| /** |
| * acpi_idle_enter_bm - enters C3 with proper BM handling |
| * @dev: the target CPU |
| + * @drv: cpuidle driver containing state data |
| * @index: the index of suggested state |
| * |
| * If BM is detected, the deepest non-C3 idle state is entered instead. |
| */ |
| -static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index) |
| +static int acpi_idle_enter_bm(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index) |
| { |
| struct acpi_processor *pr; |
| struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; |
| @@ -896,9 +902,9 @@ static int acpi_idle_enter_bm(struct cpu |
| } |
| |
| if (!cx->bm_sts_skip && acpi_idle_bm_check()) { |
| - if (dev->safe_state_index >= 0) { |
| - return dev->states[dev->safe_state_index].enter(dev, |
| - dev->safe_state_index); |
| + if (drv->safe_state_index >= 0) { |
| + return drv->states[drv->safe_state_index].enter(dev, |
| + drv, drv->safe_state_index); |
| } else { |
| local_irq_disable(); |
| acpi_safe_halt(); |
| @@ -993,14 +999,15 @@ struct cpuidle_driver acpi_idle_driver = |
| }; |
| |
| /** |
| - * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE |
| + * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE |
| + * device i.e. per-cpu data |
| + * |
| * @pr: the ACPI processor |
| */ |
| -static int acpi_processor_setup_cpuidle(struct acpi_processor *pr) |
| +static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr) |
| { |
| int i, count = CPUIDLE_DRIVER_STATE_START; |
| struct acpi_processor_cx *cx; |
| - struct cpuidle_state *state; |
| struct cpuidle_state_usage *state_usage; |
| struct cpuidle_device *dev = &pr->power.dev; |
| |
| @@ -1015,18 +1022,12 @@ static int acpi_processor_setup_cpuidle( |
| return -EINVAL; |
| |
| dev->cpu = pr->id; |
| - dev->safe_state_index = -1; |
| - for (i = 0; i < CPUIDLE_STATE_MAX; i++) { |
| - dev->states[i].name[0] = '\0'; |
| - dev->states[i].desc[0] = '\0'; |
| - } |
| |
| if (max_cstate == 0) |
| max_cstate = 1; |
| |
| for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { |
| cx = &pr->power.states[i]; |
| - state = &dev->states[count]; |
| state_usage = &dev->states_usage[count]; |
| |
| if (!cx->valid) |
| @@ -1038,8 +1039,64 @@ static int acpi_processor_setup_cpuidle( |
| !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) |
| continue; |
| #endif |
| + |
| cpuidle_set_statedata(state_usage, cx); |
| |
| + count++; |
| + if (count == CPUIDLE_STATE_MAX) |
| + break; |
| + } |
| + |
| + dev->state_count = count; |
| + |
| + if (!count) |
| + return -EINVAL; |
| + |
| + return 0; |
| +} |
| + |
| +/** |
| + * acpi_processor_setup_cpuidle states- prepares and configures cpuidle |
| + * global state data i.e. idle routines |
| + * |
| + * @pr: the ACPI processor |
| + */ |
| +static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) |
| +{ |
| + int i, count = CPUIDLE_DRIVER_STATE_START; |
| + struct acpi_processor_cx *cx; |
| + struct cpuidle_state *state; |
| + struct cpuidle_driver *drv = &acpi_idle_driver; |
| + |
| + if (!pr->flags.power_setup_done) |
| + return -EINVAL; |
| + |
| + if (pr->flags.power == 0) |
| + return -EINVAL; |
| + |
| + drv->safe_state_index = -1; |
| + for (i = 0; i < CPUIDLE_STATE_MAX; i++) { |
| + drv->states[i].name[0] = '\0'; |
| + drv->states[i].desc[0] = '\0'; |
| + } |
| + |
| + if (max_cstate == 0) |
| + max_cstate = 1; |
| + |
| + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { |
| + cx = &pr->power.states[i]; |
| + |
| + if (!cx->valid) |
| + continue; |
| + |
| +#ifdef CONFIG_HOTPLUG_CPU |
| + if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && |
| + !pr->flags.has_cst && |
| + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) |
| + continue; |
| +#endif |
| + |
| + state = &drv->states[count]; |
| snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); |
| strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); |
| state->exit_latency = cx->latency; |
| @@ -1052,13 +1109,13 @@ static int acpi_processor_setup_cpuidle( |
| state->flags |= CPUIDLE_FLAG_TIME_VALID; |
| |
| state->enter = acpi_idle_enter_c1; |
| - dev->safe_state_index = count; |
| + drv->safe_state_index = count; |
| break; |
| |
| case ACPI_STATE_C2: |
| state->flags |= CPUIDLE_FLAG_TIME_VALID; |
| state->enter = acpi_idle_enter_simple; |
| - dev->safe_state_index = count; |
| + drv->safe_state_index = count; |
| break; |
| |
| case ACPI_STATE_C3: |
| @@ -1074,7 +1131,7 @@ static int acpi_processor_setup_cpuidle( |
| break; |
| } |
| |
| - dev->state_count = count; |
| + drv->state_count = count; |
| |
| if (!count) |
| return -EINVAL; |
| @@ -1082,7 +1139,7 @@ static int acpi_processor_setup_cpuidle( |
| return 0; |
| } |
| |
| -int acpi_processor_cst_has_changed(struct acpi_processor *pr) |
| +int acpi_processor_hotplug(struct acpi_processor *pr) |
| { |
| int ret = 0; |
| |
| @@ -1103,7 +1160,7 @@ int acpi_processor_cst_has_changed(struc |
| cpuidle_disable_device(&pr->power.dev); |
| acpi_processor_get_power_info(pr); |
| if (pr->flags.power) { |
| - acpi_processor_setup_cpuidle(pr); |
| + acpi_processor_setup_cpuidle_cx(pr); |
| ret = cpuidle_enable_device(&pr->power.dev); |
| } |
| cpuidle_resume_and_unlock(); |
| @@ -1111,10 +1168,72 @@ int acpi_processor_cst_has_changed(struc |
| return ret; |
| } |
| |
| +int acpi_processor_cst_has_changed(struct acpi_processor *pr) |
| +{ |
| + int cpu; |
| + struct acpi_processor *_pr; |
| + |
| + if (disabled_by_idle_boot_param()) |
| + return 0; |
| + |
| + if (!pr) |
| + return -EINVAL; |
| + |
| + if (nocst) |
| + return -ENODEV; |
| + |
| + if (!pr->flags.power_setup_done) |
| + return -ENODEV; |
| + |
| + /* |
| + * FIXME: Design the ACPI notification to make it once per |
| + * system instead of once per-cpu. This condition is a hack |
| + * to make the code that updates C-States be called once. |
| + */ |
| + |
| + if (smp_processor_id() == 0 && |
| + cpuidle_get_driver() == &acpi_idle_driver) { |
| + |
| + cpuidle_pause_and_lock(); |
| + /* Protect against cpu-hotplug */ |
| + get_online_cpus(); |
| + |
| + /* Disable all cpuidle devices */ |
| + for_each_online_cpu(cpu) { |
| + _pr = per_cpu(processors, cpu); |
| + if (!_pr || !_pr->flags.power_setup_done) |
| + continue; |
| + cpuidle_disable_device(&_pr->power.dev); |
| + } |
| + |
| + /* Populate Updated C-state information */ |
| + acpi_processor_setup_cpuidle_states(pr); |
| + |
| + /* Enable all cpuidle devices */ |
| + for_each_online_cpu(cpu) { |
| + _pr = per_cpu(processors, cpu); |
| + if (!_pr || !_pr->flags.power_setup_done) |
| + continue; |
| + acpi_processor_get_power_info(_pr); |
| + if (_pr->flags.power) { |
| + acpi_processor_setup_cpuidle_cx(_pr); |
| + cpuidle_enable_device(&_pr->power.dev); |
| + } |
| + } |
| + put_online_cpus(); |
| + cpuidle_resume_and_unlock(); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static int acpi_processor_registered; |
| + |
| int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, |
| struct acpi_device *device) |
| { |
| acpi_status status = 0; |
| + int retval; |
| static int first_run; |
| |
| if (disabled_by_idle_boot_param()) |
| @@ -1151,9 +1270,26 @@ int __cpuinit acpi_processor_power_init( |
| * platforms that only support C1. |
| */ |
| if (pr->flags.power) { |
| - acpi_processor_setup_cpuidle(pr); |
| - if (cpuidle_register_device(&pr->power.dev)) |
| - return -EIO; |
| + /* Register acpi_idle_driver if not already registered */ |
| + if (!acpi_processor_registered) { |
| + acpi_processor_setup_cpuidle_states(pr); |
| + retval = cpuidle_register_driver(&acpi_idle_driver); |
| + if (retval) |
| + return retval; |
| + printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", |
| + acpi_idle_driver.name); |
| + } |
| + /* Register per-cpu cpuidle_device. Cpuidle driver |
| + * must already be registered before registering device |
| + */ |
| + acpi_processor_setup_cpuidle_cx(pr); |
| + retval = cpuidle_register_device(&pr->power.dev); |
| + if (retval) { |
| + if (acpi_processor_registered == 0) |
| + cpuidle_unregister_driver(&acpi_idle_driver); |
| + return retval; |
| + } |
| + acpi_processor_registered++; |
| } |
| return 0; |
| } |
| @@ -1164,8 +1300,13 @@ int acpi_processor_power_exit(struct acp |
| if (disabled_by_idle_boot_param()) |
| return 0; |
| |
| - cpuidle_unregister_device(&pr->power.dev); |
| - pr->flags.power_setup_done = 0; |
| + if (pr->flags.power) { |
| + cpuidle_unregister_device(&pr->power.dev); |
| + acpi_processor_registered--; |
| + if (acpi_processor_registered == 0) |
| + cpuidle_unregister_driver(&acpi_idle_driver); |
| + } |
| |
| + pr->flags.power_setup_done = 0; |
| return 0; |
| } |
| --- a/drivers/cpuidle/cpuidle.c |
| +++ b/drivers/cpuidle/cpuidle.c |
| @@ -62,6 +62,7 @@ static int __cpuidle_register_device(str |
| int cpuidle_idle_call(void) |
| { |
| struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); |
| + struct cpuidle_driver *drv = cpuidle_get_driver(); |
| struct cpuidle_state *target_state; |
| int next_state, entered_state; |
| |
| @@ -85,18 +86,18 @@ int cpuidle_idle_call(void) |
| #endif |
| |
| /* ask the governor for the next state */ |
| - next_state = cpuidle_curr_governor->select(dev); |
| + next_state = cpuidle_curr_governor->select(drv, dev); |
| if (need_resched()) { |
| local_irq_enable(); |
| return 0; |
| } |
| |
| - target_state = &dev->states[next_state]; |
| + target_state = &drv->states[next_state]; |
| |
| trace_power_start(POWER_CSTATE, next_state, dev->cpu); |
| trace_cpu_idle(next_state, dev->cpu); |
| |
| - entered_state = target_state->enter(dev, next_state); |
| + entered_state = target_state->enter(dev, drv, next_state); |
| |
| trace_power_end(dev->cpu); |
| trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu); |
| @@ -164,7 +165,8 @@ void cpuidle_resume_and_unlock(void) |
| EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); |
| |
| #ifdef CONFIG_ARCH_HAS_CPU_RELAX |
| -static int poll_idle(struct cpuidle_device *dev, int index) |
| +static int poll_idle(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index) |
| { |
| ktime_t t1, t2; |
| s64 diff; |
| @@ -184,12 +186,9 @@ static int poll_idle(struct cpuidle_devi |
| return index; |
| } |
| |
| -static void poll_idle_init(struct cpuidle_device *dev) |
| +static void poll_idle_init(struct cpuidle_driver *drv) |
| { |
| - struct cpuidle_state *state = &dev->states[0]; |
| - struct cpuidle_state_usage *state_usage = &dev->states_usage[0]; |
| - |
| - cpuidle_set_statedata(state_usage, NULL); |
| + struct cpuidle_state *state = &drv->states[0]; |
| |
| snprintf(state->name, CPUIDLE_NAME_LEN, "POLL"); |
| snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); |
| @@ -200,7 +199,7 @@ static void poll_idle_init(struct cpuidl |
| state->enter = poll_idle; |
| } |
| #else |
| -static void poll_idle_init(struct cpuidle_device *dev) {} |
| +static void poll_idle_init(struct cpuidle_driver *drv) {} |
| #endif /* CONFIG_ARCH_HAS_CPU_RELAX */ |
| |
| /** |
| @@ -227,13 +226,13 @@ int cpuidle_enable_device(struct cpuidle |
| return ret; |
| } |
| |
| - poll_idle_init(dev); |
| + poll_idle_init(cpuidle_get_driver()); |
| |
| if ((ret = cpuidle_add_state_sysfs(dev))) |
| return ret; |
| |
| if (cpuidle_curr_governor->enable && |
| - (ret = cpuidle_curr_governor->enable(dev))) |
| + (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev))) |
| goto fail_sysfs; |
| |
| for (i = 0; i < dev->state_count; i++) { |
| @@ -274,7 +273,7 @@ void cpuidle_disable_device(struct cpuid |
| dev->enabled = 0; |
| |
| if (cpuidle_curr_governor->disable) |
| - cpuidle_curr_governor->disable(dev); |
| + cpuidle_curr_governor->disable(cpuidle_get_driver(), dev); |
| |
| cpuidle_remove_state_sysfs(dev); |
| enabled_devices--; |
| @@ -302,26 +301,6 @@ static int __cpuidle_register_device(str |
| |
| init_completion(&dev->kobj_unregister); |
| |
| - /* |
| - * cpuidle driver should set the dev->power_specified bit |
| - * before registering the device if the driver provides |
| - * power_usage numbers. |
| - * |
| - * For those devices whose ->power_specified is not set, |
| - * we fill in power_usage with decreasing values as the |
| - * cpuidle code has an implicit assumption that state Cn |
| - * uses less power than C(n-1). |
| - * |
| - * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned |
| - * an power value of -1. So we use -2, -3, etc, for other |
| - * c-states. |
| - */ |
| - if (!dev->power_specified) { |
| - int i; |
| - for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) |
| - dev->states[i].power_usage = -1 - i; |
| - } |
| - |
| per_cpu(cpuidle_devices, dev->cpu) = dev; |
| list_add(&dev->device_list, &cpuidle_detected_devices); |
| if ((ret = cpuidle_add_sysfs(sys_dev))) { |
| --- a/drivers/cpuidle/driver.c |
| +++ b/drivers/cpuidle/driver.c |
| @@ -17,6 +17,30 @@ |
| static struct cpuidle_driver *cpuidle_curr_driver; |
| DEFINE_SPINLOCK(cpuidle_driver_lock); |
| |
| +static void __cpuidle_register_driver(struct cpuidle_driver *drv) |
| +{ |
| + int i; |
| + /* |
| + * cpuidle driver should set the drv->power_specified bit |
| + * before registering if the driver provides |
| + * power_usage numbers. |
| + * |
| + * If power_specified is not set, |
| + * we fill in power_usage with decreasing values as the |
| + * cpuidle code has an implicit assumption that state Cn |
| + * uses less power than C(n-1). |
| + * |
| + * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned |
| + * an power value of -1. So we use -2, -3, etc, for other |
| + * c-states. |
| + */ |
| + if (!drv->power_specified) { |
| + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) |
| + drv->states[i].power_usage = -1 - i; |
| + } |
| +} |
| + |
| + |
| /** |
| * cpuidle_register_driver - registers a driver |
| * @drv: the driver |
| @@ -34,6 +58,7 @@ int cpuidle_register_driver(struct cpuid |
| spin_unlock(&cpuidle_driver_lock); |
| return -EBUSY; |
| } |
| + __cpuidle_register_driver(drv); |
| cpuidle_curr_driver = drv; |
| spin_unlock(&cpuidle_driver_lock); |
| |
| --- a/drivers/cpuidle/governors/ladder.c |
| +++ b/drivers/cpuidle/governors/ladder.c |
| @@ -60,9 +60,11 @@ static inline void ladder_do_selection(s |
| |
| /** |
| * ladder_select_state - selects the next state to enter |
| + * @drv: cpuidle driver |
| * @dev: the CPU |
| */ |
| -static int ladder_select_state(struct cpuidle_device *dev) |
| +static int ladder_select_state(struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev) |
| { |
| struct ladder_device *ldev = &__get_cpu_var(ladder_devices); |
| struct ladder_device_state *last_state; |
| @@ -77,15 +79,17 @@ static int ladder_select_state(struct cp |
| |
| last_state = &ldev->states[last_idx]; |
| |
| - if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) |
| - last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency; |
| + if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) { |
| + last_residency = cpuidle_get_last_residency(dev) - \ |
| + drv->states[last_idx].exit_latency; |
| + } |
| else |
| last_residency = last_state->threshold.promotion_time + 1; |
| |
| /* consider promotion */ |
| - if (last_idx < dev->state_count - 1 && |
| + if (last_idx < drv->state_count - 1 && |
| last_residency > last_state->threshold.promotion_time && |
| - dev->states[last_idx + 1].exit_latency <= latency_req) { |
| + drv->states[last_idx + 1].exit_latency <= latency_req) { |
| last_state->stats.promotion_count++; |
| last_state->stats.demotion_count = 0; |
| if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { |
| @@ -96,11 +100,11 @@ static int ladder_select_state(struct cp |
| |
| /* consider demotion */ |
| if (last_idx > CPUIDLE_DRIVER_STATE_START && |
| - dev->states[last_idx].exit_latency > latency_req) { |
| + drv->states[last_idx].exit_latency > latency_req) { |
| int i; |
| |
| for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) { |
| - if (dev->states[i].exit_latency <= latency_req) |
| + if (drv->states[i].exit_latency <= latency_req) |
| break; |
| } |
| ladder_do_selection(ldev, last_idx, i); |
| @@ -123,9 +127,11 @@ static int ladder_select_state(struct cp |
| |
| /** |
| * ladder_enable_device - setup for the governor |
| + * @drv: cpuidle driver |
| * @dev: the CPU |
| */ |
| -static int ladder_enable_device(struct cpuidle_device *dev) |
| +static int ladder_enable_device(struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev) |
| { |
| int i; |
| struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu); |
| @@ -134,8 +140,8 @@ static int ladder_enable_device(struct c |
| |
| ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START; |
| |
| - for (i = 0; i < dev->state_count; i++) { |
| - state = &dev->states[i]; |
| + for (i = 0; i < drv->state_count; i++) { |
| + state = &drv->states[i]; |
| lstate = &ldev->states[i]; |
| |
| lstate->stats.promotion_count = 0; |
| @@ -144,7 +150,7 @@ static int ladder_enable_device(struct c |
| lstate->threshold.promotion_count = PROMOTION_COUNT; |
| lstate->threshold.demotion_count = DEMOTION_COUNT; |
| |
| - if (i < dev->state_count - 1) |
| + if (i < drv->state_count - 1) |
| lstate->threshold.promotion_time = state->exit_latency; |
| if (i > 0) |
| lstate->threshold.demotion_time = state->exit_latency; |
| --- a/drivers/cpuidle/governors/menu.c |
| +++ b/drivers/cpuidle/governors/menu.c |
| @@ -183,7 +183,7 @@ static inline int performance_multiplier |
| |
| static DEFINE_PER_CPU(struct menu_device, menu_devices); |
| |
| -static void menu_update(struct cpuidle_device *dev); |
| +static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); |
| |
| /* This implements DIV_ROUND_CLOSEST but avoids 64 bit division */ |
| static u64 div_round64(u64 dividend, u32 divisor) |
| @@ -229,9 +229,10 @@ static void detect_repeating_patterns(st |
| |
| /** |
| * menu_select - selects the next idle state to enter |
| + * @drv: cpuidle driver containing state data |
| * @dev: the CPU |
| */ |
| -static int menu_select(struct cpuidle_device *dev) |
| +static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) |
| { |
| struct menu_device *data = &__get_cpu_var(menu_devices); |
| int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); |
| @@ -241,7 +242,7 @@ static int menu_select(struct cpuidle_de |
| struct timespec t; |
| |
| if (data->needs_update) { |
| - menu_update(dev); |
| + menu_update(drv, dev); |
| data->needs_update = 0; |
| } |
| |
| @@ -286,8 +287,8 @@ static int menu_select(struct cpuidle_de |
| * Find the idle state with the lowest power while satisfying |
| * our constraints. |
| */ |
| - for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) { |
| - struct cpuidle_state *s = &dev->states[i]; |
| + for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { |
| + struct cpuidle_state *s = &drv->states[i]; |
| |
| if (s->target_residency > data->predicted_us) |
| continue; |
| @@ -324,14 +325,15 @@ static void menu_reflect(struct cpuidle_ |
| |
| /** |
| * menu_update - attempts to guess what happened after entry |
| + * @drv: cpuidle driver containing state data |
| * @dev: the CPU |
| */ |
| -static void menu_update(struct cpuidle_device *dev) |
| +static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) |
| { |
| struct menu_device *data = &__get_cpu_var(menu_devices); |
| int last_idx = data->last_state_idx; |
| unsigned int last_idle_us = cpuidle_get_last_residency(dev); |
| - struct cpuidle_state *target = &dev->states[last_idx]; |
| + struct cpuidle_state *target = &drv->states[last_idx]; |
| unsigned int measured_us; |
| u64 new_factor; |
| |
| @@ -385,9 +387,11 @@ static void menu_update(struct cpuidle_d |
| |
| /** |
| * menu_enable_device - scans a CPU's states and does setup |
| + * @drv: cpuidle driver |
| * @dev: the CPU |
| */ |
| -static int menu_enable_device(struct cpuidle_device *dev) |
| +static int menu_enable_device(struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev) |
| { |
| struct menu_device *data = &per_cpu(menu_devices, dev->cpu); |
| |
| --- a/drivers/cpuidle/sysfs.c |
| +++ b/drivers/cpuidle/sysfs.c |
| @@ -322,13 +322,14 @@ int cpuidle_add_state_sysfs(struct cpuid |
| { |
| int i, ret = -ENOMEM; |
| struct cpuidle_state_kobj *kobj; |
| + struct cpuidle_driver *drv = cpuidle_get_driver(); |
| |
| /* state statistics */ |
| for (i = 0; i < device->state_count; i++) { |
| kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL); |
| if (!kobj) |
| goto error_state; |
| - kobj->state = &device->states[i]; |
| + kobj->state = &drv->states[i]; |
| kobj->state_usage = &device->states_usage[i]; |
| init_completion(&kobj->kobj_unregister); |
| |
| --- a/drivers/idle/intel_idle.c |
| +++ b/drivers/idle/intel_idle.c |
| @@ -81,7 +81,8 @@ static unsigned int mwait_substates; |
| static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ |
| |
| static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; |
| -static int intel_idle(struct cpuidle_device *dev, int index); |
| +static int intel_idle(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index); |
| |
| static struct cpuidle_state *cpuidle_state_table; |
| |
| @@ -227,13 +228,15 @@ static int get_driver_data(int cstate) |
| /** |
| * intel_idle |
| * @dev: cpuidle_device |
| + * @drv: cpuidle driver |
| * @index: index of cpuidle state |
| * |
| */ |
| -static int intel_idle(struct cpuidle_device *dev, int index) |
| +static int intel_idle(struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, int index) |
| { |
| unsigned long ecx = 1; /* break on interrupt flag */ |
| - struct cpuidle_state *state = &dev->states[index]; |
| + struct cpuidle_state *state = &drv->states[index]; |
| struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; |
| unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage); |
| unsigned int cstate; |
| @@ -421,6 +424,60 @@ static void intel_idle_cpuidle_devices_u |
| return; |
| } |
| /* |
| + * intel_idle_cpuidle_driver_init() |
| + * allocate, initialize cpuidle_states |
| + */ |
| +static int intel_idle_cpuidle_driver_init(void) |
| +{ |
| + int cstate; |
| + struct cpuidle_driver *drv = &intel_idle_driver; |
| + |
| + drv->state_count = 1; |
| + |
| + for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) { |
| + int num_substates; |
| + |
| + if (cstate > max_cstate) { |
| + printk(PREFIX "max_cstate %d reached\n", |
| + max_cstate); |
| + break; |
| + } |
| + |
| + /* does the state exist in CPUID.MWAIT? */ |
| + num_substates = (mwait_substates >> ((cstate) * 4)) |
| + & MWAIT_SUBSTATE_MASK; |
| + if (num_substates == 0) |
| + continue; |
| + /* is the state not enabled? */ |
| + if (cpuidle_state_table[cstate].enter == NULL) { |
| + /* does the driver not know about the state? */ |
| + if (*cpuidle_state_table[cstate].name == '\0') |
| + pr_debug(PREFIX "unaware of model 0x%x" |
| + " MWAIT %d please" |
| + " contact lenb@kernel.org", |
| + boot_cpu_data.x86_model, cstate); |
| + continue; |
| + } |
| + |
| + if ((cstate > 2) && |
| + !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) |
| + mark_tsc_unstable("TSC halts in idle" |
| + " states deeper than C2"); |
| + |
| + drv->states[drv->state_count] = /* structure copy */ |
| + cpuidle_state_table[cstate]; |
| + |
| + drv->state_count += 1; |
| + } |
| + |
| + if (auto_demotion_disable_flags) |
| + smp_call_function(auto_demotion_disable, NULL, 1); |
| + |
| + return 0; |
| +} |
| + |
| + |
| +/* |
| * intel_idle_cpuidle_devices_init() |
| * allocate, initialize, register cpuidle_devices |
| */ |
| @@ -454,23 +511,9 @@ static int intel_idle_cpuidle_devices_in |
| continue; |
| /* is the state not enabled? */ |
| if (cpuidle_state_table[cstate].enter == NULL) { |
| - /* does the driver not know about the state? */ |
| - if (*cpuidle_state_table[cstate].name == '\0') |
| - pr_debug(PREFIX "unaware of model 0x%x" |
| - " MWAIT %d please" |
| - " contact lenb@kernel.org", |
| - boot_cpu_data.x86_model, cstate); |
| continue; |
| } |
| |
| - if ((cstate > 2) && |
| - !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) |
| - mark_tsc_unstable("TSC halts in idle" |
| - " states deeper than C2"); |
| - |
| - dev->states[dev->state_count] = /* structure copy */ |
| - cpuidle_state_table[cstate]; |
| - |
| dev->states_usage[dev->state_count].driver_data = |
| (void *)get_driver_data(cstate); |
| |
| @@ -485,8 +528,6 @@ static int intel_idle_cpuidle_devices_in |
| return -EIO; |
| } |
| } |
| - if (auto_demotion_disable_flags) |
| - on_each_cpu(auto_demotion_disable, NULL, 1); |
| |
| return 0; |
| } |
| @@ -504,6 +545,7 @@ static int __init intel_idle_init(void) |
| if (retval) |
| return retval; |
| |
| + intel_idle_cpuidle_driver_init(); |
| retval = cpuidle_register_driver(&intel_idle_driver); |
| if (retval) { |
| printk(KERN_DEBUG PREFIX "intel_idle yielding to %s", |
| --- a/include/acpi/processor.h |
| +++ b/include/acpi/processor.h |
| @@ -329,6 +329,7 @@ extern void acpi_processor_throttling_in |
| int acpi_processor_power_init(struct acpi_processor *pr, |
| struct acpi_device *device); |
| int acpi_processor_cst_has_changed(struct acpi_processor *pr); |
| +int acpi_processor_hotplug(struct acpi_processor *pr); |
| int acpi_processor_power_exit(struct acpi_processor *pr, |
| struct acpi_device *device); |
| int acpi_processor_suspend(struct acpi_device * device, pm_message_t state); |
| --- a/include/linux/cpuidle.h |
| +++ b/include/linux/cpuidle.h |
| @@ -22,6 +22,7 @@ |
| #define CPUIDLE_DESC_LEN 32 |
| |
| struct cpuidle_device; |
| +struct cpuidle_driver; |
| |
| |
| /**************************** |
| @@ -45,6 +46,7 @@ struct cpuidle_state { |
| unsigned int target_residency; /* in US */ |
| |
| int (*enter) (struct cpuidle_device *dev, |
| + struct cpuidle_driver *drv, |
| int index); |
| }; |
| |
| @@ -83,12 +85,10 @@ struct cpuidle_state_kobj { |
| struct cpuidle_device { |
| unsigned int registered:1; |
| unsigned int enabled:1; |
| - unsigned int power_specified:1; |
| unsigned int cpu; |
| |
| int last_residency; |
| int state_count; |
| - struct cpuidle_state states[CPUIDLE_STATE_MAX]; |
| struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; |
| struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; |
| |
| @@ -96,7 +96,6 @@ struct cpuidle_device { |
| struct kobject kobj; |
| struct completion kobj_unregister; |
| void *governor_data; |
| - int safe_state_index; |
| }; |
| |
| DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices); |
| @@ -120,6 +119,11 @@ static inline int cpuidle_get_last_resid |
| struct cpuidle_driver { |
| char name[CPUIDLE_NAME_LEN]; |
| struct module *owner; |
| + |
| + unsigned int power_specified:1; |
| + struct cpuidle_state states[CPUIDLE_STATE_MAX]; |
| + int state_count; |
| + int safe_state_index; |
| }; |
| |
| #ifdef CONFIG_CPU_IDLE |
| @@ -166,10 +170,13 @@ struct cpuidle_governor { |
| struct list_head governor_list; |
| unsigned int rating; |
| |
| - int (*enable) (struct cpuidle_device *dev); |
| - void (*disable) (struct cpuidle_device *dev); |
| + int (*enable) (struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev); |
| + void (*disable) (struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev); |
| |
| - int (*select) (struct cpuidle_device *dev); |
| + int (*select) (struct cpuidle_driver *drv, |
| + struct cpuidle_device *dev); |
| void (*reflect) (struct cpuidle_device *dev, int index); |
| |
| struct module *owner; |