aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2016-12-01 05:58:21 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-12-05 20:27:59 -0500
commit4dab160eb1586f67e8ba7c55ffdd2373f7a5553e (patch)
tree661c8b2380f1483fb1ff3fde1b499e85e64f327e
parent947355850fcb3bb6549294316667d0f53bc03082 (diff)
PM / OPP: Allow platform specific custom set_opp() callbacks
The generic set_opp() handler isn't sufficient for platforms with complex DVFS. For example, some TI platforms have multiple regulators for a CPU device. The order in which various supplies need to be programmed is only known to the platform code and its best to leave it to it. This patch implements APIs to register platform specific set_opp() callback. 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.c114
-rw-r--r--drivers/base/power/opp/opp.h2
-rw-r--r--include/linux/pm_opp.h10
3 files changed, 125 insertions, 1 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index e33198ce41b4..eceebef36f21 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -687,6 +687,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
687{ 687{
688 struct opp_table *opp_table; 688 struct opp_table *opp_table;
689 unsigned long freq, old_freq; 689 unsigned long freq, old_freq;
690 int (*set_opp)(struct dev_pm_set_opp_data *data);
690 struct dev_pm_opp *old_opp, *opp; 691 struct dev_pm_opp *old_opp, *opp;
691 struct regulator **regulators; 692 struct regulator **regulators;
692 struct dev_pm_set_opp_data *data; 693 struct dev_pm_set_opp_data *data;
@@ -751,6 +752,11 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
751 return _generic_set_opp_clk_only(dev, clk, old_freq, freq); 752 return _generic_set_opp_clk_only(dev, clk, old_freq, freq);
752 } 753 }
753 754
755 if (opp_table->set_opp)
756 set_opp = opp_table->set_opp;
757 else
758 set_opp = _generic_set_opp;
759
754 data = opp_table->set_opp_data; 760 data = opp_table->set_opp_data;
755 data->regulators = regulators; 761 data->regulators = regulators;
756 data->regulator_count = opp_table->regulator_count; 762 data->regulator_count = opp_table->regulator_count;
@@ -769,7 +775,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
769 775
770 rcu_read_unlock(); 776 rcu_read_unlock();
771 777
772 return _generic_set_opp(data); 778 return set_opp(data);
773} 779}
774EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate); 780EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
775 781
@@ -903,6 +909,9 @@ static void _remove_opp_table(struct opp_table *opp_table)
903 if (opp_table->regulators) 909 if (opp_table->regulators)
904 return; 910 return;
905 911
912 if (opp_table->set_opp)
913 return;
914
906 /* Release clk */ 915 /* Release clk */
907 if (!IS_ERR(opp_table->clk)) 916 if (!IS_ERR(opp_table->clk))
908 clk_put(opp_table->clk); 917 clk_put(opp_table->clk);
@@ -1570,6 +1579,109 @@ unlock:
1570EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators); 1579EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulators);
1571 1580
1572/** 1581/**
1582 * dev_pm_opp_register_set_opp_helper() - Register custom set OPP helper
1583 * @dev: Device for which the helper is getting registered.
1584 * @set_opp: Custom set OPP helper.
1585 *
1586 * This is useful to support complex platforms (like platforms with multiple
1587 * regulators per device), instead of the generic OPP set rate helper.
1588 *
1589 * This must be called before any OPPs are initialized for the device.
1590 *
1591 * Locking: The internal opp_table and opp structures are RCU protected.
1592 * Hence this function internally uses RCU updater strategy with mutex locks
1593 * to keep the integrity of the internal data structures. Callers should ensure
1594 * that this function is *NOT* called under RCU protection or in contexts where
1595 * mutex cannot be locked.
1596 */
1597int dev_pm_opp_register_set_opp_helper(struct device *dev,
1598 int (*set_opp)(struct dev_pm_set_opp_data *data))
1599{
1600 struct opp_table *opp_table;
1601 int ret;
1602
1603 if (!set_opp)
1604 return -EINVAL;
1605
1606 mutex_lock(&opp_table_lock);
1607
1608 opp_table = _add_opp_table(dev);
1609 if (!opp_table) {
1610 ret = -ENOMEM;
1611 goto unlock;
1612 }
1613
1614 /* This should be called before OPPs are initialized */
1615 if (WARN_ON(!list_empty(&opp_table->opp_list))) {
1616 ret = -EBUSY;
1617 goto err;
1618 }
1619
1620 /* Already have custom set_opp helper */
1621 if (WARN_ON(opp_table->set_opp)) {
1622 ret = -EBUSY;
1623 goto err;
1624 }
1625
1626 opp_table->set_opp = set_opp;
1627
1628 mutex_unlock(&opp_table_lock);
1629 return 0;
1630
1631err:
1632 _remove_opp_table(opp_table);
1633unlock:
1634 mutex_unlock(&opp_table_lock);
1635
1636 return ret;
1637}
1638EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);
1639
1640/**
1641 * dev_pm_opp_register_put_opp_helper() - Releases resources blocked for
1642 * set_opp helper
1643 * @dev: Device for which custom set_opp helper has to be cleared.
1644 *
1645 * Locking: The internal opp_table and opp structures are RCU protected.
1646 * Hence this function internally uses RCU updater strategy with mutex locks
1647 * to keep the integrity of the internal data structures. Callers should ensure
1648 * that this function is *NOT* called under RCU protection or in contexts where
1649 * mutex cannot be locked.
1650 */
1651void dev_pm_opp_register_put_opp_helper(struct device *dev)
1652{
1653 struct opp_table *opp_table;
1654
1655 mutex_lock(&opp_table_lock);
1656
1657 /* Check for existing table for 'dev' first */
1658 opp_table = _find_opp_table(dev);
1659 if (IS_ERR(opp_table)) {
1660 dev_err(dev, "Failed to find opp_table: %ld\n",
1661 PTR_ERR(opp_table));
1662 goto unlock;
1663 }
1664
1665 if (!opp_table->set_opp) {
1666 dev_err(dev, "%s: Doesn't have custom set_opp helper set\n",
1667 __func__);
1668 goto unlock;
1669 }
1670
1671 /* Make sure there are no concurrent readers while updating opp_table */
1672 WARN_ON(!list_empty(&opp_table->opp_list));
1673
1674 opp_table->set_opp = NULL;
1675
1676 /* Try freeing opp_table if this was the last blocking resource */
1677 _remove_opp_table(opp_table);
1678
1679unlock:
1680 mutex_unlock(&opp_table_lock);
1681}
1682EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper);
1683
1684/**
1573 * dev_pm_opp_add() - Add an OPP table from a table definitions 1685 * dev_pm_opp_add() - Add an OPP table from a table definitions
1574 * @dev: device for which we do this operation 1686 * @dev: device for which we do this operation
1575 * @freq: Frequency in Hz for this OPP 1687 * @freq: Frequency in Hz for this OPP
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index a05e43912c6b..af9f2b849a66 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: Platform specific set_opp callback
144 * @set_opp_data: Data to be passed to set_opp callback 145 * @set_opp_data: Data to be passed to set_opp callback
145 * @dentry: debugfs dentry pointer of the real device directory (not links). 146 * @dentry: debugfs dentry pointer of the real device directory (not links).
146 * @dentry_name: Name of the real dentry. 147 * @dentry_name: Name of the real dentry.
@@ -179,6 +180,7 @@ struct opp_table {
179 struct regulator **regulators; 180 struct regulator **regulators;
180 unsigned int regulator_count; 181 unsigned int regulator_count;
181 182
183 int (*set_opp)(struct dev_pm_set_opp_data *data);
182 struct dev_pm_set_opp_data *set_opp_data; 184 struct dev_pm_set_opp_data *set_opp_data;
183 185
184#ifdef CONFIG_DEBUG_FS 186#ifdef CONFIG_DEBUG_FS
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 779b40a9287d..0edd88f93904 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -116,6 +116,8 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
116void dev_pm_opp_put_prop_name(struct device *dev); 116void dev_pm_opp_put_prop_name(struct device *dev);
117struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count); 117struct opp_table *dev_pm_opp_set_regulators(struct device *dev, const char * const names[], unsigned int count);
118void dev_pm_opp_put_regulators(struct opp_table *opp_table); 118void dev_pm_opp_put_regulators(struct opp_table *opp_table);
119int dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
120void dev_pm_opp_register_put_opp_helper(struct device *dev);
119int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); 121int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
120int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); 122int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
121int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); 123int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@@ -215,6 +217,14 @@ static inline int dev_pm_opp_set_supported_hw(struct device *dev,
215 217
216static inline void dev_pm_opp_put_supported_hw(struct device *dev) {} 218static inline void dev_pm_opp_put_supported_hw(struct device *dev) {}
217 219
220static inline int dev_pm_opp_register_set_opp_helper(struct device *dev,
221 int (*set_opp)(struct dev_pm_set_opp_data *data))
222{
223 return -ENOTSUPP;
224}
225
226static inline void dev_pm_opp_register_put_opp_helper(struct device *dev) {}
227
218static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) 228static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
219{ 229{
220 return -ENOTSUPP; 230 return -ENOTSUPP;