diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2017-10-11 12:54:14 +0530 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-10-14 00:54:40 +0200 |
commit | 009acd196fc860045bf7b2c3f5812f0f5efb2782 (patch) | |
tree | 76f26d4863ee59701532f74d80b1807e6b9ebd0f /drivers/opp/core.c | |
parent | 69f658e399f3d9cb9c9680afb714ba23860d7ccb (diff) | |
download | linux-009acd196fc860045bf7b2c3f5812f0f5efb2782.tar.bz2 |
PM / OPP: Support updating performance state of device's power domain
The genpd framework now provides an API to request device's power
domain to update its performance state. Use that interface from the
OPP core for devices whose power domains support performance states.
Note that this commit doesn't add any mechanism by which performance
states are made available to the OPP core. That would be done by a
later commit.
Note that the current implementation is restricted to the case where
the device doesn't have separate regulators for itself. We shouldn't
over engineer the code before we have real use case for them. We can
always come back and add more code to support such cases later on.
Tested-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/opp/core.c')
-rw-r--r-- | drivers/opp/core.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 80c21207e48c..0ce8069d6843 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/device.h> #include <linux/export.h> +#include <linux/pm_domain.h> #include <linux/regulator/consumer.h> #include "opp.h" @@ -535,6 +536,44 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk, return ret; } +static inline int +_generic_set_opp_domain(struct device *dev, struct clk *clk, + unsigned long old_freq, unsigned long freq, + unsigned int old_pstate, unsigned int new_pstate) +{ + int ret; + + /* Scaling up? Scale domain performance state before frequency */ + if (freq > old_freq) { + ret = dev_pm_genpd_set_performance_state(dev, new_pstate); + if (ret) + return ret; + } + + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); + if (ret) + goto restore_domain_state; + + /* Scaling down? Scale domain performance state after frequency */ + if (freq < old_freq) { + ret = dev_pm_genpd_set_performance_state(dev, new_pstate); + if (ret) + goto restore_freq; + } + + return 0; + +restore_freq: + if (_generic_set_opp_clk_only(dev, clk, freq, old_freq)) + dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", + __func__, old_freq); +restore_domain_state: + if (freq > old_freq) + dev_pm_genpd_set_performance_state(dev, old_pstate); + + return ret; +} + static int _generic_set_opp_regulator(const struct opp_table *opp_table, struct device *dev, unsigned long old_freq, @@ -653,7 +692,16 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) /* Only frequency scaling */ if (!opp_table->regulators) { - ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); + /* + * We don't support devices with both regulator and + * domain performance-state for now. + */ + if (opp_table->genpd_performance_state) + ret = _generic_set_opp_domain(dev, clk, old_freq, freq, + IS_ERR(old_opp) ? 0 : old_opp->pstate, + opp->pstate); + else + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); } else if (!opp_table->set_opp) { ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, IS_ERR(old_opp) ? NULL : old_opp->supplies, @@ -1706,6 +1754,13 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev, if (remove_all || !opp->dynamic) dev_pm_opp_put(opp); } + + /* + * The OPP table is getting removed, drop the performance state + * constraints. + */ + if (opp_table->genpd_performance_state) + dev_pm_genpd_set_performance_state(dev, 0); } else { _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); } |