diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r-- | drivers/devfreq/devfreq.c | 207 |
1 files changed, 206 insertions, 1 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 984c5e9e7bdd..1d6c803804d5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/list.h> | 25 | #include <linux/list.h> |
26 | #include <linux/printk.h> | 26 | #include <linux/printk.h> |
27 | #include <linux/hrtimer.h> | 27 | #include <linux/hrtimer.h> |
28 | #include <linux/of.h> | ||
28 | #include "governor.h" | 29 | #include "governor.h" |
29 | 30 | ||
30 | static struct class *devfreq_class; | 31 | static struct class *devfreq_class; |
@@ -188,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
188 | return ERR_PTR(-ENODEV); | 189 | return ERR_PTR(-ENODEV); |
189 | } | 190 | } |
190 | 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 | |||
191 | /* Load monitoring helper functions for governors use */ | 215 | /* Load monitoring helper functions for governors use */ |
192 | 216 | ||
193 | /** | 217 | /** |
@@ -199,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
199 | */ | 223 | */ |
200 | int update_devfreq(struct devfreq *devfreq) | 224 | int update_devfreq(struct devfreq *devfreq) |
201 | { | 225 | { |
202 | unsigned long freq; | 226 | struct devfreq_freqs freqs; |
227 | unsigned long freq, cur_freq; | ||
203 | int err = 0; | 228 | int err = 0; |
204 | u32 flags = 0; | 229 | u32 flags = 0; |
205 | 230 | ||
@@ -233,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq) | |||
233 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ | 258 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ |
234 | } | 259 | } |
235 | 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 | |||
236 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); | 270 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); |
237 | if (err) | 271 | if (err) |
238 | return err; | 272 | return err; |
239 | 273 | ||
274 | freqs.new = freq; | ||
275 | devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); | ||
276 | |||
240 | if (devfreq->profile->freq_table) | 277 | if (devfreq->profile->freq_table) |
241 | if (devfreq_update_status(devfreq, freq)) | 278 | if (devfreq_update_status(devfreq, freq)) |
242 | dev_err(&devfreq->dev, | 279 | dev_err(&devfreq->dev, |
@@ -541,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
541 | goto err_out; | 578 | goto err_out; |
542 | } | 579 | } |
543 | 580 | ||
581 | srcu_init_notifier_head(&devfreq->transition_notifier_list); | ||
582 | |||
544 | mutex_unlock(&devfreq->lock); | 583 | mutex_unlock(&devfreq->lock); |
545 | 584 | ||
546 | mutex_lock(&devfreq_list_lock); | 585 | mutex_lock(&devfreq_list_lock); |
@@ -639,6 +678,49 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, | |||
639 | } | 678 | } |
640 | EXPORT_SYMBOL(devm_devfreq_add_device); | 679 | EXPORT_SYMBOL(devm_devfreq_add_device); |
641 | 680 | ||
681 | #ifdef CONFIG_OF | ||
682 | /* | ||
683 | * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree | ||
684 | * @dev - instance to the given device | ||
685 | * @index - index into list of devfreq | ||
686 | * | ||
687 | * return the instance of devfreq device | ||
688 | */ | ||
689 | struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) | ||
690 | { | ||
691 | struct device_node *node; | ||
692 | struct devfreq *devfreq; | ||
693 | |||
694 | if (!dev) | ||
695 | return ERR_PTR(-EINVAL); | ||
696 | |||
697 | if (!dev->of_node) | ||
698 | return ERR_PTR(-EINVAL); | ||
699 | |||
700 | node = of_parse_phandle(dev->of_node, "devfreq", index); | ||
701 | if (!node) | ||
702 | return ERR_PTR(-ENODEV); | ||
703 | |||
704 | mutex_lock(&devfreq_list_lock); | ||
705 | list_for_each_entry(devfreq, &devfreq_list, node) { | ||
706 | if (devfreq->dev.parent | ||
707 | && devfreq->dev.parent->of_node == node) { | ||
708 | mutex_unlock(&devfreq_list_lock); | ||
709 | return devfreq; | ||
710 | } | ||
711 | } | ||
712 | mutex_unlock(&devfreq_list_lock); | ||
713 | |||
714 | return ERR_PTR(-EPROBE_DEFER); | ||
715 | } | ||
716 | #else | ||
717 | struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) | ||
718 | { | ||
719 | return ERR_PTR(-ENODEV); | ||
720 | } | ||
721 | #endif /* CONFIG_OF */ | ||
722 | EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); | ||
723 | |||
642 | /** | 724 | /** |
643 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() | 725 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() |
644 | * @dev: the device to add devfreq feature. | 726 | * @dev: the device to add devfreq feature. |
@@ -1266,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
1266 | } | 1348 | } |
1267 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); | 1349 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); |
1268 | 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 | |||
1269 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1474 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
1270 | MODULE_DESCRIPTION("devfreq class support"); | 1475 | MODULE_DESCRIPTION("devfreq class support"); |
1271 | MODULE_LICENSE("GPL"); | 1476 | MODULE_LICENSE("GPL"); |