On Sat, 6 Feb 2010, Alan Stern wrote: > On Sat, 6 Feb 2010, Mark Brown wrote: > > > On Fri, Feb 05, 2010 at 09:57:46PM -0500, Alan Stern wrote: > > > On Fri, 5 Feb 2010, Mark Brown wrote: > > > > > > It's not that it's hard per se, it's that it feels like it's peering > > > > inside the implementation of the API. Having the PM core provide > > > > > You mean like providing an is_runtime_suspended() test? Then you could > > > write: > > > > > int my_suspend(struct device *dev) > > > { > > > if (is_runtime_suspended(dev)) > > > return 0; > > > return my_runtime_suspend(dev); > > > } > > > > > Or maybe you'd prefer to see a convenient pm_use_runtime_suspend() > > > function that you could use for your dev_pm_ops.suspend pointer, which > > > would do essentially the same as the above? (Along with a > > > corresponding pm_use_runtime_resume() function, of course.) > > > > Either (or both, of course - implementing the second would probably > > imply the former). The ops that can be assigned would be the clearest > > option but the accessor function should be enough to make it clear that > > this is something drivers are supposed to be doing. > > Providing the new ops would be a little awkward because > CONFIG_PM_SLEEP and CONFIG_PM_RUNTIME are independent: Either can be > enabled without the other. This means the code to call the > runtime_suspend and runtime_resume methods would have to be split out > into a separate source file that would get built whenever either config > option is enabled. It's doable, just somewhat awkward. > > I'll give it a try and we'll see how it turns out... And here it is. You can see that the patch isn't ideal. Anyway, it should do what you want -- I haven't tested it. Alan Stern Index: usb-2.6/include/linux/pm.h =================================================================== --- usb-2.6.orig/include/linux/pm.h +++ usb-2.6/include/linux/pm.h @@ -508,6 +508,9 @@ extern void __suspend_report_result(cons __suspend_report_result(__func__, fn, ret); \ } while (0) +extern int dpm_use_runtime_suspend(struct device *dev); +extern int dpm_use_runtime_resume(struct device *dev); + #else /* !CONFIG_PM_SLEEP */ #define device_pm_lock() do {} while (0) @@ -520,8 +523,15 @@ static inline int dpm_suspend_start(pm_m #define suspend_report_result(fn, ret) do {} while (0) +#define dpm_use_runtime_suspend NULL +#define dpm_use_runtime_resume NULL + #endif /* !CONFIG_PM_SLEEP */ +void rpm_invoke_runtime_idle(struct device *dev); +int rpm_invoke_runtime_suspend(struct device *dev); +int rpm_invoke_runtime_resume(struct device *dev); + /* How to reorder dpm_list after device_move() */ enum dpm_order { DPM_ORDER_NONE, Index: usb-2.6/include/linux/pm_runtime.h =================================================================== --- usb-2.6.orig/include/linux/pm_runtime.h +++ usb-2.6/include/linux/pm_runtime.h @@ -60,6 +60,11 @@ static inline void device_set_run_wake(s dev->power.run_wake = enable; } +static inline bool pm_is_runtime_suspended(struct device *dev) +{ + return dev->power.runtime_status == RPM_SUSPENDED; +} + #else /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_idle(struct device *dev) { return -ENOSYS; } @@ -86,6 +91,9 @@ static inline void pm_runtime_put_noidle 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_is_runtime_suspended(struct device *dev) + { return false; } + #endif /* !CONFIG_PM_RUNTIME */ static inline int pm_runtime_get(struct device *dev) Index: usb-2.6/drivers/base/power/Makefile =================================================================== --- usb-2.6.orig/drivers/base/power/Makefile +++ usb-2.6/drivers/base/power/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o -obj-$(CONFIG_PM_SLEEP) += main.o -obj-$(CONFIG_PM_RUNTIME) += runtime.o +obj-$(CONFIG_PM_SLEEP) += main.o invoke_runtime.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o invoke_runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG Index: usb-2.6/drivers/base/power/main.c =================================================================== --- usb-2.6.orig/drivers/base/power/main.c +++ usb-2.6/drivers/base/power/main.c @@ -936,3 +936,52 @@ void __suspend_report_result(const char printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); } EXPORT_SYMBOL_GPL(__suspend_report_result); + +/* Convenience routines for drivers that want to use the same functions + * for system suspend/resume and runtime suspend/resume: + * Set driver->pm_ops->suspend = dpm_use_runtime_suspend and + * driver->pm_ops->resume = dpm_use_runtime_resume. + */ + +/** + * dpm_use_runtime_suspend - Perform system suspend via runtime_suspend method + * @dev: Device to suspend. + * + * This convenience routine implements a system suspend by invoking the + * runtime_suspend method. The method is invoked only if @dev isn't already + * runtime-suspended. + */ +int dpm_use_runtime_suspend(struct device *dev) +{ + int retval; + + if (pm_is_runtime_suspended(dev)) + return 0; + retval = rpm_invoke_runtime_suspend(dev); + if (retval == -ENOSYS) + retval = 0; + return retval; +} +EXPORT_SYMBOL_GPL(dpm_use_runtime_suspend); + +/** + * dpm_use_runtime_resume - Perform system resume via runtime_resume method + * @dev: Device to resume. + * + * This convenience routine implements a system resume by invoking the + * runtime_resume method. If the method is successful then the device's + * runtime status is set to RPM_ACTIVE. + */ +int dpm_use_runtime_resume(struct device *dev) +{ + int retval; + + retval = rpm_invoke_runtime_resume(dev); + if (retval == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + return retval; +} +EXPORT_SYMBOL_GPL(dpm_use_runtime_resume); Index: usb-2.6/drivers/base/power/runtime.c =================================================================== --- usb-2.6.orig/drivers/base/power/runtime.c +++ usb-2.6/drivers/base/power/runtime.c @@ -79,26 +79,9 @@ static int __pm_runtime_idle(struct devi dev->power.idle_notification = true; - if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { - spin_unlock_irq(&dev->power.lock); - - dev->bus->pm->runtime_idle(dev); - - spin_lock_irq(&dev->power.lock); - } else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) { - spin_unlock_irq(&dev->power.lock); - - dev->type->pm->runtime_idle(dev); - - spin_lock_irq(&dev->power.lock); - } else if (dev->class && dev->class->pm - && dev->class->pm->runtime_idle) { - spin_unlock_irq(&dev->power.lock); - - dev->class->pm->runtime_idle(dev); - - spin_lock_irq(&dev->power.lock); - } + spin_unlock_irq(&dev->power.lock); + rpm_invoke_runtime_idle(dev); + spin_lock_irq(&dev->power.lock); dev->power.idle_notification = false; wake_up_all(&dev->power.wait_queue); @@ -200,32 +183,11 @@ int __pm_runtime_suspend(struct device * dev->power.runtime_status = RPM_SUSPENDING; dev->power.deferred_resume = false; - if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->bus->pm->runtime_suspend(dev); - - spin_lock_irq(&dev->power.lock); - dev->power.runtime_error = retval; - } else if (dev->type && dev->type->pm - && dev->type->pm->runtime_suspend) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->type->pm->runtime_suspend(dev); - - spin_lock_irq(&dev->power.lock); - dev->power.runtime_error = retval; - } else if (dev->class && dev->class->pm - && dev->class->pm->runtime_suspend) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->class->pm->runtime_suspend(dev); - - spin_lock_irq(&dev->power.lock); + spin_unlock_irq(&dev->power.lock); + retval = rpm_invoke_runtime_suspend(dev); + spin_lock_irq(&dev->power.lock); + if (retval != -ENOSYS) dev->power.runtime_error = retval; - } else { - retval = -ENOSYS; - } if (retval) { dev->power.runtime_status = RPM_ACTIVE; @@ -381,32 +343,11 @@ int __pm_runtime_resume(struct device *d dev->power.runtime_status = RPM_RESUMING; - if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->bus->pm->runtime_resume(dev); - - spin_lock_irq(&dev->power.lock); - dev->power.runtime_error = retval; - } else if (dev->type && dev->type->pm - && dev->type->pm->runtime_resume) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->type->pm->runtime_resume(dev); - - spin_lock_irq(&dev->power.lock); - dev->power.runtime_error = retval; - } else if (dev->class && dev->class->pm - && dev->class->pm->runtime_resume) { - spin_unlock_irq(&dev->power.lock); - - retval = dev->class->pm->runtime_resume(dev); - - spin_lock_irq(&dev->power.lock); + spin_unlock_irq(&dev->power.lock); + retval = rpm_invoke_runtime_resume(dev); + spin_lock_irq(&dev->power.lock); + if (retval != -ENOSYS) dev->power.runtime_error = retval; - } else { - retval = -ENOSYS; - } if (retval) { dev->power.runtime_status = RPM_SUSPENDED; Index: usb-2.6/drivers/base/power/invoke_runtime.c =================================================================== --- /dev/null +++ usb-2.6/drivers/base/power/invoke_runtime.c @@ -0,0 +1,80 @@ +/* + * drivers/base/power/runtime.c - Helper functions for device run-time PM + * + * Copyright (c) 2009 Rafael J. Wysocki <rjw@xxxxxxx>, Novell Inc. + * Copyright (c) 2010 Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> + * + * This file is released under the GPLv2. + */ + +#include <linux/pm_runtime.h> + +/* The following functions are separated out from runtime.c so that they + * can be used even when CONFIG_PM_RUNTIME is disabled. + */ + +/** + * rpm_invoke_runtime_idle - Invoke a device's runtime PM idle method(s) + * @dev: Device to handle. + */ +void rpm_invoke_runtime_idle(struct device *dev) +{ + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { + dev->bus->pm->runtime_idle(dev); + } else if (dev->type && dev->type->pm && dev->type->pm->runtime_idle) { + dev->type->pm->runtime_idle(dev); + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_idle) { + dev->class->pm->runtime_idle(dev); + } +} + +/** + * rpm_invoke_runtime_suspend - Invoke a device's runtime PM suspend method(s) + * @dev: Device to handle. + */ +int rpm_invoke_runtime_suspend(struct device *dev) +{ + int retval; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { + retval = dev->bus->pm->runtime_suspend(dev); + dev->power.runtime_error = retval; + } else if (dev->type && dev->type->pm + && dev->type->pm->runtime_suspend) { + retval = dev->type->pm->runtime_suspend(dev); + dev->power.runtime_error = retval; + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_suspend) { + retval = dev->class->pm->runtime_suspend(dev); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + return retval; +} + +/** + * rpm_invoke_runtime_resume - Invoke a device's runtime PM resume method(s) + * @dev: Device to handle. + */ +int rpm_invoke_runtime_resume(struct device *dev) +{ + int retval; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { + retval = dev->bus->pm->runtime_resume(dev); + dev->power.runtime_error = retval; + } else if (dev->type && dev->type->pm + && dev->type->pm->runtime_resume) { + retval = dev->type->pm->runtime_resume(dev); + dev->power.runtime_error = retval; + } else if (dev->class && dev->class->pm + && dev->class->pm->runtime_resume) { + retval = dev->class->pm->runtime_resume(dev); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + return retval; +} Index: usb-2.6/Documentation/power/runtime_pm.txt =================================================================== --- usb-2.6.orig/Documentation/power/runtime_pm.txt +++ usb-2.6/Documentation/power/runtime_pm.txt @@ -329,6 +329,11 @@ drivers/base/power/runtime.c and include 'power.runtime_error' is set or 'power.disable_depth' is greater than zero) + bool pm_is_runtime_suspended(struct device *dev); + - returns 'true' if the device's run-time PM status is 'suspended'; the + status is constantly subject to change, hence this function should be + used only in contexts such as during a system sleep transition + It is safe to execute the following helper functions from interrupt context: pm_request_idle() @@ -342,6 +347,7 @@ pm_suspend_ignore_children() pm_runtime_set_active() pm_runtime_set_suspended() pm_runtime_enable() +pm_is_runtime_suspended() 5. Run-time PM Initialization, Device Probing and Removal _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm