diff options
author | Anton Vorontsov <avorontsov@ru.mvista.com> | 2009-10-12 01:50:41 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-10-30 02:20:53 -0400 |
commit | d35ef90bf9e7cab9aa85e9c0724bd1ac6f784601 (patch) | |
tree | 7236409292af05f7ad27fece43148e99487f7535 /drivers | |
parent | 3d541c4b7f6efd55a98189afd1b2f1c9d048c1b3 (diff) |
of/platform: Implement support for dev_pm_ops
Linux power management subsystem supports vast amount of new PM
callbacks that are crucial for proper suspend and hibernation support
in drivers.
This patch implements support for dev_pm_ops, preserving support
for legacy callbacks.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/of/platform.c | 305 |
1 files changed, 290 insertions, 15 deletions
diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 298de0f95d70..d58ade170c4b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c | |||
@@ -65,47 +65,322 @@ static int of_platform_device_remove(struct device *dev) | |||
65 | return 0; | 65 | return 0; |
66 | } | 66 | } |
67 | 67 | ||
68 | static int of_platform_device_suspend(struct device *dev, pm_message_t state) | 68 | static void of_platform_device_shutdown(struct device *dev) |
69 | { | 69 | { |
70 | struct of_device *of_dev = to_of_device(dev); | 70 | struct of_device *of_dev = to_of_device(dev); |
71 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); | 71 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); |
72 | int error = 0; | ||
73 | 72 | ||
74 | if (dev->driver && drv->suspend) | 73 | if (dev->driver && drv->shutdown) |
75 | error = drv->suspend(of_dev, state); | 74 | drv->shutdown(of_dev); |
76 | return error; | ||
77 | } | 75 | } |
78 | 76 | ||
79 | static int of_platform_device_resume(struct device * dev) | 77 | #ifdef CONFIG_PM_SLEEP |
78 | |||
79 | static int of_platform_legacy_suspend(struct device *dev, pm_message_t mesg) | ||
80 | { | 80 | { |
81 | struct of_device *of_dev = to_of_device(dev); | 81 | struct of_device *of_dev = to_of_device(dev); |
82 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); | 82 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); |
83 | int error = 0; | 83 | int ret = 0; |
84 | 84 | ||
85 | if (dev->driver && drv->resume) | 85 | if (dev->driver && drv->suspend) |
86 | error = drv->resume(of_dev); | 86 | ret = drv->suspend(of_dev, mesg); |
87 | return error; | 87 | return ret; |
88 | } | 88 | } |
89 | 89 | ||
90 | static void of_platform_device_shutdown(struct device *dev) | 90 | static int of_platform_legacy_resume(struct device *dev) |
91 | { | 91 | { |
92 | struct of_device *of_dev = to_of_device(dev); | 92 | struct of_device *of_dev = to_of_device(dev); |
93 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); | 93 | struct of_platform_driver *drv = to_of_platform_driver(dev->driver); |
94 | int ret = 0; | ||
94 | 95 | ||
95 | if (dev->driver && drv->shutdown) | 96 | if (dev->driver && drv->resume) |
96 | drv->shutdown(of_dev); | 97 | ret = drv->resume(of_dev); |
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static int of_platform_pm_prepare(struct device *dev) | ||
102 | { | ||
103 | struct device_driver *drv = dev->driver; | ||
104 | int ret = 0; | ||
105 | |||
106 | if (drv && drv->pm && drv->pm->prepare) | ||
107 | ret = drv->pm->prepare(dev); | ||
108 | |||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | static void of_platform_pm_complete(struct device *dev) | ||
113 | { | ||
114 | struct device_driver *drv = dev->driver; | ||
115 | |||
116 | if (drv && drv->pm && drv->pm->complete) | ||
117 | drv->pm->complete(dev); | ||
118 | } | ||
119 | |||
120 | #ifdef CONFIG_SUSPEND | ||
121 | |||
122 | static int of_platform_pm_suspend(struct device *dev) | ||
123 | { | ||
124 | struct device_driver *drv = dev->driver; | ||
125 | int ret = 0; | ||
126 | |||
127 | if (!drv) | ||
128 | return 0; | ||
129 | |||
130 | if (drv->pm) { | ||
131 | if (drv->pm->suspend) | ||
132 | ret = drv->pm->suspend(dev); | ||
133 | } else { | ||
134 | ret = of_platform_legacy_suspend(dev, PMSG_SUSPEND); | ||
135 | } | ||
136 | |||
137 | return ret; | ||
97 | } | 138 | } |
98 | 139 | ||
140 | static int of_platform_pm_suspend_noirq(struct device *dev) | ||
141 | { | ||
142 | struct device_driver *drv = dev->driver; | ||
143 | int ret = 0; | ||
144 | |||
145 | if (!drv) | ||
146 | return 0; | ||
147 | |||
148 | if (drv->pm) { | ||
149 | if (drv->pm->suspend_noirq) | ||
150 | ret = drv->pm->suspend_noirq(dev); | ||
151 | } | ||
152 | |||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | static int of_platform_pm_resume(struct device *dev) | ||
157 | { | ||
158 | struct device_driver *drv = dev->driver; | ||
159 | int ret = 0; | ||
160 | |||
161 | if (!drv) | ||
162 | return 0; | ||
163 | |||
164 | if (drv->pm) { | ||
165 | if (drv->pm->resume) | ||
166 | ret = drv->pm->resume(dev); | ||
167 | } else { | ||
168 | ret = of_platform_legacy_resume(dev); | ||
169 | } | ||
170 | |||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | static int of_platform_pm_resume_noirq(struct device *dev) | ||
175 | { | ||
176 | struct device_driver *drv = dev->driver; | ||
177 | int ret = 0; | ||
178 | |||
179 | if (!drv) | ||
180 | return 0; | ||
181 | |||
182 | if (drv->pm) { | ||
183 | if (drv->pm->resume_noirq) | ||
184 | ret = drv->pm->resume_noirq(dev); | ||
185 | } | ||
186 | |||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | #else /* !CONFIG_SUSPEND */ | ||
191 | |||
192 | #define of_platform_pm_suspend NULL | ||
193 | #define of_platform_pm_resume NULL | ||
194 | #define of_platform_pm_suspend_noirq NULL | ||
195 | #define of_platform_pm_resume_noirq NULL | ||
196 | |||
197 | #endif /* !CONFIG_SUSPEND */ | ||
198 | |||
199 | #ifdef CONFIG_HIBERNATION | ||
200 | |||
201 | static int of_platform_pm_freeze(struct device *dev) | ||
202 | { | ||
203 | struct device_driver *drv = dev->driver; | ||
204 | int ret = 0; | ||
205 | |||
206 | if (!drv) | ||
207 | return 0; | ||
208 | |||
209 | if (drv->pm) { | ||
210 | if (drv->pm->freeze) | ||
211 | ret = drv->pm->freeze(dev); | ||
212 | } else { | ||
213 | ret = of_platform_legacy_suspend(dev, PMSG_FREEZE); | ||
214 | } | ||
215 | |||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static int of_platform_pm_freeze_noirq(struct device *dev) | ||
220 | { | ||
221 | struct device_driver *drv = dev->driver; | ||
222 | int ret = 0; | ||
223 | |||
224 | if (!drv) | ||
225 | return 0; | ||
226 | |||
227 | if (drv->pm) { | ||
228 | if (drv->pm->freeze_noirq) | ||
229 | ret = drv->pm->freeze_noirq(dev); | ||
230 | } | ||
231 | |||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | static int of_platform_pm_thaw(struct device *dev) | ||
236 | { | ||
237 | struct device_driver *drv = dev->driver; | ||
238 | int ret = 0; | ||
239 | |||
240 | if (!drv) | ||
241 | return 0; | ||
242 | |||
243 | if (drv->pm) { | ||
244 | if (drv->pm->thaw) | ||
245 | ret = drv->pm->thaw(dev); | ||
246 | } else { | ||
247 | ret = of_platform_legacy_resume(dev); | ||
248 | } | ||
249 | |||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | static int of_platform_pm_thaw_noirq(struct device *dev) | ||
254 | { | ||
255 | struct device_driver *drv = dev->driver; | ||
256 | int ret = 0; | ||
257 | |||
258 | if (!drv) | ||
259 | return 0; | ||
260 | |||
261 | if (drv->pm) { | ||
262 | if (drv->pm->thaw_noirq) | ||
263 | ret = drv->pm->thaw_noirq(dev); | ||
264 | } | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static int of_platform_pm_poweroff(struct device *dev) | ||
270 | { | ||
271 | struct device_driver *drv = dev->driver; | ||
272 | int ret = 0; | ||
273 | |||
274 | if (!drv) | ||
275 | return 0; | ||
276 | |||
277 | if (drv->pm) { | ||
278 | if (drv->pm->poweroff) | ||
279 | ret = drv->pm->poweroff(dev); | ||
280 | } else { | ||
281 | ret = of_platform_legacy_suspend(dev, PMSG_HIBERNATE); | ||
282 | } | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static int of_platform_pm_poweroff_noirq(struct device *dev) | ||
288 | { | ||
289 | struct device_driver *drv = dev->driver; | ||
290 | int ret = 0; | ||
291 | |||
292 | if (!drv) | ||
293 | return 0; | ||
294 | |||
295 | if (drv->pm) { | ||
296 | if (drv->pm->poweroff_noirq) | ||
297 | ret = drv->pm->poweroff_noirq(dev); | ||
298 | } | ||
299 | |||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static int of_platform_pm_restore(struct device *dev) | ||
304 | { | ||
305 | struct device_driver *drv = dev->driver; | ||
306 | int ret = 0; | ||
307 | |||
308 | if (!drv) | ||
309 | return 0; | ||
310 | |||
311 | if (drv->pm) { | ||
312 | if (drv->pm->restore) | ||
313 | ret = drv->pm->restore(dev); | ||
314 | } else { | ||
315 | ret = of_platform_legacy_resume(dev); | ||
316 | } | ||
317 | |||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | static int of_platform_pm_restore_noirq(struct device *dev) | ||
322 | { | ||
323 | struct device_driver *drv = dev->driver; | ||
324 | int ret = 0; | ||
325 | |||
326 | if (!drv) | ||
327 | return 0; | ||
328 | |||
329 | if (drv->pm) { | ||
330 | if (drv->pm->restore_noirq) | ||
331 | ret = drv->pm->restore_noirq(dev); | ||
332 | } | ||
333 | |||
334 | return ret; | ||
335 | } | ||
336 | |||
337 | #else /* !CONFIG_HIBERNATION */ | ||
338 | |||
339 | #define of_platform_pm_freeze NULL | ||
340 | #define of_platform_pm_thaw NULL | ||
341 | #define of_platform_pm_poweroff NULL | ||
342 | #define of_platform_pm_restore NULL | ||
343 | #define of_platform_pm_freeze_noirq NULL | ||
344 | #define of_platform_pm_thaw_noirq NULL | ||
345 | #define of_platform_pm_poweroff_noirq NULL | ||
346 | #define of_platform_pm_restore_noirq NULL | ||
347 | |||
348 | #endif /* !CONFIG_HIBERNATION */ | ||
349 | |||
350 | static struct dev_pm_ops of_platform_dev_pm_ops = { | ||
351 | .prepare = of_platform_pm_prepare, | ||
352 | .complete = of_platform_pm_complete, | ||
353 | .suspend = of_platform_pm_suspend, | ||
354 | .resume = of_platform_pm_resume, | ||
355 | .freeze = of_platform_pm_freeze, | ||
356 | .thaw = of_platform_pm_thaw, | ||
357 | .poweroff = of_platform_pm_poweroff, | ||
358 | .restore = of_platform_pm_restore, | ||
359 | .suspend_noirq = of_platform_pm_suspend_noirq, | ||
360 | .resume_noirq = of_platform_pm_resume_noirq, | ||
361 | .freeze_noirq = of_platform_pm_freeze_noirq, | ||
362 | .thaw_noirq = of_platform_pm_thaw_noirq, | ||
363 | .poweroff_noirq = of_platform_pm_poweroff_noirq, | ||
364 | .restore_noirq = of_platform_pm_restore_noirq, | ||
365 | }; | ||
366 | |||
367 | #define OF_PLATFORM_PM_OPS_PTR (&of_platform_dev_pm_ops) | ||
368 | |||
369 | #else /* !CONFIG_PM_SLEEP */ | ||
370 | |||
371 | #define OF_PLATFORM_PM_OPS_PTR NULL | ||
372 | |||
373 | #endif /* !CONFIG_PM_SLEEP */ | ||
374 | |||
99 | int of_bus_type_init(struct bus_type *bus, const char *name) | 375 | int of_bus_type_init(struct bus_type *bus, const char *name) |
100 | { | 376 | { |
101 | bus->name = name; | 377 | bus->name = name; |
102 | bus->match = of_platform_bus_match; | 378 | bus->match = of_platform_bus_match; |
103 | bus->probe = of_platform_device_probe; | 379 | bus->probe = of_platform_device_probe; |
104 | bus->remove = of_platform_device_remove; | 380 | bus->remove = of_platform_device_remove; |
105 | bus->suspend = of_platform_device_suspend; | ||
106 | bus->resume = of_platform_device_resume; | ||
107 | bus->shutdown = of_platform_device_shutdown; | 381 | bus->shutdown = of_platform_device_shutdown; |
108 | bus->dev_attrs = of_platform_device_attrs; | 382 | bus->dev_attrs = of_platform_device_attrs; |
383 | bus->pm = OF_PLATFORM_PM_OPS_PTR; | ||
109 | return bus_register(bus); | 384 | return bus_register(bus); |
110 | } | 385 | } |
111 | 386 | ||