From 9e9704ea5baf09e4c61be5901439da34c39995d0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 6 Oct 2017 09:02:06 +0200 Subject: [PATCH 1/8] PM / Domains: Rename genpd internals from pm_genpd_* to genpd_* Most of the functions names has already moved the genpd naming rules, however let's make this complete to avoid any further confusions. Signed-off-by: Ulf Hansson Reviewed-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 104 +++++++++++++++++------------------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e8ca5e2cf1e5..a6e4c8d7d837 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -749,11 +749,7 @@ late_initcall(genpd_power_off_unused); #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) -/** - * pm_genpd_present - Check if the given PM domain has been initialized. - * @genpd: PM domain to check. - */ -static bool pm_genpd_present(const struct generic_pm_domain *genpd) +static bool genpd_present(const struct generic_pm_domain *genpd) { const struct generic_pm_domain *gpd; @@ -863,7 +859,7 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, * @genpd: PM domain the device belongs to. * * There are two cases in which a device that can wake up the system from sleep - * states should be resumed by pm_genpd_prepare(): (1) if the device is enabled + * states should be resumed by genpd_prepare(): (1) if the device is enabled * to wake up the system and it has to remain active for this purpose while the * system is in the sleep state and (2) if the device is not enabled to wake up * the system from sleep states and it generally doesn't generate wakeup signals @@ -886,7 +882,7 @@ static bool resume_needed(struct device *dev, } /** - * pm_genpd_prepare - Start power transition of a device in a PM domain. + * genpd_prepare - Start power transition of a device in a PM domain. * @dev: Device to start the transition of. * * Start a power transition of a device (during a system-wide power transition) @@ -894,7 +890,7 @@ static bool resume_needed(struct device *dev, * an object of type struct generic_pm_domain representing a PM domain * consisting of I/O devices. */ -static int pm_genpd_prepare(struct device *dev) +static int genpd_prepare(struct device *dev) { struct generic_pm_domain *genpd; int ret; @@ -975,13 +971,13 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) } /** - * pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. + * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. * @dev: Device to suspend. * * Stop the device and remove power from the domain if all devices in it have * been stopped. */ -static int pm_genpd_suspend_noirq(struct device *dev) +static int genpd_suspend_noirq(struct device *dev) { dev_dbg(dev, "%s()\n", __func__); @@ -989,12 +985,12 @@ static int pm_genpd_suspend_noirq(struct device *dev) } /** - * pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. + * genpd_resume_noirq - Start of resume of device in an I/O PM domain. * @dev: Device to resume. * * Restore power to the device's PM domain, if necessary, and start the device. */ -static int pm_genpd_resume_noirq(struct device *dev) +static int genpd_resume_noirq(struct device *dev) { struct generic_pm_domain *genpd; int ret = 0; @@ -1024,7 +1020,7 @@ static int pm_genpd_resume_noirq(struct device *dev) } /** - * pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. + * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. * @dev: Device to freeze. * * Carry out a late freeze of a device under the assumption that its @@ -1032,7 +1028,7 @@ static int pm_genpd_resume_noirq(struct device *dev) * struct generic_pm_domain representing a power domain consisting of I/O * devices. */ -static int pm_genpd_freeze_noirq(struct device *dev) +static int genpd_freeze_noirq(struct device *dev) { const struct generic_pm_domain *genpd; int ret = 0; @@ -1054,13 +1050,13 @@ static int pm_genpd_freeze_noirq(struct device *dev) } /** - * pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. + * genpd_thaw_noirq - Early thaw of device in an I/O PM domain. * @dev: Device to thaw. * * Start the device, unless power has been removed from the domain already * before the system transition. */ -static int pm_genpd_thaw_noirq(struct device *dev) +static int genpd_thaw_noirq(struct device *dev) { const struct generic_pm_domain *genpd; int ret = 0; @@ -1081,14 +1077,14 @@ static int pm_genpd_thaw_noirq(struct device *dev) } /** - * pm_genpd_poweroff_noirq - Completion of hibernation of device in an + * genpd_poweroff_noirq - Completion of hibernation of device in an * I/O PM domain. * @dev: Device to poweroff. * * Stop the device and remove power from the domain if all devices in it have * been stopped. */ -static int pm_genpd_poweroff_noirq(struct device *dev) +static int genpd_poweroff_noirq(struct device *dev) { dev_dbg(dev, "%s()\n", __func__); @@ -1096,13 +1092,13 @@ static int pm_genpd_poweroff_noirq(struct device *dev) } /** - * pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. + * genpd_restore_noirq - Start of restore of device in an I/O PM domain. * @dev: Device to resume. * * Make sure the domain will be in the same power state as before the * hibernation the system is resuming from and start the device if necessary. */ -static int pm_genpd_restore_noirq(struct device *dev) +static int genpd_restore_noirq(struct device *dev) { struct generic_pm_domain *genpd; int ret = 0; @@ -1139,7 +1135,7 @@ static int pm_genpd_restore_noirq(struct device *dev) } /** - * pm_genpd_complete - Complete power transition of a device in a power domain. + * genpd_complete - Complete power transition of a device in a power domain. * @dev: Device to complete the transition of. * * Complete a power transition of a device (during a system-wide power @@ -1147,7 +1143,7 @@ static int pm_genpd_restore_noirq(struct device *dev) * domain member of an object of type struct generic_pm_domain representing * a power domain consisting of I/O devices. */ -static void pm_genpd_complete(struct device *dev) +static void genpd_complete(struct device *dev) { struct generic_pm_domain *genpd; @@ -1180,7 +1176,7 @@ static void genpd_syscore_switch(struct device *dev, bool suspend) struct generic_pm_domain *genpd; genpd = dev_to_genpd(dev); - if (!pm_genpd_present(genpd)) + if (!genpd_present(genpd)) return; if (suspend) { @@ -1206,14 +1202,14 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); #else /* !CONFIG_PM_SLEEP */ -#define pm_genpd_prepare NULL -#define pm_genpd_suspend_noirq NULL -#define pm_genpd_resume_noirq NULL -#define pm_genpd_freeze_noirq NULL -#define pm_genpd_thaw_noirq NULL -#define pm_genpd_poweroff_noirq NULL -#define pm_genpd_restore_noirq NULL -#define pm_genpd_complete NULL +#define genpd_prepare NULL +#define genpd_suspend_noirq NULL +#define genpd_resume_noirq NULL +#define genpd_freeze_noirq NULL +#define genpd_thaw_noirq NULL +#define genpd_poweroff_noirq NULL +#define genpd_restore_noirq NULL +#define genpd_complete NULL #endif /* CONFIG_PM_SLEEP */ @@ -1574,14 +1570,14 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->accounting_time = ktime_get(); genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_resume = genpd_runtime_resume; - genpd->domain.ops.prepare = pm_genpd_prepare; - genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; - genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; - genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; - genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; - genpd->domain.ops.poweroff_noirq = pm_genpd_poweroff_noirq; - genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; - genpd->domain.ops.complete = pm_genpd_complete; + genpd->domain.ops.prepare = genpd_prepare; + genpd->domain.ops.suspend_noirq = genpd_suspend_noirq; + genpd->domain.ops.resume_noirq = genpd_resume_noirq; + genpd->domain.ops.freeze_noirq = genpd_freeze_noirq; + genpd->domain.ops.thaw_noirq = genpd_thaw_noirq; + genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq; + genpd->domain.ops.restore_noirq = genpd_restore_noirq; + genpd->domain.ops.complete = genpd_complete; if (genpd->flags & GENPD_FLAG_PM_CLK) { genpd->dev_ops.stop = pm_clk_suspend; @@ -1795,7 +1791,7 @@ int of_genpd_add_provider_simple(struct device_node *np, mutex_lock(&gpd_list_lock); - if (pm_genpd_present(genpd)) { + if (genpd_present(genpd)) { ret = genpd_add_provider(np, genpd_xlate_simple, genpd); if (!ret) { genpd->provider = &np->fwnode; @@ -1831,7 +1827,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, for (i = 0; i < data->num_domains; i++) { if (!data->domains[i]) continue; - if (!pm_genpd_present(data->domains[i])) + if (!genpd_present(data->domains[i])) goto error; data->domains[i]->provider = &np->fwnode; @@ -2274,7 +2270,7 @@ EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); #include #include #include -static struct dentry *pm_genpd_debugfs_dir; +static struct dentry *genpd_debugfs_dir; /* * TODO: This function is a slightly modified version of rtpm_status_show @@ -2302,8 +2298,8 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev) seq_puts(s, p); } -static int pm_genpd_summary_one(struct seq_file *s, - struct generic_pm_domain *genpd) +static int genpd_summary_one(struct seq_file *s, + struct generic_pm_domain *genpd) { static const char * const status_lookup[] = { [GPD_STATE_ACTIVE] = "on", @@ -2373,7 +2369,7 @@ static int genpd_summary_show(struct seq_file *s, void *data) return -ERESTARTSYS; list_for_each_entry(genpd, &gpd_list, gpd_list_node) { - ret = pm_genpd_summary_one(s, genpd); + ret = genpd_summary_one(s, genpd); if (ret) break; } @@ -2559,23 +2555,23 @@ define_genpd_debugfs_fops(active_time); define_genpd_debugfs_fops(total_idle_time); define_genpd_debugfs_fops(devices); -static int __init pm_genpd_debug_init(void) +static int __init genpd_debug_init(void) { struct dentry *d; struct generic_pm_domain *genpd; - pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); + genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); - if (!pm_genpd_debugfs_dir) + if (!genpd_debugfs_dir) return -ENOMEM; d = debugfs_create_file("pm_genpd_summary", S_IRUGO, - pm_genpd_debugfs_dir, NULL, &genpd_summary_fops); + genpd_debugfs_dir, NULL, &genpd_summary_fops); if (!d) return -ENOMEM; list_for_each_entry(genpd, &gpd_list, gpd_list_node) { - d = debugfs_create_dir(genpd->name, pm_genpd_debugfs_dir); + d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); if (!d) return -ENOMEM; @@ -2595,11 +2591,11 @@ static int __init pm_genpd_debug_init(void) return 0; } -late_initcall(pm_genpd_debug_init); +late_initcall(genpd_debug_init); -static void __exit pm_genpd_debug_exit(void) +static void __exit genpd_debug_exit(void) { - debugfs_remove_recursive(pm_genpd_debugfs_dir); + debugfs_remove_recursive(genpd_debugfs_dir); } -__exitcall(pm_genpd_debug_exit); +__exitcall(genpd_debug_exit); #endif /* CONFIG_DEBUG_FS */ From 42f6284ae602469762ee721ec31ddfc6170e00bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 12 Oct 2017 15:07:23 +0530 Subject: [PATCH 2/8] PM / Domains: Add support to select performance-state of domains Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms. The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state. This patch adds a new genpd API, which is called by user drivers (like OPP framework): - int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state); This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the genpd->set_performance_state() callback, if implemented, else -ENODEV is returned to the caller. The PM domain drivers need to implement the following callback if they want to support performance states. - int (*set_performance_state)(struct generic_pm_domain *genpd, unsigned int state); This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way). The power domains can avoid supplying above callback, if they don't support setting performance-states. Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it. Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 98 +++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 +++++ 2 files changed, 110 insertions(+) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a6e4c8d7d837..7e01ae364d78 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -237,6 +237,95 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd) static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} #endif +/** + * dev_pm_genpd_set_performance_state- Set performance state of device's power + * domain. + * + * @dev: Device for which the performance-state needs to be set. + * @state: Target performance state of the device. This can be set as 0 when the + * device doesn't have any performance state constraints left (And so + * the device wouldn't participate anymore to find the target + * performance state of the genpd). + * + * It is assumed that the users guarantee that the genpd wouldn't be detached + * while this routine is getting called. + * + * Returns 0 on success and negative error values on failures. + */ +int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) +{ + struct generic_pm_domain *genpd; + struct generic_pm_domain_data *gpd_data, *pd_data; + struct pm_domain_data *pdd; + unsigned int prev; + int ret = 0; + + genpd = dev_to_genpd(dev); + if (IS_ERR(genpd)) + return -ENODEV; + + if (unlikely(!genpd->set_performance_state)) + return -EINVAL; + + if (unlikely(!dev->power.subsys_data || + !dev->power.subsys_data->domain_data)) { + WARN_ON(1); + return -EINVAL; + } + + genpd_lock(genpd); + + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + prev = gpd_data->performance_state; + gpd_data->performance_state = state; + + /* New requested state is same as Max requested state */ + if (state == genpd->performance_state) + goto unlock; + + /* New requested state is higher than Max requested state */ + if (state > genpd->performance_state) + goto update_state; + + /* Traverse all devices within the domain */ + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + pd_data = to_gpd_data(pdd); + + if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + + if (state == genpd->performance_state) + goto unlock; + + /* + * We aren't propagating performance state changes of a subdomain to its + * masters as we don't have hardware that needs it. Over that, the + * performance states of subdomain and its masters may not have + * one-to-one mapping and would require additional information. We can + * get back to this once we have hardware that needs it. For that + * reason, we don't have to consider performance state of the subdomains + * of genpd here. + */ + +update_state: + if (genpd_status_on(genpd)) { + ret = genpd->set_performance_state(genpd, state); + if (ret) { + gpd_data->performance_state = prev; + goto unlock; + } + } + + genpd->performance_state = state; + +unlock: + genpd_unlock(genpd); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -256,6 +345,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) return ret; elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + + if (unlikely(genpd->set_performance_state)) { + ret = genpd->set_performance_state(genpd, genpd->performance_state); + if (ret) { + pr_warn("%s: Failed to set performance state %d (%d)\n", + genpd->name, genpd->performance_state, ret); + } + } + if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) return ret; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 84f423d5633e..9af0356bd69c 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -64,8 +64,11 @@ struct generic_pm_domain { unsigned int device_count; /* Number of devices */ unsigned int suspended_count; /* System suspend device counter */ unsigned int prepared_count; /* Suspend counter of prepared devices */ + unsigned int performance_state; /* Aggregated max performance state */ int (*power_off)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain); + int (*set_performance_state)(struct generic_pm_domain *genpd, + unsigned int state); struct gpd_dev_ops dev_ops; s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ bool max_off_time_changed; @@ -121,6 +124,7 @@ struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_timing_data td; struct notifier_block nb; + unsigned int performance_state; void *data; }; @@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, extern int pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); extern int pm_genpd_remove(struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state(struct device *dev, + unsigned int state); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state(struct device *dev, + unsigned int state) +{ + return -ENOTSUPP; +} + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #endif From 95a20ef6f7e54c6a982715a7d0da2fd81790db28 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:11 +0100 Subject: [PATCH 3/8] PM / Domains: Allow genpd users to specify default active wakeup behavior It is quite common for PM Domains to require slave devices to be kept active during system suspend if they are to be used as wakeup sources. To enable this, currently each PM Domain or driver has to provide its own gpd_dev_ops.active_wakeup() callback. Introduce a new flag GENPD_FLAG_ACTIVE_WAKEUP to consolidate this. If specified, all slave devices configured as wakeup sources will be kept active during system suspend. PM Domains that need more fine-grained controls, based on the slave device, can still provide their own callbacks, as before. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 +++ include/linux/pm_domain.h | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 7e01ae364d78..e343844357c8 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -124,6 +124,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { #define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) +#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) @@ -868,6 +869,8 @@ static bool genpd_present(const struct generic_pm_domain *genpd) static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, struct device *dev) { + if (genpd_is_active_wakeup(genpd)) + return true; return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); } diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 9af0356bd69c..28c24c58d479 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -18,9 +18,10 @@ #include /* Defines used for the flags field in the struct generic_pm_domain */ -#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ -#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */ -#define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */ +#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ +#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */ +#define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */ +#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) /* Keep devices active if wakeup */ enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ From eb0ddf9dd22be098301ab8a09e9be5a13ae8c804 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:12 +0100 Subject: [PATCH 4/8] ARM: shmobile: pm-rmobile: Use GENPD_FLAG_ACTIVE_WAKEUP Set the newly introduced GENPD_FLAG_ACTIVE_WAKEUP, which allows to remove the driver's own "always true" callback. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Acked-by: Simon Horman Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/pm-rmobile.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c index 3a4ed4c33a68..e348bcfe389d 100644 --- a/arch/arm/mach-shmobile/pm-rmobile.c +++ b/arch/arm/mach-shmobile/pm-rmobile.c @@ -120,18 +120,12 @@ static int rmobile_pd_power_up(struct generic_pm_domain *genpd) return __rmobile_pd_power_up(to_rmobile_pd(genpd), true); } -static bool rmobile_pd_active_wakeup(struct device *dev) -{ - return true; -} - static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) { struct generic_pm_domain *genpd = &rmobile_pd->genpd; struct dev_power_governor *gov = rmobile_pd->gov; - genpd->flags |= GENPD_FLAG_PM_CLK; - genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup; + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; genpd->power_off = rmobile_pd_power_down; genpd->power_on = rmobile_pd_power_up; genpd->attach_dev = cpg_mstp_attach_dev; From 7534d181a8e60dff0c2a8e12aa6515a87a25b47d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:13 +0100 Subject: [PATCH 5/8] soc: mediatek: Use GENPD_FLAG_ACTIVE_WAKEUP Set the newly introduced GENPD_FLAG_ACTIVE_WAKEUP, which allows to remove the driver's own flag-based callback. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Acked-by: Matthias Brugger Signed-off-by: Rafael J. Wysocki --- drivers/soc/mediatek/mtk-scpsys.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index e1ce8b1b5090..e570b6af2e6f 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -361,17 +361,6 @@ out: return ret; } -static bool scpsys_active_wakeup(struct device *dev) -{ - struct generic_pm_domain *genpd; - struct scp_domain *scpd; - - genpd = pd_to_genpd(dev->pm_domain); - scpd = container_of(genpd, struct scp_domain, genpd); - - return scpd->data->active_wakeup; -} - static void init_clks(struct platform_device *pdev, struct clk **clk) { int i; @@ -466,7 +455,8 @@ static struct scp *init_scp(struct platform_device *pdev, genpd->name = data->name; genpd->power_off = scpsys_power_off; genpd->power_on = scpsys_power_on; - genpd->dev_ops.active_wakeup = scpsys_active_wakeup; + if (scpd->data->active_wakeup) + genpd->flags |= GENPD_FLAG_ACTIVE_WAKEUP; } return scp; From 89c7aea915c0c9820191a533e1f304e234074b2d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:14 +0100 Subject: [PATCH 6/8] soc: rockchip: power-domain: Use GENPD_FLAG_ACTIVE_WAKEUP Set the newly introduced GENPD_FLAG_ACTIVE_WAKEUP, which allows to remove the driver's own flag-based callback. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Acked-by: Heiko Stuebner Signed-off-by: Rafael J. Wysocki --- drivers/soc/rockchip/pm_domains.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 40b75748835f..5c342167b9db 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -358,17 +358,6 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd, pm_clk_destroy(dev); } -static bool rockchip_active_wakeup(struct device *dev) -{ - struct generic_pm_domain *genpd; - struct rockchip_pm_domain *pd; - - genpd = pd_to_genpd(dev->pm_domain); - pd = container_of(genpd, struct rockchip_pm_domain, genpd); - - return pd->info->active_wakeup; -} - static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, struct device_node *node) { @@ -489,8 +478,9 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, pd->genpd.power_on = rockchip_pd_power_on; pd->genpd.attach_dev = rockchip_pd_attach_dev; pd->genpd.detach_dev = rockchip_pd_detach_dev; - pd->genpd.dev_ops.active_wakeup = rockchip_active_wakeup; pd->genpd.flags = GENPD_FLAG_PM_CLK; + if (pd_info->active_wakeup) + pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; pm_genpd_init(&pd->genpd, NULL, false); pmu->genpd_data.domains[id] = &pd->genpd; From d0af45f1f6528949e05385976eb61c5ebd31854e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:15 +0100 Subject: [PATCH 7/8] PM / Domains: Remove gpd_dev_ops.active_wakeup() callback There are no more users left of the gpd_dev_ops.active_wakeup() callback. All have been converted to GENPD_FLAG_ACTIVE_WAKEUP. Hence remove the callback. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 14 +++----------- include/linux/pm_domain.h | 1 - 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e343844357c8..65bb40c240fb 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -866,14 +866,6 @@ static bool genpd_present(const struct generic_pm_domain *genpd) #ifdef CONFIG_PM_SLEEP -static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, - struct device *dev) -{ - if (genpd_is_active_wakeup(genpd)) - return true; - return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); -} - /** * genpd_sync_power_off - Synchronously power off a PM domain and its masters. * @genpd: PM domain to power off, if possible. @@ -978,7 +970,7 @@ static bool resume_needed(struct device *dev, if (!device_can_wakeup(dev)) return false; - active_wakeup = genpd_dev_active_wakeup(genpd, dev); + active_wakeup = genpd_is_active_wakeup(genpd); return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; } @@ -1047,7 +1039,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) if (IS_ERR(genpd)) return -EINVAL; - if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) + if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) return 0; if (poweroff) @@ -1102,7 +1094,7 @@ static int genpd_resume_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) + if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) return 0; genpd_lock(genpd); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 28c24c58d479..04dbef9847d3 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -36,7 +36,6 @@ struct dev_power_governor { struct gpd_dev_ops { int (*start)(struct device *dev); int (*stop)(struct device *dev); - bool (*active_wakeup)(struct device *dev); }; struct genpd_power_state { From 704d2ce6603f7e40bb607ae9452ff18a4cec701f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Nov 2017 02:23:18 +0100 Subject: [PATCH 8/8] PM / domains: Rework governor code to be more consistent The genpd governor currently uses negative PM QoS values to indicate the "no suspend" condition and 0 as "no restriction", but it doesn't use them consistently. Moreover, it tries to refresh QoS values for already suspended devices in a quite questionable way. For the above reasons, rework it to be a bit more consistent. First off, note that dev_pm_qos_read_value() in dev_update_qos_constraint() and __default_power_down_ok() is evaluated for devices in suspend. Moreover, that only happens if the effective_constraint_ns value for them is negative (meaning "no suspend"). It is not evaluated in any other cases, so effectively the QoS values are only updated for devices in suspend that should not have been suspended in the first place. In all of the other cases, the QoS values taken into account are the effective ones from the time before the device has been suspended, so generally devices need to be resumed and suspended again for new QoS values to take effect anyway. Thus evaluating dev_update_qos_constraint() in those two places doesn't make sense at all, so drop it. Second, initialize effective_constraint_ns to 0 ("no constraint") rather than to (-1) ("no suspend"), which makes more sense in general and in case effective_constraint_ns is never updated (the device is in suspend all the time or it is never suspended) it doesn't affect the device's parent and so on. Finally, rework default_suspend_ok() to explicitly handle the "no restriction" and "no suspend" special cases. Signed-off-by: Rafael J. Wysocki Tested-by: Geert Uytterhoeven Tested-by: Tero Kristo Reviewed-by: Ramesh Thomas --- drivers/base/power/domain.c | 2 +- drivers/base/power/domain_governor.c | 71 +++++++++++++++++++--------- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 65bb40c240fb..b914e373a478 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1328,7 +1328,7 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, gpd_data->base.dev = dev; gpd_data->td.constraint_changed = true; - gpd_data->td.effective_constraint_ns = -1; + gpd_data->td.effective_constraint_ns = 0; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; spin_lock_irq(&dev->power.lock); diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 281f949c5ffe..e4cca8adab32 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -14,22 +14,33 @@ static int dev_update_qos_constraint(struct device *dev, void *data) { s64 *constraint_ns_p = data; - s32 constraint_ns = -1; + s64 constraint_ns; - if (dev->power.subsys_data && dev->power.subsys_data->domain_data) + if (dev->power.subsys_data && dev->power.subsys_data->domain_data) { + /* + * Only take suspend-time QoS constraints of devices into + * account, because constraints updated after the device has + * been suspended are not guaranteed to be taken into account + * anyway. In order for them to take effect, the device has to + * be resumed and suspended again. + */ constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; - - if (constraint_ns < 0) { + } else { + /* + * The child is not in a domain and there's no info on its + * suspend/resume latencies, so assume them to be negligible and + * take its current PM QoS constraint (that's the only thing + * known at this point anyway). + */ constraint_ns = dev_pm_qos_read_value(dev); - constraint_ns *= NSEC_PER_USEC; + if (constraint_ns > 0) + constraint_ns *= NSEC_PER_USEC; } + + /* 0 means "no constraint" */ if (constraint_ns == 0) return 0; - /* - * constraint_ns cannot be negative here, because the device has been - * suspended. - */ if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) *constraint_ns_p = constraint_ns; @@ -76,14 +87,32 @@ static bool default_suspend_ok(struct device *dev) device_for_each_child(dev, &constraint_ns, dev_update_qos_constraint); - if (constraint_ns > 0) { + if (constraint_ns == 0) { + /* "No restriction", so the device is allowed to suspend. */ + td->effective_constraint_ns = 0; + td->cached_suspend_ok = true; + } else if (constraint_ns < 0) { + /* + * This triggers if one of the children that don't belong to a + * domain has a negative PM QoS constraint and it's better not + * to suspend then. effective_constraint_ns is negative already + * and cached_suspend_ok is false, so bail out. + */ + return false; + } else { constraint_ns -= td->suspend_latency_ns + td->resume_latency_ns; - if (constraint_ns == 0) + /* + * effective_constraint_ns is negative already and + * cached_suspend_ok is false, so if the computed value is not + * positive, return right away. + */ + if (constraint_ns <= 0) return false; + + td->effective_constraint_ns = constraint_ns; + td->cached_suspend_ok = true; } - td->effective_constraint_ns = constraint_ns; - td->cached_suspend_ok = constraint_ns >= 0; /* * The children have been suspended already, so we don't need to take @@ -144,18 +173,16 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, */ td = &to_gpd_data(pdd)->td; constraint_ns = td->effective_constraint_ns; - /* default_suspend_ok() need not be called before us. */ - if (constraint_ns < 0) { - constraint_ns = dev_pm_qos_read_value(pdd->dev); - constraint_ns *= NSEC_PER_USEC; - } + /* + * Negative values mean "no suspend at all" and this runs only + * when all devices in the domain are suspended, so it must be + * 0 at least. + * + * 0 means "no constraint" + */ if (constraint_ns == 0) continue; - /* - * constraint_ns cannot be negative here, because the device has - * been suspended. - */ if (constraint_ns <= off_on_time_ns) return false;