diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2016-01-25 23:21:26 -0500 |
---|---|---|
committer | MyungJoo Ham <myungjoo.ham@samsung.com> | 2016-05-02 22:20:07 -0400 |
commit | 0fe3a66410a3ba96679be903f1e287d7a0a264a9 (patch) | |
tree | 1ce223e8508fce15daebc93b6ce511721e418fc9 | |
parent | 8f510aeb223b26c4ffbece9fa92e4befea470f57 (diff) |
PM / devfreq: Add new DEVFREQ_TRANSITION_NOTIFIER notifier
This patch adds the new DEVFREQ_TRANSITION_NOTIFIER notifier to send
the notification when the frequency of device is changed.
This notifier has two state as following:
- DEVFREQ_PRECHANGE : Notify it before chaning the frequency of device
- DEVFREQ_POSTCHANGE : Notify it after changed the frequency of device
And this patch adds the resourced-managed function to release the resource
automatically when error happen.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
[m.reichl and linux.amoon: Tested it on exynos4412-odroidu3 board]
Tested-by: Markus Reichl <m.reichl@fivetechno.de>
Tested-by: Anand Moon <linux.amoon@gmail.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Acked-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
-rw-r--r-- | drivers/devfreq/devfreq.c | 163 | ||||
-rw-r--r-- | include/linux/devfreq.h | 59 |
2 files changed, 220 insertions, 2 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 20a9422c2552..1d6c803804d5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -189,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
189 | return ERR_PTR(-ENODEV); | 189 | return ERR_PTR(-ENODEV); |
190 | } | 190 | } |
191 | 191 | ||
192 | static int devfreq_notify_transition(struct devfreq *devfreq, | ||
193 | struct devfreq_freqs *freqs, unsigned int state) | ||
194 | { | ||
195 | if (!devfreq) | ||
196 | return -EINVAL; | ||
197 | |||
198 | switch (state) { | ||
199 | case DEVFREQ_PRECHANGE: | ||
200 | srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||
201 | DEVFREQ_PRECHANGE, freqs); | ||
202 | break; | ||
203 | |||
204 | case DEVFREQ_POSTCHANGE: | ||
205 | srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||
206 | DEVFREQ_POSTCHANGE, freqs); | ||
207 | break; | ||
208 | default: | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
192 | /* Load monitoring helper functions for governors use */ | 215 | /* Load monitoring helper functions for governors use */ |
193 | 216 | ||
194 | /** | 217 | /** |
@@ -200,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
200 | */ | 223 | */ |
201 | int update_devfreq(struct devfreq *devfreq) | 224 | int update_devfreq(struct devfreq *devfreq) |
202 | { | 225 | { |
203 | unsigned long freq; | 226 | struct devfreq_freqs freqs; |
227 | unsigned long freq, cur_freq; | ||
204 | int err = 0; | 228 | int err = 0; |
205 | u32 flags = 0; | 229 | u32 flags = 0; |
206 | 230 | ||
@@ -234,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq) | |||
234 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ | 258 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ |
235 | } | 259 | } |
236 | 260 | ||
261 | if (devfreq->profile->get_cur_freq) | ||
262 | devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); | ||
263 | else | ||
264 | cur_freq = devfreq->previous_freq; | ||
265 | |||
266 | freqs.old = cur_freq; | ||
267 | freqs.new = freq; | ||
268 | devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); | ||
269 | |||
237 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); | 270 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); |
238 | if (err) | 271 | if (err) |
239 | return err; | 272 | return err; |
240 | 273 | ||
274 | freqs.new = freq; | ||
275 | devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); | ||
276 | |||
241 | if (devfreq->profile->freq_table) | 277 | if (devfreq->profile->freq_table) |
242 | if (devfreq_update_status(devfreq, freq)) | 278 | if (devfreq_update_status(devfreq, freq)) |
243 | dev_err(&devfreq->dev, | 279 | dev_err(&devfreq->dev, |
@@ -542,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
542 | goto err_out; | 578 | goto err_out; |
543 | } | 579 | } |
544 | 580 | ||
581 | srcu_init_notifier_head(&devfreq->transition_notifier_list); | ||
582 | |||
545 | mutex_unlock(&devfreq->lock); | 583 | mutex_unlock(&devfreq->lock); |
546 | 584 | ||
547 | mutex_lock(&devfreq_list_lock); | 585 | mutex_lock(&devfreq_list_lock); |
@@ -1310,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
1310 | } | 1348 | } |
1311 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); | 1349 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); |
1312 | 1350 | ||
1351 | /** | ||
1352 | * devfreq_register_notifier() - Register a driver with devfreq | ||
1353 | * @devfreq: The devfreq object. | ||
1354 | * @nb: The notifier block to register. | ||
1355 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1356 | */ | ||
1357 | int devfreq_register_notifier(struct devfreq *devfreq, | ||
1358 | struct notifier_block *nb, | ||
1359 | unsigned int list) | ||
1360 | { | ||
1361 | int ret = 0; | ||
1362 | |||
1363 | if (!devfreq) | ||
1364 | return -EINVAL; | ||
1365 | |||
1366 | switch (list) { | ||
1367 | case DEVFREQ_TRANSITION_NOTIFIER: | ||
1368 | ret = srcu_notifier_chain_register( | ||
1369 | &devfreq->transition_notifier_list, nb); | ||
1370 | break; | ||
1371 | default: | ||
1372 | ret = -EINVAL; | ||
1373 | } | ||
1374 | |||
1375 | return ret; | ||
1376 | } | ||
1377 | EXPORT_SYMBOL(devfreq_register_notifier); | ||
1378 | |||
1379 | /* | ||
1380 | * devfreq_unregister_notifier() - Unregister a driver with devfreq | ||
1381 | * @devfreq: The devfreq object. | ||
1382 | * @nb: The notifier block to be unregistered. | ||
1383 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1384 | */ | ||
1385 | int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
1386 | struct notifier_block *nb, | ||
1387 | unsigned int list) | ||
1388 | { | ||
1389 | int ret = 0; | ||
1390 | |||
1391 | if (!devfreq) | ||
1392 | return -EINVAL; | ||
1393 | |||
1394 | switch (list) { | ||
1395 | case DEVFREQ_TRANSITION_NOTIFIER: | ||
1396 | ret = srcu_notifier_chain_unregister( | ||
1397 | &devfreq->transition_notifier_list, nb); | ||
1398 | break; | ||
1399 | default: | ||
1400 | ret = -EINVAL; | ||
1401 | } | ||
1402 | |||
1403 | return ret; | ||
1404 | } | ||
1405 | EXPORT_SYMBOL(devfreq_unregister_notifier); | ||
1406 | |||
1407 | struct devfreq_notifier_devres { | ||
1408 | struct devfreq *devfreq; | ||
1409 | struct notifier_block *nb; | ||
1410 | unsigned int list; | ||
1411 | }; | ||
1412 | |||
1413 | static void devm_devfreq_notifier_release(struct device *dev, void *res) | ||
1414 | { | ||
1415 | struct devfreq_notifier_devres *this = res; | ||
1416 | |||
1417 | devfreq_unregister_notifier(this->devfreq, this->nb, this->list); | ||
1418 | } | ||
1419 | |||
1420 | /** | ||
1421 | * devm_devfreq_register_notifier() | ||
1422 | - Resource-managed devfreq_register_notifier() | ||
1423 | * @dev: The devfreq user device. (parent of devfreq) | ||
1424 | * @devfreq: The devfreq object. | ||
1425 | * @nb: The notifier block to be unregistered. | ||
1426 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1427 | */ | ||
1428 | int devm_devfreq_register_notifier(struct device *dev, | ||
1429 | struct devfreq *devfreq, | ||
1430 | struct notifier_block *nb, | ||
1431 | unsigned int list) | ||
1432 | { | ||
1433 | struct devfreq_notifier_devres *ptr; | ||
1434 | int ret; | ||
1435 | |||
1436 | ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), | ||
1437 | GFP_KERNEL); | ||
1438 | if (!ptr) | ||
1439 | return -ENOMEM; | ||
1440 | |||
1441 | ret = devfreq_register_notifier(devfreq, nb, list); | ||
1442 | if (ret) { | ||
1443 | devres_free(ptr); | ||
1444 | return ret; | ||
1445 | } | ||
1446 | |||
1447 | ptr->devfreq = devfreq; | ||
1448 | ptr->nb = nb; | ||
1449 | ptr->list = list; | ||
1450 | devres_add(dev, ptr); | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | EXPORT_SYMBOL(devm_devfreq_register_notifier); | ||
1455 | |||
1456 | /** | ||
1457 | * devm_devfreq_unregister_notifier() | ||
1458 | - Resource-managed devfreq_unregister_notifier() | ||
1459 | * @dev: The devfreq user device. (parent of devfreq) | ||
1460 | * @devfreq: The devfreq object. | ||
1461 | * @nb: The notifier block to be unregistered. | ||
1462 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1463 | */ | ||
1464 | void devm_devfreq_unregister_notifier(struct device *dev, | ||
1465 | struct devfreq *devfreq, | ||
1466 | struct notifier_block *nb, | ||
1467 | unsigned int list) | ||
1468 | { | ||
1469 | WARN_ON(devres_release(dev, devm_devfreq_notifier_release, | ||
1470 | devm_devfreq_dev_match, devfreq)); | ||
1471 | } | ||
1472 | EXPORT_SYMBOL(devm_devfreq_unregister_notifier); | ||
1473 | |||
1313 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1474 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
1314 | MODULE_DESCRIPTION("devfreq class support"); | 1475 | MODULE_DESCRIPTION("devfreq class support"); |
1315 | MODULE_LICENSE("GPL"); | 1476 | MODULE_LICENSE("GPL"); |
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index aa0b8424ebc3..98c699304e12 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -19,6 +19,13 @@ | |||
19 | 19 | ||
20 | #define DEVFREQ_NAME_LEN 16 | 20 | #define DEVFREQ_NAME_LEN 16 |
21 | 21 | ||
22 | /* DEVFREQ notifier interface */ | ||
23 | #define DEVFREQ_TRANSITION_NOTIFIER (0) | ||
24 | |||
25 | /* Transition notifiers of DEVFREQ_TRANSITION_NOTIFIER */ | ||
26 | #define DEVFREQ_PRECHANGE (0) | ||
27 | #define DEVFREQ_POSTCHANGE (1) | ||
28 | |||
22 | struct devfreq; | 29 | struct devfreq; |
23 | 30 | ||
24 | /** | 31 | /** |
@@ -143,6 +150,7 @@ struct devfreq_governor { | |||
143 | * @trans_table: Statistics of devfreq transitions | 150 | * @trans_table: Statistics of devfreq transitions |
144 | * @time_in_state: Statistics of devfreq states | 151 | * @time_in_state: Statistics of devfreq states |
145 | * @last_stat_updated: The last time stat updated | 152 | * @last_stat_updated: The last time stat updated |
153 | * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier | ||
146 | * | 154 | * |
147 | * This structure stores the devfreq information for a give device. | 155 | * This structure stores the devfreq information for a give device. |
148 | * | 156 | * |
@@ -177,6 +185,13 @@ struct devfreq { | |||
177 | unsigned int *trans_table; | 185 | unsigned int *trans_table; |
178 | unsigned long *time_in_state; | 186 | unsigned long *time_in_state; |
179 | unsigned long last_stat_updated; | 187 | unsigned long last_stat_updated; |
188 | |||
189 | struct srcu_notifier_head transition_notifier_list; | ||
190 | }; | ||
191 | |||
192 | struct devfreq_freqs { | ||
193 | unsigned long old; | ||
194 | unsigned long new; | ||
180 | }; | 195 | }; |
181 | 196 | ||
182 | #if defined(CONFIG_PM_DEVFREQ) | 197 | #if defined(CONFIG_PM_DEVFREQ) |
@@ -207,7 +222,20 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev, | |||
207 | struct devfreq *devfreq); | 222 | struct devfreq *devfreq); |
208 | extern void devm_devfreq_unregister_opp_notifier(struct device *dev, | 223 | extern void devm_devfreq_unregister_opp_notifier(struct device *dev, |
209 | struct devfreq *devfreq); | 224 | struct devfreq *devfreq); |
210 | 225 | extern int devfreq_register_notifier(struct devfreq *devfreq, | |
226 | struct notifier_block *nb, | ||
227 | unsigned int list); | ||
228 | extern int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
229 | struct notifier_block *nb, | ||
230 | unsigned int list); | ||
231 | extern int devm_devfreq_register_notifier(struct device *dev, | ||
232 | struct devfreq *devfreq, | ||
233 | struct notifier_block *nb, | ||
234 | unsigned int list); | ||
235 | extern void devm_devfreq_unregister_notifier(struct device *dev, | ||
236 | struct devfreq *devfreq, | ||
237 | struct notifier_block *nb, | ||
238 | unsigned int list); | ||
211 | extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | 239 | extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, |
212 | int index); | 240 | int index); |
213 | 241 | ||
@@ -310,6 +338,35 @@ static inline void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
310 | { | 338 | { |
311 | } | 339 | } |
312 | 340 | ||
341 | static inline int devfreq_register_notifier(struct devfreq *devfreq, | ||
342 | struct notifier_block *nb, | ||
343 | unsigned int list) | ||
344 | { | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static inline int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
349 | struct notifier_block *nb, | ||
350 | unsigned int list) | ||
351 | { | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static inline int devm_devfreq_register_notifier(struct device *dev, | ||
356 | struct devfreq *devfreq, | ||
357 | struct notifier_block *nb, | ||
358 | unsigned int list) | ||
359 | { | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static inline void devm_devfreq_unregister_notifier(struct device *dev, | ||
364 | struct devfreq *devfreq, | ||
365 | struct notifier_block *nb, | ||
366 | unsigned int list) | ||
367 | { | ||
368 | } | ||
369 | |||
313 | static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | 370 | static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, |
314 | int index) | 371 | int index) |
315 | { | 372 | { |