Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6.git] / drivers / ps3 / ps3stor_lib.c
index 18066d5..af0afa1 100644 (file)
 #include <asm/lv1call.h>
 #include <asm/ps3stor.h>
 
+/*
+ * A workaround for flash memory I/O errors when the internal hard disk
+ * has not been formatted for OtherOS use.  Delay disk close until flash
+ * memory is closed.
+ */
+
+static struct ps3_flash_workaround {
+       int flash_open;
+       int disk_open;
+       struct ps3_system_bus_device *disk_sbd;
+} ps3_flash_workaround;
+
+static int ps3stor_open_hv_device(struct ps3_system_bus_device *sbd)
+{
+       int error = ps3_open_hv_device(sbd);
+
+       if (error)
+               return error;
+
+       if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH)
+               ps3_flash_workaround.flash_open = 1;
+
+       if (sbd->match_id == PS3_MATCH_ID_STOR_DISK)
+               ps3_flash_workaround.disk_open = 1;
+
+       return 0;
+}
+
+static int ps3stor_close_hv_device(struct ps3_system_bus_device *sbd)
+{
+       int error;
+
+       if (sbd->match_id == PS3_MATCH_ID_STOR_DISK
+               && ps3_flash_workaround.disk_open
+               && ps3_flash_workaround.flash_open) {
+               ps3_flash_workaround.disk_sbd = sbd;
+               return 0;
+       }
+
+       error = ps3_close_hv_device(sbd);
+
+       if (error)
+               return error;
+
+       if (sbd->match_id == PS3_MATCH_ID_STOR_DISK)
+               ps3_flash_workaround.disk_open = 0;
+
+       if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) {
+               ps3_flash_workaround.flash_open = 0;
+
+               if (ps3_flash_workaround.disk_sbd) {
+                       ps3_close_hv_device(ps3_flash_workaround.disk_sbd);
+                       ps3_flash_workaround.disk_open = 0;
+                       ps3_flash_workaround.disk_sbd = NULL;
+               }
+       }
+
+       return 0;
+}
 
 static int ps3stor_probe_access(struct ps3_storage_device *dev)
 {
@@ -90,7 +149,7 @@ int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler)
        int error, res, alignment;
        enum ps3_dma_page_size page_size;
 
-       error = ps3_open_hv_device(&dev->sbd);
+       error = ps3stor_open_hv_device(&dev->sbd);
        if (error) {
                dev_err(&dev->sbd.core,
                        "%s:%u: ps3_open_hv_device failed %d\n", __func__,
@@ -166,7 +225,7 @@ fail_free_irq:
 fail_sb_event_receive_port_destroy:
        ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq);
 fail_close_device:
-       ps3_close_hv_device(&dev->sbd);
+       ps3stor_close_hv_device(&dev->sbd);
 fail:
        return error;
 }
@@ -193,7 +252,7 @@ void ps3stor_teardown(struct ps3_storage_device *dev)
                        "%s:%u: destroy event receive port failed %d\n",
                        __func__, __LINE__, error);
 
-       error = ps3_close_hv_device(&dev->sbd);
+       error = ps3stor_close_hv_device(&dev->sbd);
        if (error)
                dev_err(&dev->sbd.core,
                        "%s:%u: ps3_close_hv_device failed %d\n", __func__,