Blame view
drivers/opp/core.c
63.8 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
e1f60b292 PM: Introduce lib... |
2 3 4 5 6 7 8 |
/* * Generic OPP Interface * * Copyright (C) 2009-2010 Texas Instruments Incorporated. * Nishanth Menon * Romit Dasgupta * Kevin Hilman |
e1f60b292 PM: Introduce lib... |
9 |
*/ |
d6d2a5289 PM / OPP: Improve... |
10 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
d54974c25 PM / OPP: Manage ... |
11 |
#include <linux/clk.h> |
e1f60b292 PM: Introduce lib... |
12 13 |
#include <linux/errno.h> #include <linux/err.h> |
e1f60b292 PM: Introduce lib... |
14 |
#include <linux/slab.h> |
51990e825 device.h: cleanup... |
15 |
#include <linux/device.h> |
80126ce7a PM / OPP: Export ... |
16 |
#include <linux/export.h> |
009acd196 PM / OPP: Support... |
17 |
#include <linux/pm_domain.h> |
9f8ea969d PM / OPP: get/put... |
18 |
#include <linux/regulator/consumer.h> |
e1f60b292 PM: Introduce lib... |
19 |
|
f59d3ee84 PM / OPP: Move cp... |
20 |
#include "opp.h" |
e1f60b292 PM: Introduce lib... |
21 22 |
/* |
2c2709dc6 PM / OPP: Rename ... |
23 24 |
* The root of the list of all opp-tables. All opp_table structures branch off * from here, with each opp_table containing the list of opps it supports in |
e1f60b292 PM: Introduce lib... |
25 26 |
* various states of availability. */ |
f47b72a15 PM / OPP: Move CO... |
27 |
LIST_HEAD(opp_tables); |
e1f60b292 PM: Introduce lib... |
28 |
/* Lock to allow exclusive modification to the device and opp lists */ |
2c2709dc6 PM / OPP: Rename ... |
29 |
DEFINE_MUTEX(opp_table_lock); |
e1f60b292 PM: Introduce lib... |
30 |
|
2c2709dc6 PM / OPP: Rename ... |
31 32 |
static struct opp_device *_find_opp_dev(const struct device *dev, struct opp_table *opp_table) |
064416586 PM / OPP: Add OPP... |
33 |
{ |
2c2709dc6 PM / OPP: Rename ... |
34 |
struct opp_device *opp_dev; |
064416586 PM / OPP: Add OPP... |
35 |
|
2c2709dc6 PM / OPP: Rename ... |
36 37 38 |
list_for_each_entry(opp_dev, &opp_table->dev_list, node) if (opp_dev->dev == dev) return opp_dev; |
064416586 PM / OPP: Add OPP... |
39 40 41 |
return NULL; } |
6ac423973 PM / OPP: Make _f... |
42 |
static struct opp_table *_find_opp_table_unlocked(struct device *dev) |
5b650b388 PM / OPP: Take kr... |
43 44 |
{ struct opp_table *opp_table; |
3d2556992 OPP: Protect dev_... |
45 |
bool found; |
5b650b388 PM / OPP: Take kr... |
46 47 |
list_for_each_entry(opp_table, &opp_tables, node) { |
3d2556992 OPP: Protect dev_... |
48 49 50 51 52 |
mutex_lock(&opp_table->lock); found = !!_find_opp_dev(dev, opp_table); mutex_unlock(&opp_table->lock); if (found) { |
5b650b388 PM / OPP: Take kr... |
53 54 55 56 57 58 59 60 |
_get_opp_table_kref(opp_table); return opp_table; } } return ERR_PTR(-ENODEV); } |
e1f60b292 PM: Introduce lib... |
61 |
/** |
2c2709dc6 PM / OPP: Rename ... |
62 63 |
* _find_opp_table() - find opp_table struct using device pointer * @dev: device pointer used to lookup OPP table |
e1f60b292 PM: Introduce lib... |
64 |
* |
052c6f191 PM / OPP: Move aw... |
65 |
* Search OPP table for one containing matching device. |
e1f60b292 PM: Introduce lib... |
66 |
* |
2c2709dc6 PM / OPP: Rename ... |
67 |
* Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or |
e1f60b292 PM: Introduce lib... |
68 69 |
* -EINVAL based on type of error. * |
5b650b388 PM / OPP: Take kr... |
70 |
* The callers must call dev_pm_opp_put_opp_table() after the table is used. |
e1f60b292 PM: Introduce lib... |
71 |
*/ |
2c2709dc6 PM / OPP: Rename ... |
72 |
struct opp_table *_find_opp_table(struct device *dev) |
e1f60b292 PM: Introduce lib... |
73 |
{ |
2c2709dc6 PM / OPP: Rename ... |
74 |
struct opp_table *opp_table; |
e1f60b292 PM: Introduce lib... |
75 |
|
50a3cb04a PM / OPP: Drop un... |
76 |
if (IS_ERR_OR_NULL(dev)) { |
e1f60b292 PM: Introduce lib... |
77 78 79 80 |
pr_err("%s: Invalid parameters ", __func__); return ERR_PTR(-EINVAL); } |
5b650b388 PM / OPP: Take kr... |
81 82 83 |
mutex_lock(&opp_table_lock); opp_table = _find_opp_table_unlocked(dev); mutex_unlock(&opp_table_lock); |
e1f60b292 PM: Introduce lib... |
84 |
|
5b650b388 PM / OPP: Take kr... |
85 |
return opp_table; |
e1f60b292 PM: Introduce lib... |
86 87 88 |
} /** |
d6d007429 PM / OPP: get the... |
89 |
* dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp |
e1f60b292 PM: Introduce lib... |
90 91 |
* @opp: opp for which voltage has to be returned for * |
984f16c84 PM / OPP: Update ... |
92 |
* Return: voltage in micro volt corresponding to the opp, else |
e1f60b292 PM: Introduce lib... |
93 94 |
* return 0 * |
dfbe4678d PM / OPP: Add inf... |
95 |
* This is useful only for devices with single power supply. |
e1f60b292 PM: Introduce lib... |
96 |
*/ |
47d43ba73 PM / OPP: rename ... |
97 |
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) |
e1f60b292 PM: Introduce lib... |
98 |
{ |
052c6f191 PM / OPP: Move aw... |
99 |
if (IS_ERR_OR_NULL(opp)) { |
e1f60b292 PM: Introduce lib... |
100 101 |
pr_err("%s: Invalid parameters ", __func__); |
052c6f191 PM / OPP: Move aw... |
102 103 |
return 0; } |
e1f60b292 PM: Introduce lib... |
104 |
|
052c6f191 PM / OPP: Move aw... |
105 |
return opp->supplies[0].u_volt; |
e1f60b292 PM: Introduce lib... |
106 |
} |
5d4879cda PM / OPP: rename ... |
107 |
EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); |
e1f60b292 PM: Introduce lib... |
108 109 |
/** |
5d4879cda PM / OPP: rename ... |
110 |
* dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp |
e1f60b292 PM: Introduce lib... |
111 112 |
* @opp: opp for which frequency has to be returned for * |
984f16c84 PM / OPP: Update ... |
113 |
* Return: frequency in hertz corresponding to the opp, else |
e1f60b292 PM: Introduce lib... |
114 |
* return 0 |
e1f60b292 PM: Introduce lib... |
115 |
*/ |
47d43ba73 PM / OPP: rename ... |
116 |
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) |
e1f60b292 PM: Introduce lib... |
117 |
{ |
06a8a059e opp: Allow disabl... |
118 |
if (IS_ERR_OR_NULL(opp)) { |
e1f60b292 PM: Introduce lib... |
119 120 |
pr_err("%s: Invalid parameters ", __func__); |
052c6f191 PM / OPP: Move aw... |
121 122 |
return 0; } |
e1f60b292 PM: Introduce lib... |
123 |
|
052c6f191 PM / OPP: Move aw... |
124 |
return opp->rate; |
e1f60b292 PM: Introduce lib... |
125 |
} |
5d4879cda PM / OPP: rename ... |
126 |
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); |
e1f60b292 PM: Introduce lib... |
127 128 |
/** |
5b93ac542 OPP: Add support ... |
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
* dev_pm_opp_get_level() - Gets the level corresponding to an available opp * @opp: opp for which level value has to be returned for * * Return: level read from device tree corresponding to the opp, else * return 0. */ unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) { if (IS_ERR_OR_NULL(opp) || !opp->available) { pr_err("%s: Invalid parameters ", __func__); return 0; } return opp->level; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); /** |
19445b25e PM / OPP: add dev... |
148 149 150 151 152 153 154 155 |
* dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not * @opp: opp for which turbo mode is being verified * * Turbo OPPs are not for normal use, and can be enabled (under certain * conditions) for short duration of times to finish high throughput work * quickly. Running on them for longer times may overheat the chip. * * Return: true if opp is turbo opp, else false. |
19445b25e PM / OPP: add dev... |
156 157 158 |
*/ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) { |
052c6f191 PM / OPP: Move aw... |
159 |
if (IS_ERR_OR_NULL(opp) || !opp->available) { |
19445b25e PM / OPP: add dev... |
160 161 162 163 |
pr_err("%s: Invalid parameters ", __func__); return false; } |
052c6f191 PM / OPP: Move aw... |
164 |
return opp->turbo; |
19445b25e PM / OPP: add dev... |
165 166 167 168 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); /** |
3ca9bb33c PM / OPP: Add clo... |
169 170 171 172 |
* dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max clock latency in nanoseconds. |
3ca9bb33c PM / OPP: Add clo... |
173 174 175 |
*/ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) { |
2c2709dc6 PM / OPP: Rename ... |
176 |
struct opp_table *opp_table; |
3ca9bb33c PM / OPP: Add clo... |
177 |
unsigned long clock_latency_ns; |
2c2709dc6 PM / OPP: Rename ... |
178 179 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) |
5b650b388 PM / OPP: Take kr... |
180 181 182 183 184 |
return 0; clock_latency_ns = opp_table->clock_latency_ns_max; dev_pm_opp_put_opp_table(opp_table); |
3ca9bb33c PM / OPP: Add clo... |
185 |
|
3ca9bb33c PM / OPP: Add clo... |
186 187 188 189 190 |
return clock_latency_ns; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); /** |
655c9df96 PM / OPP: Introdu... |
191 192 193 194 |
* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max voltage latency in nanoseconds. |
655c9df96 PM / OPP: Introdu... |
195 196 197 |
*/ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev) { |
2c2709dc6 PM / OPP: Rename ... |
198 |
struct opp_table *opp_table; |
655c9df96 PM / OPP: Introdu... |
199 |
struct dev_pm_opp *opp; |
478256bdd PM / OPP: Don't c... |
200 |
struct regulator *reg; |
655c9df96 PM / OPP: Introdu... |
201 |
unsigned long latency_ns = 0; |
dfbe4678d PM / OPP: Add inf... |
202 203 204 205 206 |
int ret, i, count; struct { unsigned long min; unsigned long max; } *uV; |
cdd3e614c PM / OPP: Simplif... |
207 208 209 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return 0; |
dfbe4678d PM / OPP: Add inf... |
210 |
/* Regulator may not be required for the device */ |
90e3577b5 OPP: Use opp_tabl... |
211 |
if (!opp_table->regulators) |
cdd3e614c PM / OPP: Simplif... |
212 |
goto put_opp_table; |
dfbe4678d PM / OPP: Add inf... |
213 |
|
90e3577b5 OPP: Use opp_tabl... |
214 |
count = opp_table->regulator_count; |
dfbe4678d PM / OPP: Add inf... |
215 216 |
uV = kmalloc_array(count, sizeof(*uV), GFP_KERNEL); if (!uV) |
478256bdd PM / OPP: Don't c... |
217 |
goto put_opp_table; |
655c9df96 PM / OPP: Introdu... |
218 |
|
052c6f191 PM / OPP: Move aw... |
219 |
mutex_lock(&opp_table->lock); |
dfbe4678d PM / OPP: Add inf... |
220 221 222 |
for (i = 0; i < count; i++) { uV[i].min = ~0; uV[i].max = 0; |
655c9df96 PM / OPP: Introdu... |
223 |
|
052c6f191 PM / OPP: Move aw... |
224 |
list_for_each_entry(opp, &opp_table->opp_list, node) { |
dfbe4678d PM / OPP: Add inf... |
225 226 227 228 229 230 231 232 |
if (!opp->available) continue; if (opp->supplies[i].u_volt_min < uV[i].min) uV[i].min = opp->supplies[i].u_volt_min; if (opp->supplies[i].u_volt_max > uV[i].max) uV[i].max = opp->supplies[i].u_volt_max; } |
655c9df96 PM / OPP: Introdu... |
233 |
} |
052c6f191 PM / OPP: Move aw... |
234 |
mutex_unlock(&opp_table->lock); |
655c9df96 PM / OPP: Introdu... |
235 236 |
/* |
2c2709dc6 PM / OPP: Rename ... |
237 |
* The caller needs to ensure that opp_table (and hence the regulator) |
655c9df96 PM / OPP: Introdu... |
238 239 |
* isn't freed, while we are executing this routine. */ |
8cc311167 PM / OPP: fix off... |
240 |
for (i = 0; i < count; i++) { |
478256bdd PM / OPP: Don't c... |
241 |
reg = opp_table->regulators[i]; |
dfbe4678d PM / OPP: Add inf... |
242 243 244 245 |
ret = regulator_set_voltage_time(reg, uV[i].min, uV[i].max); if (ret > 0) latency_ns += ret * 1000; } |
dfbe4678d PM / OPP: Add inf... |
246 |
kfree(uV); |
cdd3e614c PM / OPP: Simplif... |
247 248 |
put_opp_table: dev_pm_opp_put_opp_table(opp_table); |
655c9df96 PM / OPP: Introdu... |
249 250 251 252 253 254 |
return latency_ns; } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency); /** |
217434476 PM / OPP: Introdu... |
255 256 257 258 259 260 |
* dev_pm_opp_get_max_transition_latency() - Get max transition latency in * nanoseconds * @dev: device for which we do this operation * * Return: This function returns the max transition latency, in nanoseconds, to * switch from one OPP to other. |
217434476 PM / OPP: Introdu... |
261 262 263 264 265 266 267 268 269 |
*/ unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev) { return dev_pm_opp_get_max_volt_latency(dev) + dev_pm_opp_get_max_clock_latency(dev); } EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency); /** |
3aa26a3b2 PM / OPP: Rename ... |
270 |
* dev_pm_opp_get_suspend_opp_freq() - Get frequency of suspend opp in Hz |
4eafbd15b PM / OPP: add dev... |
271 272 |
* @dev: device for which we do this operation * |
3aa26a3b2 PM / OPP: Rename ... |
273 274 |
* Return: This function returns the frequency of the OPP marked as suspend_opp * if one is available, else returns 0; |
4eafbd15b PM / OPP: add dev... |
275 |
*/ |
3aa26a3b2 PM / OPP: Rename ... |
276 |
unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) |
4eafbd15b PM / OPP: add dev... |
277 |
{ |
2c2709dc6 PM / OPP: Rename ... |
278 |
struct opp_table *opp_table; |
3aa26a3b2 PM / OPP: Rename ... |
279 |
unsigned long freq = 0; |
4eafbd15b PM / OPP: add dev... |
280 |
|
2c2709dc6 PM / OPP: Rename ... |
281 |
opp_table = _find_opp_table(dev); |
5b650b388 PM / OPP: Take kr... |
282 283 |
if (IS_ERR(opp_table)) return 0; |
3aa26a3b2 PM / OPP: Rename ... |
284 |
|
5b650b388 PM / OPP: Take kr... |
285 286 287 288 |
if (opp_table->suspend_opp && opp_table->suspend_opp->available) freq = dev_pm_opp_get_freq(opp_table->suspend_opp); dev_pm_opp_put_opp_table(opp_table); |
4eafbd15b PM / OPP: add dev... |
289 |
|
3aa26a3b2 PM / OPP: Rename ... |
290 |
return freq; |
4eafbd15b PM / OPP: add dev... |
291 |
} |
3aa26a3b2 PM / OPP: Rename ... |
292 |
EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); |
4eafbd15b PM / OPP: add dev... |
293 |
|
a1e8c1360 PM / OPP: "opp-hz... |
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
int _get_opp_count(struct opp_table *opp_table) { struct dev_pm_opp *opp; int count = 0; mutex_lock(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->available) count++; } mutex_unlock(&opp_table->lock); return count; } |
4eafbd15b PM / OPP: add dev... |
310 |
/** |
2c2709dc6 PM / OPP: Rename ... |
311 |
* dev_pm_opp_get_opp_count() - Get number of opps available in the opp table |
e1f60b292 PM: Introduce lib... |
312 313 |
* @dev: device for which we do this operation * |
984f16c84 PM / OPP: Update ... |
314 |
* Return: This function returns the number of available opps if there are any, |
e1f60b292 PM: Introduce lib... |
315 |
* else returns 0 if none or the corresponding error value. |
e1f60b292 PM: Introduce lib... |
316 |
*/ |
5d4879cda PM / OPP: rename ... |
317 |
int dev_pm_opp_get_opp_count(struct device *dev) |
e1f60b292 PM: Introduce lib... |
318 |
{ |
2c2709dc6 PM / OPP: Rename ... |
319 |
struct opp_table *opp_table; |
a1e8c1360 PM / OPP: "opp-hz... |
320 |
int count; |
e1f60b292 PM: Introduce lib... |
321 |
|
2c2709dc6 PM / OPP: Rename ... |
322 323 324 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { count = PTR_ERR(opp_table); |
035ed0720 PM / OPP: Move er... |
325 326 |
dev_dbg(dev, "%s: OPP table not found (%d) ", |
b4718c02f PM / OPP: take RC... |
327 |
__func__, count); |
09f662f95 OPP: Return error... |
328 |
return count; |
e1f60b292 PM: Introduce lib... |
329 |
} |
a1e8c1360 PM / OPP: "opp-hz... |
330 |
count = _get_opp_count(opp_table); |
5b650b388 PM / OPP: Take kr... |
331 |
dev_pm_opp_put_opp_table(opp_table); |
e1f60b292 PM: Introduce lib... |
332 333 |
return count; } |
5d4879cda PM / OPP: rename ... |
334 |
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); |
e1f60b292 PM: Introduce lib... |
335 336 |
/** |
5d4879cda PM / OPP: rename ... |
337 |
* dev_pm_opp_find_freq_exact() - search for an exact frequency |
e1f60b292 PM: Introduce lib... |
338 339 |
* @dev: device for which we do this operation * @freq: frequency to search for |
7ae496187 PM / OPP: opp_fin... |
340 |
* @available: true/false - match for available opp |
e1f60b292 PM: Introduce lib... |
341 |
* |
2c2709dc6 PM / OPP: Rename ... |
342 |
* Return: Searches for exact match in the opp table and returns pointer to the |
984f16c84 PM / OPP: Update ... |
343 344 |
* matching opp if found, else returns ERR_PTR in case of error and should * be handled using IS_ERR. Error return values can be: |
0779726cc PM / OPP: predict... |
345 346 347 |
* EINVAL: for bad pointer * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices |
e1f60b292 PM: Introduce lib... |
348 349 350 351 352 353 354 355 |
* * Note: available is a modifier for the search. if available=true, then the * match is for exact matching frequency and is available in the stored OPP * table. if false, the match is for exact frequency which is not available. * * This provides a mechanism to enable an opp which is not available currently * or the opposite as well. * |
8a31d9d94 PM / OPP: Update ... |
356 357 |
* The callers are required to call dev_pm_opp_put() for the returned OPP after * use. |
e1f60b292 PM: Introduce lib... |
358 |
*/ |
47d43ba73 PM / OPP: rename ... |
359 360 361 |
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) |
e1f60b292 PM: Introduce lib... |
362 |
{ |
2c2709dc6 PM / OPP: Rename ... |
363 |
struct opp_table *opp_table; |
47d43ba73 PM / OPP: rename ... |
364 |
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
e1f60b292 PM: Introduce lib... |
365 |
|
2c2709dc6 PM / OPP: Rename ... |
366 367 368 369 370 371 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { int r = PTR_ERR(opp_table); dev_err(dev, "%s: OPP table not found (%d) ", __func__, r); |
e1f60b292 PM: Introduce lib... |
372 373 |
return ERR_PTR(r); } |
052c6f191 PM / OPP: Move aw... |
374 |
mutex_lock(&opp_table->lock); |
5b650b388 PM / OPP: Take kr... |
375 |
|
052c6f191 PM / OPP: Move aw... |
376 |
list_for_each_entry(temp_opp, &opp_table->opp_list, node) { |
e1f60b292 PM: Introduce lib... |
377 378 379 |
if (temp_opp->available == available && temp_opp->rate == freq) { opp = temp_opp; |
8a31d9d94 PM / OPP: Update ... |
380 381 382 |
/* Increment the reference count of OPP */ dev_pm_opp_get(opp); |
e1f60b292 PM: Introduce lib... |
383 384 385 |
break; } } |
052c6f191 PM / OPP: Move aw... |
386 |
mutex_unlock(&opp_table->lock); |
5b650b388 PM / OPP: Take kr... |
387 |
dev_pm_opp_put_opp_table(opp_table); |
8a31d9d94 PM / OPP: Update ... |
388 |
|
e1f60b292 PM: Introduce lib... |
389 390 |
return opp; } |
5d4879cda PM / OPP: rename ... |
391 |
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); |
e1f60b292 PM: Introduce lib... |
392 |
|
71419d84c opp: Add dev_pm_o... |
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
/** * dev_pm_opp_find_level_exact() - search for an exact level * @dev: device for which we do this operation * @level: level to search for * * Return: Searches for exact match in the opp table and returns pointer to the * matching opp if found, else returns ERR_PTR in case of error and should * be handled using IS_ERR. Error return values can be: * EINVAL: for bad pointer * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices * * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level) { struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { int r = PTR_ERR(opp_table); dev_err(dev, "%s: OPP table not found (%d) ", __func__, r); return ERR_PTR(r); } mutex_lock(&opp_table->lock); list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->level == level) { opp = temp_opp; /* Increment the reference count of OPP */ dev_pm_opp_get(opp); break; } } mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); |
067b7ce08 PM / OPP: optimiz... |
441 442 443 444 |
static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
052c6f191 PM / OPP: Move aw... |
445 446 447 |
mutex_lock(&opp_table->lock); list_for_each_entry(temp_opp, &opp_table->opp_list, node) { |
067b7ce08 PM / OPP: optimiz... |
448 449 450 |
if (temp_opp->available && temp_opp->rate >= *freq) { opp = temp_opp; *freq = opp->rate; |
8a31d9d94 PM / OPP: Update ... |
451 452 453 |
/* Increment the reference count of OPP */ dev_pm_opp_get(opp); |
067b7ce08 PM / OPP: optimiz... |
454 455 456 |
break; } } |
052c6f191 PM / OPP: Move aw... |
457 |
mutex_unlock(&opp_table->lock); |
067b7ce08 PM / OPP: optimiz... |
458 459 |
return opp; } |
e1f60b292 PM: Introduce lib... |
460 |
/** |
5d4879cda PM / OPP: rename ... |
461 |
* dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq |
e1f60b292 PM: Introduce lib... |
462 463 464 465 466 467 |
* @dev: device for which we do this operation * @freq: Start frequency * * Search for the matching ceil *available* OPP from a starting freq * for a device. * |
984f16c84 PM / OPP: Update ... |
468 |
* Return: matching *opp and refreshes *freq accordingly, else returns |
0779726cc PM / OPP: predict... |
469 470 471 472 473 |
* ERR_PTR in case of error and should be handled using IS_ERR. Error return * values can be: * EINVAL: for bad pointer * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices |
e1f60b292 PM: Introduce lib... |
474 |
* |
8a31d9d94 PM / OPP: Update ... |
475 476 |
* The callers are required to call dev_pm_opp_put() for the returned OPP after * use. |
e1f60b292 PM: Introduce lib... |
477 |
*/ |
47d43ba73 PM / OPP: rename ... |
478 479 |
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) |
e1f60b292 PM: Introduce lib... |
480 |
{ |
2c2709dc6 PM / OPP: Rename ... |
481 |
struct opp_table *opp_table; |
8a31d9d94 PM / OPP: Update ... |
482 |
struct dev_pm_opp *opp; |
b02ded246 PM / OPP: add som... |
483 |
|
e1f60b292 PM: Introduce lib... |
484 485 486 487 488 |
if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p ", __func__, freq); return ERR_PTR(-EINVAL); } |
2c2709dc6 PM / OPP: Rename ... |
489 |
opp_table = _find_opp_table(dev); |
5b650b388 PM / OPP: Take kr... |
490 |
if (IS_ERR(opp_table)) |
2c2709dc6 PM / OPP: Rename ... |
491 |
return ERR_CAST(opp_table); |
5b650b388 PM / OPP: Take kr... |
492 |
|
8a31d9d94 PM / OPP: Update ... |
493 |
opp = _find_freq_ceil(opp_table, freq); |
e1f60b292 PM: Introduce lib... |
494 |
|
5b650b388 PM / OPP: Take kr... |
495 |
dev_pm_opp_put_opp_table(opp_table); |
8a31d9d94 PM / OPP: Update ... |
496 497 |
return opp; |
e1f60b292 PM: Introduce lib... |
498 |
} |
5d4879cda PM / OPP: rename ... |
499 |
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); |
e1f60b292 PM: Introduce lib... |
500 501 |
/** |
5d4879cda PM / OPP: rename ... |
502 |
* dev_pm_opp_find_freq_floor() - Search for a rounded floor freq |
e1f60b292 PM: Introduce lib... |
503 504 505 506 507 508 |
* @dev: device for which we do this operation * @freq: Start frequency * * Search for the matching floor *available* OPP from a starting freq * for a device. * |
984f16c84 PM / OPP: Update ... |
509 |
* Return: matching *opp and refreshes *freq accordingly, else returns |
0779726cc PM / OPP: predict... |
510 511 512 513 514 |
* ERR_PTR in case of error and should be handled using IS_ERR. Error return * values can be: * EINVAL: for bad pointer * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices |
e1f60b292 PM: Introduce lib... |
515 |
* |
8a31d9d94 PM / OPP: Update ... |
516 517 |
* The callers are required to call dev_pm_opp_put() for the returned OPP after * use. |
e1f60b292 PM: Introduce lib... |
518 |
*/ |
47d43ba73 PM / OPP: rename ... |
519 520 |
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) |
e1f60b292 PM: Introduce lib... |
521 |
{ |
2c2709dc6 PM / OPP: Rename ... |
522 |
struct opp_table *opp_table; |
47d43ba73 PM / OPP: rename ... |
523 |
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); |
e1f60b292 PM: Introduce lib... |
524 525 526 527 528 529 |
if (!dev || !freq) { dev_err(dev, "%s: Invalid argument freq=%p ", __func__, freq); return ERR_PTR(-EINVAL); } |
2c2709dc6 PM / OPP: Rename ... |
530 |
opp_table = _find_opp_table(dev); |
5b650b388 PM / OPP: Take kr... |
531 |
if (IS_ERR(opp_table)) |
2c2709dc6 PM / OPP: Rename ... |
532 |
return ERR_CAST(opp_table); |
5b650b388 PM / OPP: Take kr... |
533 |
|
052c6f191 PM / OPP: Move aw... |
534 |
mutex_lock(&opp_table->lock); |
e1f60b292 PM: Introduce lib... |
535 |
|
052c6f191 PM / OPP: Move aw... |
536 |
list_for_each_entry(temp_opp, &opp_table->opp_list, node) { |
e1f60b292 PM: Introduce lib... |
537 538 539 540 541 542 543 544 |
if (temp_opp->available) { /* go to the next node, before choosing prev */ if (temp_opp->rate > *freq) break; else opp = temp_opp; } } |
8a31d9d94 PM / OPP: Update ... |
545 546 547 548 |
/* Increment the reference count of OPP */ if (!IS_ERR(opp)) dev_pm_opp_get(opp); |
052c6f191 PM / OPP: Move aw... |
549 |
mutex_unlock(&opp_table->lock); |
5b650b388 PM / OPP: Take kr... |
550 |
dev_pm_opp_put_opp_table(opp_table); |
8a31d9d94 PM / OPP: Update ... |
551 |
|
e1f60b292 PM: Introduce lib... |
552 553 554 555 556 |
if (!IS_ERR(opp)) *freq = opp->rate; return opp; } |
5d4879cda PM / OPP: rename ... |
557 |
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); |
e1f60b292 PM: Introduce lib... |
558 |
|
2f36bde0f OPP: Introduce de... |
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
/** * dev_pm_opp_find_freq_ceil_by_volt() - Find OPP with highest frequency for * target voltage. * @dev: Device for which we do this operation. * @u_volt: Target voltage. * * Search for OPP with highest (ceil) frequency and has voltage <= u_volt. * * Return: matching *opp, else returns ERR_PTR in case of error which should be * handled using IS_ERR. * * Error return values can be: * EINVAL: bad parameters * * The callers are required to call dev_pm_opp_put() for the returned OPP after * use. */ struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev, unsigned long u_volt) { struct opp_table *opp_table; struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); if (!dev || !u_volt) { dev_err(dev, "%s: Invalid argument volt=%lu ", __func__, u_volt); return ERR_PTR(-EINVAL); } opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return ERR_CAST(opp_table); mutex_lock(&opp_table->lock); list_for_each_entry(temp_opp, &opp_table->opp_list, node) { if (temp_opp->available) { if (temp_opp->supplies[0].u_volt > u_volt) break; opp = temp_opp; } } /* Increment the reference count of OPP */ if (!IS_ERR(opp)) dev_pm_opp_get(opp); mutex_unlock(&opp_table->lock); dev_pm_opp_put_opp_table(opp_table); return opp; } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_by_volt); |
6a0712f6f PM / OPP: Add dev... |
613 |
static int _set_opp_voltage(struct device *dev, struct regulator *reg, |
ce31781a7 PM / OPP: Pass st... |
614 |
struct dev_pm_opp_supply *supply) |
6a0712f6f PM / OPP: Add dev... |
615 616 617 618 619 620 621 622 623 624 |
{ int ret; /* Regulator not available for device */ if (IS_ERR(reg)) { dev_dbg(dev, "%s: regulator not available: %ld ", __func__, PTR_ERR(reg)); return 0; } |
ce31781a7 PM / OPP: Pass st... |
625 626 627 |
dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu ", __func__, supply->u_volt_min, supply->u_volt, supply->u_volt_max); |
6a0712f6f PM / OPP: Add dev... |
628 |
|
ce31781a7 PM / OPP: Pass st... |
629 630 |
ret = regulator_set_voltage_triplet(reg, supply->u_volt_min, supply->u_volt, supply->u_volt_max); |
6a0712f6f PM / OPP: Add dev... |
631 632 633 |
if (ret) dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d ", |
ce31781a7 PM / OPP: Pass st... |
634 635 |
__func__, supply->u_volt_min, supply->u_volt, supply->u_volt_max, ret); |
6a0712f6f PM / OPP: Add dev... |
636 637 638 |
return ret; } |
285881b51 PM / OPP: Remove ... |
639 640 |
static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk, unsigned long freq) |
947355850 PM / OPP: Separat... |
641 642 643 644 645 646 647 648 649 650 651 652 |
{ int ret; ret = clk_set_rate(clk, freq); if (ret) { dev_err(dev, "%s: failed to set clock rate: %d ", __func__, ret); } return ret; } |
8d45719ca opp: core: add re... |
653 |
static int _generic_set_opp_regulator(struct opp_table *opp_table, |
c74b32fad PM / OPP: Reorgan... |
654 655 656 657 658 |
struct device *dev, unsigned long old_freq, unsigned long freq, struct dev_pm_opp_supply *old_supply, struct dev_pm_opp_supply *new_supply) |
947355850 PM / OPP: Separat... |
659 |
{ |
c74b32fad PM / OPP: Reorgan... |
660 |
struct regulator *reg = opp_table->regulators[0]; |
947355850 PM / OPP: Separat... |
661 662 663 |
int ret; /* This function only supports single regulator per device */ |
c74b32fad PM / OPP: Reorgan... |
664 |
if (WARN_ON(opp_table->regulator_count > 1)) { |
947355850 PM / OPP: Separat... |
665 666 667 668 669 670 |
dev_err(dev, "multiple regulators are not supported "); return -EINVAL; } /* Scaling up? Scale voltage before frequency */ |
c5c2a97b3 PM / OPP: Update ... |
671 |
if (freq >= old_freq) { |
947355850 PM / OPP: Separat... |
672 673 674 675 676 677 |
ret = _set_opp_voltage(dev, reg, new_supply); if (ret) goto restore_voltage; } /* Change frequency */ |
285881b51 PM / OPP: Remove ... |
678 |
ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq); |
947355850 PM / OPP: Separat... |
679 680 681 682 683 684 685 686 687 |
if (ret) goto restore_voltage; /* Scaling down? Scale voltage after frequency */ if (freq < old_freq) { ret = _set_opp_voltage(dev, reg, new_supply); if (ret) goto restore_freq; } |
8d45719ca opp: core: add re... |
688 689 690 691 |
/* * Enable the regulator after setting its voltages, otherwise it breaks * some boot-enabled regulators. */ |
72f80ce4e opp: Rename regul... |
692 |
if (unlikely(!opp_table->enabled)) { |
8d45719ca opp: core: add re... |
693 694 695 |
ret = regulator_enable(reg); if (ret < 0) dev_warn(dev, "Failed to enable regulator: %d", ret); |
8d45719ca opp: core: add re... |
696 |
} |
947355850 PM / OPP: Separat... |
697 698 699 |
return 0; restore_freq: |
285881b51 PM / OPP: Remove ... |
700 |
if (_generic_set_opp_clk_only(dev, opp_table->clk, old_freq)) |
947355850 PM / OPP: Separat... |
701 702 703 704 705 |
dev_err(dev, "%s: failed to restore old-freq (%lu Hz) ", __func__, old_freq); restore_voltage: /* This shouldn't harm even if the voltages weren't updated earlier */ |
c74b32fad PM / OPP: Reorgan... |
706 |
if (old_supply) |
947355850 PM / OPP: Separat... |
707 708 709 710 |
_set_opp_voltage(dev, reg, old_supply); return ret; } |
b00e667a6 opp: Remove bandw... |
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
static int _set_opp_bw(const struct opp_table *opp_table, struct dev_pm_opp *opp, struct device *dev, bool remove) { u32 avg, peak; int i, ret; if (!opp_table->paths) return 0; for (i = 0; i < opp_table->path_count; i++) { if (remove) { avg = 0; peak = 0; } else { avg = opp->bandwidth[i].avg; peak = opp->bandwidth[i].peak; } ret = icc_set_bw(opp_table->paths[i], avg, peak); if (ret) { dev_err(dev, "Failed to %s bandwidth[%d]: %d ", remove ? "remove" : "set", i, ret); return ret; } } return 0; } |
7e535993f OPP: Separate out... |
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 |
static int _set_opp_custom(const struct opp_table *opp_table, struct device *dev, unsigned long old_freq, unsigned long freq, struct dev_pm_opp_supply *old_supply, struct dev_pm_opp_supply *new_supply) { struct dev_pm_set_opp_data *data; int size; data = opp_table->set_opp_data; data->regulators = opp_table->regulators; data->regulator_count = opp_table->regulator_count; data->clk = opp_table->clk; data->dev = dev; data->old_opp.rate = old_freq; size = sizeof(*old_supply) * opp_table->regulator_count; |
560d1bcad opp: Don't use IS... |
756 |
if (!old_supply) |
7e535993f OPP: Separate out... |
757 758 759 760 761 762 763 764 765 |
memset(data->old_opp.supplies, 0, size); else memcpy(data->old_opp.supplies, old_supply, size); data->new_opp.rate = freq; memcpy(data->new_opp.supplies, new_supply, size); return opp_table->set_opp(data); } |
60cdeae0d opp: Reduce code ... |
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 |
static int _set_required_opp(struct device *dev, struct device *pd_dev, struct dev_pm_opp *opp, int i) { unsigned int pstate = likely(opp) ? opp->required_opps[i]->pstate : 0; int ret; if (!pd_dev) return 0; ret = dev_pm_genpd_set_performance_state(pd_dev, pstate); if (ret) { dev_err(dev, "Failed to set performance rate of %s: %d (%d) ", dev_name(pd_dev), pstate, ret); } return ret; } |
ca1b5d77b OPP: Configure al... |
784 785 786 |
/* This is only called for PM domain for now */ static int _set_required_opps(struct device *dev, struct opp_table *opp_table, |
2c59138c2 opp: Set required... |
787 |
struct dev_pm_opp *opp, bool up) |
ca1b5d77b OPP: Configure al... |
788 789 790 |
{ struct opp_table **required_opp_tables = opp_table->required_opp_tables; struct device **genpd_virt_devs = opp_table->genpd_virt_devs; |
ca1b5d77b OPP: Configure al... |
791 792 793 794 795 796 |
int i, ret = 0; if (!required_opp_tables) return 0; /* Single genpd case */ |
60cdeae0d opp: Reduce code ... |
797 798 |
if (!genpd_virt_devs) return _set_required_opp(dev, dev, opp, 0); |
ca1b5d77b OPP: Configure al... |
799 800 801 802 803 804 805 806 |
/* Multiple genpd case */ /* * Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev * after it is freed from another thread. */ mutex_lock(&opp_table->genpd_virt_dev_lock); |
2c59138c2 opp: Set required... |
807 808 809 810 811 812 813 814 815 816 817 818 |
/* Scaling up? Set required OPPs in normal order, else reverse */ if (up) { for (i = 0; i < opp_table->required_opp_count; i++) { ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i); if (ret) break; } } else { for (i = opp_table->required_opp_count - 1; i >= 0; i--) { ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i); if (ret) break; |
ca1b5d77b OPP: Configure al... |
819 820 |
} } |
2c59138c2 opp: Set required... |
821 |
|
ca1b5d77b OPP: Configure al... |
822 823 824 825 |
mutex_unlock(&opp_table->genpd_virt_dev_lock); return ret; } |
6a0712f6f PM / OPP: Add dev... |
826 |
/** |
3ae1f39ae OPP: Add and expo... |
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 |
* dev_pm_opp_set_bw() - sets bandwidth levels corresponding to an opp * @dev: device for which we do this operation * @opp: opp based on which the bandwidth levels are to be configured * * This configures the bandwidth to the levels specified by the OPP. However * if the OPP specified is NULL the bandwidth levels are cleared out. * * Return: 0 on success or a negative error value. */ int dev_pm_opp_set_bw(struct device *dev, struct dev_pm_opp *opp) { struct opp_table *opp_table; int ret; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { dev_err(dev, "%s: device opp table doesn't exist ", __func__); return PTR_ERR(opp_table); } if (opp) ret = _set_opp_bw(opp_table, opp, dev, false); else ret = _set_opp_bw(opp_table, NULL, dev, true); dev_pm_opp_put_opp_table(opp_table); return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_set_bw); |
f3364e17d opp: Split out _o... |
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 |
static int _opp_set_rate_zero(struct device *dev, struct opp_table *opp_table) { int ret; if (!opp_table->enabled) return 0; /* * Some drivers need to support cases where some platforms may * have OPP table for the device, while others don't and * opp_set_rate() just needs to behave like clk_set_rate(). */ if (!_get_opp_count(opp_table)) return 0; ret = _set_opp_bw(opp_table, NULL, dev, true); if (ret) return ret; if (opp_table->regulators) regulator_disable(opp_table->regulators[0]); |
2c59138c2 opp: Set required... |
878 |
ret = _set_required_opps(dev, opp_table, NULL, false); |
f3364e17d opp: Split out _o... |
879 880 881 882 |
opp_table->enabled = false; return ret; } |
3ae1f39ae OPP: Add and expo... |
883 |
/** |
6a0712f6f PM / OPP: Add dev... |
884 885 886 887 |
* dev_pm_opp_set_rate() - Configure new OPP based on frequency * @dev: device for which we do this operation * @target_freq: frequency to achieve * |
b3e3759ee opp: Don't overwr... |
888 889 890 891 892 |
* This configures the power-supplies to the levels specified by the OPP * corresponding to the target_freq, and programs the clock to a value <= * target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax * provided by the opp, should have already rounded to the target OPP's * frequency. |
6a0712f6f PM / OPP: Add dev... |
893 894 895 |
*/ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { |
2c2709dc6 PM / OPP: Rename ... |
896 |
struct opp_table *opp_table; |
b3e3759ee opp: Don't overwr... |
897 |
unsigned long freq, old_freq, temp_freq; |
6a0712f6f PM / OPP: Add dev... |
898 |
struct dev_pm_opp *old_opp, *opp; |
6a0712f6f PM / OPP: Add dev... |
899 |
struct clk *clk; |
b00e667a6 opp: Remove bandw... |
900 |
int ret; |
6a0712f6f PM / OPP: Add dev... |
901 |
|
052c6f191 PM / OPP: Move aw... |
902 903 904 905 906 907 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { dev_err(dev, "%s: device opp doesn't exist ", __func__); return PTR_ERR(opp_table); } |
cd7ea5828 opp: Make dev_pm_... |
908 |
if (unlikely(!target_freq)) { |
f3364e17d opp: Split out _o... |
909 |
ret = _opp_set_rate_zero(dev, opp_table); |
cd7ea5828 opp: Make dev_pm_... |
910 911 |
goto put_opp_table; } |
052c6f191 PM / OPP: Move aw... |
912 913 914 915 916 917 918 919 |
clk = opp_table->clk; if (IS_ERR(clk)) { dev_err(dev, "%s: No clock available for the device ", __func__); ret = PTR_ERR(clk); goto put_opp_table; } |
6a0712f6f PM / OPP: Add dev... |
920 921 922 923 924 925 926 927 |
freq = clk_round_rate(clk, target_freq); if ((long)freq <= 0) freq = target_freq; old_freq = clk_get_rate(clk); /* Return early if nothing to do */ |
10b217365 opp: Reuse the en... |
928 929 930 931 932 933 |
if (opp_table->enabled && old_freq == freq) { dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do ", __func__, freq); ret = 0; goto put_opp_table; |
6a0712f6f PM / OPP: Add dev... |
934 |
} |
aca48b61f opp: Manage empty... |
935 936 937 938 939 940 941 942 943 944 945 |
/* * For IO devices which require an OPP on some platforms/SoCs * while just needing to scale the clock on some others * we look for empty OPP tables with just a clock handle and * scale only the clk. This makes dev_pm_opp_set_rate() * equivalent to a clk_set_rate() */ if (!_get_opp_count(opp_table)) { ret = _generic_set_opp_clk_only(dev, clk, freq); goto put_opp_table; } |
b3e3759ee opp: Don't overwr... |
946 947 |
temp_freq = old_freq; old_opp = _find_freq_ceil(opp_table, &temp_freq); |
4df27c918 PM / OPP: avoid m... |
948 |
if (IS_ERR(old_opp)) { |
6a0712f6f PM / OPP: Add dev... |
949 950 951 952 |
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld) ", __func__, old_freq, PTR_ERR(old_opp)); } |
b3e3759ee opp: Don't overwr... |
953 954 |
temp_freq = freq; opp = _find_freq_ceil(opp_table, &temp_freq); |
6a0712f6f PM / OPP: Add dev... |
955 956 957 958 959 |
if (IS_ERR(opp)) { ret = PTR_ERR(opp); dev_err(dev, "%s: failed to find OPP for freq %lu (%d) ", __func__, freq, ret); |
052c6f191 PM / OPP: Move aw... |
960 |
goto put_old_opp; |
6a0712f6f PM / OPP: Add dev... |
961 |
} |
947355850 PM / OPP: Separat... |
962 963 964 |
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz ", __func__, old_freq, freq); |
dfbe4678d PM / OPP: Add inf... |
965 |
|
ca1b5d77b OPP: Configure al... |
966 |
/* Scaling up? Configure required OPPs before frequency */ |
faef080f6 PM / OPP: Update ... |
967 |
if (freq >= old_freq) { |
2c59138c2 opp: Set required... |
968 |
ret = _set_required_opps(dev, opp_table, opp, true); |
ca1b5d77b OPP: Configure al... |
969 970 971 |
if (ret) goto put_opp; } |
7e535993f OPP: Separate out... |
972 973 974 975 976 |
if (opp_table->set_opp) { ret = _set_opp_custom(opp_table, dev, old_freq, freq, IS_ERR(old_opp) ? NULL : old_opp->supplies, opp->supplies); } else if (opp_table->regulators) { |
c74b32fad PM / OPP: Reorgan... |
977 978 979 980 |
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, IS_ERR(old_opp) ? NULL : old_opp->supplies, opp->supplies); } else { |
7e535993f OPP: Separate out... |
981 |
/* Only frequency scaling */ |
285881b51 PM / OPP: Remove ... |
982 |
ret = _generic_set_opp_clk_only(dev, clk, freq); |
ca1b5d77b OPP: Configure al... |
983 |
} |
c74b32fad PM / OPP: Reorgan... |
984 |
|
ca1b5d77b OPP: Configure al... |
985 986 |
/* Scaling down? Configure required OPPs after frequency */ if (!ret && freq < old_freq) { |
2c59138c2 opp: Set required... |
987 |
ret = _set_required_opps(dev, opp_table, opp, false); |
ca1b5d77b OPP: Configure al... |
988 989 990 |
if (ret) dev_err(dev, "Failed to set required opps: %d ", ret); |
dfbe4678d PM / OPP: Add inf... |
991 |
} |
72f80ce4e opp: Rename regul... |
992 |
if (!ret) { |
b00e667a6 opp: Remove bandw... |
993 |
ret = _set_opp_bw(opp_table, opp, dev, false); |
72f80ce4e opp: Rename regul... |
994 995 996 |
if (!ret) opp_table->enabled = true; } |
fe2af4025 opp: Update the b... |
997 |
|
ca1b5d77b OPP: Configure al... |
998 |
put_opp: |
8a31d9d94 PM / OPP: Update ... |
999 |
dev_pm_opp_put(opp); |
052c6f191 PM / OPP: Move aw... |
1000 |
put_old_opp: |
8a31d9d94 PM / OPP: Update ... |
1001 1002 |
if (!IS_ERR(old_opp)) dev_pm_opp_put(old_opp); |
052c6f191 PM / OPP: Move aw... |
1003 |
put_opp_table: |
5b650b388 PM / OPP: Take kr... |
1004 |
dev_pm_opp_put_opp_table(opp_table); |
052c6f191 PM / OPP: Move aw... |
1005 |
return ret; |
6a0712f6f PM / OPP: Add dev... |
1006 1007 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); |
2c2709dc6 PM / OPP: Rename ... |
1008 |
/* OPP-dev Helpers */ |
2c2709dc6 PM / OPP: Rename ... |
1009 1010 |
static void _remove_opp_dev(struct opp_device *opp_dev, struct opp_table *opp_table) |
064416586 PM / OPP: Add OPP... |
1011 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1012 1013 |
opp_debug_unregister(opp_dev, opp_table); list_del(&opp_dev->node); |
052c6f191 PM / OPP: Move aw... |
1014 |
kfree(opp_dev); |
064416586 PM / OPP: Add OPP... |
1015 |
} |
283d55e68 OPP: Prevent crea... |
1016 1017 |
static struct opp_device *_add_opp_dev_unlocked(const struct device *dev, struct opp_table *opp_table) |
064416586 PM / OPP: Add OPP... |
1018 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1019 |
struct opp_device *opp_dev; |
064416586 PM / OPP: Add OPP... |
1020 |
|
2c2709dc6 PM / OPP: Rename ... |
1021 1022 |
opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL); if (!opp_dev) |
064416586 PM / OPP: Add OPP... |
1023 |
return NULL; |
2c2709dc6 PM / OPP: Rename ... |
1024 1025 |
/* Initialize opp-dev */ opp_dev->dev = dev; |
3d2556992 OPP: Protect dev_... |
1026 |
|
052c6f191 PM / OPP: Move aw... |
1027 |
list_add(&opp_dev->node, &opp_table->dev_list); |
064416586 PM / OPP: Add OPP... |
1028 |
|
2c2709dc6 PM / OPP: Rename ... |
1029 |
/* Create debugfs entries for the opp_table */ |
a2dea4cb9 opp: no need to c... |
1030 |
opp_debug_register(opp_dev, opp_table); |
283d55e68 OPP: Prevent crea... |
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 |
return opp_dev; } struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table) { struct opp_device *opp_dev; mutex_lock(&opp_table->lock); opp_dev = _add_opp_dev_unlocked(dev, opp_table); |
3d2556992 OPP: Protect dev_... |
1042 |
mutex_unlock(&opp_table->lock); |
deaa51465 PM / OPP: Add deb... |
1043 |
|
2c2709dc6 PM / OPP: Rename ... |
1044 |
return opp_dev; |
064416586 PM / OPP: Add OPP... |
1045 |
} |
eb7c8743d OPP: Pass index t... |
1046 |
static struct opp_table *_allocate_opp_table(struct device *dev, int index) |
07cce74a7 PM / OPP: handle ... |
1047 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1048 1049 |
struct opp_table *opp_table; struct opp_device *opp_dev; |
d54974c25 PM / OPP: Manage ... |
1050 |
int ret; |
07cce74a7 PM / OPP: handle ... |
1051 1052 |
/* |
2c2709dc6 PM / OPP: Rename ... |
1053 |
* Allocate a new OPP table. In the infrequent case where a new |
07cce74a7 PM / OPP: handle ... |
1054 1055 |
* device is needed to be added, we pay this penalty. */ |
2c2709dc6 PM / OPP: Rename ... |
1056 1057 |
opp_table = kzalloc(sizeof(*opp_table), GFP_KERNEL); if (!opp_table) |
dd461cd91 opp: Allow dev_pm... |
1058 |
return ERR_PTR(-ENOMEM); |
07cce74a7 PM / OPP: handle ... |
1059 |
|
3d2556992 OPP: Protect dev_... |
1060 |
mutex_init(&opp_table->lock); |
4f018bc0e OPP: Add dev_pm_o... |
1061 |
mutex_init(&opp_table->genpd_virt_dev_lock); |
2c2709dc6 PM / OPP: Rename ... |
1062 |
INIT_LIST_HEAD(&opp_table->dev_list); |
064416586 PM / OPP: Add OPP... |
1063 |
|
46f48aca2 OPP: Fix missing ... |
1064 1065 |
/* Mark regulator count uninitialized */ opp_table->regulator_count = -1; |
2c2709dc6 PM / OPP: Rename ... |
1066 1067 |
opp_dev = _add_opp_dev(dev, opp_table); if (!opp_dev) { |
dd461cd91 opp: Allow dev_pm... |
1068 1069 |
ret = -ENOMEM; goto err; |
064416586 PM / OPP: Add OPP... |
1070 |
} |
eb7c8743d OPP: Pass index t... |
1071 |
_of_init_opp_table(opp_table, dev, index); |
50f8cfbd5 PM / OPP: Parse c... |
1072 |
|
d54974c25 PM / OPP: Manage ... |
1073 |
/* Find clk for the device */ |
2c2709dc6 PM / OPP: Rename ... |
1074 1075 1076 |
opp_table->clk = clk_get(dev, NULL); if (IS_ERR(opp_table->clk)) { ret = PTR_ERR(opp_table->clk); |
dd461cd91 opp: Allow dev_pm... |
1077 |
if (ret == -EPROBE_DEFER) |
e8322837a opp: fix memory l... |
1078 |
goto remove_opp_dev; |
dd461cd91 opp: Allow dev_pm... |
1079 1080 1081 |
dev_dbg(dev, "%s: Couldn't find clock: %d ", __func__, ret); |
d54974c25 PM / OPP: Manage ... |
1082 |
} |
6d3f922c4 opp: Add support ... |
1083 1084 |
/* Find interconnect path(s) for the device */ ret = dev_pm_opp_of_find_icc_paths(dev, opp_table); |
dd461cd91 opp: Allow dev_pm... |
1085 1086 |
if (ret) { if (ret == -EPROBE_DEFER) |
1a58c171a opp: Call the mis... |
1087 |
goto put_clk; |
dd461cd91 opp: Allow dev_pm... |
1088 |
|
6d3f922c4 opp: Add support ... |
1089 1090 1091 |
dev_warn(dev, "%s: Error finding interconnect paths: %d ", __func__, ret); |
dd461cd91 opp: Allow dev_pm... |
1092 |
} |
6d3f922c4 opp: Add support ... |
1093 |
|
052c6f191 PM / OPP: Move aw... |
1094 |
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head); |
2c2709dc6 PM / OPP: Rename ... |
1095 |
INIT_LIST_HEAD(&opp_table->opp_list); |
f067a982c PM / OPP: Add 'st... |
1096 |
kref_init(&opp_table->kref); |
07cce74a7 PM / OPP: handle ... |
1097 |
|
2c2709dc6 PM / OPP: Rename ... |
1098 |
/* Secure the device table modification */ |
052c6f191 PM / OPP: Move aw... |
1099 |
list_add(&opp_table->node, &opp_tables); |
2c2709dc6 PM / OPP: Rename ... |
1100 |
return opp_table; |
dd461cd91 opp: Allow dev_pm... |
1101 |
|
1a58c171a opp: Call the mis... |
1102 1103 1104 |
put_clk: if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); |
e8322837a opp: fix memory l... |
1105 1106 |
remove_opp_dev: _remove_opp_dev(opp_dev, opp_table); |
dd461cd91 opp: Allow dev_pm... |
1107 1108 1109 |
err: kfree(opp_table); return ERR_PTR(ret); |
07cce74a7 PM / OPP: handle ... |
1110 |
} |
f067a982c PM / OPP: Add 'st... |
1111 |
void _get_opp_table_kref(struct opp_table *opp_table) |
b6160e269 PM / OPP: Split o... |
1112 |
{ |
f067a982c PM / OPP: Add 'st... |
1113 1114 |
kref_get(&opp_table->kref); } |
eb7c8743d OPP: Pass index t... |
1115 |
static struct opp_table *_opp_get_opp_table(struct device *dev, int index) |
f067a982c PM / OPP: Add 'st... |
1116 1117 1118 1119 1120 |
{ struct opp_table *opp_table; /* Hold our table modification lock here */ mutex_lock(&opp_table_lock); |
5b650b388 PM / OPP: Take kr... |
1121 1122 |
opp_table = _find_opp_table_unlocked(dev); if (!IS_ERR(opp_table)) |
f067a982c PM / OPP: Add 'st... |
1123 |
goto unlock; |
f067a982c PM / OPP: Add 'st... |
1124 |
|
283d55e68 OPP: Prevent crea... |
1125 1126 1127 1128 |
opp_table = _managed_opp(dev, index); if (opp_table) { if (!_add_opp_dev_unlocked(dev, opp_table)) { dev_pm_opp_put_opp_table(opp_table); |
dd461cd91 opp: Allow dev_pm... |
1129 |
opp_table = ERR_PTR(-ENOMEM); |
283d55e68 OPP: Prevent crea... |
1130 1131 1132 |
} goto unlock; } |
eb7c8743d OPP: Pass index t... |
1133 |
opp_table = _allocate_opp_table(dev, index); |
f067a982c PM / OPP: Add 'st... |
1134 1135 1136 1137 1138 1139 |
unlock: mutex_unlock(&opp_table_lock); return opp_table; } |
eb7c8743d OPP: Pass index t... |
1140 1141 1142 1143 1144 |
struct opp_table *dev_pm_opp_get_opp_table(struct device *dev) { return _opp_get_opp_table(dev, 0); } |
f067a982c PM / OPP: Add 'st... |
1145 |
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table); |
eb7c8743d OPP: Pass index t... |
1146 1147 1148 1149 1150 |
struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev, int index) { return _opp_get_opp_table(dev, index); } |
b83c1899a PM / OPP: Use dev... |
1151 |
static void _opp_table_kref_release(struct kref *kref) |
f067a982c PM / OPP: Add 'st... |
1152 1153 |
{ struct opp_table *opp_table = container_of(kref, struct opp_table, kref); |
cdd6ed90c OPP: Use a single... |
1154 |
struct opp_device *opp_dev, *temp; |
6d3f922c4 opp: Add support ... |
1155 |
int i; |
b6160e269 PM / OPP: Split o... |
1156 |
|
e0df59de6 opp: Reduce the s... |
1157 1158 1159 |
/* Drop the lock as soon as we can */ list_del(&opp_table->node); mutex_unlock(&opp_table_lock); |
5d6d106fa OPP: Populate req... |
1160 |
_of_clear_opp_table(opp_table); |
b6160e269 PM / OPP: Split o... |
1161 1162 1163 |
/* Release clk */ if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); |
6d3f922c4 opp: Add support ... |
1164 1165 1166 1167 1168 |
if (opp_table->paths) { for (i = 0; i < opp_table->path_count; i++) icc_put(opp_table->paths[i]); kfree(opp_table->paths); } |
cdd6ed90c OPP: Use a single... |
1169 |
WARN_ON(!list_empty(&opp_table->opp_list)); |
b6160e269 PM / OPP: Split o... |
1170 |
|
cdd6ed90c OPP: Use a single... |
1171 1172 1173 1174 1175 1176 1177 |
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) { /* * The OPP table is getting removed, drop the performance state * constraints. */ if (opp_table->genpd_performance_state) dev_pm_genpd_set_performance_state((struct device *)(opp_dev->dev), 0); |
b6160e269 PM / OPP: Split o... |
1178 |
|
cdd6ed90c OPP: Use a single... |
1179 1180 |
_remove_opp_dev(opp_dev, opp_table); } |
b6160e269 PM / OPP: Split o... |
1181 |
|
4f018bc0e OPP: Add dev_pm_o... |
1182 |
mutex_destroy(&opp_table->genpd_virt_dev_lock); |
37a73ec0c PM / OPP: Add per... |
1183 |
mutex_destroy(&opp_table->lock); |
052c6f191 PM / OPP: Move aw... |
1184 |
kfree(opp_table); |
f067a982c PM / OPP: Add 'st... |
1185 1186 1187 1188 1189 1190 1191 1192 |
} void dev_pm_opp_put_opp_table(struct opp_table *opp_table) { kref_put_mutex(&opp_table->kref, _opp_table_kref_release, &opp_table_lock); } EXPORT_SYMBOL_GPL(dev_pm_opp_put_opp_table); |
8cd2f6e8f PM / OPP: Don't a... |
1193 |
void _opp_free(struct dev_pm_opp *opp) |
969fceb3c PM / OPP: Add lig... |
1194 1195 |
{ kfree(opp); |
969fceb3c PM / OPP: Add lig... |
1196 |
} |
1690d8bb9 cpufreq: scpi/scm... |
1197 1198 |
static void _opp_kref_release(struct dev_pm_opp *opp, struct opp_table *opp_table) |
129eec55d PM / OPP Introduc... |
1199 1200 1201 1202 1203 |
{ /* * Notify the changes in the availability of the operable * frequency/voltage list. */ |
052c6f191 PM / OPP: Move aw... |
1204 |
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_REMOVE, opp); |
da544b61e OPP: Populate OPP... |
1205 |
_of_opp_free_required_opps(opp_table, opp); |
deaa51465 PM / OPP: Add deb... |
1206 |
opp_debug_remove_one(opp); |
052c6f191 PM / OPP: Move aw... |
1207 1208 |
list_del(&opp->node); kfree(opp); |
1690d8bb9 cpufreq: scpi/scm... |
1209 |
} |
129eec55d PM / OPP Introduc... |
1210 |
|
1690d8bb9 cpufreq: scpi/scm... |
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 |
static void _opp_kref_release_unlocked(struct kref *kref) { struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref); struct opp_table *opp_table = opp->opp_table; _opp_kref_release(opp, opp_table); } static void _opp_kref_release_locked(struct kref *kref) { struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref); struct opp_table *opp_table = opp->opp_table; _opp_kref_release(opp, opp_table); |
37a73ec0c PM / OPP: Add per... |
1225 |
mutex_unlock(&opp_table->lock); |
129eec55d PM / OPP Introduc... |
1226 |
} |
a88bd2a51 PM / OPP: Impleme... |
1227 |
void dev_pm_opp_get(struct dev_pm_opp *opp) |
8a31d9d94 PM / OPP: Update ... |
1228 1229 1230 |
{ kref_get(&opp->kref); } |
7034764a1 PM / OPP: Add 'st... |
1231 1232 |
void dev_pm_opp_put(struct dev_pm_opp *opp) { |
1690d8bb9 cpufreq: scpi/scm... |
1233 1234 |
kref_put_mutex(&opp->kref, _opp_kref_release_locked, &opp->opp_table->lock); |
7034764a1 PM / OPP: Add 'st... |
1235 1236 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_put); |
1690d8bb9 cpufreq: scpi/scm... |
1237 1238 1239 1240 |
static void dev_pm_opp_put_unlocked(struct dev_pm_opp *opp) { kref_put(&opp->kref, _opp_kref_release_unlocked); } |
129eec55d PM / OPP Introduc... |
1241 |
/** |
2c2709dc6 PM / OPP: Rename ... |
1242 |
* dev_pm_opp_remove() - Remove an OPP from OPP table |
129eec55d PM / OPP Introduc... |
1243 1244 1245 |
* @dev: device for which we do this operation * @freq: OPP to remove with matching 'freq' * |
2c2709dc6 PM / OPP: Rename ... |
1246 |
* This function removes an opp from the opp table. |
129eec55d PM / OPP Introduc... |
1247 1248 1249 1250 |
*/ void dev_pm_opp_remove(struct device *dev, unsigned long freq) { struct dev_pm_opp *opp; |
2c2709dc6 PM / OPP: Rename ... |
1251 |
struct opp_table *opp_table; |
129eec55d PM / OPP Introduc... |
1252 |
bool found = false; |
2c2709dc6 PM / OPP: Rename ... |
1253 1254 |
opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) |
5b650b388 PM / OPP: Take kr... |
1255 |
return; |
129eec55d PM / OPP Introduc... |
1256 |
|
37a73ec0c PM / OPP: Add per... |
1257 |
mutex_lock(&opp_table->lock); |
2c2709dc6 PM / OPP: Rename ... |
1258 |
list_for_each_entry(opp, &opp_table->opp_list, node) { |
129eec55d PM / OPP Introduc... |
1259 1260 1261 1262 1263 |
if (opp->rate == freq) { found = true; break; } } |
37a73ec0c PM / OPP: Add per... |
1264 |
mutex_unlock(&opp_table->lock); |
5b650b388 PM / OPP: Take kr... |
1265 1266 |
if (found) { dev_pm_opp_put(opp); |
0ad8c6239 OPP: Don't take O... |
1267 1268 1269 |
/* Drop the reference taken by dev_pm_opp_add() */ dev_pm_opp_put_opp_table(opp_table); |
5b650b388 PM / OPP: Take kr... |
1270 |
} else { |
129eec55d PM / OPP Introduc... |
1271 1272 1273 |
dev_warn(dev, "%s: Couldn't find OPP with freq: %lu ", __func__, freq); |
129eec55d PM / OPP Introduc... |
1274 |
} |
0ad8c6239 OPP: Don't take O... |
1275 |
/* Drop the reference taken by _find_opp_table() */ |
5b650b388 PM / OPP: Take kr... |
1276 |
dev_pm_opp_put_opp_table(opp_table); |
129eec55d PM / OPP Introduc... |
1277 1278 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_remove); |
922ff0759 opp: Don't drop r... |
1279 |
bool _opp_remove_all_static(struct opp_table *opp_table) |
03758d602 opp: Replace list... |
1280 1281 |
{ struct dev_pm_opp *opp, *tmp; |
922ff0759 opp: Don't drop r... |
1282 |
bool ret = true; |
03758d602 opp: Replace list... |
1283 1284 |
mutex_lock(&opp_table->lock); |
922ff0759 opp: Don't drop r... |
1285 1286 1287 1288 1289 1290 |
if (!opp_table->parsed_static_opps) { ret = false; goto unlock; } if (--opp_table->parsed_static_opps) |
03758d602 opp: Replace list... |
1291 1292 1293 1294 1295 1296 1297 1298 1299 |
goto unlock; list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) { if (!opp->dynamic) dev_pm_opp_put_unlocked(opp); } unlock: mutex_unlock(&opp_table->lock); |
922ff0759 opp: Don't drop r... |
1300 1301 |
return ret; |
03758d602 opp: Replace list... |
1302 |
} |
1690d8bb9 cpufreq: scpi/scm... |
1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
/** * dev_pm_opp_remove_all_dynamic() - Remove all dynamically created OPPs * @dev: device for which we do this operation * * This function removes all dynamically created OPPs from the opp table. */ void dev_pm_opp_remove_all_dynamic(struct device *dev) { struct opp_table *opp_table; struct dev_pm_opp *opp, *temp; int count = 0; opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) return; mutex_lock(&opp_table->lock); list_for_each_entry_safe(opp, temp, &opp_table->opp_list, node) { if (opp->dynamic) { dev_pm_opp_put_unlocked(opp); count++; } } mutex_unlock(&opp_table->lock); /* Drop the references taken by dev_pm_opp_add() */ while (count--) dev_pm_opp_put_opp_table(opp_table); /* Drop the reference taken by _find_opp_table() */ dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic); |
8cd2f6e8f PM / OPP: Don't a... |
1336 |
struct dev_pm_opp *_opp_allocate(struct opp_table *table) |
e1f60b292 PM: Introduce lib... |
1337 |
{ |
23dacf6d2 PM / OPP: Break _... |
1338 |
struct dev_pm_opp *opp; |
6d3f922c4 opp: Add support ... |
1339 |
int supply_count, supply_size, icc_size; |
e1f60b292 PM: Introduce lib... |
1340 |
|
dfbe4678d PM / OPP: Add inf... |
1341 |
/* Allocate space for at least one supply */ |
6d3f922c4 opp: Add support ... |
1342 1343 1344 |
supply_count = table->regulator_count > 0 ? table->regulator_count : 1; supply_size = sizeof(*opp->supplies) * supply_count; icc_size = sizeof(*opp->bandwidth) * table->path_count; |
e1f60b292 PM: Introduce lib... |
1345 |
|
dfbe4678d PM / OPP: Add inf... |
1346 |
/* allocate new OPP node and supplies structures */ |
6d3f922c4 opp: Add support ... |
1347 |
opp = kzalloc(sizeof(*opp) + supply_size + icc_size, GFP_KERNEL); |
8cd2f6e8f PM / OPP: Don't a... |
1348 |
if (!opp) |
23dacf6d2 PM / OPP: Break _... |
1349 |
return NULL; |
23dacf6d2 PM / OPP: Break _... |
1350 |
|
dfbe4678d PM / OPP: Add inf... |
1351 1352 |
/* Put the supplies at the end of the OPP structure as an empty array */ opp->supplies = (struct dev_pm_opp_supply *)(opp + 1); |
6d3f922c4 opp: Add support ... |
1353 1354 |
if (icc_size) opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->supplies + supply_count); |
dfbe4678d PM / OPP: Add inf... |
1355 |
INIT_LIST_HEAD(&opp->node); |
23dacf6d2 PM / OPP: Break _... |
1356 1357 |
return opp; } |
7d34d56ef PM / OPP: Disable... |
1358 |
static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, |
2c2709dc6 PM / OPP: Rename ... |
1359 |
struct opp_table *opp_table) |
7d34d56ef PM / OPP: Disable... |
1360 |
{ |
dfbe4678d PM / OPP: Add inf... |
1361 1362 |
struct regulator *reg; int i; |
90e3577b5 OPP: Use opp_tabl... |
1363 1364 |
if (!opp_table->regulators) return true; |
dfbe4678d PM / OPP: Add inf... |
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 |
for (i = 0; i < opp_table->regulator_count; i++) { reg = opp_table->regulators[i]; if (!regulator_is_supported_voltage(reg, opp->supplies[i].u_volt_min, opp->supplies[i].u_volt_max)) { pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator ", __func__, opp->supplies[i].u_volt_min, opp->supplies[i].u_volt_max); return false; } |
7d34d56ef PM / OPP: Disable... |
1377 1378 1379 1380 |
} return true; } |
6c591eec6 OPP: Add helpers ... |
1381 1382 1383 1384 |
int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2) { if (opp1->rate != opp2->rate) return opp1->rate < opp2->rate ? -1 : 1; |
6d3f922c4 opp: Add support ... |
1385 1386 1387 |
if (opp1->bandwidth && opp2->bandwidth && opp1->bandwidth[0].peak != opp2->bandwidth[0].peak) return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 1; |
6c591eec6 OPP: Add helpers ... |
1388 1389 1390 1391 |
if (opp1->level != opp2->level) return opp1->level < opp2->level ? -1 : 1; return 0; } |
a1e8c1360 PM / OPP: "opp-hz... |
1392 1393 1394 |
static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, struct list_head **head) |
23dacf6d2 PM / OPP: Break _... |
1395 1396 |
{ struct dev_pm_opp *opp; |
6c591eec6 OPP: Add helpers ... |
1397 |
int opp_cmp; |
23dacf6d2 PM / OPP: Break _... |
1398 1399 1400 1401 1402 |
/* * Insert new OPP in order of increasing frequency and discard if * already present. * |
2c2709dc6 PM / OPP: Rename ... |
1403 |
* Need to use &opp_table->opp_list in the condition part of the 'for' |
23dacf6d2 PM / OPP: Break _... |
1404 1405 1406 |
* loop, don't replace it with head otherwise it will become an infinite * loop. */ |
052c6f191 PM / OPP: Move aw... |
1407 |
list_for_each_entry(opp, &opp_table->opp_list, node) { |
6c591eec6 OPP: Add helpers ... |
1408 1409 |
opp_cmp = _opp_compare_key(new_opp, opp); if (opp_cmp > 0) { |
a1e8c1360 PM / OPP: "opp-hz... |
1410 |
*head = &opp->node; |
23dacf6d2 PM / OPP: Break _... |
1411 1412 |
continue; } |
6c591eec6 OPP: Add helpers ... |
1413 |
if (opp_cmp < 0) |
a1e8c1360 PM / OPP: "opp-hz... |
1414 |
return 0; |
23dacf6d2 PM / OPP: Break _... |
1415 1416 |
/* Duplicate OPPs */ |
064416586 PM / OPP: Add OPP... |
1417 1418 |
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d ", |
dfbe4678d PM / OPP: Add inf... |
1419 1420 1421 |
__func__, opp->rate, opp->supplies[0].u_volt, opp->available, new_opp->rate, new_opp->supplies[0].u_volt, new_opp->available); |
23dacf6d2 PM / OPP: Break _... |
1422 |
|
dfbe4678d PM / OPP: Add inf... |
1423 |
/* Should we compare voltages for all regulators here ? */ |
a1e8c1360 PM / OPP: "opp-hz... |
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 |
return opp->available && new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST; } return 0; } /* * Returns: * 0: On success. And appropriate error message for duplicate OPPs. * -EBUSY: For OPP with same freq/volt and is available. The callers of * _opp_add() must return 0 if they receive -EBUSY from it. This is to make * sure we don't print error messages unnecessarily if different parts of * kernel try to initialize the OPP table. * -EEXIST: For OPP with same freq but different volt or is unavailable. This * should be considered an error by the callers of _opp_add(). */ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available) { struct list_head *head; int ret; mutex_lock(&opp_table->lock); head = &opp_table->opp_list; |
37a73ec0c PM / OPP: Add per... |
1449 |
|
a1e8c1360 PM / OPP: "opp-hz... |
1450 1451 1452 1453 1454 1455 |
if (likely(!rate_not_available)) { ret = _opp_is_duplicate(dev, new_opp, opp_table, &head); if (ret) { mutex_unlock(&opp_table->lock); return ret; } |
23dacf6d2 PM / OPP: Break _... |
1456 |
} |
052c6f191 PM / OPP: Move aw... |
1457 |
list_add(&new_opp->node, head); |
37a73ec0c PM / OPP: Add per... |
1458 1459 1460 |
mutex_unlock(&opp_table->lock); new_opp->opp_table = opp_table; |
7034764a1 PM / OPP: Add 'st... |
1461 |
kref_init(&new_opp->kref); |
23dacf6d2 PM / OPP: Break _... |
1462 |
|
a2dea4cb9 opp: no need to c... |
1463 |
opp_debug_create_one(new_opp, opp_table); |
deaa51465 PM / OPP: Add deb... |
1464 |
|
2c2709dc6 PM / OPP: Rename ... |
1465 |
if (!_opp_supported_by_regulators(new_opp, opp_table)) { |
7d34d56ef PM / OPP: Disable... |
1466 1467 1468 1469 1470 |
new_opp->available = false; dev_warn(dev, "%s: OPP not supported by regulators (%lu) ", __func__, new_opp->rate); } |
23dacf6d2 PM / OPP: Break _... |
1471 1472 |
return 0; } |
737002b5d PM / OPP: Relocat... |
1473 |
/** |
b64b9c3f9 PM / OPP: Rename ... |
1474 |
* _opp_add_v1() - Allocate a OPP based on v1 bindings. |
8cd2f6e8f PM / OPP: Don't a... |
1475 |
* @opp_table: OPP table |
984f16c84 PM / OPP: Update ... |
1476 1477 1478 1479 1480 |
* @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP * @u_volt: Voltage in uVolts for this OPP * @dynamic: Dynamically added OPPs. * |
2c2709dc6 PM / OPP: Rename ... |
1481 |
* This function adds an opp definition to the opp table and returns status. |
984f16c84 PM / OPP: Update ... |
1482 1483 1484 |
* The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. * |
8f8d37b25 PM / OPP: Prefix ... |
1485 1486 |
* NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table * and freed by dev_pm_opp_of_remove_table. |
984f16c84 PM / OPP: Update ... |
1487 |
* |
984f16c84 PM / OPP: Update ... |
1488 1489 1490 1491 1492 1493 1494 |
* Return: * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available * -EEXIST Freq are same and volt are different OR * Duplicate OPPs (both freq and volt are same) and !opp->available * -ENOMEM Memory allocation failure */ |
8cd2f6e8f PM / OPP: Don't a... |
1495 1496 |
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic) |
e1f60b292 PM: Introduce lib... |
1497 |
{ |
23dacf6d2 PM / OPP: Break _... |
1498 |
struct dev_pm_opp *new_opp; |
50f8cfbd5 PM / OPP: Parse c... |
1499 |
unsigned long tol; |
6ce4184d0 PM / OPP: do erro... |
1500 |
int ret; |
e1f60b292 PM: Introduce lib... |
1501 |
|
8cd2f6e8f PM / OPP: Don't a... |
1502 1503 1504 |
new_opp = _opp_allocate(opp_table); if (!new_opp) return -ENOMEM; |
23dacf6d2 PM / OPP: Break _... |
1505 |
|
a7470db6f PM / OPP don't ma... |
1506 |
/* populate the opp table */ |
a7470db6f PM / OPP don't ma... |
1507 |
new_opp->rate = freq; |
2c2709dc6 PM / OPP: Rename ... |
1508 |
tol = u_volt * opp_table->voltage_tolerance_v1 / 100; |
dfbe4678d PM / OPP: Add inf... |
1509 1510 1511 |
new_opp->supplies[0].u_volt = u_volt; new_opp->supplies[0].u_volt_min = u_volt - tol; new_opp->supplies[0].u_volt_max = u_volt + tol; |
a7470db6f PM / OPP don't ma... |
1512 |
new_opp->available = true; |
23dacf6d2 PM / OPP: Break _... |
1513 |
new_opp->dynamic = dynamic; |
a7470db6f PM / OPP don't ma... |
1514 |
|
a1e8c1360 PM / OPP: "opp-hz... |
1515 |
ret = _opp_add(dev, new_opp, opp_table, false); |
7f8538eba PM / OPP: Fix mem... |
1516 1517 1518 1519 |
if (ret) { /* Don't return error for duplicate OPPs */ if (ret == -EBUSY) ret = 0; |
6ce4184d0 PM / OPP: do erro... |
1520 |
goto free_opp; |
7f8538eba PM / OPP: Fix mem... |
1521 |
} |
64ce85457 PM / OPP: discard... |
1522 |
|
03ca370fb PM / OPP: Add OPP... |
1523 1524 1525 1526 |
/* * Notify the changes in the availability of the operable * frequency/voltage list. */ |
052c6f191 PM / OPP: Move aw... |
1527 |
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); |
e1f60b292 PM: Introduce lib... |
1528 |
return 0; |
6ce4184d0 PM / OPP: do erro... |
1529 1530 |
free_opp: |
8cd2f6e8f PM / OPP: Don't a... |
1531 |
_opp_free(new_opp); |
6ce4184d0 PM / OPP: do erro... |
1532 |
return ret; |
e1f60b292 PM: Introduce lib... |
1533 |
} |
38393409d PM / OPP mark OPP... |
1534 |
|
274659029 PM / OPP: Add sup... |
1535 |
/** |
7de36b0aa PM / OPP: Parse '... |
1536 1537 1538 1539 1540 1541 1542 1543 1544 |
* dev_pm_opp_set_supported_hw() - Set supported platforms * @dev: Device for which supported-hw has to be set. * @versions: Array of hierarchy of versions to match. * @count: Number of elements in the array. * * This is required only for the V2 bindings, and it enables a platform to * specify the hierarchy of versions it supports. OPP layer will then enable * OPPs, which are available for those versions, based on its 'opp-supported-hw' * property. |
7de36b0aa PM / OPP: Parse '... |
1545 |
*/ |
fa30184d1 PM / OPP: Return ... |
1546 1547 |
struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count) |
7de36b0aa PM / OPP: Parse '... |
1548 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1549 |
struct opp_table *opp_table; |
7de36b0aa PM / OPP: Parse '... |
1550 |
|
fa30184d1 PM / OPP: Return ... |
1551 |
opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
1552 1553 |
if (IS_ERR(opp_table)) return opp_table; |
7de36b0aa PM / OPP: Parse '... |
1554 |
|
2c2709dc6 PM / OPP: Rename ... |
1555 1556 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); |
7de36b0aa PM / OPP: Parse '... |
1557 |
|
25419de1b PM / OPP: Fix sha... |
1558 1559 1560 |
/* Another CPU that shares the OPP table has set the property ? */ if (opp_table->supported_hw) return opp_table; |
7de36b0aa PM / OPP: Parse '... |
1561 |
|
2c2709dc6 PM / OPP: Rename ... |
1562 |
opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions), |
7de36b0aa PM / OPP: Parse '... |
1563 |
GFP_KERNEL); |
2c2709dc6 PM / OPP: Rename ... |
1564 |
if (!opp_table->supported_hw) { |
25419de1b PM / OPP: Fix sha... |
1565 1566 |
dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(-ENOMEM); |
7de36b0aa PM / OPP: Parse '... |
1567 |
} |
2c2709dc6 PM / OPP: Rename ... |
1568 |
opp_table->supported_hw_count = count; |
fa30184d1 PM / OPP: Return ... |
1569 1570 |
return opp_table; |
7de36b0aa PM / OPP: Parse '... |
1571 1572 1573 1574 1575 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); /** * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw |
fa30184d1 PM / OPP: Return ... |
1576 |
* @opp_table: OPP table returned by dev_pm_opp_set_supported_hw(). |
7de36b0aa PM / OPP: Parse '... |
1577 1578 |
* * This is required only for the V2 bindings, and is called for a matching |
2c2709dc6 PM / OPP: Rename ... |
1579 |
* dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure |
7de36b0aa PM / OPP: Parse '... |
1580 |
* will not be freed. |
7de36b0aa PM / OPP: Parse '... |
1581 |
*/ |
fa30184d1 PM / OPP: Return ... |
1582 |
void dev_pm_opp_put_supported_hw(struct opp_table *opp_table) |
7de36b0aa PM / OPP: Parse '... |
1583 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1584 1585 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); |
7de36b0aa PM / OPP: Parse '... |
1586 |
|
2c2709dc6 PM / OPP: Rename ... |
1587 1588 1589 |
kfree(opp_table->supported_hw); opp_table->supported_hw = NULL; opp_table->supported_hw_count = 0; |
7de36b0aa PM / OPP: Parse '... |
1590 |
|
fa30184d1 PM / OPP: Return ... |
1591 |
dev_pm_opp_put_opp_table(opp_table); |
7de36b0aa PM / OPP: Parse '... |
1592 1593 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); |
01fb4d3c3 PM / OPP: Parse '... |
1594 1595 |
/** * dev_pm_opp_set_prop_name() - Set prop-extn name |
a5da64477 PM / OPP: Fix inc... |
1596 |
* @dev: Device for which the prop-name has to be set. |
01fb4d3c3 PM / OPP: Parse '... |
1597 1598 1599 1600 1601 1602 |
* @name: name to postfix to properties. * * This is required only for the V2 bindings, and it enables a platform to * specify the extn to be used for certain property names. The properties to * which the extension will apply are opp-microvolt and opp-microamp. OPP core * should postfix the property name with -<name> while looking for them. |
01fb4d3c3 PM / OPP: Parse '... |
1603 |
*/ |
fa30184d1 PM / OPP: Return ... |
1604 |
struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) |
01fb4d3c3 PM / OPP: Parse '... |
1605 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1606 |
struct opp_table *opp_table; |
01fb4d3c3 PM / OPP: Parse '... |
1607 |
|
fa30184d1 PM / OPP: Return ... |
1608 |
opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
1609 1610 |
if (IS_ERR(opp_table)) return opp_table; |
01fb4d3c3 PM / OPP: Parse '... |
1611 |
|
2c2709dc6 PM / OPP: Rename ... |
1612 1613 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); |
01fb4d3c3 PM / OPP: Parse '... |
1614 |
|
878ec1a9f PM / OPP: Fix sha... |
1615 1616 1617 |
/* Another CPU that shares the OPP table has set the property ? */ if (opp_table->prop_name) return opp_table; |
01fb4d3c3 PM / OPP: Parse '... |
1618 |
|
2c2709dc6 PM / OPP: Rename ... |
1619 1620 |
opp_table->prop_name = kstrdup(name, GFP_KERNEL); if (!opp_table->prop_name) { |
878ec1a9f PM / OPP: Fix sha... |
1621 1622 |
dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(-ENOMEM); |
01fb4d3c3 PM / OPP: Parse '... |
1623 |
} |
fa30184d1 PM / OPP: Return ... |
1624 |
return opp_table; |
01fb4d3c3 PM / OPP: Parse '... |
1625 1626 1627 1628 1629 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); /** * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name |
fa30184d1 PM / OPP: Return ... |
1630 |
* @opp_table: OPP table returned by dev_pm_opp_set_prop_name(). |
01fb4d3c3 PM / OPP: Parse '... |
1631 1632 |
* * This is required only for the V2 bindings, and is called for a matching |
2c2709dc6 PM / OPP: Rename ... |
1633 |
* dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure |
01fb4d3c3 PM / OPP: Parse '... |
1634 |
* will not be freed. |
01fb4d3c3 PM / OPP: Parse '... |
1635 |
*/ |
fa30184d1 PM / OPP: Return ... |
1636 |
void dev_pm_opp_put_prop_name(struct opp_table *opp_table) |
01fb4d3c3 PM / OPP: Parse '... |
1637 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1638 1639 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); |
01fb4d3c3 PM / OPP: Parse '... |
1640 |
|
2c2709dc6 PM / OPP: Rename ... |
1641 1642 |
kfree(opp_table->prop_name); opp_table->prop_name = NULL; |
01fb4d3c3 PM / OPP: Parse '... |
1643 |
|
fa30184d1 PM / OPP: Return ... |
1644 |
dev_pm_opp_put_opp_table(opp_table); |
01fb4d3c3 PM / OPP: Parse '... |
1645 1646 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); |
947355850 PM / OPP: Separat... |
1647 1648 1649 1650 |
static int _allocate_set_opp_data(struct opp_table *opp_table) { struct dev_pm_set_opp_data *data; int len, count = opp_table->regulator_count; |
90e3577b5 OPP: Use opp_tabl... |
1651 |
if (WARN_ON(!opp_table->regulators)) |
947355850 PM / OPP: Separat... |
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 |
return -EINVAL; /* space for set_opp_data */ len = sizeof(*data); /* space for old_opp.supplies and new_opp.supplies */ len += 2 * sizeof(struct dev_pm_opp_supply) * count; data = kzalloc(len, GFP_KERNEL); if (!data) return -ENOMEM; data->old_opp.supplies = (void *)(data + 1); data->new_opp.supplies = data->old_opp.supplies + count; opp_table->set_opp_data = data; return 0; } static void _free_set_opp_data(struct opp_table *opp_table) { kfree(opp_table->set_opp_data); opp_table->set_opp_data = NULL; } |
9f8ea969d PM / OPP: get/put... |
1677 |
/** |
dfbe4678d PM / OPP: Add inf... |
1678 |
* dev_pm_opp_set_regulators() - Set regulator names for the device |
9f8ea969d PM / OPP: get/put... |
1679 |
* @dev: Device for which regulator name is being set. |
dfbe4678d PM / OPP: Add inf... |
1680 1681 |
* @names: Array of pointers to the names of the regulator. * @count: Number of regulators. |
9f8ea969d PM / OPP: get/put... |
1682 1683 |
* * In order to support OPP switching, OPP layer needs to know the name of the |
dfbe4678d PM / OPP: Add inf... |
1684 1685 |
* device's regulators, as the core would be required to switch voltages as * well. |
9f8ea969d PM / OPP: get/put... |
1686 1687 |
* * This must be called before any OPPs are initialized for the device. |
9f8ea969d PM / OPP: get/put... |
1688 |
*/ |
dfbe4678d PM / OPP: Add inf... |
1689 1690 1691 |
struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count) |
9f8ea969d PM / OPP: get/put... |
1692 |
{ |
2c2709dc6 PM / OPP: Rename ... |
1693 |
struct opp_table *opp_table; |
9f8ea969d PM / OPP: get/put... |
1694 |
struct regulator *reg; |
dfbe4678d PM / OPP: Add inf... |
1695 |
int ret, i; |
9f8ea969d PM / OPP: get/put... |
1696 |
|
fa30184d1 PM / OPP: Return ... |
1697 |
opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
1698 1699 |
if (IS_ERR(opp_table)) return opp_table; |
9f8ea969d PM / OPP: get/put... |
1700 1701 |
/* This should be called before OPPs are initialized */ |
2c2709dc6 PM / OPP: Rename ... |
1702 |
if (WARN_ON(!list_empty(&opp_table->opp_list))) { |
9f8ea969d PM / OPP: get/put... |
1703 1704 1705 |
ret = -EBUSY; goto err; } |
779b783cf PM / OPP: Fix sha... |
1706 1707 1708 |
/* Another CPU that shares the OPP table has set the regulators ? */ if (opp_table->regulators) return opp_table; |
dfbe4678d PM / OPP: Add inf... |
1709 1710 1711 1712 1713 1714 |
opp_table->regulators = kmalloc_array(count, sizeof(*opp_table->regulators), GFP_KERNEL); if (!opp_table->regulators) { ret = -ENOMEM; |
9f8ea969d PM / OPP: get/put... |
1715 1716 |
goto err; } |
dfbe4678d PM / OPP: Add inf... |
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 |
for (i = 0; i < count; i++) { reg = regulator_get_optional(dev, names[i]); if (IS_ERR(reg)) { ret = PTR_ERR(reg); if (ret != -EPROBE_DEFER) dev_err(dev, "%s: no regulator (%s) found: %d ", __func__, names[i], ret); goto free_regulators; } opp_table->regulators[i] = reg; } opp_table->regulator_count = count; |
9f8ea969d PM / OPP: get/put... |
1732 |
|
947355850 PM / OPP: Separat... |
1733 1734 1735 1736 |
/* Allocate block only once to pass to set_opp() routines */ ret = _allocate_set_opp_data(opp_table); if (ret) goto free_regulators; |
91291d9ad PM / OPP: Pass op... |
1737 |
return opp_table; |
9f8ea969d PM / OPP: get/put... |
1738 |
|
dfbe4678d PM / OPP: Add inf... |
1739 |
free_regulators: |
24957db10 opp: core: Revert... |
1740 1741 |
while (i != 0) regulator_put(opp_table->regulators[--i]); |
dfbe4678d PM / OPP: Add inf... |
1742 1743 1744 |
kfree(opp_table->regulators); opp_table->regulators = NULL; |
46f48aca2 OPP: Fix missing ... |
1745 |
opp_table->regulator_count = -1; |
9f8ea969d PM / OPP: get/put... |
1746 |
err: |
fa30184d1 PM / OPP: Return ... |
1747 |
dev_pm_opp_put_opp_table(opp_table); |
9f8ea969d PM / OPP: get/put... |
1748 |
|
91291d9ad PM / OPP: Pass op... |
1749 |
return ERR_PTR(ret); |
9f8ea969d PM / OPP: get/put... |
1750 |
} |
dfbe4678d PM / OPP: Add inf... |
1751 |
EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulators); |
9f8ea969d PM / OPP: get/put... |
1752 1753 |
/** |
dfbe4678d PM / OPP: Add inf... |
1754 1755 |
* dev_pm_opp_put_regulators() - Releases resources blocked for regulator * @opp_table: OPP table returned from dev_pm_opp_set_regulators(). |
9f8ea969d PM / OPP: get/put... |
1756 |
*/ |
dfbe4678d PM / OPP: Add inf... |
1757 |
void dev_pm_opp_put_regulators(struct opp_table *opp_table) |
9f8ea969d PM / OPP: get/put... |
1758 |
{ |
dfbe4678d PM / OPP: Add inf... |
1759 |
int i; |
779b783cf PM / OPP: Fix sha... |
1760 1761 |
if (!opp_table->regulators) goto put_opp_table; |
9f8ea969d PM / OPP: get/put... |
1762 |
|
2c2709dc6 PM / OPP: Rename ... |
1763 1764 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); |
9f8ea969d PM / OPP: get/put... |
1765 |
|
72f80ce4e opp: Rename regul... |
1766 |
if (opp_table->enabled) { |
8d45719ca opp: core: add re... |
1767 1768 |
for (i = opp_table->regulator_count - 1; i >= 0; i--) regulator_disable(opp_table->regulators[i]); |
8d45719ca opp: core: add re... |
1769 |
} |
24957db10 opp: core: Revert... |
1770 |
for (i = opp_table->regulator_count - 1; i >= 0; i--) |
dfbe4678d PM / OPP: Add inf... |
1771 |
regulator_put(opp_table->regulators[i]); |
947355850 PM / OPP: Separat... |
1772 |
_free_set_opp_data(opp_table); |
dfbe4678d PM / OPP: Add inf... |
1773 1774 |
kfree(opp_table->regulators); opp_table->regulators = NULL; |
46f48aca2 OPP: Fix missing ... |
1775 |
opp_table->regulator_count = -1; |
9f8ea969d PM / OPP: get/put... |
1776 |
|
779b783cf PM / OPP: Fix sha... |
1777 |
put_opp_table: |
fa30184d1 PM / OPP: Return ... |
1778 |
dev_pm_opp_put_opp_table(opp_table); |
9f8ea969d PM / OPP: get/put... |
1779 |
} |
dfbe4678d PM / OPP: Add inf... |
1780 |
EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); |
9f8ea969d PM / OPP: get/put... |
1781 |
|
38393409d PM / OPP mark OPP... |
1782 |
/** |
829a4e8c0 PM / OPP: Add dev... |
1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 |
* dev_pm_opp_set_clkname() - Set clk name for the device * @dev: Device for which clk name is being set. * @name: Clk name. * * In order to support OPP switching, OPP layer needs to get pointer to the * clock for the device. Simple cases work fine without using this routine (i.e. * by passing connection-id as NULL), but for a device with multiple clocks * available, the OPP core needs to know the exact name of the clk to use. * * This must be called before any OPPs are initialized for the device. */ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char *name) { struct opp_table *opp_table; int ret; opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
1800 1801 |
if (IS_ERR(opp_table)) return opp_table; |
829a4e8c0 PM / OPP: Add dev... |
1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 |
/* This should be called before OPPs are initialized */ if (WARN_ON(!list_empty(&opp_table->opp_list))) { ret = -EBUSY; goto err; } /* Already have default clk set, free it */ if (!IS_ERR(opp_table->clk)) clk_put(opp_table->clk); /* Find clk for the device */ opp_table->clk = clk_get(dev, name); if (IS_ERR(opp_table->clk)) { ret = PTR_ERR(opp_table->clk); if (ret != -EPROBE_DEFER) { dev_err(dev, "%s: Couldn't find clock: %d ", __func__, ret); } goto err; } return opp_table; err: dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(dev_pm_opp_set_clkname); /** * dev_pm_opp_put_clkname() - Releases resources blocked for clk. * @opp_table: OPP table returned from dev_pm_opp_set_clkname(). */ void dev_pm_opp_put_clkname(struct opp_table *opp_table) { /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); clk_put(opp_table->clk); opp_table->clk = ERR_PTR(-EINVAL); dev_pm_opp_put_opp_table(opp_table); } EXPORT_SYMBOL_GPL(dev_pm_opp_put_clkname); /** |
4dab160eb PM / OPP: Allow p... |
1851 1852 1853 1854 1855 1856 1857 1858 |
* dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper * @dev: Device for which the helper is getting registered. * @set_opp: Custom set OPP helper. * * This is useful to support complex platforms (like platforms with multiple * regulators per device), instead of the generic OPP set rate helper. * * This must be called before any OPPs are initialized for the device. |
4dab160eb PM / OPP: Allow p... |
1859 |
*/ |
fa30184d1 PM / OPP: Return ... |
1860 |
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, |
4dab160eb PM / OPP: Allow p... |
1861 1862 1863 |
int (*set_opp)(struct dev_pm_set_opp_data *data)) { struct opp_table *opp_table; |
4dab160eb PM / OPP: Allow p... |
1864 1865 |
if (!set_opp) |
fa30184d1 PM / OPP: Return ... |
1866 |
return ERR_PTR(-EINVAL); |
4dab160eb PM / OPP: Allow p... |
1867 |
|
fa30184d1 PM / OPP: Return ... |
1868 |
opp_table = dev_pm_opp_get_opp_table(dev); |
47efcbcb3 opp: Fix early ex... |
1869 |
if (IS_ERR(opp_table)) |
dd461cd91 opp: Allow dev_pm... |
1870 |
return opp_table; |
4dab160eb PM / OPP: Allow p... |
1871 1872 1873 |
/* This should be called before OPPs are initialized */ if (WARN_ON(!list_empty(&opp_table->opp_list))) { |
5019acc69 PM / OPP: Fix sha... |
1874 1875 |
dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(-EBUSY); |
4dab160eb PM / OPP: Allow p... |
1876 |
} |
5019acc69 PM / OPP: Fix sha... |
1877 1878 1879 |
/* Another CPU that shares the OPP table has set the helper ? */ if (!opp_table->set_opp) opp_table->set_opp = set_opp; |
4dab160eb PM / OPP: Allow p... |
1880 |
|
fa30184d1 PM / OPP: Return ... |
1881 |
return opp_table; |
4dab160eb PM / OPP: Allow p... |
1882 1883 1884 1885 |
} EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); /** |
604a7aeb4 PM / OPP: Rename ... |
1886 |
* dev_pm_opp_unregister_set_opp_helper() - Releases resources blocked for |
4dab160eb PM / OPP: Allow p... |
1887 |
* set_opp helper |
fa30184d1 PM / OPP: Return ... |
1888 |
* @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper(). |
4dab160eb PM / OPP: Allow p... |
1889 |
* |
fa30184d1 PM / OPP: Return ... |
1890 |
* Release resources blocked for platform specific set_opp helper. |
4dab160eb PM / OPP: Allow p... |
1891 |
*/ |
604a7aeb4 PM / OPP: Rename ... |
1892 |
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) |
4dab160eb PM / OPP: Allow p... |
1893 |
{ |
4dab160eb PM / OPP: Allow p... |
1894 1895 1896 1897 |
/* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); opp_table->set_opp = NULL; |
fa30184d1 PM / OPP: Return ... |
1898 |
dev_pm_opp_put_opp_table(opp_table); |
4dab160eb PM / OPP: Allow p... |
1899 |
} |
604a7aeb4 PM / OPP: Rename ... |
1900 |
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); |
4dab160eb PM / OPP: Allow p... |
1901 |
|
6319aee10 opp: Attach genpd... |
1902 1903 1904 |
static void _opp_detach_genpd(struct opp_table *opp_table) { int index; |
cb60e9602 opp: Prevent memo... |
1905 1906 |
if (!opp_table->genpd_virt_devs) return; |
6319aee10 opp: Attach genpd... |
1907 1908 1909 1910 1911 1912 1913 |
for (index = 0; index < opp_table->required_opp_count; index++) { if (!opp_table->genpd_virt_devs[index]) continue; dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false); opp_table->genpd_virt_devs[index] = NULL; } |
c0ab9e081 opp: Allocate gen... |
1914 1915 1916 |
kfree(opp_table->genpd_virt_devs); opp_table->genpd_virt_devs = NULL; |
6319aee10 opp: Attach genpd... |
1917 |
} |
4dab160eb PM / OPP: Allow p... |
1918 |
/** |
6319aee10 opp: Attach genpd... |
1919 1920 1921 |
* dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer * @dev: Consumer device for which the genpd is getting attached. * @names: Null terminated array of pointers containing names of genpd to attach. |
17a8f868a opp: Return genpd... |
1922 |
* @virt_devs: Pointer to return the array of virtual devices. |
4f018bc0e OPP: Add dev_pm_o... |
1923 1924 1925 1926 1927 |
* * Multiple generic power domains for a device are supported with the help of * virtual genpd devices, which are created for each consumer device - genpd * pair. These are the device structures which are attached to the power domain * and are required by the OPP core to set the performance state of the genpd. |
6319aee10 opp: Attach genpd... |
1928 1929 |
* The same API also works for the case where single genpd is available and so * we don't need to support that separately. |
4f018bc0e OPP: Add dev_pm_o... |
1930 1931 |
* * This helper will normally be called by the consumer driver of the device |
6319aee10 opp: Attach genpd... |
1932 |
* "dev", as only that has details of the genpd names. |
4f018bc0e OPP: Add dev_pm_o... |
1933 |
* |
6319aee10 opp: Attach genpd... |
1934 1935 |
* This helper needs to be called once with a list of all genpd to attach. * Otherwise the original device structure will be used instead by the OPP core. |
baea35e4d opp: Not all powe... |
1936 1937 1938 |
* * The order of entries in the names array must match the order in which * "required-opps" are added in DT. |
4f018bc0e OPP: Add dev_pm_o... |
1939 |
*/ |
17a8f868a opp: Return genpd... |
1940 1941 |
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) |
4f018bc0e OPP: Add dev_pm_o... |
1942 1943 |
{ struct opp_table *opp_table; |
6319aee10 opp: Attach genpd... |
1944 |
struct device *virt_dev; |
baea35e4d opp: Not all powe... |
1945 |
int index = 0, ret = -EINVAL; |
6319aee10 opp: Attach genpd... |
1946 |
const char **name = names; |
4f018bc0e OPP: Add dev_pm_o... |
1947 1948 |
opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
1949 1950 |
if (IS_ERR(opp_table)) return opp_table; |
4f018bc0e OPP: Add dev_pm_o... |
1951 |
|
cb60e9602 opp: Prevent memo... |
1952 1953 |
if (opp_table->genpd_virt_devs) return opp_table; |
4f018bc0e OPP: Add dev_pm_o... |
1954 |
|
6319aee10 opp: Attach genpd... |
1955 1956 1957 1958 1959 1960 1961 1962 1963 |
/* * If the genpd's OPP table isn't already initialized, parsing of the * required-opps fail for dev. We should retry this after genpd's OPP * table is added. */ if (!opp_table->required_opp_count) { ret = -EPROBE_DEFER; goto put_table; } |
4f018bc0e OPP: Add dev_pm_o... |
1964 |
mutex_lock(&opp_table->genpd_virt_dev_lock); |
c0ab9e081 opp: Allocate gen... |
1965 1966 1967 1968 1969 |
opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count, sizeof(*opp_table->genpd_virt_devs), GFP_KERNEL); if (!opp_table->genpd_virt_devs) goto unlock; |
4f018bc0e OPP: Add dev_pm_o... |
1970 |
|
6319aee10 opp: Attach genpd... |
1971 |
while (*name) { |
6319aee10 opp: Attach genpd... |
1972 1973 1974 1975 1976 1977 |
if (index >= opp_table->required_opp_count) { dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d) ", *name, opp_table->required_opp_count, index); goto err; } |
4f018bc0e OPP: Add dev_pm_o... |
1978 |
|
6319aee10 opp: Attach genpd... |
1979 1980 1981 1982 1983 1984 1985 1986 1987 |
virt_dev = dev_pm_domain_attach_by_name(dev, *name); if (IS_ERR(virt_dev)) { ret = PTR_ERR(virt_dev); dev_err(dev, "Couldn't attach to pm_domain: %d ", ret); goto err; } opp_table->genpd_virt_devs[index] = virt_dev; |
baea35e4d opp: Not all powe... |
1988 |
index++; |
6319aee10 opp: Attach genpd... |
1989 |
name++; |
4f018bc0e OPP: Add dev_pm_o... |
1990 |
} |
17a8f868a opp: Return genpd... |
1991 1992 |
if (virt_devs) *virt_devs = opp_table->genpd_virt_devs; |
4f018bc0e OPP: Add dev_pm_o... |
1993 1994 1995 |
mutex_unlock(&opp_table->genpd_virt_dev_lock); return opp_table; |
6319aee10 opp: Attach genpd... |
1996 1997 1998 |
err: _opp_detach_genpd(opp_table); |
c0ab9e081 opp: Allocate gen... |
1999 |
unlock: |
6319aee10 opp: Attach genpd... |
2000 2001 2002 2003 2004 2005 |
mutex_unlock(&opp_table->genpd_virt_dev_lock); put_table: dev_pm_opp_put_opp_table(opp_table); return ERR_PTR(ret); |
4f018bc0e OPP: Add dev_pm_o... |
2006 |
} |
6319aee10 opp: Attach genpd... |
2007 |
EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd); |
4f018bc0e OPP: Add dev_pm_o... |
2008 2009 |
/** |
6319aee10 opp: Attach genpd... |
2010 2011 |
* dev_pm_opp_detach_genpd() - Detach genpd(s) from the device. * @opp_table: OPP table returned by dev_pm_opp_attach_genpd(). |
4f018bc0e OPP: Add dev_pm_o... |
2012 |
* |
6319aee10 opp: Attach genpd... |
2013 2014 |
* This detaches the genpd(s), resets the virtual device pointers, and puts the * OPP table. |
4f018bc0e OPP: Add dev_pm_o... |
2015 |
*/ |
6319aee10 opp: Attach genpd... |
2016 |
void dev_pm_opp_detach_genpd(struct opp_table *opp_table) |
4f018bc0e OPP: Add dev_pm_o... |
2017 |
{ |
4f018bc0e OPP: Add dev_pm_o... |
2018 2019 2020 2021 2022 |
/* * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting * used in parallel. */ mutex_lock(&opp_table->genpd_virt_dev_lock); |
6319aee10 opp: Attach genpd... |
2023 |
_opp_detach_genpd(opp_table); |
4f018bc0e OPP: Add dev_pm_o... |
2024 |
mutex_unlock(&opp_table->genpd_virt_dev_lock); |
6319aee10 opp: Attach genpd... |
2025 |
dev_pm_opp_put_opp_table(opp_table); |
4f018bc0e OPP: Add dev_pm_o... |
2026 |
} |
6319aee10 opp: Attach genpd... |
2027 |
EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd); |
4f018bc0e OPP: Add dev_pm_o... |
2028 2029 |
/** |
c8a59103e OPP: Add dev_pm_o... |
2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 |
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table. * @src_table: OPP table which has dst_table as one of its required OPP table. * @dst_table: Required OPP table of the src_table. * @pstate: Current performance state of the src_table. * * This Returns pstate of the OPP (present in @dst_table) pointed out by the * "required-opps" property of the OPP (present in @src_table) which has * performance state set to @pstate. * * Return: Zero or positive performance state on success, otherwise negative * value on errors. */ int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate) { struct dev_pm_opp *opp; int dest_pstate = -EINVAL; int i; |
c8a59103e OPP: Add dev_pm_o... |
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 |
/* * Normally the src_table will have the "required_opps" property set to * point to one of the OPPs in the dst_table, but in some cases the * genpd and its master have one to one mapping of performance states * and so none of them have the "required-opps" property set. Return the * pstate of the src_table as it is in such cases. */ if (!src_table->required_opp_count) return pstate; for (i = 0; i < src_table->required_opp_count; i++) { if (src_table->required_opp_tables[i]->np == dst_table->np) break; } if (unlikely(i == src_table->required_opp_count)) { pr_err("%s: Couldn't find matching OPP table (%p: %p) ", __func__, src_table, dst_table); return -EINVAL; } mutex_lock(&src_table->lock); list_for_each_entry(opp, &src_table->opp_list, node) { if (opp->pstate == pstate) { dest_pstate = opp->required_opps[i]->pstate; goto unlock; } } pr_err("%s: Couldn't find matching OPP (%p: %p) ", __func__, src_table, dst_table); unlock: mutex_unlock(&src_table->lock); return dest_pstate; } /** |
38393409d PM / OPP mark OPP... |
2091 2092 2093 2094 2095 |
* dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP * @u_volt: Voltage in uVolts for this OPP * |
2c2709dc6 PM / OPP: Rename ... |
2096 |
* This function adds an opp definition to the opp table and returns status. |
38393409d PM / OPP mark OPP... |
2097 2098 2099 |
* The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions. * |
38393409d PM / OPP mark OPP... |
2100 |
* Return: |
984f16c84 PM / OPP: Update ... |
2101 |
* 0 On success OR |
38393409d PM / OPP mark OPP... |
2102 |
* Duplicate OPPs (both freq and volt are same) and opp->available |
984f16c84 PM / OPP: Update ... |
2103 |
* -EEXIST Freq are same and volt are different OR |
38393409d PM / OPP mark OPP... |
2104 |
* Duplicate OPPs (both freq and volt are same) and !opp->available |
984f16c84 PM / OPP: Update ... |
2105 |
* -ENOMEM Memory allocation failure |
38393409d PM / OPP mark OPP... |
2106 2107 2108 |
*/ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) { |
8cd2f6e8f PM / OPP: Don't a... |
2109 2110 |
struct opp_table *opp_table; int ret; |
b83c1899a PM / OPP: Use dev... |
2111 |
opp_table = dev_pm_opp_get_opp_table(dev); |
dd461cd91 opp: Allow dev_pm... |
2112 2113 |
if (IS_ERR(opp_table)) return PTR_ERR(opp_table); |
8cd2f6e8f PM / OPP: Don't a... |
2114 |
|
46f48aca2 OPP: Fix missing ... |
2115 2116 |
/* Fix regulator count for dynamic OPPs */ opp_table->regulator_count = 1; |
8cd2f6e8f PM / OPP: Don't a... |
2117 |
ret = _opp_add_v1(opp_table, dev, freq, u_volt, true); |
0ad8c6239 OPP: Don't take O... |
2118 2119 |
if (ret) dev_pm_opp_put_opp_table(opp_table); |
8cd2f6e8f PM / OPP: Don't a... |
2120 |
|
8cd2f6e8f PM / OPP: Don't a... |
2121 |
return ret; |
38393409d PM / OPP mark OPP... |
2122 |
} |
5d4879cda PM / OPP: rename ... |
2123 |
EXPORT_SYMBOL_GPL(dev_pm_opp_add); |
e1f60b292 PM: Introduce lib... |
2124 |
|
984f16c84 PM / OPP: Update ... |
2125 |
/** |
327854c87 PM / OPP: Ensure ... |
2126 |
* _opp_set_availability() - helper to set the availability of an opp |
e1f60b292 PM: Introduce lib... |
2127 2128 2129 2130 |
* @dev: device for which we do this operation * @freq: OPP frequency to modify availability * @availability_req: availability status requested for this opp * |
052c6f191 PM / OPP: Move aw... |
2131 2132 |
* Set the availability of an OPP, opp_{enable,disable} share a common logic * which is isolated here. |
e1f60b292 PM: Introduce lib... |
2133 |
* |
984f16c84 PM / OPP: Update ... |
2134 |
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the |
e1a2d49cd PM / OPP: Fix typ... |
2135 |
* copy operation, returns 0 if no modification was done OR modification was |
e1f60b292 PM: Introduce lib... |
2136 |
* successful. |
e1f60b292 PM: Introduce lib... |
2137 |
*/ |
327854c87 PM / OPP: Ensure ... |
2138 2139 |
static int _opp_set_availability(struct device *dev, unsigned long freq, bool availability_req) |
e1f60b292 PM: Introduce lib... |
2140 |
{ |
2c2709dc6 PM / OPP: Rename ... |
2141 |
struct opp_table *opp_table; |
a7f3987ea PM / OPP: Simplif... |
2142 |
struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV); |
e1f60b292 PM: Introduce lib... |
2143 |
int r = 0; |
2c2709dc6 PM / OPP: Rename ... |
2144 2145 2146 2147 |
/* Find the opp_table */ opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { r = PTR_ERR(opp_table); |
e1f60b292 PM: Introduce lib... |
2148 2149 |
dev_warn(dev, "%s: Device OPP not found (%d) ", __func__, r); |
a7f3987ea PM / OPP: Simplif... |
2150 |
return r; |
e1f60b292 PM: Introduce lib... |
2151 |
} |
37a73ec0c PM / OPP: Add per... |
2152 |
mutex_lock(&opp_table->lock); |
e1f60b292 PM: Introduce lib... |
2153 |
/* Do we have the frequency? */ |
2c2709dc6 PM / OPP: Rename ... |
2154 |
list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { |
e1f60b292 PM: Introduce lib... |
2155 2156 2157 2158 2159 |
if (tmp_opp->rate == freq) { opp = tmp_opp; break; } } |
37a73ec0c PM / OPP: Add per... |
2160 |
|
e1f60b292 PM: Introduce lib... |
2161 2162 2163 2164 2165 2166 2167 2168 |
if (IS_ERR(opp)) { r = PTR_ERR(opp); goto unlock; } /* Is update really needed? */ if (opp->available == availability_req) goto unlock; |
e1f60b292 PM: Introduce lib... |
2169 |
|
a7f3987ea PM / OPP: Simplif... |
2170 |
opp->available = availability_req; |
e1f60b292 PM: Introduce lib... |
2171 |
|
e4d8ae001 PM / OPP: Call no... |
2172 2173 |
dev_pm_opp_get(opp); mutex_unlock(&opp_table->lock); |
03ca370fb PM / OPP: Add OPP... |
2174 2175 |
/* Notify the change of the OPP availability */ if (availability_req) |
052c6f191 PM / OPP: Move aw... |
2176 |
blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ENABLE, |
a7f3987ea PM / OPP: Simplif... |
2177 |
opp); |
03ca370fb PM / OPP: Add OPP... |
2178 |
else |
052c6f191 PM / OPP: Move aw... |
2179 |
blocking_notifier_call_chain(&opp_table->head, |
a7f3987ea PM / OPP: Simplif... |
2180 |
OPP_EVENT_DISABLE, opp); |
e1f60b292 PM: Introduce lib... |
2181 |
|
e4d8ae001 PM / OPP: Call no... |
2182 2183 |
dev_pm_opp_put(opp); goto put_table; |
e1f60b292 PM: Introduce lib... |
2184 |
unlock: |
5b650b388 PM / OPP: Take kr... |
2185 |
mutex_unlock(&opp_table->lock); |
e4d8ae001 PM / OPP: Call no... |
2186 |
put_table: |
5b650b388 PM / OPP: Take kr... |
2187 |
dev_pm_opp_put_opp_table(opp_table); |
e1f60b292 PM: Introduce lib... |
2188 2189 2190 2191 |
return r; } /** |
25cb20a21 PM / OPP: Support... |
2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 |
* dev_pm_opp_adjust_voltage() - helper to change the voltage of an OPP * @dev: device for which we do this operation * @freq: OPP frequency to adjust voltage of * @u_volt: new OPP target voltage * @u_volt_min: new OPP min voltage * @u_volt_max: new OPP max voltage * * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modifcation was done OR modification was * successful. */ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, unsigned long u_volt, unsigned long u_volt_min, unsigned long u_volt_max) { struct opp_table *opp_table; struct dev_pm_opp *tmp_opp, *opp = ERR_PTR(-ENODEV); int r = 0; /* Find the opp_table */ opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { r = PTR_ERR(opp_table); dev_warn(dev, "%s: Device OPP not found (%d) ", __func__, r); return r; } mutex_lock(&opp_table->lock); /* Do we have the frequency? */ list_for_each_entry(tmp_opp, &opp_table->opp_list, node) { if (tmp_opp->rate == freq) { opp = tmp_opp; break; } } if (IS_ERR(opp)) { r = PTR_ERR(opp); goto adjust_unlock; } /* Is update really needed? */ if (opp->supplies->u_volt == u_volt) goto adjust_unlock; opp->supplies->u_volt = u_volt; opp->supplies->u_volt_min = u_volt_min; opp->supplies->u_volt_max = u_volt_max; dev_pm_opp_get(opp); mutex_unlock(&opp_table->lock); /* Notify the voltage change of the OPP */ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADJUST_VOLTAGE, opp); dev_pm_opp_put(opp); goto adjust_put_table; adjust_unlock: mutex_unlock(&opp_table->lock); adjust_put_table: dev_pm_opp_put_opp_table(opp_table); return r; } |
036491542 opp: core: Add mi... |
2260 |
EXPORT_SYMBOL_GPL(dev_pm_opp_adjust_voltage); |
25cb20a21 PM / OPP: Support... |
2261 2262 |
/** |
5d4879cda PM / OPP: rename ... |
2263 |
* dev_pm_opp_enable() - Enable a specific OPP |
e1f60b292 PM: Introduce lib... |
2264 2265 2266 2267 2268 |
* @dev: device for which we do this operation * @freq: OPP frequency to enable * * Enables a provided opp. If the operation is valid, this returns 0, else the * corresponding error value. It is meant to be used for users an OPP available |
5d4879cda PM / OPP: rename ... |
2269 |
* after being temporarily made unavailable with dev_pm_opp_disable. |
e1f60b292 PM: Introduce lib... |
2270 |
* |
984f16c84 PM / OPP: Update ... |
2271 |
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the |
e1a2d49cd PM / OPP: Fix typ... |
2272 |
* copy operation, returns 0 if no modification was done OR modification was |
984f16c84 PM / OPP: Update ... |
2273 |
* successful. |
e1f60b292 PM: Introduce lib... |
2274 |
*/ |
5d4879cda PM / OPP: rename ... |
2275 |
int dev_pm_opp_enable(struct device *dev, unsigned long freq) |
e1f60b292 PM: Introduce lib... |
2276 |
{ |
327854c87 PM / OPP: Ensure ... |
2277 |
return _opp_set_availability(dev, freq, true); |
e1f60b292 PM: Introduce lib... |
2278 |
} |
5d4879cda PM / OPP: rename ... |
2279 |
EXPORT_SYMBOL_GPL(dev_pm_opp_enable); |
e1f60b292 PM: Introduce lib... |
2280 2281 |
/** |
5d4879cda PM / OPP: rename ... |
2282 |
* dev_pm_opp_disable() - Disable a specific OPP |
e1f60b292 PM: Introduce lib... |
2283 2284 2285 2286 2287 2288 |
* @dev: device for which we do this operation * @freq: OPP frequency to disable * * Disables a provided opp. If the operation is valid, this returns * 0, else the corresponding error value. It is meant to be a temporary * control by users to make this OPP not available until the circumstances are |
5d4879cda PM / OPP: rename ... |
2289 |
* right to make it available again (with a call to dev_pm_opp_enable). |
e1f60b292 PM: Introduce lib... |
2290 |
* |
984f16c84 PM / OPP: Update ... |
2291 |
* Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the |
e1a2d49cd PM / OPP: Fix typ... |
2292 |
* copy operation, returns 0 if no modification was done OR modification was |
984f16c84 PM / OPP: Update ... |
2293 |
* successful. |
e1f60b292 PM: Introduce lib... |
2294 |
*/ |
5d4879cda PM / OPP: rename ... |
2295 |
int dev_pm_opp_disable(struct device *dev, unsigned long freq) |
e1f60b292 PM: Introduce lib... |
2296 |
{ |
327854c87 PM / OPP: Ensure ... |
2297 |
return _opp_set_availability(dev, freq, false); |
e1f60b292 PM: Introduce lib... |
2298 |
} |
5d4879cda PM / OPP: rename ... |
2299 |
EXPORT_SYMBOL_GPL(dev_pm_opp_disable); |
e1f60b292 PM: Introduce lib... |
2300 |
|
03ca370fb PM / OPP: Add OPP... |
2301 |
/** |
dc2c9ad52 PM / OPP: Don't e... |
2302 2303 2304 |
* dev_pm_opp_register_notifier() - Register OPP notifier for the device * @dev: Device for which notifier needs to be registered * @nb: Notifier block to be registered |
984f16c84 PM / OPP: Update ... |
2305 |
* |
dc2c9ad52 PM / OPP: Don't e... |
2306 2307 2308 2309 2310 2311 |
* Return: 0 on success or a negative error value. */ int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb) { struct opp_table *opp_table; int ret; |
dc2c9ad52 PM / OPP: Don't e... |
2312 |
opp_table = _find_opp_table(dev); |
5b650b388 PM / OPP: Take kr... |
2313 2314 |
if (IS_ERR(opp_table)) return PTR_ERR(opp_table); |
052c6f191 PM / OPP: Move aw... |
2315 |
ret = blocking_notifier_chain_register(&opp_table->head, nb); |
dc2c9ad52 PM / OPP: Don't e... |
2316 |
|
5b650b388 PM / OPP: Take kr... |
2317 |
dev_pm_opp_put_opp_table(opp_table); |
dc2c9ad52 PM / OPP: Don't e... |
2318 2319 2320 2321 2322 2323 2324 2325 2326 |
return ret; } EXPORT_SYMBOL(dev_pm_opp_register_notifier); /** * dev_pm_opp_unregister_notifier() - Unregister OPP notifier for the device * @dev: Device for which notifier needs to be unregistered * @nb: Notifier block to be unregistered |
984f16c84 PM / OPP: Update ... |
2327 |
* |
dc2c9ad52 PM / OPP: Don't e... |
2328 |
* Return: 0 on success or a negative error value. |
03ca370fb PM / OPP: Add OPP... |
2329 |
*/ |
dc2c9ad52 PM / OPP: Don't e... |
2330 2331 |
int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb) |
03ca370fb PM / OPP: Add OPP... |
2332 |
{ |
dc2c9ad52 PM / OPP: Don't e... |
2333 2334 |
struct opp_table *opp_table; int ret; |
03ca370fb PM / OPP: Add OPP... |
2335 |
|
dc2c9ad52 PM / OPP: Don't e... |
2336 |
opp_table = _find_opp_table(dev); |
5b650b388 PM / OPP: Take kr... |
2337 2338 |
if (IS_ERR(opp_table)) return PTR_ERR(opp_table); |
dc2c9ad52 PM / OPP: Don't e... |
2339 |
|
052c6f191 PM / OPP: Move aw... |
2340 |
ret = blocking_notifier_chain_unregister(&opp_table->head, nb); |
03ca370fb PM / OPP: Add OPP... |
2341 |
|
5b650b388 PM / OPP: Take kr... |
2342 |
dev_pm_opp_put_opp_table(opp_table); |
dc2c9ad52 PM / OPP: Don't e... |
2343 2344 |
return ret; |
03ca370fb PM / OPP: Add OPP... |
2345 |
} |
dc2c9ad52 PM / OPP: Don't e... |
2346 |
EXPORT_SYMBOL(dev_pm_opp_unregister_notifier); |
b496dfbc9 PM / OPP: Initial... |
2347 |
|
8aaf6264f opp: Remove _dev_... |
2348 2349 2350 2351 2352 2353 2354 2355 |
/** * dev_pm_opp_remove_table() - Free all OPPs associated with the device * @dev: device pointer used to lookup OPP table. * * Free both OPPs created using static entries present in DT and the * dynamically added entries. */ void dev_pm_opp_remove_table(struct device *dev) |
9274c8924 PM / OPP: Rename ... |
2356 2357 |
{ struct opp_table *opp_table; |
2c2709dc6 PM / OPP: Rename ... |
2358 2359 2360 2361 |
/* Check for existing table for 'dev' */ opp_table = _find_opp_table(dev); if (IS_ERR(opp_table)) { int error = PTR_ERR(opp_table); |
737002b5d PM / OPP: Relocat... |
2362 2363 |
if (error != -ENODEV) |
2c2709dc6 PM / OPP: Rename ... |
2364 2365 |
WARN(1, "%s: opp_table: %d ", |
737002b5d PM / OPP: Relocat... |
2366 2367 2368 |
IS_ERR_OR_NULL(dev) ? "Invalid device" : dev_name(dev), error); |
5b650b388 PM / OPP: Take kr... |
2369 |
return; |
737002b5d PM / OPP: Relocat... |
2370 |
} |
922ff0759 opp: Don't drop r... |
2371 2372 2373 2374 2375 2376 |
/* * Drop the extra reference only if the OPP table was successfully added * with dev_pm_opp_of_add_table() earlier. **/ if (_opp_remove_all_static(opp_table)) dev_pm_opp_put_opp_table(opp_table); |
cdd6ed90c OPP: Use a single... |
2377 2378 2379 |
/* Drop reference taken by _find_opp_table() */ dev_pm_opp_put_opp_table(opp_table); |
737002b5d PM / OPP: Relocat... |
2380 |
} |
411466c50 PM / OPP: add non... |
2381 |
EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table); |