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

PM: domains: Drop/restore performance state votes for devices at runtime PM

A subsystem/driver that need to manage OPPs for its device, should
typically drop its vote for the OPP when the device becomes runtime
suspended. In this way, the corresponding aggregation of the performance
state votes that is managed in genpd for the attached PM domain, may find
that the aggregated vote can be decreased. Hence, it may allow genpd to set
the lower performance state for the PM domain, thus avoiding to waste
energy.

To accomplish this, typically a subsystem/driver would need to call
dev_pm_opp_set_rate|opp() for its device from its ->runtime_suspend()
callback, to drop the vote for the OPP. Accordingly, it needs another call
to dev_pm_opp_set_rate|opp() to restore the vote for the OPP from its
->runtime_resume() callback.

To avoid boilerplate code in subsystems/driver to deal with these things,
let's instead manage this internally in genpd.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Ulf Hansson and committed by
Rafael J. Wysocki
5937c3ce d97fe100

+26 -2
+25 -2
drivers/base/power/domain.c
··· 400 400 return ret; 401 401 } 402 402 403 + static int genpd_drop_performance_state(struct device *dev) 404 + { 405 + unsigned int prev_state = dev_gpd_data(dev)->performance_state; 406 + 407 + if (!genpd_set_performance_state(dev, 0)) 408 + return prev_state; 409 + 410 + return 0; 411 + } 412 + 413 + static void genpd_restore_performance_state(struct device *dev, 414 + unsigned int state) 415 + { 416 + if (state) 417 + genpd_set_performance_state(dev, state); 418 + } 419 + 403 420 /** 404 421 * dev_pm_genpd_set_performance_state- Set performance state of device's power 405 422 * domain. ··· 860 843 { 861 844 struct generic_pm_domain *genpd; 862 845 bool (*suspend_ok)(struct device *__dev); 863 - struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 846 + struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 847 + struct gpd_timing_data *td = &gpd_data->td; 864 848 bool runtime_pm = pm_runtime_enabled(dev); 865 849 ktime_t time_start; 866 850 s64 elapsed_ns; ··· 918 900 return 0; 919 901 920 902 genpd_lock(genpd); 903 + gpd_data->rpm_pstate = genpd_drop_performance_state(dev); 921 904 genpd_power_off(genpd, true, 0); 922 905 genpd_unlock(genpd); 923 906 ··· 936 917 static int genpd_runtime_resume(struct device *dev) 937 918 { 938 919 struct generic_pm_domain *genpd; 939 - struct gpd_timing_data *td = &dev_gpd_data(dev)->td; 920 + struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev); 921 + struct gpd_timing_data *td = &gpd_data->td; 940 922 bool runtime_pm = pm_runtime_enabled(dev); 941 923 ktime_t time_start; 942 924 s64 elapsed_ns; ··· 961 941 962 942 genpd_lock(genpd); 963 943 ret = genpd_power_on(genpd, 0); 944 + if (!ret) 945 + genpd_restore_performance_state(dev, gpd_data->rpm_pstate); 964 946 genpd_unlock(genpd); 965 947 966 948 if (ret) ··· 1001 979 err_poweroff: 1002 980 if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) { 1003 981 genpd_lock(genpd); 982 + gpd_data->rpm_pstate = genpd_drop_performance_state(dev); 1004 983 genpd_power_off(genpd, true, 0); 1005 984 genpd_unlock(genpd); 1006 985 }
+1
include/linux/pm_domain.h
··· 198 198 struct notifier_block *power_nb; 199 199 int cpu; 200 200 unsigned int performance_state; 201 + unsigned int rpm_pstate; 201 202 ktime_t next_wakeup; 202 203 void *data; 203 204 };