diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-11-06 19:30:40 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-11-06 19:30:40 -0500 |
commit | 1ede53f73171722fcf1b5be63bcd64e42d14affc (patch) | |
tree | ae2391cccb97dcb4b09f573572457613d367f1ba | |
parent | 3930f660b4eea1de077969174e6e055d51413fab (diff) | |
parent | 0597e818501f595090a49a1779ab6ec377051b11 (diff) |
Merge branch 'pm-opp'
* pm-opp:
PM / OPP: Add opp_rcu_lockdep_assert() to _find_device_opp()
PM / OPP: Hold dev_opp_list_lock for writers
PM / OPP: Protect updates to list_dev with mutex
PM / OPP: Propagate error properly from dev_pm_opp_set_sharing_cpus()
PM / OPP: Parse all power-supply related bindings together
PM / OPP: Rename routines specific to old bindings with _v1
PM / OPP: Improve print messages with pr_fmt
-rw-r--r-- | drivers/base/power/opp/core.c | 44 | ||||
-rw-r--r-- | drivers/base/power/opp/cpu.c | 13 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 3 |
3 files changed, 41 insertions, 19 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 | */ |
28 | static LIST_HEAD(dev_opp_list); | 30 | static 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 */ |
30 | static DEFINE_MUTEX(dev_opp_list_lock); | 32 | DEFINE_MUTEX(dev_opp_list_lock); |
31 | 33 | ||
32 | #define opp_rcu_lockdep_assert() \ | 34 | #define opp_rcu_lockdep_assert() \ |
33 | do { \ | 35 | do { \ |
@@ -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 | */ |
86 | struct device_opp *_find_device_opp(struct device *dev) | 90 | struct 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 | */ |
730 | static int _opp_add_dynamic(struct device *dev, unsigned long freq, | 736 | static 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 */ |
773 | static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) | 779 | static 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 | */ |
940 | int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) | 947 | int 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 | } |
944 | EXPORT_SYMBOL_GPL(dev_pm_opp_add); | 951 | EXPORT_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 | ||
1210 | free_table: | 1226 | free_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; |
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 7654c5606307..7b445e88a0d5 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c | |||
@@ -10,6 +10,9 @@ | |||
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | |||
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
15 | |||
13 | #include <linux/cpu.h> | 16 | #include <linux/cpu.h> |
14 | #include <linux/cpufreq.h> | 17 | #include <linux/cpufreq.h> |
15 | #include <linux/err.h> | 18 | #include <linux/err.h> |
@@ -124,12 +127,12 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | |||
124 | struct device *dev; | 127 | struct device *dev; |
125 | int cpu, ret = 0; | 128 | int cpu, ret = 0; |
126 | 129 | ||
127 | rcu_read_lock(); | 130 | mutex_lock(&dev_opp_list_lock); |
128 | 131 | ||
129 | dev_opp = _find_device_opp(cpu_dev); | 132 | dev_opp = _find_device_opp(cpu_dev); |
130 | if (IS_ERR(dev_opp)) { | 133 | if (IS_ERR(dev_opp)) { |
131 | ret = -EINVAL; | 134 | ret = -EINVAL; |
132 | goto out_rcu_read_unlock; | 135 | goto unlock; |
133 | } | 136 | } |
134 | 137 | ||
135 | for_each_cpu(cpu, cpumask) { | 138 | for_each_cpu(cpu, cpumask) { |
@@ -150,10 +153,10 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | |||
150 | continue; | 153 | continue; |
151 | } | 154 | } |
152 | } | 155 | } |
153 | out_rcu_read_unlock: | 156 | unlock: |
154 | rcu_read_unlock(); | 157 | mutex_unlock(&dev_opp_list_lock); |
155 | 158 | ||
156 | return 0; | 159 | return ret; |
157 | } | 160 | } |
158 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); | 161 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); |
159 | 162 | ||
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index dcb38f78dae4..7366b2aa8997 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h | |||
@@ -21,6 +21,9 @@ | |||
21 | #include <linux/rculist.h> | 21 | #include <linux/rculist.h> |
22 | #include <linux/rcupdate.h> | 22 | #include <linux/rcupdate.h> |
23 | 23 | ||
24 | /* Lock to allow exclusive modification to the device and opp lists */ | ||
25 | extern struct mutex dev_opp_list_lock; | ||
26 | |||
24 | /* | 27 | /* |
25 | * Internal data structure organization with the OPP layer library is as | 28 | * Internal data structure organization with the OPP layer library is as |
26 | * follows: | 29 | * follows: |