aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@codeaurora.org>2016-11-30 05:51:25 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-06 04:40:15 -0500
commitc7a8a0ac8fee26d3c20402da306a17bcbbbb367b (patch)
tree3bc42f9ceb956d26100eae2cc148c34eeee18fe6
parenteab1c4e2d0ad4509ccb8476a604074547dc202e0 (diff)
PM / OPP: Pass opp_table to dev_pm_opp_put_regulator()
commit 91291d9ad92faa65a56a9a19d658d8049b78d3d4 upstream. Joonyoung Shim reported an interesting problem on his ARM octa-core Odoroid-XU3 platform. During system suspend, dev_pm_opp_put_regulator() was failing for a struct device for which dev_pm_opp_set_regulator() is called earlier. This happened because an earlier call to dev_pm_opp_of_cpumask_remove_table() function (from cpufreq-dt.c file) removed all the entries from opp_table->dev_list apart from the last CPU device in the cpumask of CPUs sharing the OPP. But both dev_pm_opp_set_regulator() and dev_pm_opp_put_regulator() routines get CPU device for the first CPU in the cpumask. And so the OPP core failed to find the OPP table for the struct device. This patch attempts to fix this problem by returning a pointer to the opp_table from dev_pm_opp_set_regulator() and using that as the parameter to dev_pm_opp_put_regulator(). This ensures that the dev_pm_opp_put_regulator() doesn't fail to find the opp table. Note that similar design problem also exists with other dev_pm_opp_put_*() APIs, but those aren't used currently by anyone and so we don't need to update them for now. Reported-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> [ Viresh: Wrote commit log and tested on exynos 5250 ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/base/power/opp/core.c22
-rw-r--r--drivers/cpufreq/cpufreq-dt.c12
-rw-r--r--include/linux/pm_opp.h11
3 files changed, 20 insertions, 25 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 4c7c6da7a989..2824d3a5e9f0 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -1316,7 +1316,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
1316 * that this function is *NOT* called under RCU protection or in contexts where 1316 * that this function is *NOT* called under RCU protection or in contexts where
1317 * mutex cannot be locked. 1317 * mutex cannot be locked.
1318 */ 1318 */
1319int dev_pm_opp_set_regulator(struct device *dev, const char *name) 1319struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
1320{ 1320{
1321 struct opp_table *opp_table; 1321 struct opp_table *opp_table;
1322 struct regulator *reg; 1322 struct regulator *reg;
@@ -1354,20 +1354,20 @@ int dev_pm_opp_set_regulator(struct device *dev, const char *name)
1354 opp_table->regulator = reg; 1354 opp_table->regulator = reg;
1355 1355
1356 mutex_unlock(&opp_table_lock); 1356 mutex_unlock(&opp_table_lock);
1357 return 0; 1357 return opp_table;
1358 1358
1359err: 1359err:
1360 _remove_opp_table(opp_table); 1360 _remove_opp_table(opp_table);
1361unlock: 1361unlock:
1362 mutex_unlock(&opp_table_lock); 1362 mutex_unlock(&opp_table_lock);
1363 1363
1364 return ret; 1364 return ERR_PTR(ret);
1365} 1365}
1366EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator); 1366EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
1367 1367
1368/** 1368/**
1369 * dev_pm_opp_put_regulator() - Releases resources blocked for regulator 1369 * dev_pm_opp_put_regulator() - Releases resources blocked for regulator
1370 * @dev: Device for which regulator was set. 1370 * @opp_table: OPP table returned from dev_pm_opp_set_regulator().
1371 * 1371 *
1372 * Locking: The internal opp_table and opp structures are RCU protected. 1372 * Locking: The internal opp_table and opp structures are RCU protected.
1373 * Hence this function internally uses RCU updater strategy with mutex locks 1373 * Hence this function internally uses RCU updater strategy with mutex locks
@@ -1375,22 +1375,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
1375 * that this function is *NOT* called under RCU protection or in contexts where 1375 * that this function is *NOT* called under RCU protection or in contexts where
1376 * mutex cannot be locked. 1376 * mutex cannot be locked.
1377 */ 1377 */
1378void dev_pm_opp_put_regulator(struct device *dev) 1378void dev_pm_opp_put_regulator(struct opp_table *opp_table)
1379{ 1379{
1380 struct opp_table *opp_table;
1381
1382 mutex_lock(&opp_table_lock); 1380 mutex_lock(&opp_table_lock);
1383 1381
1384 /* Check for existing table for 'dev' first */
1385 opp_table = _find_opp_table(dev);
1386 if (IS_ERR(opp_table)) {
1387 dev_err(dev, "Failed to find opp_table: %ld\n",
1388 PTR_ERR(opp_table));
1389 goto unlock;
1390 }
1391
1392 if (IS_ERR(opp_table->regulator)) { 1382 if (IS_ERR(opp_table->regulator)) {
1393 dev_err(dev, "%s: Doesn't have regulator set\n", __func__); 1383 pr_err("%s: Doesn't have regulator set\n", __func__);
1394 goto unlock; 1384 goto unlock;
1395 } 1385 }
1396 1386
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 5c07ae05d69a..4d3ec92cbabf 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -28,6 +28,7 @@
28#include "cpufreq-dt.h" 28#include "cpufreq-dt.h"
29 29
30struct private_data { 30struct private_data {
31 struct opp_table *opp_table;
31 struct device *cpu_dev; 32 struct device *cpu_dev;
32 struct thermal_cooling_device *cdev; 33 struct thermal_cooling_device *cdev;
33 const char *reg_name; 34 const char *reg_name;
@@ -143,6 +144,7 @@ static int resources_available(void)
143static int cpufreq_init(struct cpufreq_policy *policy) 144static int cpufreq_init(struct cpufreq_policy *policy)
144{ 145{
145 struct cpufreq_frequency_table *freq_table; 146 struct cpufreq_frequency_table *freq_table;
147 struct opp_table *opp_table = NULL;
146 struct private_data *priv; 148 struct private_data *priv;
147 struct device *cpu_dev; 149 struct device *cpu_dev;
148 struct clk *cpu_clk; 150 struct clk *cpu_clk;
@@ -186,8 +188,9 @@ static int cpufreq_init(struct cpufreq_policy *policy)
186 */ 188 */
187 name = find_supply_name(cpu_dev); 189 name = find_supply_name(cpu_dev);
188 if (name) { 190 if (name) {
189 ret = dev_pm_opp_set_regulator(cpu_dev, name); 191 opp_table = dev_pm_opp_set_regulator(cpu_dev, name);
190 if (ret) { 192 if (IS_ERR(opp_table)) {
193 ret = PTR_ERR(opp_table);
191 dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n", 194 dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
192 policy->cpu, ret); 195 policy->cpu, ret);
193 goto out_put_clk; 196 goto out_put_clk;
@@ -237,6 +240,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
237 } 240 }
238 241
239 priv->reg_name = name; 242 priv->reg_name = name;
243 priv->opp_table = opp_table;
240 244
241 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); 245 ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
242 if (ret) { 246 if (ret) {
@@ -285,7 +289,7 @@ out_free_priv:
285out_free_opp: 289out_free_opp:
286 dev_pm_opp_of_cpumask_remove_table(policy->cpus); 290 dev_pm_opp_of_cpumask_remove_table(policy->cpus);
287 if (name) 291 if (name)
288 dev_pm_opp_put_regulator(cpu_dev); 292 dev_pm_opp_put_regulator(opp_table);
289out_put_clk: 293out_put_clk:
290 clk_put(cpu_clk); 294 clk_put(cpu_clk);
291 295
@@ -300,7 +304,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
300 dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); 304 dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
301 dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); 305 dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
302 if (priv->reg_name) 306 if (priv->reg_name)
303 dev_pm_opp_put_regulator(priv->cpu_dev); 307 dev_pm_opp_put_regulator(priv->opp_table);
304 308
305 clk_put(policy->clk); 309 clk_put(policy->clk);
306 kfree(priv); 310 kfree(priv);
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index bca26157f5b6..f6bc76501912 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -19,6 +19,7 @@
19 19
20struct dev_pm_opp; 20struct dev_pm_opp;
21struct device; 21struct device;
22struct opp_table;
22 23
23enum dev_pm_opp_event { 24enum dev_pm_opp_event {
24 OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE, 25 OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
@@ -62,8 +63,8 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
62void dev_pm_opp_put_supported_hw(struct device *dev); 63void dev_pm_opp_put_supported_hw(struct device *dev);
63int dev_pm_opp_set_prop_name(struct device *dev, const char *name); 64int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
64void dev_pm_opp_put_prop_name(struct device *dev); 65void dev_pm_opp_put_prop_name(struct device *dev);
65int dev_pm_opp_set_regulator(struct device *dev, const char *name); 66struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name);
66void dev_pm_opp_put_regulator(struct device *dev); 67void dev_pm_opp_put_regulator(struct opp_table *opp_table);
67int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); 68int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
68int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); 69int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
69int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); 70int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -170,12 +171,12 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
170 171
171static inline void dev_pm_opp_put_prop_name(struct device *dev) {} 172static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
172 173
173static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name) 174static inline struct opp_table *dev_pm_opp_set_regulator(struct device *dev, const char *name)
174{ 175{
175 return -ENOTSUPP; 176 return ERR_PTR(-ENOTSUPP);
176} 177}
177 178
178static inline void dev_pm_opp_put_regulator(struct device *dev) {} 179static inline void dev_pm_opp_put_regulator(struct opp_table *opp_table) {}
179 180
180static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) 181static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
181{ 182{