From 240c7337a4cd3d91b196c5ef97ad461b3a22fa09 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 23 Mar 2010 00:50:07 +0100 Subject: PM: Allow runtime_suspend methods to call pm_schedule_suspend() This patch (as1361) changes the runtime PM interface slightly; it allows suspend requests to be scheduled while the runtime_suspend method is running. If the method succeeds then the scheduled request is cancelled, whereas if the method fails then an idle notification is sent only if no request was scheduled. Being able to schedule suspend requests from within a runtime_suspend method is useful for drivers that need to test for idleness and suspend the device all while holding a single spinlock, or for drivers that want to check for idleness by polling. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 626dd147b75f..b0ec0e9f27e9 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -229,14 +229,16 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq) if (retval) { dev->power.runtime_status = RPM_ACTIVE; - pm_runtime_cancel_pending(dev); - if (retval == -EAGAIN || retval == -EBUSY) { - notify = true; + if (dev->power.timer_expires == 0) + notify = true; dev->power.runtime_error = 0; + } else { + pm_runtime_cancel_pending(dev); } } else { dev->power.runtime_status = RPM_SUSPENDED; + pm_runtime_deactivate_timer(dev); if (dev->parent) { parent = dev->parent; @@ -659,8 +661,6 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay) if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; - else if (dev->power.runtime_status == RPM_SUSPENDING) - retval = -EINPROGRESS; else if (atomic_read(&dev->power.usage_count) > 0 || dev->power.disable_depth > 0) retval = -EAGAIN; -- cgit v1.2.3 From c92445fadb9179d811b5cb044947ad4712403541 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Fri, 23 Apr 2010 20:32:23 +0200 Subject: PM / Runtime: Add sysfs debug files Add a few sysfs files relating to runtime power management for advanced debug purposes: runtime_enabled: is runtime PM enabled for this device? States are "enabled", "disabled", "forbidden" or a combination of the latter two. runtime_status: what state is the device in currently? E.g., it reports "suspended" for runtime-suspended devices, and "active" for active devices. NOTE: if runtime_enabled returns "disabled", the value of this file may not reflect its physical state. runtime_usage: the runtime PM usage count of a device runtime_active_kids: the runtime PM children usage count of a device, or 0 if the ignore_children flag is set. Also, CONFIG_PM_SLEEP_ADVANCED_DEBUG is not defined in any Kconfig file, so replace it with CONFIG_PM_ADVANCED_DEBUG. Signed-off-by: Dominik Brodowski Acked-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- drivers/base/power/sysfs.c | 65 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 86fd9373447e..a4c33bc51257 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "power.h" /* @@ -143,7 +144,59 @@ wake_store(struct device * dev, struct device_attribute *attr, static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_ADVANCED_DEBUG +#ifdef CONFIG_PM_RUNTIME + +static ssize_t rtpm_usagecount_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&dev->power.usage_count)); +} + +static ssize_t rtpm_children_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev->power.ignore_children ? + 0 : atomic_read(&dev->power.child_count)); +} + +static ssize_t rtpm_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if ((dev->power.disable_depth) && (dev->power.runtime_auto == false)) + return sprintf(buf, "disabled & forbidden\n"); + else if (dev->power.disable_depth) + return sprintf(buf, "disabled\n"); + else if (dev->power.runtime_auto == false) + return sprintf(buf, "forbidden\n"); + return sprintf(buf, "enabled\n"); +} + +static ssize_t rtpm_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (dev->power.runtime_error) + return sprintf(buf, "error\n"); + switch (dev->power.runtime_status) { + case RPM_SUSPENDED: + return sprintf(buf, "suspended\n"); + case RPM_SUSPENDING: + return sprintf(buf, "suspending\n"); + case RPM_RESUMING: + return sprintf(buf, "resuming\n"); + case RPM_ACTIVE: + return sprintf(buf, "active\n"); + } + return -EIO; +} + +static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL); +static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL); +static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL); +static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL); + +#endif + static ssize_t async_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -170,15 +223,21 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(async, 0644, async_show, async_store); -#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ +#endif /* CONFIG_PM_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_control.attr, #endif &dev_attr_wakeup.attr, -#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +#ifdef CONFIG_PM_ADVANCED_DEBUG &dev_attr_async.attr, +#ifdef CONFIG_PM_RUNTIME + &dev_attr_runtime_usage.attr, + &dev_attr_runtime_active_kids.attr, + &dev_attr_runtime_status.attr, + &dev_attr_runtime_enabled.attr, +#endif #endif NULL, }; -- cgit v1.2.3 From ed77134bfccf5e75b6cbadab268e559dbe6a4ebb Mon Sep 17 00:00:00 2001 From: Mark Gross Date: Thu, 6 May 2010 01:59:26 +0200 Subject: PM QOS update This patch changes the string based list management to a handle base implementation to help with the hot path use of pm-qos, it also renames much of the API to use "request" as opposed to "requirement" that was used in the initial implementation. I did this because request more accurately represents what it actually does. Also, I added a string based ABI for users wanting to use a string interface. So if the user writes 0xDDDDDDDD formatted hex it will be accepted by the interface. (someone asked me for it and I don't think it hurts anything.) This patch updates some documentation input I got from Randy. Signed-off-by: markgross Signed-off-by: Rafael J. Wysocki --- Documentation/power/pm_qos_interface.txt | 48 +++---- drivers/acpi/processor_idle.c | 2 +- drivers/cpuidle/governors/ladder.c | 2 +- drivers/cpuidle/governors/menu.c | 2 +- drivers/net/e1000e/netdev.c | 22 ++-- drivers/net/igbvf/netdev.c | 6 +- drivers/net/wireless/ipw2x00/ipw2100.c | 11 +- include/linux/netdevice.h | 4 + include/linux/pm_qos_params.h | 14 +- include/sound/pcm.h | 3 +- kernel/pm_qos_params.c | 214 +++++++++++++++---------------- net/mac80211/mlme.c | 2 +- sound/core/pcm.c | 3 - sound/core/pcm_native.c | 14 +- 14 files changed, 176 insertions(+), 171 deletions(-) (limited to 'drivers') diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index c40866e8b957..bfed898a03fc 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -18,44 +18,46 @@ and pm_qos_params.h. This is done because having the available parameters being runtime configurable or changeable from a driver was seen as too easy to abuse. -For each parameter a list of performance requirements is maintained along with +For each parameter a list of performance requests is maintained along with an aggregated target value. The aggregated target value is updated with -changes to the requirement list or elements of the list. Typically the -aggregated target value is simply the max or min of the requirement values held +changes to the request list or elements of the list. Typically the +aggregated target value is simply the max or min of the request values held in the parameter list elements. From kernel mode the use of this interface is simple: -pm_qos_add_requirement(param_id, name, target_value): -Will insert a named element in the list for that identified PM_QOS parameter -with the target value. Upon change to this list the new target is recomputed -and any registered notifiers are called only if the target value is now -different. -pm_qos_update_requirement(param_id, name, new_target_value): -Will search the list identified by the param_id for the named list element and -then update its target value, calling the notification tree if the aggregated -target is changed. with that name is already registered. +handle = pm_qos_add_request(param_class, target_value): +Will insert an element into the list for that identified PM_QOS class with the +target value. Upon change to this list the new target is recomputed and any +registered notifiers are called only if the target value is now different. +Clients of pm_qos need to save the returned handle. -pm_qos_remove_requirement(param_id, name): -Will search the identified list for the named element and remove it, after -removal it will update the aggregate target and call the notification tree if -the target was changed as a result of removing the named requirement. +void pm_qos_update_request(handle, new_target_value): +Will update the list element pointed to by the handle with the new target value +and recompute the new aggregated target, calling the notification tree if the +target is changed. + +void pm_qos_remove_request(handle): +Will remove the element. After removal it will update the aggregate target and +call the notification tree if the target was changed as a result of removing +the request. From user mode: -Only processes can register a pm_qos requirement. To provide for automatic -cleanup for process the interface requires the process to register its -parameter requirements in the following way: +Only processes can register a pm_qos request. To provide for automatic +cleanup of a process, the interface requires the process to register its +parameter requests in the following way: To register the default pm_qos target for the specific parameter, the process must open one of /dev/[cpu_dma_latency, network_latency, network_throughput] As long as the device node is held open that process has a registered -requirement on the parameter. The name of the requirement is "process_" -derived from the current->pid from within the open system call. +request on the parameter. -To change the requested target value the process needs to write a s32 value to -the open device node. This translates to a pm_qos_update_requirement call. +To change the requested target value the process needs to write an s32 value to +the open device node. Alternatively the user mode program could write a hex +string for the value using 10 char long format e.g. "0x12345678". This +translates to a pm_qos_update_request call. To remove the user mode request for a target value simply close the device node. diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 5939e7f7d8e9..c3817e1f32c7 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -698,7 +698,7 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) "max_cstate: C%d\n" "maximum allowed latency: %d usec\n", pr->power.state ? pr->power.state - pr->power.states : 0, - max_cstate, pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)); + max_cstate, pm_qos_request(PM_QOS_CPU_DMA_LATENCY)); seq_puts(seq, "states:\n"); diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index 1c1ceb4f218f..12c98900dcf8 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c @@ -67,7 +67,7 @@ static int ladder_select_state(struct cpuidle_device *dev) struct ladder_device *ldev = &__get_cpu_var(ladder_devices); struct ladder_device_state *last_state; int last_residency, last_idx = ldev->last_state_idx; - int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); /* Special case when user has set very strict latency requirement */ if (unlikely(latency_req == 0)) { diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index f8e57c6303f2..b81ad9c731ae 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -182,7 +182,7 @@ static u64 div_round64(u64 dividend, u32 divisor) static int menu_select(struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); - int latency_req = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); int i; int multiplier; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index dbf81788bb40..d5d55c6a373f 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -2524,12 +2524,12 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) * excessive C-state transition latencies result in * dropped transactions. */ - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, 55); + pm_qos_update_request( + adapter->netdev->pm_qos_req, 55); } else { - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, - PM_QOS_DEFAULT_VALUE); + pm_qos_update_request( + adapter->netdev->pm_qos_req, + PM_QOS_DEFAULT_VALUE); } } @@ -2824,8 +2824,8 @@ int e1000e_up(struct e1000_adapter *adapter) /* DMA latency requirement to workaround early-receive/jumbo issue */ if (adapter->flags & FLAG_HAS_ERT) - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name, + adapter->netdev->pm_qos_req = + pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); /* hardware has been reset, we need to reload some things */ @@ -2887,9 +2887,11 @@ void e1000e_down(struct e1000_adapter *adapter) e1000_clean_tx_ring(adapter); e1000_clean_rx_ring(adapter); - if (adapter->flags & FLAG_HAS_ERT) - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - adapter->netdev->name); + if (adapter->flags & FLAG_HAS_ERT) { + pm_qos_remove_request( + adapter->netdev->pm_qos_req); + adapter->netdev->pm_qos_req = NULL; + } /* * TODO: for power management, we could drop the link and diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c index 1b1edad1eb5e..f16e981812a9 100644 --- a/drivers/net/igbvf/netdev.c +++ b/drivers/net/igbvf/netdev.c @@ -48,6 +48,7 @@ #define DRV_VERSION "1.0.0-k0" char igbvf_driver_name[] = "igbvf"; const char igbvf_driver_version[] = DRV_VERSION; +struct pm_qos_request_list *igbvf_driver_pm_qos_req; static const char igbvf_driver_string[] = "Intel(R) Virtual Function Network Driver"; static const char igbvf_copyright[] = "Copyright (c) 2009 Intel Corporation."; @@ -2899,7 +2900,7 @@ static int __init igbvf_init_module(void) printk(KERN_INFO "%s\n", igbvf_copyright); ret = pci_register_driver(&igbvf_driver); - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, igbvf_driver_name, + igbvf_driver_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); return ret; @@ -2915,7 +2916,8 @@ module_init(igbvf_init_module); static void __exit igbvf_exit_module(void) { pci_unregister_driver(&igbvf_driver); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, igbvf_driver_name); + pm_qos_remove_request(igbvf_driver_pm_qos_req); + igbvf_driver_pm_qos_req = NULL; } module_exit(igbvf_exit_module); diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 9b72c45a7748..2b05fe5e994c 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -174,6 +174,8 @@ that only one external action is invoked at a time. #define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" +struct pm_qos_request_list *ipw2100_pm_qos_req; + /* Debugging stuff */ #ifdef CONFIG_IPW2100_DEBUG #define IPW2100_RX_DEBUG /* Reception debugging */ @@ -1739,7 +1741,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* the ipw2100 hardware really doesn't want power management delays * longer than 175usec */ - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", 175); + pm_qos_update_request(ipw2100_pm_qos_req, 175); /* If the interrupt is enabled, turn it off... */ spin_lock_irqsave(&priv->low_lock, flags); @@ -1887,8 +1889,7 @@ static void ipw2100_down(struct ipw2100_priv *priv) ipw2100_disable_interrupts(priv); spin_unlock_irqrestore(&priv->low_lock, flags); - pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", - PM_QOS_DEFAULT_VALUE); + pm_qos_update_request(ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); /* We have to signal any supplicant if we are disassociating */ if (associated) @@ -6669,7 +6670,7 @@ static int __init ipw2100_init(void) if (ret) goto out; - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100", + ipw2100_pm_qos_req = pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); #ifdef CONFIG_IPW2100_DEBUG ipw2100_debug_level = debug; @@ -6692,7 +6693,7 @@ static void __exit ipw2100_exit(void) &driver_attr_debug_level); #endif pci_unregister_driver(&ipw2100_pci_driver); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, "ipw2100"); + pm_qos_remove_request(ipw2100_pm_qos_req); } module_init(ipw2100_init); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fa8b47637997..3857517f1ca5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -31,6 +31,7 @@ #include #ifdef __KERNEL__ +#include #include #include #include @@ -711,6 +712,9 @@ struct net_device { * the interface. */ char name[IFNAMSIZ]; + + struct pm_qos_request_list *pm_qos_req; + /* device name hash chain */ struct hlist_node name_hlist; /* snmp alias */ diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h index d74f75ed1e47..8ba440e5eb7f 100644 --- a/include/linux/pm_qos_params.h +++ b/include/linux/pm_qos_params.h @@ -14,12 +14,14 @@ #define PM_QOS_NUM_CLASSES 4 #define PM_QOS_DEFAULT_VALUE -1 -int pm_qos_add_requirement(int qos, char *name, s32 value); -int pm_qos_update_requirement(int qos, char *name, s32 new_value); -void pm_qos_remove_requirement(int qos, char *name); +struct pm_qos_request_list; -int pm_qos_requirement(int qos); +struct pm_qos_request_list *pm_qos_add_request(int pm_qos_class, s32 value); +void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req, + s32 new_value); +void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req); -int pm_qos_add_notifier(int qos, struct notifier_block *notifier); -int pm_qos_remove_notifier(int qos, struct notifier_block *notifier); +int pm_qos_request(int pm_qos_class); +int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); +int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 8b611a561985..dd76cdede64d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -29,6 +29,7 @@ #include #include #include +#include #define snd_pcm_substream_chip(substream) ((substream)->private_data) #define snd_pcm_chip(pcm) ((pcm)->private_data) @@ -365,7 +366,7 @@ struct snd_pcm_substream { int number; char name[32]; /* substream name */ int stream; /* stream (direction) */ - char latency_id[20]; /* latency identifier */ + struct pm_qos_request_list *latency_pm_qos_req; /* pm_qos request */ size_t buffer_bytes_max; /* limit ring buffer size */ struct snd_dma_buffer dma_buffer; unsigned int dma_buf_id; diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c index 3db49b9ca374..a1aea040eb57 100644 --- a/kernel/pm_qos_params.c +++ b/kernel/pm_qos_params.c @@ -2,7 +2,7 @@ * This module exposes the interface to kernel space for specifying * QoS dependencies. It provides infrastructure for registration of: * - * Dependents on a QoS value : register requirements + * Dependents on a QoS value : register requests * Watchers of QoS value : get notified when target QoS value changes * * This QoS design is best effort based. Dependents register their QoS needs. @@ -14,19 +14,21 @@ * timeout: usec <-- currently not used. * throughput: kbs (kilo byte / sec) * - * There are lists of pm_qos_objects each one wrapping requirements, notifiers + * There are lists of pm_qos_objects each one wrapping requests, notifiers * - * User mode requirements on a QOS parameter register themselves to the + * User mode requests on a QOS parameter register themselves to the * subsystem by opening the device node /dev/... and writing there request to * the node. As long as the process holds a file handle open to the node the * client continues to be accounted for. Upon file release the usermode - * requirement is removed and a new qos target is computed. This way when the - * requirement that the application has is cleaned up when closes the file + * request is removed and a new qos target is computed. This way when the + * request that the application has is cleaned up when closes the file * pointer or exits the pm_qos_object will get an opportunity to clean up. * * Mark Gross */ +/*#define DEBUG*/ + #include #include #include @@ -42,25 +44,25 @@ #include /* - * locking rule: all changes to requirements or notifiers lists + * locking rule: all changes to requests or notifiers lists * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock * held, taken with _irqsave. One lock to rule them all */ -struct requirement_list { +struct pm_qos_request_list { struct list_head list; union { s32 value; s32 usec; s32 kbps; }; - char *name; + int pm_qos_class; }; static s32 max_compare(s32 v1, s32 v2); static s32 min_compare(s32 v1, s32 v2); struct pm_qos_object { - struct requirement_list requirements; + struct pm_qos_request_list requests; struct blocking_notifier_head *notifiers; struct miscdevice pm_qos_power_miscdev; char *name; @@ -72,7 +74,7 @@ struct pm_qos_object { static struct pm_qos_object null_pm_qos; static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_object cpu_dma_pm_qos = { - .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, + .requests = {LIST_HEAD_INIT(cpu_dma_pm_qos.requests.list)}, .notifiers = &cpu_dma_lat_notifier, .name = "cpu_dma_latency", .default_value = 2000 * USEC_PER_SEC, @@ -82,7 +84,7 @@ static struct pm_qos_object cpu_dma_pm_qos = { static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); static struct pm_qos_object network_lat_pm_qos = { - .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, + .requests = {LIST_HEAD_INIT(network_lat_pm_qos.requests.list)}, .notifiers = &network_lat_notifier, .name = "network_latency", .default_value = 2000 * USEC_PER_SEC, @@ -93,8 +95,7 @@ static struct pm_qos_object network_lat_pm_qos = { static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); static struct pm_qos_object network_throughput_pm_qos = { - .requirements = - {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, + .requests = {LIST_HEAD_INIT(network_throughput_pm_qos.requests.list)}, .notifiers = &network_throughput_notifier, .name = "network_throughput", .default_value = 0, @@ -135,31 +136,34 @@ static s32 min_compare(s32 v1, s32 v2) } -static void update_target(int target) +static void update_target(int pm_qos_class) { s32 extreme_value; - struct requirement_list *node; + struct pm_qos_request_list *node; unsigned long flags; int call_notifier = 0; spin_lock_irqsave(&pm_qos_lock, flags); - extreme_value = pm_qos_array[target]->default_value; + extreme_value = pm_qos_array[pm_qos_class]->default_value; list_for_each_entry(node, - &pm_qos_array[target]->requirements.list, list) { - extreme_value = pm_qos_array[target]->comparitor( + &pm_qos_array[pm_qos_class]->requests.list, list) { + extreme_value = pm_qos_array[pm_qos_class]->comparitor( extreme_value, node->value); } - if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) { + if (atomic_read(&pm_qos_array[pm_qos_class]->target_value) != + extreme_value) { call_notifier = 1; - atomic_set(&pm_qos_array[target]->target_value, extreme_value); - pr_debug(KERN_ERR "new target for qos %d is %d\n", target, - atomic_read(&pm_qos_array[target]->target_value)); + atomic_set(&pm_qos_array[pm_qos_class]->target_value, + extreme_value); + pr_debug(KERN_ERR "new target for qos %d is %d\n", pm_qos_class, + atomic_read(&pm_qos_array[pm_qos_class]->target_value)); } spin_unlock_irqrestore(&pm_qos_lock, flags); if (call_notifier) - blocking_notifier_call_chain(pm_qos_array[target]->notifiers, - (unsigned long) extreme_value, NULL); + blocking_notifier_call_chain( + pm_qos_array[pm_qos_class]->notifiers, + (unsigned long) extreme_value, NULL); } static int register_pm_qos_misc(struct pm_qos_object *qos) @@ -185,125 +189,110 @@ static int find_pm_qos_object_by_minor(int minor) } /** - * pm_qos_requirement - returns current system wide qos expectation + * pm_qos_request - returns current system wide qos expectation * @pm_qos_class: identification of which qos value is requested * * This function returns the current target value in an atomic manner. */ -int pm_qos_requirement(int pm_qos_class) +int pm_qos_request(int pm_qos_class) { return atomic_read(&pm_qos_array[pm_qos_class]->target_value); } -EXPORT_SYMBOL_GPL(pm_qos_requirement); +EXPORT_SYMBOL_GPL(pm_qos_request); /** - * pm_qos_add_requirement - inserts new qos request into the list + * pm_qos_add_request - inserts new qos request into the list * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request * @value: defines the qos request * * This function inserts a new entry in the pm_qos_class list of requested qos * performance characteristics. It recomputes the aggregate QoS expectations - * for the pm_qos_class of parameters. + * for the pm_qos_class of parameters, and returns the pm_qos_request list + * element as a handle for use in updating and removal. Call needs to save + * this handle for later use. */ -int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) +struct pm_qos_request_list *pm_qos_add_request(int pm_qos_class, s32 value) { - struct requirement_list *dep; + struct pm_qos_request_list *dep; unsigned long flags; - dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); + dep = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL); if (dep) { if (value == PM_QOS_DEFAULT_VALUE) dep->value = pm_qos_array[pm_qos_class]->default_value; else dep->value = value; - dep->name = kstrdup(name, GFP_KERNEL); - if (!dep->name) - goto cleanup; + dep->pm_qos_class = pm_qos_class; spin_lock_irqsave(&pm_qos_lock, flags); list_add(&dep->list, - &pm_qos_array[pm_qos_class]->requirements.list); + &pm_qos_array[pm_qos_class]->requests.list); spin_unlock_irqrestore(&pm_qos_lock, flags); update_target(pm_qos_class); - - return 0; } -cleanup: - kfree(dep); - return -ENOMEM; + return dep; } -EXPORT_SYMBOL_GPL(pm_qos_add_requirement); +EXPORT_SYMBOL_GPL(pm_qos_add_request); /** - * pm_qos_update_requirement - modifies an existing qos request - * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request + * pm_qos_update_request - modifies an existing qos request + * @pm_qos_req : handle to list element holding a pm_qos request to use * @value: defines the qos request * - * Updates an existing qos requirement for the pm_qos_class of parameters along + * Updates an existing qos request for the pm_qos_class of parameters along * with updating the target pm_qos_class value. * - * If the named request isn't in the list then no change is made. + * Attempts are made to make this code callable on hot code paths. */ -int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) +void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req, + s32 new_value) { unsigned long flags; - struct requirement_list *node; int pending_update = 0; + s32 temp; spin_lock_irqsave(&pm_qos_lock, flags); - list_for_each_entry(node, - &pm_qos_array[pm_qos_class]->requirements.list, list) { - if (strcmp(node->name, name) == 0) { - if (new_value == PM_QOS_DEFAULT_VALUE) - node->value = - pm_qos_array[pm_qos_class]->default_value; - else - node->value = new_value; - pending_update = 1; - break; - } + if (new_value == PM_QOS_DEFAULT_VALUE) + temp = pm_qos_array[pm_qos_req->pm_qos_class]->default_value; + else + temp = new_value; + + if (temp != pm_qos_req->value) { + pending_update = 1; + pm_qos_req->value = temp; } spin_unlock_irqrestore(&pm_qos_lock, flags); if (pending_update) - update_target(pm_qos_class); - - return 0; + update_target(pm_qos_req->pm_qos_class); } -EXPORT_SYMBOL_GPL(pm_qos_update_requirement); +EXPORT_SYMBOL_GPL(pm_qos_update_request); /** - * pm_qos_remove_requirement - modifies an existing qos request - * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request + * pm_qos_remove_request - modifies an existing qos request + * @pm_qos_req: handle to request list element * - * Will remove named qos request from pm_qos_class list of parameters and - * recompute the current target value for the pm_qos_class. + * Will remove pm qos request from the list of requests and + * recompute the current target value for the pm_qos_class. Call this + * on slow code paths. */ -void pm_qos_remove_requirement(int pm_qos_class, char *name) +void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req) { unsigned long flags; - struct requirement_list *node; - int pending_update = 0; + int qos_class; + + if (pm_qos_req == NULL) + return; + /* silent return to keep pcm code cleaner */ + qos_class = pm_qos_req->pm_qos_class; spin_lock_irqsave(&pm_qos_lock, flags); - list_for_each_entry(node, - &pm_qos_array[pm_qos_class]->requirements.list, list) { - if (strcmp(node->name, name) == 0) { - kfree(node->name); - list_del(&node->list); - kfree(node); - pending_update = 1; - break; - } - } + list_del(&pm_qos_req->list); + kfree(pm_qos_req); spin_unlock_irqrestore(&pm_qos_lock, flags); - if (pending_update) - update_target(pm_qos_class); + update_target(qos_class); } -EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); +EXPORT_SYMBOL_GPL(pm_qos_remove_request); /** * pm_qos_add_notifier - sets notification entry for changes to target value @@ -313,7 +302,7 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); * will register the notifier into a notification chain that gets called * upon changes to the pm_qos_class target value. */ - int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) +int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { int retval; @@ -343,21 +332,16 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) } EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); -#define PID_NAME_LEN 32 - static int pm_qos_power_open(struct inode *inode, struct file *filp) { - int ret; long pm_qos_class; - char name[PID_NAME_LEN]; pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); if (pm_qos_class >= 0) { - filp->private_data = (void *)pm_qos_class; - snprintf(name, PID_NAME_LEN, "process_%d", current->pid); - ret = pm_qos_add_requirement(pm_qos_class, name, - PM_QOS_DEFAULT_VALUE); - if (ret >= 0) + filp->private_data = (void *) pm_qos_add_request(pm_qos_class, + PM_QOS_DEFAULT_VALUE); + + if (filp->private_data) return 0; } return -EPERM; @@ -365,32 +349,40 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) static int pm_qos_power_release(struct inode *inode, struct file *filp) { - int pm_qos_class; - char name[PID_NAME_LEN]; + struct pm_qos_request_list *req; - pm_qos_class = (long)filp->private_data; - snprintf(name, PID_NAME_LEN, "process_%d", current->pid); - pm_qos_remove_requirement(pm_qos_class, name); + req = (struct pm_qos_request_list *)filp->private_data; + pm_qos_remove_request(req); return 0; } + static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { s32 value; - int pm_qos_class; - char name[PID_NAME_LEN]; - - pm_qos_class = (long)filp->private_data; - if (count != sizeof(s32)) + int x; + char ascii_value[11]; + struct pm_qos_request_list *pm_qos_req; + + if (count == sizeof(s32)) { + if (copy_from_user(&value, buf, sizeof(s32))) + return -EFAULT; + } else if (count == 11) { /* len('0x12345678/0') */ + if (copy_from_user(ascii_value, buf, 11)) + return -EFAULT; + x = sscanf(ascii_value, "%x", &value); + if (x != 1) + return -EINVAL; + pr_debug(KERN_ERR "%s, %d, 0x%x\n", ascii_value, x, value); + } else return -EINVAL; - if (copy_from_user(&value, buf, sizeof(s32))) - return -EFAULT; - snprintf(name, PID_NAME_LEN, "process_%d", current->pid); - pm_qos_update_requirement(pm_qos_class, name, value); - return sizeof(s32); + pm_qos_req = (struct pm_qos_request_list *)filp->private_data; + pm_qos_update_request(pm_qos_req, value); + + return count; } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4aefa6dc3091..29de1965ff74 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -495,7 +495,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) s32 beaconint_us; if (latency < 0) - latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY); + latency = pm_qos_request(PM_QOS_NETWORK_LATENCY); beaconint_us = ieee80211_tu_to_usec( found->vif.bss_conf.beacon_int); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 0d428d0896db..cbe815dfbdc8 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -648,9 +648,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) substream->number = idx; substream->stream = stream; sprintf(substream->name, "subdevice #%i", idx); - snprintf(substream->latency_id, sizeof(substream->latency_id), - "ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device, - (stream ? 'c' : 'p'), idx); substream->buffer_bytes_max = UINT_MAX; if (prev == NULL) pstr->substream = substream; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 872887624030..605c86df71c5 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -481,11 +481,13 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_timer_resolution_change(substream); runtime->status->state = SNDRV_PCM_STATE_SETUP; - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + if (substream->latency_pm_qos_req) { + pm_qos_remove_request(substream->latency_pm_qos_req); + substream->latency_pm_qos_req = NULL; + } if ((usecs = period_to_usecs(runtime)) >= 0) - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id, usecs); + substream->latency_pm_qos_req = pm_qos_add_request( + PM_QOS_CPU_DMA_LATENCY, usecs); return 0; _error: /* hardware might be unuseable from this time, @@ -540,8 +542,8 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) if (substream->ops->hw_free) result = substream->ops->hw_free(substream); runtime->status->state = SNDRV_PCM_STATE_OPEN; - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + pm_qos_remove_request(substream->latency_pm_qos_req); + substream->latency_pm_qos_req = NULL; return result; } -- cgit v1.2.3 From 2f60ba706bd9af84c4eab704243b262e69556f2e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 10 May 2010 23:09:30 +0200 Subject: i2c: Fix bus-level power management callbacks There are three issues with the i2c bus type's power management callbacks at the moment. First, they don't include any hibernate callbacks, although they should at least include the .restore() callback (there's no guarantee that the driver will be present in memory before loading the image kernel and we must restore the pre-hibernation state of the device). Second, the "legacy" callbacks are not going to be invoked by the PM core since the bus type's pm object is not NULL. Finally, the system sleep PM (ie. suspend/resume) callbacks don't check if the device has been already suspended at run time, in which case they should skip suspending it. Also, it looks like the i2c bus type can use the generic subsystem-level runtime PM callbacks. For these reasons, rework the system sleep PM callbacks provided by the i2c bus type to handle hibernation correctly and to invoke the "legacy" callbacks for drivers that provide them. In addition to that make the i2c bus type use the generic subsystem-level runtime PM callbacks. Signed-off-by: Rafael J. Wysocki Acked-by: Mark Brown Acked-by: Jean Delvare --- drivers/i2c/i2c-core.c | 166 ++++++++++++++++++++++++++------------------- include/linux/pm_runtime.h | 7 ++ 2 files changed, 104 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index c2258a51fe0c..7c469a62c3c1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -159,107 +159,131 @@ static void i2c_device_shutdown(struct device *dev) driver->shutdown(client); } -#ifdef CONFIG_SUSPEND -static int i2c_device_pm_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg) { - const struct dev_pm_ops *pm; + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; - pm = dev->driver->pm; - if (!pm || !pm->suspend) + driver = to_i2c_driver(dev->driver); + if (!driver->suspend) return 0; - return pm->suspend(dev); + return driver->suspend(client, mesg); } -static int i2c_device_pm_resume(struct device *dev) +static int i2c_legacy_resume(struct device *dev) { - const struct dev_pm_ops *pm; + struct i2c_client *client = i2c_verify_client(dev); + struct i2c_driver *driver; - if (!dev->driver) + if (!client || !dev->driver) return 0; - pm = dev->driver->pm; - if (!pm || !pm->resume) + driver = to_i2c_driver(dev->driver); + if (!driver->resume) return 0; - return pm->resume(dev); + return driver->resume(client); } -#else -#define i2c_device_pm_suspend NULL -#define i2c_device_pm_resume NULL -#endif -#ifdef CONFIG_PM_RUNTIME -static int i2c_device_runtime_suspend(struct device *dev) +static int i2c_device_pm_suspend(struct device *dev) { - const struct dev_pm_ops *pm; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!dev->driver) - return 0; - pm = dev->driver->pm; - if (!pm || !pm->runtime_suspend) + if (pm_runtime_suspended(dev)) return 0; - return pm->runtime_suspend(dev); -} -static int i2c_device_runtime_resume(struct device *dev) -{ - const struct dev_pm_ops *pm; + if (pm) + return pm->suspend ? pm->suspend(dev) : 0; - if (!dev->driver) - return 0; - pm = dev->driver->pm; - if (!pm || !pm->runtime_resume) - return 0; - return pm->runtime_resume(dev); + return i2c_legacy_suspend(dev, PMSG_SUSPEND); } -static int i2c_device_runtime_idle(struct device *dev) +static int i2c_device_pm_resume(struct device *dev) { - const struct dev_pm_ops *pm = NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int ret; - if (dev->driver) - pm = dev->driver->pm; - if (pm && pm->runtime_idle) { - ret = pm->runtime_idle(dev); - if (ret) - return ret; + if (pm) + ret = pm->resume ? pm->resume(dev) : 0; + else + ret = i2c_legacy_resume(dev); + + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } - return pm_runtime_suspend(dev); + return ret; } -#else -#define i2c_device_runtime_suspend NULL -#define i2c_device_runtime_resume NULL -#define i2c_device_runtime_idle NULL -#endif -static int i2c_device_suspend(struct device *dev, pm_message_t mesg) +static int i2c_device_pm_freeze(struct device *dev) { - struct i2c_client *client = i2c_verify_client(dev); - struct i2c_driver *driver; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!client || !dev->driver) + if (pm_runtime_suspended(dev)) return 0; - driver = to_i2c_driver(dev->driver); - if (!driver->suspend) - return 0; - return driver->suspend(client, mesg); + + if (pm) + return pm->freeze ? pm->freeze(dev) : 0; + + return i2c_legacy_suspend(dev, PMSG_FREEZE); } -static int i2c_device_resume(struct device *dev) +static int i2c_device_pm_thaw(struct device *dev) { - struct i2c_client *client = i2c_verify_client(dev); - struct i2c_driver *driver; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - if (!client || !dev->driver) + if (pm_runtime_suspended(dev)) return 0; - driver = to_i2c_driver(dev->driver); - if (!driver->resume) + + if (pm) + return pm->thaw ? pm->thaw(dev) : 0; + + return i2c_legacy_resume(dev); +} + +static int i2c_device_pm_poweroff(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm_runtime_suspended(dev)) return 0; - return driver->resume(client); + + if (pm) + return pm->poweroff ? pm->poweroff(dev) : 0; + + return i2c_legacy_suspend(dev, PMSG_HIBERNATE); } +static int i2c_device_pm_restore(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret; + + if (pm) + ret = pm->restore ? pm->restore(dev) : 0; + else + ret = i2c_legacy_resume(dev); + + if (!ret) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + + return ret; +} +#else /* !CONFIG_PM_SLEEP */ +#define i2c_device_pm_suspend NULL +#define i2c_device_pm_resume NULL +#define i2c_device_pm_freeze NULL +#define i2c_device_pm_thaw NULL +#define i2c_device_pm_poweroff NULL +#define i2c_device_pm_restore NULL +#endif /* !CONFIG_PM_SLEEP */ + static void i2c_client_dev_release(struct device *dev) { kfree(to_i2c_client(dev)); @@ -301,9 +325,15 @@ static const struct attribute_group *i2c_dev_attr_groups[] = { static const struct dev_pm_ops i2c_device_pm_ops = { .suspend = i2c_device_pm_suspend, .resume = i2c_device_pm_resume, - .runtime_suspend = i2c_device_runtime_suspend, - .runtime_resume = i2c_device_runtime_resume, - .runtime_idle = i2c_device_runtime_idle, + .freeze = i2c_device_pm_freeze, + .thaw = i2c_device_pm_thaw, + .poweroff = i2c_device_pm_poweroff, + .restore = i2c_device_pm_restore, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) }; struct bus_type i2c_bus_type = { @@ -312,8 +342,6 @@ struct bus_type i2c_bus_type = { .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, - .suspend = i2c_device_suspend, - .resume = i2c_device_resume, .pm = &i2c_device_pm_ops, }; EXPORT_SYMBOL_GPL(i2c_bus_type); diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index b776db737244..6e81888c6222 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -30,6 +30,9 @@ extern void pm_runtime_enable(struct device *dev); extern void __pm_runtime_disable(struct device *dev, bool check_resume); extern void pm_runtime_allow(struct device *dev); extern void pm_runtime_forbid(struct device *dev); +extern int pm_generic_runtime_idle(struct device *dev); +extern int pm_generic_runtime_suspend(struct device *dev); +extern int pm_generic_runtime_resume(struct device *dev); static inline bool pm_children_suspended(struct device *dev) { @@ -96,6 +99,10 @@ static inline bool device_run_wake(struct device *dev) { return false; } static inline void device_set_run_wake(struct device *dev, bool enable) {} static inline bool pm_runtime_suspended(struct device *dev) { return false; } +static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } +static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } +static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } + #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_get(struct device *dev) -- cgit v1.2.3 From 543f2503a956601dd490c6cde0ebf6adb4653e50 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 10 May 2010 23:10:13 +0200 Subject: PM / platform_bus: Allow runtime PM by default Currently the default runtime PM callbacks for platform devices return -ENOSYS, preventing the use of runtime PM platforms until they have provided at least a default implementation. This hinders the use of runtime PM by devices which work with many platforms such as memory mapped devices, MFDs and on chip IPs shared by multiple architectures. Change the default implementation to the standard pm_generic_runtime one, allowing drivers to use runtime PM without per-architecture changes. Signed-off-by: Mark Brown Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 4b4b565c835f..a6c07797c88e 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -967,17 +967,17 @@ static int platform_pm_restore_noirq(struct device *dev) int __weak platform_pm_runtime_suspend(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_suspend(dev); }; int __weak platform_pm_runtime_resume(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_resume(dev); }; int __weak platform_pm_runtime_idle(struct device *dev) { - return -ENOSYS; + return pm_generic_runtime_idle(dev); }; #else /* !CONFIG_PM_RUNTIME */ -- cgit v1.2.3