From: Rafael J. Wysocki <rjw@xxxxxxx> Add a function deciding whether or not devices should be stopped in pm_genpd_runtime_suspend() depending on their PM QoS values. --- arch/arm/mach-shmobile/pm-sh7372.c | 2 - drivers/base/power/Makefile | 2 - drivers/base/power/domain.c | 35 ++++++++++++++++++----- drivers/base/power/domain_governor.c | 40 ++++++++++++++++++++++++++ include/linux/pm_domain.h | 52 ++++++++++++++++++++++++++++++----- 5 files changed, 115 insertions(+), 16 deletions(-) Index: linux/include/linux/pm_domain.h =================================================================== --- linux.orig/include/linux/pm_domain.h +++ linux/include/linux/pm_domain.h @@ -21,6 +21,7 @@ enum gpd_status { struct dev_power_governor { bool (*power_down_ok)(struct dev_pm_domain *domain); + bool (*stop_ok)(struct device *dev); }; struct generic_pm_domain { @@ -62,8 +63,13 @@ struct gpd_link { struct list_head slave_node; }; +struct gpd_gov_dev_data { + s64 break_even_ns; +}; + struct generic_pm_domain_data { struct pm_domain_data base; + struct gpd_gov_dev_data *gov_data; bool need_restore; }; @@ -73,18 +79,47 @@ static inline struct generic_pm_domain_d } #ifdef CONFIG_PM_GENERIC_DOMAINS -extern int pm_genpd_add_device(struct generic_pm_domain *genpd, - struct device *dev); +extern struct dev_power_governor default_qos_governor; + +extern struct generic_pm_domain *dev_to_genpd(struct device *dev); +extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data); + +static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev) +{ + return __pm_genpd_add_device(genpd, dev, NULL); +} + extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_subdomain); extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); -extern void pm_genpd_init(struct generic_pm_domain *genpd, - struct dev_power_governor *gov, bool is_off); +extern void __pm_genpd_init(struct generic_pm_domain *genpd, + struct dev_power_governor *gov, bool is_off); + +static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) +{ + __pm_genpd_init(genpd, &default_qos_governor, is_off); +} + extern int pm_genpd_poweron(struct generic_pm_domain *genpd); + #else + +static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) +{ + return ERR_PTR(-ENOSYS); +} +static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, + struct device *dev, + struct gpd_gov_dev_data *gov_data) +{ + return -ENOSYS; +} static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { @@ -105,8 +140,13 @@ static inline int pm_genpd_remove_subdom { return -ENOSYS; } -static inline void pm_genpd_init(struct generic_pm_domain *genpd, - struct dev_power_governor *gov, bool is_off) {} +static inline void __pm_genpd_init(struct generic_pm_domain *genpd, + struct dev_power_governor *gov, bool is_off) +{ +} +static inline void pm_genpd_init(struct generic_pm_domain *genpd, bool is_off) +{ +} static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) { return -ENOSYS; Index: linux/drivers/base/power/domain.c =================================================================== --- linux.orig/drivers/base/power/domain.c +++ linux/drivers/base/power/domain.c @@ -21,7 +21,7 @@ static DEFINE_MUTEX(gpd_list_lock); #ifdef CONFIG_PM -static struct generic_pm_domain *dev_to_genpd(struct device *dev) +struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); @@ -403,6 +403,22 @@ static void genpd_power_off_work_fn(stru } /** + * genpd_stop_dev - Stop a given device if that's beneficial. + * @genpd: PM domain the device belongs to. + * @dev: Device to stop. + */ +static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) +{ + bool (*stop_ok)(struct device *dev); + + stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL; + if (stop_ok && !stop_ok(dev)) + return -EBUSY; + + return genpd->stop_device(dev); +} + +/** * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain. * @dev: Device to suspend. * @@ -423,7 +439,7 @@ static int pm_genpd_runtime_suspend(stru might_sleep_if(!genpd->dev_irq_safe); if (genpd->stop_device) { - int ret = genpd->stop_device(dev); + int ret = genpd_stop_dev(genpd, dev); if (ret) return ret; } @@ -495,7 +511,7 @@ static int pm_genpd_runtime_resume(struc mutex_lock(&genpd->lock); } finish_wait(&genpd->status_wait_queue, &wait); - __pm_genpd_restore_device(dev->power.subsys_data->domain_data, genpd); + __pm_genpd_restore_device(dev_to_psd(dev)->domain_data, genpd); genpd->resume_count--; genpd_set_active(genpd); wake_up_all(&genpd->status_wait_queue); @@ -1076,11 +1092,13 @@ static void pm_genpd_complete(struct dev #endif /* CONFIG_PM_SLEEP */ /** - * pm_genpd_add_device - Add a device to an I/O PM domain. + * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. * @dev: Device to be added. + * @gov_data: Set of PM QoS parameters to attach to the device. */ -int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) +int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, + struct gpd_gov_dev_data *gov_data) { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; @@ -1123,6 +1141,7 @@ int pm_genpd_add_device(struct generic_p gpd_data->base.dev = dev; gpd_data->need_restore = false; list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->gov_data = gov_data; out: genpd_release_lock(genpd); @@ -1280,13 +1299,13 @@ int pm_genpd_remove_subdomain(struct gen } /** - * pm_genpd_init - Initialize a generic I/O PM domain object. + * __pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize. * @gov: PM domain governor to associate with the domain (may be NULL). * @is_off: Initial value of the domain's power_is_off field. */ -void pm_genpd_init(struct generic_pm_domain *genpd, - struct dev_power_governor *gov, bool is_off) +void __pm_genpd_init(struct generic_pm_domain *genpd, + struct dev_power_governor *gov, bool is_off) { if (IS_ERR_OR_NULL(genpd)) return; Index: linux/drivers/base/power/Makefile =================================================================== --- linux.orig/drivers/base/power/Makefile +++ linux/drivers/base/power/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup. obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o -obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG Index: linux/drivers/base/power/domain_governor.c =================================================================== --- /dev/null +++ linux/drivers/base/power/domain_governor.c @@ -0,0 +1,40 @@ +/* + * drivers/base/power/domain_governor.c - Governors for device PM domains. + * + * Copyright (C) 2011 Rafael J. Wysocki <rjw@xxxxxxx>, Renesas Electronics Corp. + * + * This file is released under the GPLv2. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/pm_qos.h> + +static bool default_stop_ok(struct device *dev) +{ + struct gpd_gov_dev_data *gov_data; + s64 constraint_ns; + s32 constraint; + + dev_dbg(dev, "%s()\n", __func__); + + gov_data = to_gpd_data(dev_to_psd(dev)->domain_data)->gov_data; + if (!gov_data) + return true; + + constraint = dev_pm_qos_read_value(dev); + if (constraint < 0) + return false; + else if (constraint == 0) /* 0 means "don't care" */ + return true; + + constraint_ns = constraint; + constraint_ns *= NSEC_PER_USEC; + + return constraint_ns > gov_data->break_even_ns; +} + +struct dev_power_governor default_qos_governor = { + .stop_ok = default_stop_ok, +}; Index: linux/arch/arm/mach-shmobile/pm-sh7372.c =================================================================== --- linux.orig/arch/arm/mach-shmobile/pm-sh7372.c +++ linux/arch/arm/mach-shmobile/pm-sh7372.c @@ -100,7 +100,7 @@ void sh7372_init_pm_domain(struct sh7372 { struct generic_pm_domain *genpd = &sh7372_pd->genpd; - pm_genpd_init(genpd, NULL, false); + pm_genpd_init(genpd, false); genpd->stop_device = pm_clk_suspend; genpd->start_device = pm_clk_resume; genpd->dev_irq_safe = true; _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm