[S390] pm: dcssblk power management callbacks.
Gerald Schaefer [Tue, 16 Jun 2009 08:30:51 +0000 (10:30 +0200)]
Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

drivers/s390/block/dcssblk.c

index b21caf1..016f9e9 100644 (file)
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
-#include <asm/extmem.h>
-#include <asm/io.h>
 #include <linux/completion.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <asm/extmem.h>
+#include <asm/io.h>
 
 #define DCSSBLK_NAME "dcssblk"
 #define DCSSBLK_MINORS_PER_DISK 1
@@ -940,11 +941,94 @@ dcssblk_check_params(void)
 }
 
 /*
+ * Suspend / Resume
+ */
+static int dcssblk_freeze(struct device *dev)
+{
+       struct dcssblk_dev_info *dev_info;
+       int rc = 0;
+
+       list_for_each_entry(dev_info, &dcssblk_devices, lh) {
+               switch (dev_info->segment_type) {
+                       case SEG_TYPE_SR:
+                       case SEG_TYPE_ER:
+                       case SEG_TYPE_SC:
+                               if (!dev_info->is_shared)
+                                       rc = -EINVAL;
+                               break;
+                       default:
+                               rc = -EINVAL;
+                               break;
+               }
+               if (rc)
+                       break;
+       }
+       if (rc)
+               pr_err("Suspend failed because device %s is writeable.\n",
+                      dev_info->segment_name);
+       return rc;
+}
+
+static int dcssblk_restore(struct device *dev)
+{
+       struct dcssblk_dev_info *dev_info;
+       struct segment_info *entry;
+       unsigned long start, end;
+       int rc = 0;
+
+       list_for_each_entry(dev_info, &dcssblk_devices, lh) {
+               list_for_each_entry(entry, &dev_info->seg_list, lh) {
+                       segment_unload(entry->segment_name);
+                       rc = segment_load(entry->segment_name, SEGMENT_SHARED,
+                                         &start, &end);
+                       if (rc < 0) {
+// TODO in_use check ?
+                               segment_warning(rc, entry->segment_name);
+                               goto out_panic;
+                       }
+                       if (start != entry->start || end != entry->end) {
+                               pr_err("Mismatch of start / end address after "
+                                      "resuming device %s\n",
+                                      entry->segment_name);
+                               goto out_panic;
+                       }
+               }
+       }
+       return 0;
+out_panic:
+       panic("fatal dcssblk resume error\n");
+}
+
+static int dcssblk_thaw(struct device *dev)
+{
+       return 0;
+}
+
+static struct dev_pm_ops dcssblk_pm_ops = {
+       .freeze         = dcssblk_freeze,
+       .thaw           = dcssblk_thaw,
+       .restore        = dcssblk_restore,
+};
+
+static struct platform_driver dcssblk_pdrv = {
+       .driver = {
+               .name   = "dcssblk",
+               .owner  = THIS_MODULE,
+               .pm     = &dcssblk_pm_ops,
+       },
+};
+
+static struct platform_device *dcssblk_pdev;
+
+
+/*
  * The init/exit functions.
  */
 static void __exit
 dcssblk_exit(void)
 {
+       platform_device_unregister(dcssblk_pdev);
+       platform_driver_unregister(&dcssblk_pdrv);
        root_device_unregister(dcssblk_root_dev);
        unregister_blkdev(dcssblk_major, DCSSBLK_NAME);
 }
@@ -954,30 +1038,44 @@ dcssblk_init(void)
 {
        int rc;
 
-       dcssblk_root_dev = root_device_register("dcssblk");
-       if (IS_ERR(dcssblk_root_dev))
-               return PTR_ERR(dcssblk_root_dev);
-       rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
-       if (rc) {
-               root_device_unregister(dcssblk_root_dev);
+       rc = platform_driver_register(&dcssblk_pdrv);
+       if (rc)
                return rc;
+
+       dcssblk_pdev = platform_device_register_simple("dcssblk", -1, NULL,
+                                                       0);
+       if (IS_ERR(dcssblk_pdev)) {
+               rc = PTR_ERR(dcssblk_pdev);
+               goto out_pdrv;
        }
-       rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
-       if (rc) {
-               root_device_unregister(dcssblk_root_dev);
-               return rc;
+
+       dcssblk_root_dev = root_device_register("dcssblk");
+       if (IS_ERR(dcssblk_root_dev)) {
+               rc = PTR_ERR(dcssblk_root_dev);
+               goto out_pdev;
        }
+       rc = device_create_file(dcssblk_root_dev, &dev_attr_add);
+       if (rc)
+               goto out_root;
+       rc = device_create_file(dcssblk_root_dev, &dev_attr_remove);
+       if (rc)
+               goto out_root;
        rc = register_blkdev(0, DCSSBLK_NAME);
-       if (rc < 0) {
-               root_device_unregister(dcssblk_root_dev);
-               return rc;
-       }
+       if (rc < 0)
+               goto out_root;
        dcssblk_major = rc;
        init_rwsem(&dcssblk_devices_sem);
 
        dcssblk_check_params();
-
        return 0;
+
+out_root:
+       root_device_unregister(dcssblk_root_dev);
+out_pdev:
+       platform_device_unregister(dcssblk_pdev);
+out_pdrv:
+       platform_driver_unregister(&dcssblk_pdrv);
+       return rc;
 }
 
 module_init(dcssblk_init);