aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/opp/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/opp/core.c')
-rw-r--r--drivers/base/power/opp/core.c44
1 files changed, 30 insertions, 14 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index d5c1149ff123..5e4b1d82b2f8 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -11,6 +11,8 @@
11 * published by the Free Software Foundation. 11 * published by the Free Software Foundation.
12 */ 12 */
13 13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
14#include <linux/errno.h> 16#include <linux/errno.h>
15#include <linux/err.h> 17#include <linux/err.h>
16#include <linux/slab.h> 18#include <linux/slab.h>
@@ -27,7 +29,7 @@
27 */ 29 */
28static LIST_HEAD(dev_opp_list); 30static LIST_HEAD(dev_opp_list);
29/* Lock to allow exclusive modification to the device and opp lists */ 31/* Lock to allow exclusive modification to the device and opp lists */
30static DEFINE_MUTEX(dev_opp_list_lock); 32DEFINE_MUTEX(dev_opp_list_lock);
31 33
32#define opp_rcu_lockdep_assert() \ 34#define opp_rcu_lockdep_assert() \
33do { \ 35do { \
@@ -79,14 +81,18 @@ static struct device_opp *_managed_opp(const struct device_node *np)
79 * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or 81 * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or
80 * -EINVAL based on type of error. 82 * -EINVAL based on type of error.
81 * 83 *
82 * Locking: This function must be called under rcu_read_lock(). device_opp 84 * Locking: For readers, this function must be called under rcu_read_lock().
83 * is a RCU protected pointer. This means that device_opp is valid as long 85 * device_opp is a RCU protected pointer, which means that device_opp is valid
84 * as we are under RCU lock. 86 * as long as we are under RCU lock.
87 *
88 * For Writers, this function must be called with dev_opp_list_lock held.
85 */ 89 */
86struct device_opp *_find_device_opp(struct device *dev) 90struct device_opp *_find_device_opp(struct device *dev)
87{ 91{
88 struct device_opp *dev_opp; 92 struct device_opp *dev_opp;
89 93
94 opp_rcu_lockdep_assert();
95
90 if (IS_ERR_OR_NULL(dev)) { 96 if (IS_ERR_OR_NULL(dev)) {
91 pr_err("%s: Invalid parameters\n", __func__); 97 pr_err("%s: Invalid parameters\n", __func__);
92 return ERR_PTR(-EINVAL); 98 return ERR_PTR(-EINVAL);
@@ -701,7 +707,7 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
701} 707}
702 708
703/** 709/**
704 * _opp_add_dynamic() - Allocate a dynamic OPP. 710 * _opp_add_v1() - Allocate a OPP based on v1 bindings.
705 * @dev: device for which we do this operation 711 * @dev: device for which we do this operation
706 * @freq: Frequency in Hz for this OPP 712 * @freq: Frequency in Hz for this OPP
707 * @u_volt: Voltage in uVolts for this OPP 713 * @u_volt: Voltage in uVolts for this OPP
@@ -727,8 +733,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
727 * Duplicate OPPs (both freq and volt are same) and !opp->available 733 * Duplicate OPPs (both freq and volt are same) and !opp->available
728 * -ENOMEM Memory allocation failure 734 * -ENOMEM Memory allocation failure
729 */ 735 */
730static int _opp_add_dynamic(struct device *dev, unsigned long freq, 736static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
731 long u_volt, bool dynamic) 737 bool dynamic)
732{ 738{
733 struct device_opp *dev_opp; 739 struct device_opp *dev_opp;
734 struct dev_pm_opp *new_opp; 740 struct dev_pm_opp *new_opp;
@@ -770,9 +776,10 @@ unlock:
770} 776}
771 777
772/* TODO: Support multiple regulators */ 778/* TODO: Support multiple regulators */
773static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) 779static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
774{ 780{
775 u32 microvolt[3] = {0}; 781 u32 microvolt[3] = {0};
782 u32 val;
776 int count, ret; 783 int count, ret;
777 784
778 /* Missing property isn't a problem, but an invalid entry is */ 785 /* Missing property isn't a problem, but an invalid entry is */
@@ -805,6 +812,9 @@ static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev)
805 opp->u_volt_min = microvolt[1]; 812 opp->u_volt_min = microvolt[1];
806 opp->u_volt_max = microvolt[2]; 813 opp->u_volt_max = microvolt[2];
807 814
815 if (!of_property_read_u32(opp->np, "opp-microamp", &val))
816 opp->u_amp = val;
817
808 return 0; 818 return 0;
809} 819}
810 820
@@ -869,13 +879,10 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
869 if (!of_property_read_u32(np, "clock-latency-ns", &val)) 879 if (!of_property_read_u32(np, "clock-latency-ns", &val))
870 new_opp->clock_latency_ns = val; 880 new_opp->clock_latency_ns = val;
871 881
872 ret = opp_get_microvolt(new_opp, dev); 882 ret = opp_parse_supplies(new_opp, dev);
873 if (ret) 883 if (ret)
874 goto free_opp; 884 goto free_opp;
875 885
876 if (!of_property_read_u32(new_opp->np, "opp-microamp", &val))
877 new_opp->u_amp = val;
878
879 ret = _opp_add(dev, new_opp, dev_opp); 886 ret = _opp_add(dev, new_opp, dev_opp);
880 if (ret) 887 if (ret)
881 goto free_opp; 888 goto free_opp;
@@ -939,7 +946,7 @@ unlock:
939 */ 946 */
940int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) 947int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
941{ 948{
942 return _opp_add_dynamic(dev, freq, u_volt, true); 949 return _opp_add_v1(dev, freq, u_volt, true);
943} 950}
944EXPORT_SYMBOL_GPL(dev_pm_opp_add); 951EXPORT_SYMBOL_GPL(dev_pm_opp_add);
945 952
@@ -1172,13 +1179,17 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
1172 struct device_opp *dev_opp; 1179 struct device_opp *dev_opp;
1173 int ret = 0, count = 0; 1180 int ret = 0, count = 0;
1174 1181
1182 mutex_lock(&dev_opp_list_lock);
1183
1175 dev_opp = _managed_opp(opp_np); 1184 dev_opp = _managed_opp(opp_np);
1176 if (dev_opp) { 1185 if (dev_opp) {
1177 /* OPPs are already managed */ 1186 /* OPPs are already managed */
1178 if (!_add_list_dev(dev, dev_opp)) 1187 if (!_add_list_dev(dev, dev_opp))
1179 ret = -ENOMEM; 1188 ret = -ENOMEM;
1189 mutex_unlock(&dev_opp_list_lock);
1180 return ret; 1190 return ret;
1181 } 1191 }
1192 mutex_unlock(&dev_opp_list_lock);
1182 1193
1183 /* We have opp-list node now, iterate over it and add OPPs */ 1194 /* We have opp-list node now, iterate over it and add OPPs */
1184 for_each_available_child_of_node(opp_np, np) { 1195 for_each_available_child_of_node(opp_np, np) {
@@ -1196,15 +1207,20 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
1196 if (WARN_ON(!count)) 1207 if (WARN_ON(!count))
1197 return -ENOENT; 1208 return -ENOENT;
1198 1209
1210 mutex_lock(&dev_opp_list_lock);
1211
1199 dev_opp = _find_device_opp(dev); 1212 dev_opp = _find_device_opp(dev);
1200 if (WARN_ON(IS_ERR(dev_opp))) { 1213 if (WARN_ON(IS_ERR(dev_opp))) {
1201 ret = PTR_ERR(dev_opp); 1214 ret = PTR_ERR(dev_opp);
1215 mutex_unlock(&dev_opp_list_lock);
1202 goto free_table; 1216 goto free_table;
1203 } 1217 }
1204 1218
1205 dev_opp->np = opp_np; 1219 dev_opp->np = opp_np;
1206 dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); 1220 dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared");
1207 1221
1222 mutex_unlock(&dev_opp_list_lock);
1223
1208 return 0; 1224 return 0;
1209 1225
1210free_table: 1226free_table:
@@ -1241,7 +1257,7 @@ static int _of_add_opp_table_v1(struct device *dev)
1241 unsigned long freq = be32_to_cpup(val++) * 1000; 1257 unsigned long freq = be32_to_cpup(val++) * 1000;
1242 unsigned long volt = be32_to_cpup(val++); 1258 unsigned long volt = be32_to_cpup(val++);
1243 1259
1244 if (_opp_add_dynamic(dev, freq, volt, false)) 1260 if (_opp_add_v1(dev, freq, volt, false))
1245 dev_warn(dev, "%s: Failed to add OPP %ld\n", 1261 dev_warn(dev, "%s: Failed to add OPP %ld\n",
1246 __func__, freq); 1262 __func__, freq);
1247 nr -= 2; 1263 nr -= 2;