diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-12-01 05:58:20 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-12-05 20:27:59 -0500 |
commit | 947355850fcb3bb6549294316667d0f53bc03082 (patch) | |
tree | 2e70450b998e003395897fbc13ec3ab3af55f883 | |
parent | dfbe4678d709e25e0f36e6b6333e2a7a67aefb7e (diff) |
PM / OPP: Separate out _generic_set_opp()
Later patches would add support for custom set_opp() callbacks. This
patch separates out the code for _generic_set_opp() handler in order to
prepare for that.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Tested-by: Dave Gerlach <d-gerlach@ti.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/opp/core.c | 181 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 3 | ||||
-rw-r--r-- | include/linux/pm_opp.h | 35 |
3 files changed, 166 insertions, 53 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index b4da31c5a5eb..e33198ce41b4 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c | |||
@@ -610,6 +610,69 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, | |||
610 | return ret; | 610 | return ret; |
611 | } | 611 | } |
612 | 612 | ||
613 | static inline int | ||
614 | _generic_set_opp_clk_only(struct device *dev, struct clk *clk, | ||
615 | unsigned long old_freq, unsigned long freq) | ||
616 | { | ||
617 | int ret; | ||
618 | |||
619 | ret = clk_set_rate(clk, freq); | ||
620 | if (ret) { | ||
621 | dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, | ||
622 | ret); | ||
623 | } | ||
624 | |||
625 | return ret; | ||
626 | } | ||
627 | |||
628 | static int _generic_set_opp(struct dev_pm_set_opp_data *data) | ||
629 | { | ||
630 | struct dev_pm_opp_supply *old_supply = data->old_opp.supplies; | ||
631 | struct dev_pm_opp_supply *new_supply = data->new_opp.supplies; | ||
632 | unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; | ||
633 | struct regulator *reg = data->regulators[0]; | ||
634 | struct device *dev= data->dev; | ||
635 | int ret; | ||
636 | |||
637 | /* This function only supports single regulator per device */ | ||
638 | if (WARN_ON(data->regulator_count > 1)) { | ||
639 | dev_err(dev, "multiple regulators are not supported\n"); | ||
640 | return -EINVAL; | ||
641 | } | ||
642 | |||
643 | /* Scaling up? Scale voltage before frequency */ | ||
644 | if (freq > old_freq) { | ||
645 | ret = _set_opp_voltage(dev, reg, new_supply); | ||
646 | if (ret) | ||
647 | goto restore_voltage; | ||
648 | } | ||
649 | |||
650 | /* Change frequency */ | ||
651 | ret = _generic_set_opp_clk_only(dev, data->clk, old_freq, freq); | ||
652 | if (ret) | ||
653 | goto restore_voltage; | ||
654 | |||
655 | /* Scaling down? Scale voltage after frequency */ | ||
656 | if (freq < old_freq) { | ||
657 | ret = _set_opp_voltage(dev, reg, new_supply); | ||
658 | if (ret) | ||
659 | goto restore_freq; | ||
660 | } | ||
661 | |||
662 | return 0; | ||
663 | |||
664 | restore_freq: | ||
665 | if (_generic_set_opp_clk_only(dev, data->clk, freq, old_freq)) | ||
666 | dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", | ||
667 | __func__, old_freq); | ||
668 | restore_voltage: | ||
669 | /* This shouldn't harm even if the voltages weren't updated earlier */ | ||
670 | if (old_supply->u_volt) | ||
671 | _set_opp_voltage(dev, reg, old_supply); | ||
672 | |||
673 | return ret; | ||
674 | } | ||
675 | |||
613 | /** | 676 | /** |
614 | * dev_pm_opp_set_rate() - Configure new OPP based on frequency | 677 | * dev_pm_opp_set_rate() - Configure new OPP based on frequency |
615 | * @dev: device for which we do this operation | 678 | * @dev: device for which we do this operation |
@@ -623,12 +686,12 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg, | |||
623 | int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) | 686 | int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) |
624 | { | 687 | { |
625 | struct opp_table *opp_table; | 688 | struct opp_table *opp_table; |
689 | unsigned long freq, old_freq; | ||
626 | struct dev_pm_opp *old_opp, *opp; | 690 | struct dev_pm_opp *old_opp, *opp; |
627 | struct regulator *reg = ERR_PTR(-ENXIO); | 691 | struct regulator **regulators; |
692 | struct dev_pm_set_opp_data *data; | ||
628 | struct clk *clk; | 693 | struct clk *clk; |
629 | unsigned long freq, old_freq; | 694 | int ret, size; |
630 | struct dev_pm_opp_supply old_supply, new_supply; | ||
631 | int ret; | ||
632 | 695 | ||
633 | if (unlikely(!target_freq)) { | 696 | if (unlikely(!target_freq)) { |
634 | dev_err(dev, "%s: Invalid target frequency %lu\n", __func__, | 697 | dev_err(dev, "%s: Invalid target frequency %lu\n", __func__, |
@@ -677,64 +740,36 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) | |||
677 | return ret; | 740 | return ret; |
678 | } | 741 | } |
679 | 742 | ||
680 | if (opp_table->regulators) { | 743 | dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__, |
681 | /* This function only supports single regulator per device */ | 744 | old_freq, freq); |
682 | if (WARN_ON(opp_table->regulator_count > 1)) { | ||
683 | dev_err(dev, "multiple regulators not supported\n"); | ||
684 | rcu_read_unlock(); | ||
685 | return -EINVAL; | ||
686 | } | ||
687 | 745 | ||
688 | reg = opp_table->regulators[0]; | 746 | regulators = opp_table->regulators; |
747 | |||
748 | /* Only frequency scaling */ | ||
749 | if (!regulators) { | ||
750 | rcu_read_unlock(); | ||
751 | return _generic_set_opp_clk_only(dev, clk, old_freq, freq); | ||
689 | } | 752 | } |
690 | 753 | ||
754 | data = opp_table->set_opp_data; | ||
755 | data->regulators = regulators; | ||
756 | data->regulator_count = opp_table->regulator_count; | ||
757 | data->clk = clk; | ||
758 | data->dev = dev; | ||
759 | |||
760 | data->old_opp.rate = old_freq; | ||
761 | size = sizeof(*opp->supplies) * opp_table->regulator_count; | ||
691 | if (IS_ERR(old_opp)) | 762 | if (IS_ERR(old_opp)) |
692 | old_supply.u_volt = 0; | 763 | memset(data->old_opp.supplies, 0, size); |
693 | else | 764 | else |
694 | memcpy(&old_supply, old_opp->supplies, sizeof(old_supply)); | 765 | memcpy(data->old_opp.supplies, old_opp->supplies, size); |
695 | 766 | ||
696 | memcpy(&new_supply, opp->supplies, sizeof(new_supply)); | 767 | data->new_opp.rate = freq; |
768 | memcpy(data->new_opp.supplies, opp->supplies, size); | ||
697 | 769 | ||
698 | rcu_read_unlock(); | 770 | rcu_read_unlock(); |
699 | 771 | ||
700 | /* Scaling up? Scale voltage before frequency */ | 772 | return _generic_set_opp(data); |
701 | if (freq > old_freq) { | ||
702 | ret = _set_opp_voltage(dev, reg, &new_supply); | ||
703 | if (ret) | ||
704 | goto restore_voltage; | ||
705 | } | ||
706 | |||
707 | /* Change frequency */ | ||
708 | |||
709 | dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", | ||
710 | __func__, old_freq, freq); | ||
711 | |||
712 | ret = clk_set_rate(clk, freq); | ||
713 | if (ret) { | ||
714 | dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, | ||
715 | ret); | ||
716 | goto restore_voltage; | ||
717 | } | ||
718 | |||
719 | /* Scaling down? Scale voltage after frequency */ | ||
720 | if (freq < old_freq) { | ||
721 | ret = _set_opp_voltage(dev, reg, &new_supply); | ||
722 | if (ret) | ||
723 | goto restore_freq; | ||
724 | } | ||
725 | |||
726 | return 0; | ||
727 | |||
728 | restore_freq: | ||
729 | if (clk_set_rate(clk, old_freq)) | ||
730 | dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", | ||
731 | __func__, old_freq); | ||
732 | restore_voltage: | ||
733 | /* This shouldn't harm even if the voltages weren't updated earlier */ | ||
734 | if (old_supply.u_volt) | ||
735 | _set_opp_voltage(dev, reg, &old_supply); | ||
736 | |||
737 | return ret; | ||
738 | } | 773 | } |
739 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); | 774 | EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); |
740 | 775 | ||
@@ -1368,6 +1403,38 @@ unlock: | |||
1368 | } | 1403 | } |
1369 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); | 1404 | EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); |
1370 | 1405 | ||
1406 | static int _allocate_set_opp_data(struct opp_table *opp_table) | ||
1407 | { | ||
1408 | struct dev_pm_set_opp_data *data; | ||
1409 | int len, count = opp_table->regulator_count; | ||
1410 | |||
1411 | if (WARN_ON(!count)) | ||
1412 | return -EINVAL; | ||
1413 | |||
1414 | /* space for set_opp_data */ | ||
1415 | len = sizeof(*data); | ||
1416 | |||
1417 | /* space for old_opp.supplies and new_opp.supplies */ | ||
1418 | len += 2 * sizeof(struct dev_pm_opp_supply) * count; | ||
1419 | |||
1420 | data = kzalloc(len, GFP_KERNEL); | ||
1421 | if (!data) | ||
1422 | return -ENOMEM; | ||
1423 | |||
1424 | data->old_opp.supplies = (void *)(data + 1); | ||
1425 | data->new_opp.supplies = data->old_opp.supplies + count; | ||
1426 | |||
1427 | opp_table->set_opp_data = data; | ||
1428 | |||
1429 | return 0; | ||
1430 | } | ||
1431 | |||
1432 | static void _free_set_opp_data(struct opp_table *opp_table) | ||
1433 | { | ||
1434 | kfree(opp_table->set_opp_data); | ||
1435 | opp_table->set_opp_data = NULL; | ||
1436 | } | ||
1437 | |||
1371 | /** | 1438 | /** |
1372 | * dev_pm_opp_set_regulators() - Set regulator names for the device | 1439 | * dev_pm_opp_set_regulators() - Set regulator names for the device |
1373 | * @dev: Device for which regulator name is being set. | 1440 | * @dev: Device for which regulator name is being set. |
@@ -1437,6 +1504,11 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, | |||
1437 | 1504 | ||
1438 | opp_table->regulator_count = count; | 1505 | opp_table->regulator_count = count; |
1439 | 1506 | ||
1507 | /* Allocate block only once to pass to set_opp() routines */ | ||
1508 | ret = _allocate_set_opp_data(opp_table); | ||
1509 | if (ret) | ||
1510 | goto free_regulators; | ||
1511 | |||
1440 | mutex_unlock(&opp_table_lock); | 1512 | mutex_unlock(&opp_table_lock); |
1441 | return opp_table; | 1513 | return opp_table; |
1442 | 1514 | ||
@@ -1446,6 +1518,7 @@ free_regulators: | |||
1446 | 1518 | ||
1447 | kfree(opp_table->regulators); | 1519 | kfree(opp_table->regulators); |
1448 | opp_table->regulators = NULL; | 1520 | opp_table->regulators = NULL; |
1521 | opp_table->regulator_count = 0; | ||
1449 | err: | 1522 | err: |
1450 | _remove_opp_table(opp_table); | 1523 | _remove_opp_table(opp_table); |
1451 | unlock: | 1524 | unlock: |
@@ -1482,6 +1555,8 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) | |||
1482 | for (i = opp_table->regulator_count - 1; i >= 0; i--) | 1555 | for (i = opp_table->regulator_count - 1; i >= 0; i--) |
1483 | regulator_put(opp_table->regulators[i]); | 1556 | regulator_put(opp_table->regulators[i]); |
1484 | 1557 | ||
1558 | _free_set_opp_data(opp_table); | ||
1559 | |||
1485 | kfree(opp_table->regulators); | 1560 | kfree(opp_table->regulators); |
1486 | opp_table->regulators = NULL; | 1561 | opp_table->regulators = NULL; |
1487 | opp_table->regulator_count = 0; | 1562 | opp_table->regulator_count = 0; |
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index 5b0f7e53bede..a05e43912c6b 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h | |||
@@ -141,6 +141,7 @@ enum opp_table_access { | |||
141 | * @clk: Device's clock handle | 141 | * @clk: Device's clock handle |
142 | * @regulators: Supply regulators | 142 | * @regulators: Supply regulators |
143 | * @regulator_count: Number of power supply regulators | 143 | * @regulator_count: Number of power supply regulators |
144 | * @set_opp_data: Data to be passed to set_opp callback | ||
144 | * @dentry: debugfs dentry pointer of the real device directory (not links). | 145 | * @dentry: debugfs dentry pointer of the real device directory (not links). |
145 | * @dentry_name: Name of the real dentry. | 146 | * @dentry_name: Name of the real dentry. |
146 | * | 147 | * |
@@ -178,6 +179,8 @@ struct opp_table { | |||
178 | struct regulator **regulators; | 179 | struct regulator **regulators; |
179 | unsigned int regulator_count; | 180 | unsigned int regulator_count; |
180 | 181 | ||
182 | struct dev_pm_set_opp_data *set_opp_data; | ||
183 | |||
181 | #ifdef CONFIG_DEBUG_FS | 184 | #ifdef CONFIG_DEBUG_FS |
182 | struct dentry *dentry; | 185 | struct dentry *dentry; |
183 | char dentry_name[NAME_MAX]; | 186 | char dentry_name[NAME_MAX]; |
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 9a825ae78653..779b40a9287d 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/notifier.h> | 18 | #include <linux/notifier.h> |
19 | 19 | ||
20 | struct clk; | ||
21 | struct regulator; | ||
20 | struct dev_pm_opp; | 22 | struct dev_pm_opp; |
21 | struct device; | 23 | struct device; |
22 | struct opp_table; | 24 | struct opp_table; |
@@ -41,6 +43,39 @@ struct dev_pm_opp_supply { | |||
41 | unsigned long u_amp; | 43 | unsigned long u_amp; |
42 | }; | 44 | }; |
43 | 45 | ||
46 | /** | ||
47 | * struct dev_pm_opp_info - OPP freq/voltage/current values | ||
48 | * @rate: Target clk rate in hz | ||
49 | * @supplies: Array of voltage/current values for all power supplies | ||
50 | * | ||
51 | * This structure stores the freq/voltage/current values for a single OPP. | ||
52 | */ | ||
53 | struct dev_pm_opp_info { | ||
54 | unsigned long rate; | ||
55 | struct dev_pm_opp_supply *supplies; | ||
56 | }; | ||
57 | |||
58 | /** | ||
59 | * struct dev_pm_set_opp_data - Set OPP data | ||
60 | * @old_opp: Old OPP info | ||
61 | * @new_opp: New OPP info | ||
62 | * @regulators: Array of regulator pointers | ||
63 | * @regulator_count: Number of regulators | ||
64 | * @clk: Pointer to clk | ||
65 | * @dev: Pointer to the struct device | ||
66 | * | ||
67 | * This structure contains all information required for setting an OPP. | ||
68 | */ | ||
69 | struct dev_pm_set_opp_data { | ||
70 | struct dev_pm_opp_info old_opp; | ||
71 | struct dev_pm_opp_info new_opp; | ||
72 | |||
73 | struct regulator **regulators; | ||
74 | unsigned int regulator_count; | ||
75 | struct clk *clk; | ||
76 | struct device *dev; | ||
77 | }; | ||
78 | |||
44 | #if defined(CONFIG_PM_OPP) | 79 | #if defined(CONFIG_PM_OPP) |
45 | 80 | ||
46 | unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); | 81 | unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); |