• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/scsi/
1/*
2 *	scsi_pm.c	Copyright (C) 2010 Alan Stern
3 *
4 *	SCSI dynamic Power Management
5 *		Initial version: Alan Stern <stern@rowland.harvard.edu>
6 */
7
8#include <linux/pm_runtime.h>
9
10#include <scsi/scsi.h>
11#include <scsi/scsi_device.h>
12#include <scsi/scsi_driver.h>
13#include <scsi/scsi_host.h>
14
15#include "scsi_priv.h"
16
17static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
18{
19	struct device_driver *drv;
20	int err;
21
22	err = scsi_device_quiesce(to_scsi_device(dev));
23	if (err == 0) {
24		drv = dev->driver;
25		if (drv && drv->suspend)
26			err = drv->suspend(dev, msg);
27	}
28	dev_dbg(dev, "scsi suspend: %d\n", err);
29	return err;
30}
31
32static int scsi_dev_type_resume(struct device *dev)
33{
34	struct device_driver *drv;
35	int err = 0;
36
37	drv = dev->driver;
38	if (drv && drv->resume)
39		err = drv->resume(dev);
40	scsi_device_resume(to_scsi_device(dev));
41	dev_dbg(dev, "scsi resume: %d\n", err);
42	return err;
43}
44
45#ifdef CONFIG_PM_SLEEP
46
47static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
48{
49	int err = 0;
50
51	if (scsi_is_sdev_device(dev))
52		err = scsi_dev_type_suspend(dev, msg);
53	return err;
54}
55
56static int scsi_bus_resume_common(struct device *dev)
57{
58	int err = 0;
59
60	if (scsi_is_sdev_device(dev))
61		err = scsi_dev_type_resume(dev);
62
63	if (err == 0) {
64		pm_runtime_disable(dev);
65		pm_runtime_set_active(dev);
66		pm_runtime_enable(dev);
67	}
68	return err;
69}
70
71static int scsi_bus_suspend(struct device *dev)
72{
73	return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
74}
75
76static int scsi_bus_freeze(struct device *dev)
77{
78	return scsi_bus_suspend_common(dev, PMSG_FREEZE);
79}
80
81static int scsi_bus_poweroff(struct device *dev)
82{
83	return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
84}
85
86#else /* CONFIG_PM_SLEEP */
87
88#define scsi_bus_resume_common		NULL
89#define scsi_bus_suspend		NULL
90#define scsi_bus_freeze			NULL
91#define scsi_bus_poweroff		NULL
92
93#endif /* CONFIG_PM_SLEEP */
94
95#ifdef CONFIG_PM_RUNTIME
96
97static int scsi_runtime_suspend(struct device *dev)
98{
99	int err = 0;
100
101	dev_dbg(dev, "scsi_runtime_suspend\n");
102	if (scsi_is_sdev_device(dev)) {
103		err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
104		if (err == -EAGAIN)
105			pm_schedule_suspend(dev, jiffies_to_msecs(
106				round_jiffies_up_relative(HZ/10)));
107	}
108
109	/* Insert hooks here for targets, hosts, and transport classes */
110
111	return err;
112}
113
114static int scsi_runtime_resume(struct device *dev)
115{
116	int err = 0;
117
118	dev_dbg(dev, "scsi_runtime_resume\n");
119	if (scsi_is_sdev_device(dev))
120		err = scsi_dev_type_resume(dev);
121
122	/* Insert hooks here for targets, hosts, and transport classes */
123
124	return err;
125}
126
127static int scsi_runtime_idle(struct device *dev)
128{
129	int err;
130
131	dev_dbg(dev, "scsi_runtime_idle\n");
132
133	/* Insert hooks here for targets, hosts, and transport classes */
134
135	if (scsi_is_sdev_device(dev))
136		err = pm_schedule_suspend(dev, 100);
137	else
138		err = pm_runtime_suspend(dev);
139	return err;
140}
141
142int scsi_autopm_get_device(struct scsi_device *sdev)
143{
144	int	err;
145
146	err = pm_runtime_get_sync(&sdev->sdev_gendev);
147	if (err < 0)
148		pm_runtime_put_sync(&sdev->sdev_gendev);
149	else if (err > 0)
150		err = 0;
151	return err;
152}
153EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
154
155void scsi_autopm_put_device(struct scsi_device *sdev)
156{
157	pm_runtime_put_sync(&sdev->sdev_gendev);
158}
159EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
160
161void scsi_autopm_get_target(struct scsi_target *starget)
162{
163	pm_runtime_get_sync(&starget->dev);
164}
165
166void scsi_autopm_put_target(struct scsi_target *starget)
167{
168	pm_runtime_put_sync(&starget->dev);
169}
170
171int scsi_autopm_get_host(struct Scsi_Host *shost)
172{
173	int	err;
174
175	err = pm_runtime_get_sync(&shost->shost_gendev);
176	if (err < 0)
177		pm_runtime_put_sync(&shost->shost_gendev);
178	else if (err > 0)
179		err = 0;
180	return err;
181}
182
183void scsi_autopm_put_host(struct Scsi_Host *shost)
184{
185	pm_runtime_put_sync(&shost->shost_gendev);
186}
187
188#else
189
190#define scsi_runtime_suspend	NULL
191#define scsi_runtime_resume	NULL
192#define scsi_runtime_idle	NULL
193
194#endif /* CONFIG_PM_RUNTIME */
195
196const struct dev_pm_ops scsi_bus_pm_ops = {
197	.suspend =		scsi_bus_suspend,
198	.resume =		scsi_bus_resume_common,
199	.freeze =		scsi_bus_freeze,
200	.thaw =			scsi_bus_resume_common,
201	.poweroff =		scsi_bus_poweroff,
202	.restore =		scsi_bus_resume_common,
203	.runtime_suspend =	scsi_runtime_suspend,
204	.runtime_resume =	scsi_runtime_resume,
205	.runtime_idle =		scsi_runtime_idle,
206};
207