xref: /openbmc/linux/drivers/scsi/scsi_pm.c (revision 4800cd83)
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 
17 static 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 
32 static 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 
47 static 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 
56 static 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 
71 static int scsi_bus_suspend(struct device *dev)
72 {
73 	return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
74 }
75 
76 static int scsi_bus_freeze(struct device *dev)
77 {
78 	return scsi_bus_suspend_common(dev, PMSG_FREEZE);
79 }
80 
81 static 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 
97 static 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 
114 static 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 
127 static 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 
142 int 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 }
153 EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
154 
155 void scsi_autopm_put_device(struct scsi_device *sdev)
156 {
157 	pm_runtime_put_sync(&sdev->sdev_gendev);
158 }
159 EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
160 
161 void scsi_autopm_get_target(struct scsi_target *starget)
162 {
163 	pm_runtime_get_sync(&starget->dev);
164 }
165 
166 void scsi_autopm_put_target(struct scsi_target *starget)
167 {
168 	pm_runtime_put_sync(&starget->dev);
169 }
170 
171 int 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 
183 void 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 
196 const 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