From 9e9704ea5baf09e4c61be5901439da34c39995d0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 6 Oct 2017 09:02:06 +0200 Subject: 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(-) (limited to 'drivers/base/power/domain.c') 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 */ -- cgit v1.2.3 From 42f6284ae602469762ee721ec31ddfc6170e00bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 12 Oct 2017 15:07:23 +0530 Subject: 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(+) (limited to 'drivers/base/power/domain.c') 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 -- cgit v1.2.3 From 20f97caf1120bd02e8ff4adbad3b44b63626feb5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 13 Oct 2017 15:27:24 +0200 Subject: PM / QoS: Drop PM_QOS_FLAG_REMOTE_WAKEUP The PM QoS flag PM_QOS_FLAG_REMOTE_WAKEUP is not used consistently and the vast majority of code simply assumes that remote wakeup should be enabled for devices in runtime suspend if they can generate wakeup signals, so drop it. Signed-off-by: Rafael J. Wysocki Acked-by: Ulf Hansson Reviewed-by: Mika Westerberg --- Documentation/ABI/testing/sysfs-devices-power | 16 --------------- Documentation/power/pm_qos_interface.txt | 13 ++++++------- drivers/acpi/device_pm.c | 6 ++---- drivers/base/power/domain.c | 4 +--- drivers/base/power/sysfs.c | 28 --------------------------- include/linux/pm_qos.h | 1 - 6 files changed, 9 insertions(+), 59 deletions(-) (limited to 'drivers/base/power/domain.c') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 676fdf5f2a99..f4b24c327665 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -258,19 +258,3 @@ Description: This attribute has no effect on system-wide suspend/resume and hibernation. - -What: /sys/devices/.../power/pm_qos_remote_wakeup -Date: September 2012 -Contact: Rafael J. Wysocki -Description: - The /sys/devices/.../power/pm_qos_remote_wakeup attribute - is used for manipulating the PM QoS "remote wakeup required" - flag. If set, this flag indicates to the kernel that the - device is a source of user events that have to be signaled from - its low-power states. - - Not all drivers support this attribute. If it isn't supported, - it is not present. - - This attribute has no effect on system-wide suspend/resume and - hibernation. diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 21d2d48f87a2..19c5f7b1a7ba 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -98,8 +98,7 @@ Values are updated in response to changes of the request list. The target values of resume latency and active state latency tolerance are simply the minimum of the request values held in the parameter list elements. The PM QoS flags aggregate value is a gather (bitwise OR) of all list elements' -values. Two device PM QoS flags are defined currently: PM_QOS_FLAG_NO_POWER_OFF -and PM_QOS_FLAG_REMOTE_WAKEUP. +values. One device PM QoS flag is defined currently: PM_QOS_FLAG_NO_POWER_OFF. Note: The aggregated target values are implemented in such a way that reading the aggregated value does not require any locking mechanism. @@ -153,14 +152,14 @@ PM QoS list of resume latency constraints and remove sysfs attribute pm_qos_resume_latency_us from the device's power directory. int dev_pm_qos_expose_flags(device, value) -Add a request to the device's PM QoS list of flags and create sysfs attributes -pm_qos_no_power_off and pm_qos_remote_wakeup under the device's power directory -allowing user space to change these flags' value. +Add a request to the device's PM QoS list of flags and create sysfs attribute +pm_qos_no_power_off under the device's power directory allowing user space to +change the value of the PM_QOS_FLAG_NO_POWER_OFF flag. void dev_pm_qos_hide_flags(device) Drop the request added by dev_pm_qos_expose_flags() from the device's PM QoS list -of flags and remove sysfs attributes pm_qos_no_power_off and pm_qos_remote_wakeup -under the device's power directory. +of flags and remove sysfs attribute pm_qos_no_power_off from the device's power +directory. Notification mechanisms: The per-device PM QoS framework has a per-device notification tree. diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index fbcc73f7a099..e8c820129797 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -581,8 +581,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, d_min = ret; wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid && adev->wakeup.sleep_state >= target_state; - } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != - PM_QOS_FLAGS_NONE) { + } else { wakeup = adev->wakeup.flags.valid; } @@ -865,8 +864,7 @@ int acpi_dev_runtime_suspend(struct device *dev) if (!adev) return 0; - remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > - PM_QOS_FLAGS_NONE; + remote_wakeup = acpi_device_can_wakeup(adev); if (remote_wakeup) { error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0); if (error) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e8ca5e2cf1e5..e6414e9998bb 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -346,9 +346,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, list_for_each_entry(pdd, &genpd->dev_list, list_node) { enum pm_qos_flags_status stat; - stat = dev_pm_qos_flags(pdd->dev, - PM_QOS_FLAG_NO_POWER_OFF - | PM_QOS_FLAG_REMOTE_WAKEUP); + stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF); if (stat > PM_QOS_FLAGS_NONE) return -EBUSY; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 156ab57bca77..29bf28fef136 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -309,33 +309,6 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev, static DEVICE_ATTR(pm_qos_no_power_off, 0644, pm_qos_no_power_off_show, pm_qos_no_power_off_store); -static ssize_t pm_qos_remote_wakeup_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) - & PM_QOS_FLAG_REMOTE_WAKEUP)); -} - -static ssize_t pm_qos_remote_wakeup_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t n) -{ - int ret; - - if (kstrtoint(buf, 0, &ret)) - return -EINVAL; - - if (ret != 0 && ret != 1) - return -EINVAL; - - ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); - return ret < 0 ? ret : n; -} - -static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, - pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); - #ifdef CONFIG_PM_SLEEP static const char _enabled[] = "enabled"; static const char _disabled[] = "disabled"; @@ -671,7 +644,6 @@ static const struct attribute_group pm_qos_latency_tolerance_attr_group = { static struct attribute *pm_qos_flags_attrs[] = { &dev_attr_pm_qos_no_power_off.attr, - &dev_attr_pm_qos_remote_wakeup.attr, NULL, }; static const struct attribute_group pm_qos_flags_attr_group = { diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 032b55909145..51f0d7e0b15f 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -39,7 +39,6 @@ enum pm_qos_flags_status { #define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1)) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) -#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) struct pm_qos_request { struct plist_node node; -- cgit v1.2.3 From 95a20ef6f7e54c6a982715a7d0da2fd81790db28 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:11 +0100 Subject: 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(-) (limited to 'drivers/base/power/domain.c') 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 */ -- cgit v1.2.3 From d0af45f1f6528949e05385976eb61c5ebd31854e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:15 +0100 Subject: 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(-) (limited to 'drivers/base/power/domain.c') 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 { -- cgit v1.2.3 From 704d2ce6603f7e40bb607ae9452ff18a4cec701f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Nov 2017 02:23:18 +0100 Subject: 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(-) (limited to 'drivers/base/power/domain.c') 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; -- cgit v1.2.3 From 0759e80b84e34a84e7e46e2b1adb528c83d84a47 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Nov 2017 11:33:49 +0100 Subject: PM / QoS: Fix device resume latency framework The special value of 0 for device resume latency PM QoS means "no restriction", but there are two problems with that. First, device resume latency PM QoS requests with 0 as the value are always put in front of requests with positive values in the priority lists used internally by the PM QoS framework, causing 0 to be chosen as an effective constraint value. However, that 0 is then interpreted as "no restriction" effectively overriding the other requests with specific restrictions which is incorrect. Second, the users of device resume latency PM QoS have no way to specify that *any* resume latency at all should be avoided, which is an artificial limitation in general. To address these issues, modify device resume latency PM QoS to use S32_MAX as the "no constraint" value and 0 as the "no latency at all" one and rework its users (the cpuidle menu governor, the genpd QoS governor and the runtime PM framework) to follow these changes. Also add a special "n/a" value to the corresponding user space I/F to allow user space to indicate that it cannot accept any resume latencies at all for the given device. Fixes: 85dc0b8a4019 (PM / QoS: Make it possible to expose PM QoS latency constraints) Link: https://bugzilla.kernel.org/show_bug.cgi?id=197323 Reported-by: Reinette Chatre Signed-off-by: Rafael J. Wysocki Tested-by: Reinette Chatre Tested-by: Geert Uytterhoeven Tested-by: Tero Kristo Reviewed-by: Ramesh Thomas --- Documentation/ABI/testing/sysfs-devices-power | 4 ++- drivers/base/cpu.c | 3 +- drivers/base/power/domain.c | 2 +- drivers/base/power/domain_governor.c | 40 +++++++++++---------------- drivers/base/power/qos.c | 5 +++- drivers/base/power/runtime.c | 2 +- drivers/base/power/sysfs.c | 25 ++++++++++++++--- drivers/cpuidle/governors/menu.c | 4 +-- include/linux/pm_qos.h | 26 +++++++++++------ 9 files changed, 68 insertions(+), 43 deletions(-) (limited to 'drivers/base/power/domain.c') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index f4b24c327665..80a00f7b6667 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -211,7 +211,9 @@ Description: device, after it has been suspended at run time, from a resume request to the moment the device will be ready to process I/O, in microseconds. If it is equal to 0, however, this means that - the PM QoS resume latency may be arbitrary. + the PM QoS resume latency may be arbitrary and the special value + "n/a" means that user space cannot accept any resume latency at + all for the given device. Not all drivers support this attribute. If it isn't supported, it is not present. diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 321cd7b4d817..227bac5f1191 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -377,7 +377,8 @@ int register_cpu(struct cpu *cpu, int num) per_cpu(cpu_sys_devices, num) = &cpu->dev; register_cpu_under_node(num, cpu_to_node(num)); - dev_pm_qos_expose_latency_limit(&cpu->dev, 0); + dev_pm_qos_expose_latency_limit(&cpu->dev, + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); return 0; } diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 679c79545e42..24e39ce27bd8 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1326,7 +1326,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 = 0; + gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; 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 e4cca8adab32..99896fbf18e4 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -33,15 +33,10 @@ static int dev_update_qos_constraint(struct device *dev, void *data) * known at this point anyway). */ constraint_ns = dev_pm_qos_read_value(dev); - if (constraint_ns > 0) - constraint_ns *= NSEC_PER_USEC; + constraint_ns *= NSEC_PER_USEC; } - /* 0 means "no constraint" */ - if (constraint_ns == 0) - return 0; - - if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) + if (constraint_ns < *constraint_ns_p) *constraint_ns_p = constraint_ns; return 0; @@ -69,12 +64,12 @@ static bool default_suspend_ok(struct device *dev) } td->constraint_changed = false; td->cached_suspend_ok = false; - td->effective_constraint_ns = -1; + td->effective_constraint_ns = 0; constraint_ns = __dev_pm_qos_read_value(dev); spin_unlock_irqrestore(&dev->power.lock, flags); - if (constraint_ns < 0) + if (constraint_ns == 0) return false; constraint_ns *= NSEC_PER_USEC; @@ -87,25 +82,25 @@ 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 == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) { /* "No restriction", so the device is allowed to suspend. */ - td->effective_constraint_ns = 0; + td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; td->cached_suspend_ok = true; - } else if (constraint_ns < 0) { + } 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. + * domain has a zero PM QoS constraint and it's better not to + * suspend then. effective_constraint_ns is zero already and + * cached_suspend_ok is false, so bail out. */ return false; } else { constraint_ns -= td->suspend_latency_ns + td->resume_latency_ns; /* - * effective_constraint_ns is negative already and - * cached_suspend_ok is false, so if the computed value is not - * positive, return right away. + * effective_constraint_ns is zero already and cached_suspend_ok + * is false, so if the computed value is not positive, return + * right away. */ if (constraint_ns <= 0) return false; @@ -174,13 +169,10 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, td = &to_gpd_data(pdd)->td; constraint_ns = td->effective_constraint_ns; /* - * 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" + * Zero means "no suspend at all" and this runs only when all + * devices in the domain are suspended, so it must be positive. */ - if (constraint_ns == 0) + if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) continue; if (constraint_ns <= off_on_time_ns) diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 277d43a83f53..3382542b39b7 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -139,6 +139,9 @@ static int apply_constraint(struct dev_pm_qos_request *req, switch(req->type) { case DEV_PM_QOS_RESUME_LATENCY: + if (WARN_ON(action != PM_QOS_REMOVE_REQ && value < 0)) + value = 0; + ret = pm_qos_update_target(&qos->resume_latency, &req->data.pnode, action, value); break; @@ -189,7 +192,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) plist_head_init(&c->list); c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; - c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; + c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; c->type = PM_QOS_MIN; c->notifiers = n; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 7bcf80fa9ada..13e015905543 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -253,7 +253,7 @@ static int rpm_check_suspend_allowed(struct device *dev) || (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)) retval = -EAGAIN; - else if (__dev_pm_qos_read_value(dev) < 0) + else if (__dev_pm_qos_read_value(dev) == 0) retval = -EPERM; else if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 29bf28fef136..e153e28b1857 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -218,7 +218,14 @@ static ssize_t pm_qos_resume_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev)); + s32 value = dev_pm_qos_requested_resume_latency(dev); + + if (value == 0) + return sprintf(buf, "n/a\n"); + else if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) + value = 0; + + return sprintf(buf, "%d\n", value); } static ssize_t pm_qos_resume_latency_store(struct device *dev, @@ -228,11 +235,21 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev, s32 value; int ret; - if (kstrtos32(buf, 0, &value)) - return -EINVAL; + if (!kstrtos32(buf, 0, &value)) { + /* + * Prevent users from writing negative or "no constraint" values + * directly. + */ + if (value < 0 || value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) + return -EINVAL; - if (value < 0) + if (value == 0) + value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; + } else if (!strcmp(buf, "n/a") || !strcmp(buf, "n/a\n")) { + value = 0; + } else { return -EINVAL; + } ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req, value); diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 48eaf2879228..aa390404e85f 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) data->needs_update = 0; } - /* resume_latency is 0 means no restriction */ - if (resume_latency && resume_latency < latency_req) + if (resume_latency < latency_req && + resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) latency_req = resume_latency; /* Special case when user has set very strict latency requirement */ diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 51f0d7e0b15f..2a3b36da61b1 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -27,16 +27,19 @@ enum pm_qos_flags_status { PM_QOS_FLAGS_ALL, }; -#define PM_QOS_DEFAULT_VALUE -1 +#define PM_QOS_DEFAULT_VALUE (-1) +#define PM_QOS_LATENCY_ANY S32_MAX +#define PM_QOS_LATENCY_ANY_NS ((s64)PM_QOS_LATENCY_ANY * NSEC_PER_USEC) #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 #define PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE 0 -#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE 0 +#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY +#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY +#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) -#define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1)) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) @@ -173,7 +176,8 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev) static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return IS_ERR_OR_NULL(dev->power.qos) ? - 0 : pm_qos_read_value(&dev->power.qos->resume_latency); + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT : + pm_qos_read_value(&dev->power.qos->resume_latency); } #else static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, @@ -183,9 +187,9 @@ static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) { return PM_QOS_FLAGS_UNDEFINED; } static inline s32 __dev_pm_qos_read_value(struct device *dev) - { return 0; } + { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; } static inline s32 dev_pm_qos_read_value(struct device *dev) - { return 0; } + { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; } static inline int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, @@ -231,9 +235,15 @@ static inline int dev_pm_qos_expose_latency_tolerance(struct device *dev) { return 0; } static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} -static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) +{ + return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; +} static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } -static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_raw_read_value(struct device *dev) +{ + return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; +} #endif #endif -- cgit v1.2.3 From 5241ab40f6e742f8a1631f8826faf6dc6412b3b5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 8 Nov 2017 10:11:02 +0100 Subject: PM / Domains: Fix genpd to deal with drivers returning 1 from ->prepare() During system-wide PM, genpd relies on its PM callbacks to be invoked for all its attached devices, as to deal with powering off/on the PM domain. In other words, genpd is not compatible with the direct_complete path, if executed by the PM core for any of its attached devices. However, when genpd's ->prepare() callback invokes pm_generic_prepare(), it does not take into account that it may return 1. Instead it treats that as an error internally and expects the PM core to abort the prepare phase and roll back. This leads to genpd not properly powering on/off the PM domain, because its internal counters gets wrongly balanced. To fix the behaviour, allow drivers to return 1 from their ->prepare() callbacks, but let's return 0 from genpd's ->prepare() callback in such case, as that prevents the PM core from running the direct_complete path for the device. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/base/power/domain.c') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b914e373a478..47fb71a8066a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1010,7 +1010,7 @@ static int genpd_prepare(struct device *dev) genpd_unlock(genpd); ret = pm_generic_prepare(dev); - if (ret) { + if (ret < 0) { genpd_lock(genpd); genpd->prepared_count--; @@ -1018,7 +1018,8 @@ static int genpd_prepare(struct device *dev) genpd_unlock(genpd); } - return ret; + /* Never return 1, as genpd don't cope with the direct_complete path. */ + return ret >= 0 ? 0 : ret; } /** -- cgit v1.2.3