PM / OPP: Add dev_pm_opp_set_rate()
This adds a routine, dev_pm_opp_set_rate(), responsible for configuring power-supply and clock source for an OPP. The OPP is found by matching against the target_freq passed to the routine. This shall replace similar code present in most of the OPP users and help simplify them a lot. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
d54974c251
commit
6a0712f6f1
@ -529,6 +529,182 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The caller needs to ensure that device_opp (and hence the clk) isn't freed,
|
||||||
|
* while clk returned here is used.
|
||||||
|
*/
|
||||||
|
static struct clk *_get_opp_clk(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
dev_opp = _find_device_opp(dev);
|
||||||
|
if (IS_ERR(dev_opp)) {
|
||||||
|
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
|
||||||
|
clk = ERR_CAST(dev_opp);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk = dev_opp->clk;
|
||||||
|
if (IS_ERR(clk))
|
||||||
|
dev_err(dev, "%s: No clock available for the device\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
|
return clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _set_opp_voltage(struct device *dev, struct regulator *reg,
|
||||||
|
unsigned long u_volt, unsigned long u_volt_min,
|
||||||
|
unsigned long u_volt_max)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Regulator not available for device */
|
||||||
|
if (IS_ERR(reg)) {
|
||||||
|
dev_dbg(dev, "%s: regulator not available: %ld\n", __func__,
|
||||||
|
PTR_ERR(reg));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__, u_volt_min,
|
||||||
|
u_volt, u_volt_max);
|
||||||
|
|
||||||
|
ret = regulator_set_voltage_triplet(reg, u_volt_min, u_volt,
|
||||||
|
u_volt_max);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
|
||||||
|
__func__, u_volt_min, u_volt, u_volt_max, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_set_rate() - Configure new OPP based on frequency
|
||||||
|
* @dev: device for which we do this operation
|
||||||
|
* @target_freq: frequency to achieve
|
||||||
|
*
|
||||||
|
* This configures the power-supplies and clock source to the levels specified
|
||||||
|
* by the OPP corresponding to the target_freq.
|
||||||
|
*
|
||||||
|
* Locking: This function takes rcu_read_lock().
|
||||||
|
*/
|
||||||
|
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
struct dev_pm_opp *old_opp, *opp;
|
||||||
|
struct regulator *reg;
|
||||||
|
struct clk *clk;
|
||||||
|
unsigned long freq, old_freq;
|
||||||
|
unsigned long u_volt, u_volt_min, u_volt_max;
|
||||||
|
unsigned long ou_volt, ou_volt_min, ou_volt_max;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (unlikely(!target_freq)) {
|
||||||
|
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
|
||||||
|
target_freq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk = _get_opp_clk(dev);
|
||||||
|
if (IS_ERR(clk))
|
||||||
|
return PTR_ERR(clk);
|
||||||
|
|
||||||
|
freq = clk_round_rate(clk, target_freq);
|
||||||
|
if ((long)freq <= 0)
|
||||||
|
freq = target_freq;
|
||||||
|
|
||||||
|
old_freq = clk_get_rate(clk);
|
||||||
|
|
||||||
|
/* Return early if nothing to do */
|
||||||
|
if (old_freq == freq) {
|
||||||
|
dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n",
|
||||||
|
__func__, freq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
dev_opp = _find_device_opp(dev);
|
||||||
|
if (IS_ERR(dev_opp)) {
|
||||||
|
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return PTR_ERR(dev_opp);
|
||||||
|
}
|
||||||
|
|
||||||
|
old_opp = dev_pm_opp_find_freq_ceil(dev, &old_freq);
|
||||||
|
if (!IS_ERR(old_opp)) {
|
||||||
|
ou_volt = old_opp->u_volt;
|
||||||
|
ou_volt_min = old_opp->u_volt_min;
|
||||||
|
ou_volt_max = old_opp->u_volt_max;
|
||||||
|
} else {
|
||||||
|
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
|
||||||
|
__func__, old_freq, PTR_ERR(old_opp));
|
||||||
|
}
|
||||||
|
|
||||||
|
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
|
||||||
|
if (IS_ERR(opp)) {
|
||||||
|
ret = PTR_ERR(opp);
|
||||||
|
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
|
||||||
|
__func__, freq, ret);
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_volt = opp->u_volt;
|
||||||
|
u_volt_min = opp->u_volt_min;
|
||||||
|
u_volt_max = opp->u_volt_max;
|
||||||
|
|
||||||
|
reg = dev_opp->regulator;
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
/* Scaling up? Scale voltage before frequency */
|
||||||
|
if (freq > old_freq) {
|
||||||
|
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
|
||||||
|
u_volt_max);
|
||||||
|
if (ret)
|
||||||
|
goto restore_voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change frequency */
|
||||||
|
|
||||||
|
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
|
||||||
|
__func__, old_freq, freq);
|
||||||
|
|
||||||
|
ret = clk_set_rate(clk, freq);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
|
||||||
|
ret);
|
||||||
|
goto restore_voltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scaling down? Scale voltage after frequency */
|
||||||
|
if (freq < old_freq) {
|
||||||
|
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
|
||||||
|
u_volt_max);
|
||||||
|
if (ret)
|
||||||
|
goto restore_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
restore_freq:
|
||||||
|
if (clk_set_rate(clk, old_freq))
|
||||||
|
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
|
||||||
|
__func__, old_freq);
|
||||||
|
restore_voltage:
|
||||||
|
/* This shouldn't harm even if the voltages weren't updated earlier */
|
||||||
|
if (!IS_ERR(old_opp))
|
||||||
|
_set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
|
||||||
|
|
||||||
/* List-dev Helpers */
|
/* List-dev Helpers */
|
||||||
static void _kfree_list_dev_rcu(struct rcu_head *head)
|
static void _kfree_list_dev_rcu(struct rcu_head *head)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +64,7 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
|
|||||||
void dev_pm_opp_put_prop_name(struct device *dev);
|
void dev_pm_opp_put_prop_name(struct device *dev);
|
||||||
int dev_pm_opp_set_regulator(struct device *dev, const char *name);
|
int dev_pm_opp_set_regulator(struct device *dev, const char *name);
|
||||||
void dev_pm_opp_put_regulator(struct device *dev);
|
void dev_pm_opp_put_regulator(struct device *dev);
|
||||||
|
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
||||||
#else
|
#else
|
||||||
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
|
||||||
{
|
{
|
||||||
@ -172,6 +173,11 @@ static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
|
|||||||
|
|
||||||
static inline void dev_pm_opp_put_regulator(struct device *dev) {}
|
static inline void dev_pm_opp_put_regulator(struct device *dev) {}
|
||||||
|
|
||||||
|
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PM_OPP */
|
#endif /* CONFIG_PM_OPP */
|
||||||
|
|
||||||
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
||||||
|
Loading…
Reference in New Issue
Block a user