Skip to content

Commit 6229dc0

Browse files
vireshkAlex Shi
authored andcommitted
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> (cherry picked from commit 6a0712f6f199e737aa5913d28ec4bd3a25de9660) Signed-off-by: Alex Shi <alex.shi@linaro.org>
1 parent b7737cf commit 6229dc0

2 files changed

Lines changed: 182 additions & 0 deletions

File tree

drivers/base/power/opp/core.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,182 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
529529
}
530530
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
531531

532+
/*
533+
* The caller needs to ensure that device_opp (and hence the clk) isn't freed,
534+
* while clk returned here is used.
535+
*/
536+
static struct clk *_get_opp_clk(struct device *dev)
537+
{
538+
struct device_opp *dev_opp;
539+
struct clk *clk;
540+
541+
rcu_read_lock();
542+
543+
dev_opp = _find_device_opp(dev);
544+
if (IS_ERR(dev_opp)) {
545+
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
546+
clk = ERR_CAST(dev_opp);
547+
goto unlock;
548+
}
549+
550+
clk = dev_opp->clk;
551+
if (IS_ERR(clk))
552+
dev_err(dev, "%s: No clock available for the device\n",
553+
__func__);
554+
555+
unlock:
556+
rcu_read_unlock();
557+
return clk;
558+
}
559+
560+
static int _set_opp_voltage(struct device *dev, struct regulator *reg,
561+
unsigned long u_volt, unsigned long u_volt_min,
562+
unsigned long u_volt_max)
563+
{
564+
int ret;
565+
566+
/* Regulator not available for device */
567+
if (IS_ERR(reg)) {
568+
dev_dbg(dev, "%s: regulator not available: %ld\n", __func__,
569+
PTR_ERR(reg));
570+
return 0;
571+
}
572+
573+
dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__, u_volt_min,
574+
u_volt, u_volt_max);
575+
576+
ret = regulator_set_voltage_triplet(reg, u_volt_min, u_volt,
577+
u_volt_max);
578+
if (ret)
579+
dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
580+
__func__, u_volt_min, u_volt, u_volt_max, ret);
581+
582+
return ret;
583+
}
584+
585+
/**
586+
* dev_pm_opp_set_rate() - Configure new OPP based on frequency
587+
* @dev: device for which we do this operation
588+
* @target_freq: frequency to achieve
589+
*
590+
* This configures the power-supplies and clock source to the levels specified
591+
* by the OPP corresponding to the target_freq.
592+
*
593+
* Locking: This function takes rcu_read_lock().
594+
*/
595+
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
596+
{
597+
struct device_opp *dev_opp;
598+
struct dev_pm_opp *old_opp, *opp;
599+
struct regulator *reg;
600+
struct clk *clk;
601+
unsigned long freq, old_freq;
602+
unsigned long u_volt, u_volt_min, u_volt_max;
603+
unsigned long ou_volt, ou_volt_min, ou_volt_max;
604+
int ret;
605+
606+
if (unlikely(!target_freq)) {
607+
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
608+
target_freq);
609+
return -EINVAL;
610+
}
611+
612+
clk = _get_opp_clk(dev);
613+
if (IS_ERR(clk))
614+
return PTR_ERR(clk);
615+
616+
freq = clk_round_rate(clk, target_freq);
617+
if ((long)freq <= 0)
618+
freq = target_freq;
619+
620+
old_freq = clk_get_rate(clk);
621+
622+
/* Return early if nothing to do */
623+
if (old_freq == freq) {
624+
dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n",
625+
__func__, freq);
626+
return 0;
627+
}
628+
629+
rcu_read_lock();
630+
631+
dev_opp = _find_device_opp(dev);
632+
if (IS_ERR(dev_opp)) {
633+
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
634+
rcu_read_unlock();
635+
return PTR_ERR(dev_opp);
636+
}
637+
638+
old_opp = dev_pm_opp_find_freq_ceil(dev, &old_freq);
639+
if (!IS_ERR(old_opp)) {
640+
ou_volt = old_opp->u_volt;
641+
ou_volt_min = old_opp->u_volt_min;
642+
ou_volt_max = old_opp->u_volt_max;
643+
} else {
644+
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
645+
__func__, old_freq, PTR_ERR(old_opp));
646+
}
647+
648+
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
649+
if (IS_ERR(opp)) {
650+
ret = PTR_ERR(opp);
651+
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
652+
__func__, freq, ret);
653+
rcu_read_unlock();
654+
return ret;
655+
}
656+
657+
u_volt = opp->u_volt;
658+
u_volt_min = opp->u_volt_min;
659+
u_volt_max = opp->u_volt_max;
660+
661+
reg = dev_opp->regulator;
662+
663+
rcu_read_unlock();
664+
665+
/* Scaling up? Scale voltage before frequency */
666+
if (freq > old_freq) {
667+
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
668+
u_volt_max);
669+
if (ret)
670+
goto restore_voltage;
671+
}
672+
673+
/* Change frequency */
674+
675+
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
676+
__func__, old_freq, freq);
677+
678+
ret = clk_set_rate(clk, freq);
679+
if (ret) {
680+
dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
681+
ret);
682+
goto restore_voltage;
683+
}
684+
685+
/* Scaling down? Scale voltage after frequency */
686+
if (freq < old_freq) {
687+
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
688+
u_volt_max);
689+
if (ret)
690+
goto restore_freq;
691+
}
692+
693+
return 0;
694+
695+
restore_freq:
696+
if (clk_set_rate(clk, old_freq))
697+
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
698+
__func__, old_freq);
699+
restore_voltage:
700+
/* This shouldn't harm even if the voltages weren't updated earlier */
701+
if (!IS_ERR(old_opp))
702+
_set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max);
703+
704+
return ret;
705+
}
706+
EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
707+
532708
/* List-dev Helpers */
533709
static void _kfree_list_dev_rcu(struct rcu_head *head)
534710
{

include/linux/pm_opp.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
6464
void dev_pm_opp_put_prop_name(struct device *dev);
6565
int dev_pm_opp_set_regulator(struct device *dev, const char *name);
6666
void dev_pm_opp_put_regulator(struct device *dev);
67+
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
6768
#else
6869
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
6970
{
@@ -172,6 +173,11 @@ static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
172173

173174
static inline void dev_pm_opp_put_regulator(struct device *dev) {}
174175

176+
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
177+
{
178+
return -EINVAL;
179+
}
180+
175181
#endif /* CONFIG_PM_OPP */
176182

177183
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)

0 commit comments

Comments
 (0)