diff options
Diffstat (limited to 'drivers/thermal/cpu_cooling.c')
-rw-r--r-- | drivers/thermal/cpu_cooling.c | 146 |
1 files changed, 60 insertions, 86 deletions
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 9ce0e9eef923..69d0f430b2d1 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -26,6 +26,7 @@ #include <linux/thermal.h> #include <linux/cpufreq.h> #include <linux/err.h> +#include <linux/idr.h> #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/cpu.h> @@ -104,50 +105,11 @@ struct cpufreq_cooling_device { struct device *cpu_dev; get_static_t plat_get_static_power; }; -static DEFINE_IDR(cpufreq_idr); -static DEFINE_MUTEX(cooling_cpufreq_lock); - -static unsigned int cpufreq_dev_count; +static DEFINE_IDA(cpufreq_ida); static DEFINE_MUTEX(cooling_list_lock); static LIST_HEAD(cpufreq_dev_list); -/** - * get_idr - function to get a unique id. - * @idr: struct idr * handle used to create a id. - * @id: int * value generated by this function. - * - * This function will populate @id with an unique - * id, using the idr API. - * - * Return: 0 on success, an error code on failure. - */ -static int get_idr(struct idr *idr, int *id) -{ - int ret; - - mutex_lock(&cooling_cpufreq_lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - mutex_unlock(&cooling_cpufreq_lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - - return 0; -} - -/** - * release_idr - function to free the unique id. - * @idr: struct idr * handle used for creating the id. - * @id: int value representing the unique id. - */ -static void release_idr(struct idr *idr, int id) -{ - mutex_lock(&cooling_cpufreq_lock); - idr_remove(idr, id); - mutex_unlock(&cooling_cpufreq_lock); -} - /* Below code defines functions to be used for cpufreq as cooling device */ /** @@ -297,8 +259,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, if (!power_table) return -ENOMEM; - rcu_read_lock(); - for (freq = 0, i = 0; opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); freq++, i++) { @@ -306,13 +266,13 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, u64 power; if (i >= num_opps) { - rcu_read_unlock(); ret = -EAGAIN; goto free_power_table; } freq_mhz = freq / 1000000; voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); /* * Do the multiplication with MHz and millivolt so as @@ -328,8 +288,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, power_table[i].power = power; } - rcu_read_unlock(); - if (i != num_opps) { ret = PTR_ERR(opp); goto free_power_table; @@ -433,18 +391,22 @@ static int get_static_power(struct cpufreq_cooling_device *cpufreq_device, return 0; } - rcu_read_lock(); - opp = dev_pm_opp_find_freq_exact(cpufreq_device->cpu_dev, freq_hz, true); - voltage = dev_pm_opp_get_voltage(opp); + if (IS_ERR(opp)) { + dev_warn_ratelimited(cpufreq_device->cpu_dev, + "Failed to find OPP for frequency %lu: %ld\n", + freq_hz, PTR_ERR(opp)); + return -EINVAL; + } - rcu_read_unlock(); + voltage = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); if (voltage == 0) { - dev_warn_ratelimited(cpufreq_device->cpu_dev, - "Failed to get voltage for frequency %lu: %ld\n", - freq_hz, IS_ERR(opp) ? PTR_ERR(opp) : 0); + dev_err_ratelimited(cpufreq_device->cpu_dev, + "Failed to get voltage for frequency %lu\n", + freq_hz); return -EINVAL; } @@ -652,31 +614,39 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, unsigned long state, u32 *power) { unsigned int freq, num_cpus; - cpumask_t cpumask; + cpumask_var_t cpumask; u32 static_power, dynamic_power; int ret; struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; - cpumask_and(&cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask); - num_cpus = cpumask_weight(&cpumask); + if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) + return -ENOMEM; + + cpumask_and(cpumask, &cpufreq_device->allowed_cpus, cpu_online_mask); + num_cpus = cpumask_weight(cpumask); /* None of our cpus are online, so no power */ if (num_cpus == 0) { *power = 0; - return 0; + ret = 0; + goto out; } freq = cpufreq_device->freq_table[state]; - if (!freq) - return -EINVAL; + if (!freq) { + ret = -EINVAL; + goto out; + } dynamic_power = cpu_freq_to_power(cpufreq_device, freq) * num_cpus; ret = get_static_power(cpufreq_device, tz, freq, &static_power); if (ret) - return ret; + goto out; *power = static_power + dynamic_power; - return 0; +out: + free_cpumask_var(cpumask); + return ret; } /** @@ -728,9 +698,9 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, *state = cpufreq_cooling_get_level(cpu, target_freq); if (*state == THERMAL_CSTATE_INVALID) { - dev_warn_ratelimited(&cdev->device, - "Failed to convert %dKHz for cpu %d into a cdev state\n", - target_freq, cpu); + dev_err_ratelimited(&cdev->device, + "Failed to convert %dKHz for cpu %d into a cdev state\n", + target_freq, cpu); return -EINVAL; } @@ -802,16 +772,21 @@ __cpufreq_cooling_register(struct device_node *np, struct cpufreq_cooling_device *cpufreq_dev; char dev_name[THERMAL_NAME_LENGTH]; struct cpufreq_frequency_table *pos, *table; - struct cpumask temp_mask; + cpumask_var_t temp_mask; unsigned int freq, i, num_cpus; int ret; struct thermal_cooling_device_ops *cooling_ops; + bool first; + + if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); - cpumask_and(&temp_mask, clip_cpus, cpu_online_mask); - policy = cpufreq_cpu_get(cpumask_first(&temp_mask)); + cpumask_and(temp_mask, clip_cpus, cpu_online_mask); + policy = cpufreq_cpu_get(cpumask_first(temp_mask)); if (!policy) { pr_debug("%s: CPUFreq policy not found\n", __func__); - return ERR_PTR(-EPROBE_DEFER); + cool_dev = ERR_PTR(-EPROBE_DEFER); + goto free_cpumask; } table = policy->freq_table; @@ -874,11 +849,12 @@ __cpufreq_cooling_register(struct device_node *np, cooling_ops = &cpufreq_cooling_ops; } - ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); - if (ret) { + ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); + if (ret < 0) { cool_dev = ERR_PTR(ret); goto free_power_table; } + cpufreq_dev->id = ret; /* Fill freq-table in descending order of frequencies */ for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) { @@ -898,27 +874,25 @@ __cpufreq_cooling_register(struct device_node *np, cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, cooling_ops); if (IS_ERR(cool_dev)) - goto remove_idr; + goto remove_ida; cpufreq_dev->clipped_freq = cpufreq_dev->freq_table[0]; cpufreq_dev->cool_dev = cool_dev; - mutex_lock(&cooling_cpufreq_lock); - mutex_lock(&cooling_list_lock); + /* Register the notifier for first cpufreq cooling device */ + first = list_empty(&cpufreq_dev_list); list_add(&cpufreq_dev->node, &cpufreq_dev_list); mutex_unlock(&cooling_list_lock); - /* Register the notifier for first cpufreq cooling device */ - if (!cpufreq_dev_count++) + if (first) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - mutex_unlock(&cooling_cpufreq_lock); goto put_policy; -remove_idr: - release_idr(&cpufreq_idr, cpufreq_dev->id); +remove_ida: + ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); free_power_table: kfree(cpufreq_dev->dyn_power_table); free_table: @@ -931,7 +905,8 @@ free_cdev: kfree(cpufreq_dev); put_policy: cpufreq_cpu_put(policy); - +free_cpumask: + free_cpumask_var(temp_mask); return cool_dev; } @@ -1053,26 +1028,25 @@ EXPORT_SYMBOL(of_cpufreq_power_cooling_register); void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) { struct cpufreq_cooling_device *cpufreq_dev; + bool last; if (!cdev) return; cpufreq_dev = cdev->devdata; - /* Unregister the notifier for the last cpufreq cooling device */ - mutex_lock(&cooling_cpufreq_lock); - if (!--cpufreq_dev_count) - cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, - CPUFREQ_POLICY_NOTIFIER); - mutex_lock(&cooling_list_lock); list_del(&cpufreq_dev->node); + /* Unregister the notifier for the last cpufreq cooling device */ + last = list_empty(&cpufreq_dev_list); mutex_unlock(&cooling_list_lock); - mutex_unlock(&cooling_cpufreq_lock); + if (last) + cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); thermal_cooling_device_unregister(cpufreq_dev->cool_dev); - release_idr(&cpufreq_idr, cpufreq_dev->id); + ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); kfree(cpufreq_dev->dyn_power_table); kfree(cpufreq_dev->time_in_idle_timestamp); kfree(cpufreq_dev->time_in_idle); |