diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-02-09 00:00:39 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-02-09 19:11:54 -0500 |
commit | 6a0712f6f199e737aa5913d28ec4bd3a25de9660 (patch) | |
tree | 5591576208af1bcf27a90beaec4b64895bb703b8 | |
parent | d54974c2513f487e9e70fbdc79c5da51c53e23da (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.c | 176 | ||||
-rw-r--r-- | include/linux/pm_opp.h | 6 |
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 | } |
530 | EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); | 530 | EXPORT_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 | */ | ||
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 | |||
532 | /* List-dev Helpers */ | 708 | /* List-dev Helpers */ |
533 | static void _kfree_list_dev_rcu(struct rcu_head *head) | 709 | static 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); | |||
64 | void dev_pm_opp_put_prop_name(struct device *dev); | 64 | void dev_pm_opp_put_prop_name(struct device *dev); |
65 | int dev_pm_opp_set_regulator(struct device *dev, const char *name); | 65 | int dev_pm_opp_set_regulator(struct device *dev, const char *name); |
66 | void dev_pm_opp_put_regulator(struct device *dev); | 66 | void dev_pm_opp_put_regulator(struct device *dev); |
67 | int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); | ||
67 | #else | 68 | #else |
68 | static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) | 69 | static 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 | ||
173 | static inline void dev_pm_opp_put_regulator(struct device *dev) {} | 174 | static inline void dev_pm_opp_put_regulator(struct device *dev) {} |
174 | 175 | ||
176 | static 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) |