pandora: reserve CMA area for c64_tools
[pandora-kernel.git] / drivers / scsi / scsi_pm.c
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 #include <linux/export.h>
10 #include <linux/async.h>
11
12 #include <scsi/scsi.h>
13 #include <scsi/scsi_device.h>
14 #include <scsi/scsi_driver.h>
15 #include <scsi/scsi_host.h>
16
17 #include "scsi_priv.h"
18
19 static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
20 {
21         struct device_driver *drv;
22         int err;
23
24         err = scsi_device_quiesce(to_scsi_device(dev));
25         if (err == 0) {
26                 drv = dev->driver;
27                 if (drv && drv->suspend)
28                         err = drv->suspend(dev, msg);
29         }
30         dev_dbg(dev, "scsi suspend: %d\n", err);
31         return err;
32 }
33
34 static int scsi_dev_type_resume(struct device *dev)
35 {
36         struct device_driver *drv;
37         int err = 0;
38
39         drv = dev->driver;
40         if (drv && drv->resume)
41                 err = drv->resume(dev);
42         scsi_device_resume(to_scsi_device(dev));
43         dev_dbg(dev, "scsi resume: %d\n", err);
44         return err;
45 }
46
47 #ifdef CONFIG_PM_SLEEP
48
49 static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
50 {
51         int err = 0;
52
53         if (scsi_is_sdev_device(dev))
54                 err = scsi_dev_type_suspend(dev, msg);
55         return err;
56 }
57
58 static int scsi_bus_resume_common(struct device *dev)
59 {
60         int err = 0;
61
62         if (scsi_is_sdev_device(dev))
63                 err = scsi_dev_type_resume(dev);
64
65         if (err == 0) {
66                 pm_runtime_disable(dev);
67                 pm_runtime_set_active(dev);
68                 pm_runtime_enable(dev);
69         }
70         return err;
71 }
72
73 static int scsi_bus_prepare(struct device *dev)
74 {
75         if (scsi_is_sdev_device(dev)) {
76                 /* sd probing uses async_schedule.  Wait until it finishes. */
77                 async_synchronize_full();
78
79         } else if (scsi_is_host_device(dev)) {
80                 /* Wait until async scanning is finished */
81                 scsi_complete_async_scans();
82         }
83         return 0;
84 }
85
86 static int scsi_bus_suspend(struct device *dev)
87 {
88         return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
89 }
90
91 static int scsi_bus_freeze(struct device *dev)
92 {
93         return scsi_bus_suspend_common(dev, PMSG_FREEZE);
94 }
95
96 static int scsi_bus_poweroff(struct device *dev)
97 {
98         return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
99 }
100
101 #else /* CONFIG_PM_SLEEP */
102
103 #define scsi_bus_resume_common          NULL
104 #define scsi_bus_prepare                NULL
105 #define scsi_bus_suspend                NULL
106 #define scsi_bus_freeze                 NULL
107 #define scsi_bus_poweroff               NULL
108
109 #endif /* CONFIG_PM_SLEEP */
110
111 #ifdef CONFIG_PM_RUNTIME
112
113 static int scsi_runtime_suspend(struct device *dev)
114 {
115         int err = 0;
116
117         dev_dbg(dev, "scsi_runtime_suspend\n");
118         if (scsi_is_sdev_device(dev)) {
119                 err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
120                 if (err == -EAGAIN)
121                         pm_schedule_suspend(dev, jiffies_to_msecs(
122                                 round_jiffies_up_relative(HZ/10)));
123         }
124
125         /* Insert hooks here for targets, hosts, and transport classes */
126
127         return err;
128 }
129
130 static int scsi_runtime_resume(struct device *dev)
131 {
132         int err = 0;
133
134         dev_dbg(dev, "scsi_runtime_resume\n");
135         if (scsi_is_sdev_device(dev))
136                 err = scsi_dev_type_resume(dev);
137
138         /* Insert hooks here for targets, hosts, and transport classes */
139
140         return err;
141 }
142
143 static int scsi_runtime_idle(struct device *dev)
144 {
145         int err;
146
147         dev_dbg(dev, "scsi_runtime_idle\n");
148
149         /* Insert hooks here for targets, hosts, and transport classes */
150
151         if (scsi_is_sdev_device(dev))
152                 err = pm_schedule_suspend(dev, 100);
153         else
154                 err = pm_runtime_suspend(dev);
155         return err;
156 }
157
158 int scsi_autopm_get_device(struct scsi_device *sdev)
159 {
160         int     err;
161
162         err = pm_runtime_get_sync(&sdev->sdev_gendev);
163         if (err < 0 && err !=-EACCES)
164                 pm_runtime_put_sync(&sdev->sdev_gendev);
165         else
166                 err = 0;
167         return err;
168 }
169 EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
170
171 void scsi_autopm_put_device(struct scsi_device *sdev)
172 {
173         pm_runtime_put_sync(&sdev->sdev_gendev);
174 }
175 EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
176
177 void scsi_autopm_get_target(struct scsi_target *starget)
178 {
179         pm_runtime_get_sync(&starget->dev);
180 }
181
182 void scsi_autopm_put_target(struct scsi_target *starget)
183 {
184         pm_runtime_put_sync(&starget->dev);
185 }
186
187 int scsi_autopm_get_host(struct Scsi_Host *shost)
188 {
189         int     err;
190
191         err = pm_runtime_get_sync(&shost->shost_gendev);
192         if (err < 0 && err !=-EACCES)
193                 pm_runtime_put_sync(&shost->shost_gendev);
194         else
195                 err = 0;
196         return err;
197 }
198
199 void scsi_autopm_put_host(struct Scsi_Host *shost)
200 {
201         pm_runtime_put_sync(&shost->shost_gendev);
202 }
203
204 #else
205
206 #define scsi_runtime_suspend    NULL
207 #define scsi_runtime_resume     NULL
208 #define scsi_runtime_idle       NULL
209
210 #endif /* CONFIG_PM_RUNTIME */
211
212 const struct dev_pm_ops scsi_bus_pm_ops = {
213         .prepare =              scsi_bus_prepare,
214         .suspend =              scsi_bus_suspend,
215         .resume =               scsi_bus_resume_common,
216         .freeze =               scsi_bus_freeze,
217         .thaw =                 scsi_bus_resume_common,
218         .poweroff =             scsi_bus_poweroff,
219         .restore =              scsi_bus_resume_common,
220         .runtime_suspend =      scsi_runtime_suspend,
221         .runtime_resume =       scsi_runtime_resume,
222         .runtime_idle =         scsi_runtime_idle,
223 };