diff options
Diffstat (limited to 'drivers/ps3/ps3stor_lib.c')
| -rw-r--r-- | drivers/ps3/ps3stor_lib.c | 65 | 
1 files changed, 62 insertions, 3 deletions
diff --git a/drivers/ps3/ps3stor_lib.c b/drivers/ps3/ps3stor_lib.c index 18066d55539..af0afa1db4a 100644 --- a/drivers/ps3/ps3stor_lib.c +++ b/drivers/ps3/ps3stor_lib.c @@ -23,6 +23,65 @@  #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__,  |