Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Viresh Kumar and committed by
Rafael J. Wysocki
009acd19 69f658e3

+63 -1
+56 -1
drivers/opp/core.c
··· 19 19 #include <linux/slab.h> 20 20 #include <linux/device.h> 21 21 #include <linux/export.h> 22 + #include <linux/pm_domain.h> 22 23 #include <linux/regulator/consumer.h> 23 24 24 25 #include "opp.h" ··· 536 535 return ret; 537 536 } 538 537 538 + static inline int 539 + _generic_set_opp_domain(struct device *dev, struct clk *clk, 540 + unsigned long old_freq, unsigned long freq, 541 + unsigned int old_pstate, unsigned int new_pstate) 542 + { 543 + int ret; 544 + 545 + /* Scaling up? Scale domain performance state before frequency */ 546 + if (freq > old_freq) { 547 + ret = dev_pm_genpd_set_performance_state(dev, new_pstate); 548 + if (ret) 549 + return ret; 550 + } 551 + 552 + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); 553 + if (ret) 554 + goto restore_domain_state; 555 + 556 + /* Scaling down? Scale domain performance state after frequency */ 557 + if (freq < old_freq) { 558 + ret = dev_pm_genpd_set_performance_state(dev, new_pstate); 559 + if (ret) 560 + goto restore_freq; 561 + } 562 + 563 + return 0; 564 + 565 + restore_freq: 566 + if (_generic_set_opp_clk_only(dev, clk, freq, old_freq)) 567 + dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", 568 + __func__, old_freq); 569 + restore_domain_state: 570 + if (freq > old_freq) 571 + dev_pm_genpd_set_performance_state(dev, old_pstate); 572 + 573 + return ret; 574 + } 575 + 539 576 static int _generic_set_opp_regulator(const struct opp_table *opp_table, 540 577 struct device *dev, 541 578 unsigned long old_freq, ··· 692 653 693 654 /* Only frequency scaling */ 694 655 if (!opp_table->regulators) { 695 - ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); 656 + /* 657 + * We don't support devices with both regulator and 658 + * domain performance-state for now. 659 + */ 660 + if (opp_table->genpd_performance_state) 661 + ret = _generic_set_opp_domain(dev, clk, old_freq, freq, 662 + IS_ERR(old_opp) ? 0 : old_opp->pstate, 663 + opp->pstate); 664 + else 665 + ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); 696 666 } else if (!opp_table->set_opp) { 697 667 ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, 698 668 IS_ERR(old_opp) ? NULL : old_opp->supplies, ··· 1754 1706 if (remove_all || !opp->dynamic) 1755 1707 dev_pm_opp_put(opp); 1756 1708 } 1709 + 1710 + /* 1711 + * The OPP table is getting removed, drop the performance state 1712 + * constraints. 1713 + */ 1714 + if (opp_table->genpd_performance_state) 1715 + dev_pm_genpd_set_performance_state(dev, 0); 1757 1716 } else { 1758 1717 _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); 1759 1718 }
+3
drivers/opp/debugfs.c
··· 99 99 if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) 100 100 return -ENOMEM; 101 101 102 + if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate)) 103 + return -ENOMEM; 104 + 102 105 if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) 103 106 return -ENOMEM; 104 107
+4
drivers/opp/opp.h
··· 58 58 * @dynamic: not-created from static DT entries. 59 59 * @turbo: true if turbo (boost) OPP 60 60 * @suspend: true if suspend OPP 61 + * @pstate: Device's power domain's performance state. 61 62 * @rate: Frequency in hertz 62 63 * @supplies: Power supplies voltage/current values 63 64 * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's ··· 77 76 bool dynamic; 78 77 bool turbo; 79 78 bool suspend; 79 + unsigned int pstate; 80 80 unsigned long rate; 81 81 82 82 struct dev_pm_opp_supply *supplies; ··· 137 135 * @clk: Device's clock handle 138 136 * @regulators: Supply regulators 139 137 * @regulator_count: Number of power supply regulators 138 + * @genpd_performance_state: Device's power domain support performance state. 140 139 * @set_opp: Platform specific set_opp callback 141 140 * @set_opp_data: Data to be passed to set_opp callback 142 141 * @dentry: debugfs dentry pointer of the real device directory (not links). ··· 173 170 struct clk *clk; 174 171 struct regulator **regulators; 175 172 unsigned int regulator_count; 173 + bool genpd_performance_state; 176 174 177 175 int (*set_opp)(struct dev_pm_set_opp_data *data); 178 176 struct dev_pm_set_opp_data *set_opp_data;