Merge branch 'timers-cleanup-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / base / power / generic_ops.c
1 /*
2  * drivers/base/power/generic_ops.c - Generic PM callbacks for subsystems
3  *
4  * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5  *
6  * This file is released under the GPLv2.
7  */
8
9 #include <linux/pm.h>
10 #include <linux/pm_runtime.h>
11
12 #ifdef CONFIG_PM_RUNTIME
13 /**
14  * pm_generic_runtime_idle - Generic runtime idle callback for subsystems.
15  * @dev: Device to handle.
16  *
17  * If PM operations are defined for the @dev's driver and they include
18  * ->runtime_idle(), execute it and return its error code, if nonzero.
19  * Otherwise, execute pm_runtime_suspend() for the device and return 0.
20  */
21 int pm_generic_runtime_idle(struct device *dev)
22 {
23         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
24
25         if (pm && pm->runtime_idle) {
26                 int ret = pm->runtime_idle(dev);
27                 if (ret)
28                         return ret;
29         }
30
31         pm_runtime_suspend(dev);
32         return 0;
33 }
34 EXPORT_SYMBOL_GPL(pm_generic_runtime_idle);
35
36 /**
37  * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
38  * @dev: Device to suspend.
39  *
40  * If PM operations are defined for the @dev's driver and they include
41  * ->runtime_suspend(), execute it and return its error code.  Otherwise,
42  * return 0.
43  */
44 int pm_generic_runtime_suspend(struct device *dev)
45 {
46         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
47         int ret;
48
49         ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
50
51         return ret;
52 }
53 EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
54
55 /**
56  * pm_generic_runtime_resume - Generic runtime resume callback for subsystems.
57  * @dev: Device to resume.
58  *
59  * If PM operations are defined for the @dev's driver and they include
60  * ->runtime_resume(), execute it and return its error code.  Otherwise,
61  * return 0.
62  */
63 int pm_generic_runtime_resume(struct device *dev)
64 {
65         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
66         int ret;
67
68         ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
69
70         return ret;
71 }
72 EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
73 #endif /* CONFIG_PM_RUNTIME */
74
75 #ifdef CONFIG_PM_SLEEP
76 /**
77  * pm_generic_prepare - Generic routine preparing a device for power transition.
78  * @dev: Device to prepare.
79  *
80  * Prepare a device for a system-wide power transition.
81  */
82 int pm_generic_prepare(struct device *dev)
83 {
84         struct device_driver *drv = dev->driver;
85         int ret = 0;
86
87         if (drv && drv->pm && drv->pm->prepare)
88                 ret = drv->pm->prepare(dev);
89
90         return ret;
91 }
92
93 /**
94  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
95  * @dev: Device to handle.
96  * @event: PM transition of the system under way.
97  * @bool: Whether or not this is the "noirq" stage.
98  *
99  * If the device has not been suspended at run time, execute the
100  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
101  * return its error code.  Otherwise, return zero.
102  */
103 static int __pm_generic_call(struct device *dev, int event, bool noirq)
104 {
105         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
106         int (*callback)(struct device *);
107
108         if (!pm || pm_runtime_suspended(dev))
109                 return 0;
110
111         switch (event) {
112         case PM_EVENT_SUSPEND:
113                 callback = noirq ? pm->suspend_noirq : pm->suspend;
114                 break;
115         case PM_EVENT_FREEZE:
116                 callback = noirq ? pm->freeze_noirq : pm->freeze;
117                 break;
118         case PM_EVENT_HIBERNATE:
119                 callback = noirq ? pm->poweroff_noirq : pm->poweroff;
120                 break;
121         case PM_EVENT_THAW:
122                 callback = noirq ? pm->thaw_noirq : pm->thaw;
123                 break;
124         default:
125                 callback = NULL;
126                 break;
127         }
128
129         return callback ? callback(dev) : 0;
130 }
131
132 /**
133  * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
134  * @dev: Device to suspend.
135  */
136 int pm_generic_suspend_noirq(struct device *dev)
137 {
138         return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
139 }
140 EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
141
142 /**
143  * pm_generic_suspend - Generic suspend callback for subsystems.
144  * @dev: Device to suspend.
145  */
146 int pm_generic_suspend(struct device *dev)
147 {
148         return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
149 }
150 EXPORT_SYMBOL_GPL(pm_generic_suspend);
151
152 /**
153  * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
154  * @dev: Device to freeze.
155  */
156 int pm_generic_freeze_noirq(struct device *dev)
157 {
158         return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
159 }
160 EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
161
162 /**
163  * pm_generic_freeze - Generic freeze callback for subsystems.
164  * @dev: Device to freeze.
165  */
166 int pm_generic_freeze(struct device *dev)
167 {
168         return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
169 }
170 EXPORT_SYMBOL_GPL(pm_generic_freeze);
171
172 /**
173  * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
174  * @dev: Device to handle.
175  */
176 int pm_generic_poweroff_noirq(struct device *dev)
177 {
178         return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
179 }
180 EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
181
182 /**
183  * pm_generic_poweroff - Generic poweroff callback for subsystems.
184  * @dev: Device to handle.
185  */
186 int pm_generic_poweroff(struct device *dev)
187 {
188         return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
189 }
190 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
191
192 /**
193  * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
194  * @dev: Device to thaw.
195  */
196 int pm_generic_thaw_noirq(struct device *dev)
197 {
198         return __pm_generic_call(dev, PM_EVENT_THAW, true);
199 }
200 EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
201
202 /**
203  * pm_generic_thaw - Generic thaw callback for subsystems.
204  * @dev: Device to thaw.
205  */
206 int pm_generic_thaw(struct device *dev)
207 {
208         return __pm_generic_call(dev, PM_EVENT_THAW, false);
209 }
210 EXPORT_SYMBOL_GPL(pm_generic_thaw);
211
212 /**
213  * __pm_generic_resume - Generic resume/restore callback for subsystems.
214  * @dev: Device to handle.
215  * @event: PM transition of the system under way.
216  * @bool: Whether or not this is the "noirq" stage.
217  *
218  * Execute the resume/resotre callback provided by the @dev's driver, if
219  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
220  * Return the callback's error code.
221  */
222 static int __pm_generic_resume(struct device *dev, int event, bool noirq)
223 {
224         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
225         int (*callback)(struct device *);
226         int ret;
227
228         if (!pm)
229                 return 0;
230
231         switch (event) {
232         case PM_EVENT_RESUME:
233                 callback = noirq ? pm->resume_noirq : pm->resume;
234                 break;
235         case PM_EVENT_RESTORE:
236                 callback = noirq ? pm->restore_noirq : pm->restore;
237                 break;
238         default:
239                 callback = NULL;
240                 break;
241         }
242
243         if (!callback)
244                 return 0;
245
246         ret = callback(dev);
247         if (!ret && !noirq && pm_runtime_enabled(dev)) {
248                 pm_runtime_disable(dev);
249                 pm_runtime_set_active(dev);
250                 pm_runtime_enable(dev);
251         }
252
253         return ret;
254 }
255
256 /**
257  * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
258  * @dev: Device to resume.
259  */
260 int pm_generic_resume_noirq(struct device *dev)
261 {
262         return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
263 }
264 EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
265
266 /**
267  * pm_generic_resume - Generic resume callback for subsystems.
268  * @dev: Device to resume.
269  */
270 int pm_generic_resume(struct device *dev)
271 {
272         return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
273 }
274 EXPORT_SYMBOL_GPL(pm_generic_resume);
275
276 /**
277  * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
278  * @dev: Device to restore.
279  */
280 int pm_generic_restore_noirq(struct device *dev)
281 {
282         return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
283 }
284 EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
285
286 /**
287  * pm_generic_restore - Generic restore callback for subsystems.
288  * @dev: Device to restore.
289  */
290 int pm_generic_restore(struct device *dev)
291 {
292         return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
293 }
294 EXPORT_SYMBOL_GPL(pm_generic_restore);
295
296 /**
297  * pm_generic_complete - Generic routine competing a device power transition.
298  * @dev: Device to handle.
299  *
300  * Complete a device power transition during a system-wide power transition.
301  */
302 void pm_generic_complete(struct device *dev)
303 {
304         struct device_driver *drv = dev->driver;
305
306         if (drv && drv->pm && drv->pm->complete)
307                 drv->pm->complete(dev);
308
309         /*
310          * Let runtime PM try to suspend devices that haven't been in use before
311          * going into the system-wide sleep state we're resuming from.
312          */
313         pm_runtime_idle(dev);
314 }
315 #endif /* CONFIG_PM_SLEEP */
316
317 struct dev_pm_ops generic_subsys_pm_ops = {
318 #ifdef CONFIG_PM_SLEEP
319         .prepare = pm_generic_prepare,
320         .suspend = pm_generic_suspend,
321         .suspend_noirq = pm_generic_suspend_noirq,
322         .resume = pm_generic_resume,
323         .resume_noirq = pm_generic_resume_noirq,
324         .freeze = pm_generic_freeze,
325         .freeze_noirq = pm_generic_freeze_noirq,
326         .thaw = pm_generic_thaw,
327         .thaw_noirq = pm_generic_thaw_noirq,
328         .poweroff = pm_generic_poweroff,
329         .poweroff_noirq = pm_generic_poweroff_noirq,
330         .restore = pm_generic_restore,
331         .restore_noirq = pm_generic_restore_noirq,
332         .complete = pm_generic_complete,
333 #endif
334 #ifdef CONFIG_PM_RUNTIME
335         .runtime_suspend = pm_generic_runtime_suspend,
336         .runtime_resume = pm_generic_runtime_resume,
337         .runtime_idle = pm_generic_runtime_idle,
338 #endif
339 };
340 EXPORT_SYMBOL_GPL(generic_subsys_pm_ops);