aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2016-02-09 00:00:39 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-02-09 19:11:54 -0500
commit6a0712f6f199e737aa5913d28ec4bd3a25de9660 (patch)
tree5591576208af1bcf27a90beaec4b64895bb703b8
parentd54974c2513f487e9e70fbdc79c5da51c53e23da (diff)
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>
-rw-r--r--drivers/base/power/opp/core.c176
-rw-r--r--include/linux/pm_opp.h6
2 files changed, 182 insertions, 0 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index 7d7749ce1ce4..ab711c2c3e00 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -529,6 +529,182 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
529} 529}
530EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); 530EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
531 531
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 */
536static 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
555unlock:
556 rcu_read_unlock();
557 return clk;
558}
559
560static 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 */
595int 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
695restore_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);
699restore_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}
706EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
707
532/* List-dev Helpers */ 708/* List-dev Helpers */
533static void _kfree_list_dev_rcu(struct rcu_head *head) 709static void _kfree_list_dev_rcu(struct rcu_head *head)
534{ 710{
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 59da3d9e11ea..cccaf4a29e9f 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -64,6 +64,7 @@ int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
64void dev_pm_opp_put_prop_name(struct device *dev); 64void dev_pm_opp_put_prop_name(struct device *dev);
65int dev_pm_opp_set_regulator(struct device *dev, const char *name); 65int dev_pm_opp_set_regulator(struct device *dev, const char *name);
66void dev_pm_opp_put_regulator(struct device *dev); 66void dev_pm_opp_put_regulator(struct device *dev);
67int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
67#else 68#else
68static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) 69static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
69{ 70{
@@ -172,6 +173,11 @@ static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
172 173
173static inline void dev_pm_opp_put_regulator(struct device *dev) {} 174static inline void dev_pm_opp_put_regulator(struct device *dev) {}
174 175
176static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
177{
178 return -EINVAL;
179}
180
175#endif /* CONFIG_PM_OPP */ 181#endif /* CONFIG_PM_OPP */
176 182
177#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) 183#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)