diff options
Diffstat (limited to 'drivers/base/sys.c')
-rw-r--r-- | drivers/base/sys.c | 110 |
1 files changed, 85 insertions, 25 deletions
diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 214b96435409..3431eb6004c3 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c | |||
@@ -288,6 +288,27 @@ void sysdev_shutdown(void) | |||
288 | up(&sysdev_drivers_lock); | 288 | up(&sysdev_drivers_lock); |
289 | } | 289 | } |
290 | 290 | ||
291 | static void __sysdev_resume(struct sys_device *dev) | ||
292 | { | ||
293 | struct sysdev_class *cls = dev->cls; | ||
294 | struct sysdev_driver *drv; | ||
295 | |||
296 | /* First, call the class-specific one */ | ||
297 | if (cls->resume) | ||
298 | cls->resume(dev); | ||
299 | |||
300 | /* Call auxillary drivers next. */ | ||
301 | list_for_each_entry(drv, &cls->drivers, entry) { | ||
302 | if (drv->resume) | ||
303 | drv->resume(dev); | ||
304 | } | ||
305 | |||
306 | /* Call global drivers. */ | ||
307 | list_for_each_entry(drv, &sysdev_drivers, entry) { | ||
308 | if (drv->resume) | ||
309 | drv->resume(dev); | ||
310 | } | ||
311 | } | ||
291 | 312 | ||
292 | /** | 313 | /** |
293 | * sysdev_suspend - Suspend all system devices. | 314 | * sysdev_suspend - Suspend all system devices. |
@@ -305,38 +326,93 @@ void sysdev_shutdown(void) | |||
305 | int sysdev_suspend(pm_message_t state) | 326 | int sysdev_suspend(pm_message_t state) |
306 | { | 327 | { |
307 | struct sysdev_class * cls; | 328 | struct sysdev_class * cls; |
329 | struct sys_device *sysdev, *err_dev; | ||
330 | struct sysdev_driver *drv, *err_drv; | ||
331 | int ret; | ||
308 | 332 | ||
309 | pr_debug("Suspending System Devices\n"); | 333 | pr_debug("Suspending System Devices\n"); |
310 | 334 | ||
311 | list_for_each_entry_reverse(cls, &system_subsys.kset.list, | 335 | list_for_each_entry_reverse(cls, &system_subsys.kset.list, |
312 | kset.kobj.entry) { | 336 | kset.kobj.entry) { |
313 | struct sys_device * sysdev; | ||
314 | 337 | ||
315 | pr_debug("Suspending type '%s':\n", | 338 | pr_debug("Suspending type '%s':\n", |
316 | kobject_name(&cls->kset.kobj)); | 339 | kobject_name(&cls->kset.kobj)); |
317 | 340 | ||
318 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { | 341 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { |
319 | struct sysdev_driver * drv; | ||
320 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); | 342 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); |
321 | 343 | ||
322 | /* Call global drivers first. */ | 344 | /* Call global drivers first. */ |
323 | list_for_each_entry(drv, &sysdev_drivers, entry) { | 345 | list_for_each_entry(drv, &sysdev_drivers, entry) { |
324 | if (drv->suspend) | 346 | if (drv->suspend) { |
325 | drv->suspend(sysdev, state); | 347 | ret = drv->suspend(sysdev, state); |
348 | if (ret) | ||
349 | goto gbl_driver; | ||
350 | } | ||
326 | } | 351 | } |
327 | 352 | ||
328 | /* Call auxillary drivers next. */ | 353 | /* Call auxillary drivers next. */ |
329 | list_for_each_entry(drv, &cls->drivers, entry) { | 354 | list_for_each_entry(drv, &cls->drivers, entry) { |
330 | if (drv->suspend) | 355 | if (drv->suspend) { |
331 | drv->suspend(sysdev, state); | 356 | ret = drv->suspend(sysdev, state); |
357 | if (ret) | ||
358 | goto aux_driver; | ||
359 | } | ||
332 | } | 360 | } |
333 | 361 | ||
334 | /* Now call the generic one */ | 362 | /* Now call the generic one */ |
335 | if (cls->suspend) | 363 | if (cls->suspend) { |
336 | cls->suspend(sysdev, state); | 364 | ret = cls->suspend(sysdev, state); |
365 | if (ret) | ||
366 | goto cls_driver; | ||
367 | } | ||
337 | } | 368 | } |
338 | } | 369 | } |
339 | return 0; | 370 | return 0; |
371 | /* resume current sysdev */ | ||
372 | cls_driver: | ||
373 | drv = NULL; | ||
374 | printk(KERN_ERR "Class suspend failed for %s\n", | ||
375 | kobject_name(&sysdev->kobj)); | ||
376 | |||
377 | aux_driver: | ||
378 | if (drv) | ||
379 | printk(KERN_ERR "Class driver suspend failed for %s\n", | ||
380 | kobject_name(&sysdev->kobj)); | ||
381 | list_for_each_entry(err_drv, &cls->drivers, entry) { | ||
382 | if (err_drv == drv) | ||
383 | break; | ||
384 | if (err_drv->resume) | ||
385 | err_drv->resume(sysdev); | ||
386 | } | ||
387 | drv = NULL; | ||
388 | |||
389 | gbl_driver: | ||
390 | if (drv) | ||
391 | printk(KERN_ERR "sysdev driver suspend failed for %s\n", | ||
392 | kobject_name(&sysdev->kobj)); | ||
393 | list_for_each_entry(err_drv, &sysdev_drivers, entry) { | ||
394 | if (err_drv == drv) | ||
395 | break; | ||
396 | if (err_drv->resume) | ||
397 | err_drv->resume(sysdev); | ||
398 | } | ||
399 | /* resume other sysdevs in current class */ | ||
400 | list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { | ||
401 | if (err_dev == sysdev) | ||
402 | break; | ||
403 | pr_debug(" %s\n", kobject_name(&err_dev->kobj)); | ||
404 | __sysdev_resume(err_dev); | ||
405 | } | ||
406 | |||
407 | /* resume other classes */ | ||
408 | list_for_each_entry_continue(cls, &system_subsys.kset.list, | ||
409 | kset.kobj.entry) { | ||
410 | list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { | ||
411 | pr_debug(" %s\n", kobject_name(&err_dev->kobj)); | ||
412 | __sysdev_resume(err_dev); | ||
413 | } | ||
414 | } | ||
415 | return ret; | ||
340 | } | 416 | } |
341 | 417 | ||
342 | 418 | ||
@@ -362,25 +438,9 @@ int sysdev_resume(void) | |||
362 | kobject_name(&cls->kset.kobj)); | 438 | kobject_name(&cls->kset.kobj)); |
363 | 439 | ||
364 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { | 440 | list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { |
365 | struct sysdev_driver * drv; | ||
366 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); | 441 | pr_debug(" %s\n", kobject_name(&sysdev->kobj)); |
367 | 442 | ||
368 | /* First, call the class-specific one */ | 443 | __sysdev_resume(sysdev); |
369 | if (cls->resume) | ||
370 | cls->resume(sysdev); | ||
371 | |||
372 | /* Call auxillary drivers next. */ | ||
373 | list_for_each_entry(drv, &cls->drivers, entry) { | ||
374 | if (drv->resume) | ||
375 | drv->resume(sysdev); | ||
376 | } | ||
377 | |||
378 | /* Call global drivers. */ | ||
379 | list_for_each_entry(drv, &sysdev_drivers, entry) { | ||
380 | if (drv->resume) | ||
381 | drv->resume(sysdev); | ||
382 | } | ||
383 | |||
384 | } | 444 | } |
385 | } | 445 | } |
386 | return 0; | 446 | return 0; |