|
@@ -288,6 +288,27 @@ void sysdev_shutdown(void)
|
|
up(&sysdev_drivers_lock);
|
|
up(&sysdev_drivers_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void __sysdev_resume(struct sys_device *dev)
|
|
|
|
+{
|
|
|
|
+ struct sysdev_class *cls = dev->cls;
|
|
|
|
+ struct sysdev_driver *drv;
|
|
|
|
+
|
|
|
|
+ /* First, call the class-specific one */
|
|
|
|
+ if (cls->resume)
|
|
|
|
+ cls->resume(dev);
|
|
|
|
+
|
|
|
|
+ /* Call auxillary drivers next. */
|
|
|
|
+ list_for_each_entry(drv, &cls->drivers, entry) {
|
|
|
|
+ if (drv->resume)
|
|
|
|
+ drv->resume(dev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Call global drivers. */
|
|
|
|
+ list_for_each_entry(drv, &sysdev_drivers, entry) {
|
|
|
|
+ if (drv->resume)
|
|
|
|
+ drv->resume(dev);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
/**
|
|
/**
|
|
* sysdev_suspend - Suspend all system devices.
|
|
* sysdev_suspend - Suspend all system devices.
|
|
@@ -305,38 +326,93 @@ void sysdev_shutdown(void)
|
|
int sysdev_suspend(pm_message_t state)
|
|
int sysdev_suspend(pm_message_t state)
|
|
{
|
|
{
|
|
struct sysdev_class * cls;
|
|
struct sysdev_class * cls;
|
|
|
|
+ struct sys_device *sysdev, *err_dev;
|
|
|
|
+ struct sysdev_driver *drv, *err_drv;
|
|
|
|
+ int ret;
|
|
|
|
|
|
pr_debug("Suspending System Devices\n");
|
|
pr_debug("Suspending System Devices\n");
|
|
|
|
|
|
list_for_each_entry_reverse(cls, &system_subsys.kset.list,
|
|
list_for_each_entry_reverse(cls, &system_subsys.kset.list,
|
|
kset.kobj.entry) {
|
|
kset.kobj.entry) {
|
|
- struct sys_device * sysdev;
|
|
|
|
|
|
|
|
pr_debug("Suspending type '%s':\n",
|
|
pr_debug("Suspending type '%s':\n",
|
|
kobject_name(&cls->kset.kobj));
|
|
kobject_name(&cls->kset.kobj));
|
|
|
|
|
|
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
|
|
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
|
|
- struct sysdev_driver * drv;
|
|
|
|
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
|
|
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
|
|
|
|
|
|
/* Call global drivers first. */
|
|
/* Call global drivers first. */
|
|
list_for_each_entry(drv, &sysdev_drivers, entry) {
|
|
list_for_each_entry(drv, &sysdev_drivers, entry) {
|
|
- if (drv->suspend)
|
|
|
|
- drv->suspend(sysdev, state);
|
|
|
|
|
|
+ if (drv->suspend) {
|
|
|
|
+ ret = drv->suspend(sysdev, state);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto gbl_driver;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/* Call auxillary drivers next. */
|
|
/* Call auxillary drivers next. */
|
|
list_for_each_entry(drv, &cls->drivers, entry) {
|
|
list_for_each_entry(drv, &cls->drivers, entry) {
|
|
- if (drv->suspend)
|
|
|
|
- drv->suspend(sysdev, state);
|
|
|
|
|
|
+ if (drv->suspend) {
|
|
|
|
+ ret = drv->suspend(sysdev, state);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto aux_driver;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/* Now call the generic one */
|
|
/* Now call the generic one */
|
|
- if (cls->suspend)
|
|
|
|
- cls->suspend(sysdev, state);
|
|
|
|
|
|
+ if (cls->suspend) {
|
|
|
|
+ ret = cls->suspend(sysdev, state);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto cls_driver;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
|
|
+ /* resume current sysdev */
|
|
|
|
+cls_driver:
|
|
|
|
+ drv = NULL;
|
|
|
|
+ printk(KERN_ERR "Class suspend failed for %s\n",
|
|
|
|
+ kobject_name(&sysdev->kobj));
|
|
|
|
+
|
|
|
|
+aux_driver:
|
|
|
|
+ if (drv)
|
|
|
|
+ printk(KERN_ERR "Class driver suspend failed for %s\n",
|
|
|
|
+ kobject_name(&sysdev->kobj));
|
|
|
|
+ list_for_each_entry(err_drv, &cls->drivers, entry) {
|
|
|
|
+ if (err_drv == drv)
|
|
|
|
+ break;
|
|
|
|
+ if (err_drv->resume)
|
|
|
|
+ err_drv->resume(sysdev);
|
|
|
|
+ }
|
|
|
|
+ drv = NULL;
|
|
|
|
+
|
|
|
|
+gbl_driver:
|
|
|
|
+ if (drv)
|
|
|
|
+ printk(KERN_ERR "sysdev driver suspend failed for %s\n",
|
|
|
|
+ kobject_name(&sysdev->kobj));
|
|
|
|
+ list_for_each_entry(err_drv, &sysdev_drivers, entry) {
|
|
|
|
+ if (err_drv == drv)
|
|
|
|
+ break;
|
|
|
|
+ if (err_drv->resume)
|
|
|
|
+ err_drv->resume(sysdev);
|
|
|
|
+ }
|
|
|
|
+ /* resume other sysdevs in current class */
|
|
|
|
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
|
|
|
|
+ if (err_dev == sysdev)
|
|
|
|
+ break;
|
|
|
|
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
|
|
|
|
+ __sysdev_resume(err_dev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* resume other classes */
|
|
|
|
+ list_for_each_entry_continue(cls, &system_subsys.kset.list,
|
|
|
|
+ kset.kobj.entry) {
|
|
|
|
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
|
|
|
|
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
|
|
|
|
+ __sysdev_resume(err_dev);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -362,25 +438,9 @@ int sysdev_resume(void)
|
|
kobject_name(&cls->kset.kobj));
|
|
kobject_name(&cls->kset.kobj));
|
|
|
|
|
|
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
|
|
list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
|
|
- struct sysdev_driver * drv;
|
|
|
|
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
|
|
pr_debug(" %s\n", kobject_name(&sysdev->kobj));
|
|
|
|
|
|
- /* First, call the class-specific one */
|
|
|
|
- if (cls->resume)
|
|
|
|
- cls->resume(sysdev);
|
|
|
|
-
|
|
|
|
- /* Call auxillary drivers next. */
|
|
|
|
- list_for_each_entry(drv, &cls->drivers, entry) {
|
|
|
|
- if (drv->resume)
|
|
|
|
- drv->resume(sysdev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Call global drivers. */
|
|
|
|
- list_for_each_entry(drv, &sysdev_drivers, entry) {
|
|
|
|
- if (drv->resume)
|
|
|
|
- drv->resume(sysdev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ __sysdev_resume(sysdev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|