Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6.git] / drivers / ps3 / ps3stor_lib.c
index 55955f1..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)
 {
@@ -70,7 +129,7 @@ static int ps3stor_probe_access(struct ps3_storage_device *dev)
                         __func__, __LINE__, n);
        dev->region_idx = __ffs(dev->accessible_regions);
        dev_info(&dev->sbd.core,
-                "First accessible region has index %u start %lu size %lu\n",
+                "First accessible region has index %u start %llu size %llu\n",
                 dev->region_idx, dev->regions[dev->region_idx].start,
                 dev->regions[dev->region_idx].size);
 
@@ -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__,
@@ -220,7 +279,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
        const char *op = write ? "write" : "read";
        int res;
 
-       dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n",
+       dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",
                __func__, __LINE__, op, sectors, start_sector);
 
        init_completion(&dev->done);
@@ -238,7 +297,7 @@ u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,
 
        wait_for_completion(&dev->done);
        if (dev->lv1_status) {
-               dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__,
+               dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,
                        __LINE__, op, dev->lv1_status);
                return dev->lv1_status;
        }
@@ -268,7 +327,7 @@ u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1,
 {
        int res;
 
-       dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%lx\n", __func__,
+       dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%llx\n", __func__,
                __LINE__, cmd);
 
        init_completion(&dev->done);
@@ -277,19 +336,19 @@ u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1,
                                              arg2, arg3, arg4, &dev->tag);
        if (res) {
                dev_err(&dev->sbd.core,
-                       "%s:%u: send_device_command 0x%lx failed %d\n",
+                       "%s:%u: send_device_command 0x%llx failed %d\n",
                        __func__, __LINE__, cmd, res);
                return -1;
        }
 
        wait_for_completion(&dev->done);
        if (dev->lv1_status) {
-               dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx failed 0x%lx\n",
+               dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx failed 0x%llx\n",
                        __func__, __LINE__, cmd, dev->lv1_status);
                return dev->lv1_status;
        }
 
-       dev_dbg(&dev->sbd.core, "%s:%u: command 0x%lx completed\n", __func__,
+       dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx completed\n", __func__,
                __LINE__, cmd);
 
        return 0;