diff options
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/scsi_pm.c | 96 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 7 | ||||
-rw-r--r-- | drivers/scsi/scsi_sysfs.c | 48 |
4 files changed, 105 insertions, 47 deletions
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 1c7ac49be64..2a3fca2eca6 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -163,6 +163,7 @@ scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_mod-y += scsi_trace.o +scsi_mod-$(CONFIG_PM_OPS) += scsi_pm.o scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c new file mode 100644 index 00000000000..cd83758ce0a --- /dev/null +++ b/drivers/scsi/scsi_pm.c @@ -0,0 +1,96 @@ +/* + * scsi_pm.c Copyright (C) 2010 Alan Stern + * + * SCSI dynamic Power Management + * Initial version: Alan Stern <stern@rowland.harvard.edu> + */ + +#include <linux/pm_runtime.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_host.h> + +#include "scsi_priv.h" + +static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg) +{ + struct device_driver *drv; + int err; + + err = scsi_device_quiesce(to_scsi_device(dev)); + if (err == 0) { + drv = dev->driver; + if (drv && drv->suspend) + err = drv->suspend(dev, msg); + } + dev_dbg(dev, "scsi suspend: %d\n", err); + return err; +} + +static int scsi_dev_type_resume(struct device *dev) +{ + struct device_driver *drv; + int err = 0; + + drv = dev->driver; + if (drv && drv->resume) + err = drv->resume(dev); + scsi_device_resume(to_scsi_device(dev)); + dev_dbg(dev, "scsi resume: %d\n", err); + return err; +} + +#ifdef CONFIG_PM_SLEEP + +static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg) +{ + int err = 0; + + if (scsi_is_sdev_device(dev)) + err = scsi_dev_type_suspend(dev, msg); + return err; +} + +static int scsi_bus_resume_common(struct device *dev) +{ + int err = 0; + + if (scsi_is_sdev_device(dev)) + err = scsi_dev_type_resume(dev); + return err; +} + +static int scsi_bus_suspend(struct device *dev) +{ + return scsi_bus_suspend_common(dev, PMSG_SUSPEND); +} + +static int scsi_bus_freeze(struct device *dev) +{ + return scsi_bus_suspend_common(dev, PMSG_FREEZE); +} + +static int scsi_bus_poweroff(struct device *dev) +{ + return scsi_bus_suspend_common(dev, PMSG_HIBERNATE); +} + +#else /* CONFIG_PM_SLEEP */ + +#define scsi_bus_resume_common NULL +#define scsi_bus_suspend NULL +#define scsi_bus_freeze NULL +#define scsi_bus_poweroff NULL + +#endif /* CONFIG_PM_SLEEP */ + +const struct dev_pm_ops scsi_bus_pm_ops = { + .suspend = scsi_bus_suspend, + .resume = scsi_bus_resume_common, + .freeze = scsi_bus_freeze, + .thaw = scsi_bus_resume_common, + .poweroff = scsi_bus_poweroff, + .restore = scsi_bus_resume_common, +}; diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 1fbf7c78bba..dddacc73255 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -144,6 +144,13 @@ static inline void scsi_netlink_init(void) {} static inline void scsi_netlink_exit(void) {} #endif +/* scsi_pm.c */ +#ifdef CONFIG_PM_OPS +extern const struct dev_pm_ops scsi_bus_pm_ops; +#else +#define scsi_bus_pm_ops (*NULL) +#endif + /* * internal scsi timeout functions: for use by mid-layer and transport * classes. diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c23ab978c3b..5f85f8e831f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -376,57 +376,11 @@ static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -static int scsi_bus_suspend(struct device * dev, pm_message_t state) -{ - struct device_driver *drv; - struct scsi_device *sdev; - int err; - - if (dev->type != &scsi_dev_type) - return 0; - - drv = dev->driver; - sdev = to_scsi_device(dev); - - err = scsi_device_quiesce(sdev); - if (err) - return err; - - if (drv && drv->suspend) { - err = drv->suspend(dev, state); - if (err) - return err; - } - - return 0; -} - -static int scsi_bus_resume(struct device * dev) -{ - struct device_driver *drv; - struct scsi_device *sdev; - int err = 0; - - if (dev->type != &scsi_dev_type) - return 0; - - drv = dev->driver; - sdev = to_scsi_device(dev); - - if (drv && drv->resume) - err = drv->resume(dev); - - scsi_device_resume(sdev); - - return err; -} - struct bus_type scsi_bus_type = { .name = "scsi", .match = scsi_bus_match, .uevent = scsi_bus_uevent, - .suspend = scsi_bus_suspend, - .resume = scsi_bus_resume, + .pm = &scsi_bus_pm_ops, }; EXPORT_SYMBOL_GPL(scsi_bus_type); |