diff options
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r-- | drivers/cpuidle/cpuidle-arm.c | 153 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 14 | ||||
-rw-r--r-- | drivers/cpuidle/governors/ladder.c | 7 | ||||
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 4 |
4 files changed, 107 insertions, 71 deletions
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index 52a75053ee03..ddee1b601b89 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -72,12 +72,94 @@ static const struct of_device_id arm_idle_state_match[] __initconst = { }; /* - * arm_idle_init + * arm_idle_init_cpu * * Registers the arm specific cpuidle driver with the cpuidle * framework. It relies on core code to parse the idle states * and initialize them using driver data structures accordingly. */ +static int __init arm_idle_init_cpu(int cpu) +{ + int ret; + struct cpuidle_driver *drv; + struct cpuidle_device *dev; + + drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->cpumask = (struct cpumask *)cpumask_of(cpu); + + /* + * Initialize idle states data, starting at index 1. This + * driver is DT only, if no DT idle states are detected (ret + * == 0) let the driver initialization fail accordingly since + * there is no reason to initialize the idle driver if only + * wfi is supported. + */ + ret = dt_init_idle_driver(drv, arm_idle_state_match, 1); + if (ret <= 0) { + ret = ret ? : -ENODEV; + goto out_kfree_drv; + } + + ret = cpuidle_register_driver(drv); + if (ret) { + pr_err("Failed to register cpuidle driver\n"); + goto out_kfree_drv; + } + + /* + * Call arch CPU operations in order to initialize + * idle states suspend back-end specific data + */ + ret = arm_cpuidle_init(cpu); + + /* + * Skip the cpuidle device initialization if the reported + * failure is a HW misconfiguration/breakage (-ENXIO). + */ + if (ret == -ENXIO) + return 0; + + if (ret) { + pr_err("CPU %d failed to init idle CPU ops\n", cpu); + goto out_unregister_drv; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + pr_err("Failed to allocate cpuidle device\n"); + ret = -ENOMEM; + goto out_unregister_drv; + } + dev->cpu = cpu; + + ret = cpuidle_register_device(dev); + if (ret) { + pr_err("Failed to register cpuidle device for CPU %d\n", + cpu); + goto out_kfree_dev; + } + + return 0; + +out_kfree_dev: + kfree(dev); +out_unregister_drv: + cpuidle_unregister_driver(drv); +out_kfree_drv: + kfree(drv); + return ret; +} + +/* + * arm_idle_init - Initializes arm cpuidle driver + * + * Initializes arm cpuidle driver for all CPUs, if any CPU fails + * to register cpuidle driver then rollback to cancel all CPUs + * registeration. + */ static int __init arm_idle_init(void) { int cpu, ret; @@ -85,79 +167,20 @@ static int __init arm_idle_init(void) struct cpuidle_device *dev; for_each_possible_cpu(cpu) { - - drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL); - if (!drv) { - ret = -ENOMEM; - goto out_fail; - } - - drv->cpumask = (struct cpumask *)cpumask_of(cpu); - - /* - * Initialize idle states data, starting at index 1. This - * driver is DT only, if no DT idle states are detected (ret - * == 0) let the driver initialization fail accordingly since - * there is no reason to initialize the idle driver if only - * wfi is supported. - */ - ret = dt_init_idle_driver(drv, arm_idle_state_match, 1); - if (ret <= 0) { - ret = ret ? : -ENODEV; - goto init_fail; - } - - ret = cpuidle_register_driver(drv); - if (ret) { - pr_err("Failed to register cpuidle driver\n"); - goto init_fail; - } - - /* - * Call arch CPU operations in order to initialize - * idle states suspend back-end specific data - */ - ret = arm_cpuidle_init(cpu); - - /* - * Skip the cpuidle device initialization if the reported - * failure is a HW misconfiguration/breakage (-ENXIO). - */ - if (ret == -ENXIO) - continue; - - if (ret) { - pr_err("CPU %d failed to init idle CPU ops\n", cpu); - goto out_fail; - } - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - pr_err("Failed to allocate cpuidle device\n"); - ret = -ENOMEM; + ret = arm_idle_init_cpu(cpu); + if (ret) goto out_fail; - } - dev->cpu = cpu; - - ret = cpuidle_register_device(dev); - if (ret) { - pr_err("Failed to register cpuidle device for CPU %d\n", - cpu); - kfree(dev); - goto out_fail; - } } return 0; -init_fail: - kfree(drv); + out_fail: while (--cpu >= 0) { dev = per_cpu(cpuidle_devices, cpu); + drv = cpuidle_get_cpu_driver(dev); cpuidle_unregister_device(dev); - kfree(dev); - drv = cpuidle_get_driver(); cpuidle_unregister_driver(drv); + kfree(dev); kfree(drv); } diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 484cc8909d5c..68a16827f45f 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -208,6 +208,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, return -EBUSY; } target_state = &drv->states[index]; + broadcast = false; } /* Take note of the planned idle state. */ @@ -387,9 +388,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev) if (dev->enabled) return 0; + if (!cpuidle_curr_governor) + return -EIO; + drv = cpuidle_get_cpu_driver(dev); - if (!drv || !cpuidle_curr_governor) + if (!drv) return -EIO; if (!dev->registered) @@ -399,9 +403,11 @@ int cpuidle_enable_device(struct cpuidle_device *dev) if (ret) return ret; - if (cpuidle_curr_governor->enable && - (ret = cpuidle_curr_governor->enable(drv, dev))) - goto fail_sysfs; + if (cpuidle_curr_governor->enable) { + ret = cpuidle_curr_governor->enable(drv, dev); + if (ret) + goto fail_sysfs; + } smp_wmb(); diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index ce1a2ffffb2a..1ad8745fd6d6 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -17,6 +17,7 @@ #include <linux/pm_qos.h> #include <linux/jiffies.h> #include <linux/tick.h> +#include <linux/cpu.h> #include <asm/io.h> #include <linux/uaccess.h> @@ -67,10 +68,16 @@ static int ladder_select_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); + struct device *device = get_cpu_device(dev->cpu); struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); + int resume_latency = dev_pm_qos_raw_read_value(device); + + if (resume_latency < latency_req && + resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) + latency_req = resume_latency; /* Special case when user has set very strict latency requirement */ if (unlikely(latency_req == 0)) { diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 48eaf2879228..aa390404e85f 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) data->needs_update = 0; } - /* resume_latency is 0 means no restriction */ - if (resume_latency && resume_latency < latency_req) + if (resume_latency < latency_req && + resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) latency_req = resume_latency; /* Special case when user has set very strict latency requirement */ |