18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PS3 Storage Library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Sony Computer Entertainment Inc. 68c2ecf20Sopenharmony_ci * Copyright 2007 Sony Corp. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/lv1call.h> 138c2ecf20Sopenharmony_ci#include <asm/ps3stor.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * A workaround for flash memory I/O errors when the internal hard disk 178c2ecf20Sopenharmony_ci * has not been formatted for OtherOS use. Delay disk close until flash 188c2ecf20Sopenharmony_ci * memory is closed. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic struct ps3_flash_workaround { 228c2ecf20Sopenharmony_ci int flash_open; 238c2ecf20Sopenharmony_ci int disk_open; 248c2ecf20Sopenharmony_ci struct ps3_system_bus_device *disk_sbd; 258c2ecf20Sopenharmony_ci} ps3_flash_workaround; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int ps3stor_open_hv_device(struct ps3_system_bus_device *sbd) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci int error = ps3_open_hv_device(sbd); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (error) 328c2ecf20Sopenharmony_ci return error; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) 358c2ecf20Sopenharmony_ci ps3_flash_workaround.flash_open = 1; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (sbd->match_id == PS3_MATCH_ID_STOR_DISK) 388c2ecf20Sopenharmony_ci ps3_flash_workaround.disk_open = 1; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int ps3stor_close_hv_device(struct ps3_system_bus_device *sbd) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int error; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (sbd->match_id == PS3_MATCH_ID_STOR_DISK 488c2ecf20Sopenharmony_ci && ps3_flash_workaround.disk_open 498c2ecf20Sopenharmony_ci && ps3_flash_workaround.flash_open) { 508c2ecf20Sopenharmony_ci ps3_flash_workaround.disk_sbd = sbd; 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci error = ps3_close_hv_device(sbd); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (error) 578c2ecf20Sopenharmony_ci return error; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (sbd->match_id == PS3_MATCH_ID_STOR_DISK) 608c2ecf20Sopenharmony_ci ps3_flash_workaround.disk_open = 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) { 638c2ecf20Sopenharmony_ci ps3_flash_workaround.flash_open = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (ps3_flash_workaround.disk_sbd) { 668c2ecf20Sopenharmony_ci ps3_close_hv_device(ps3_flash_workaround.disk_sbd); 678c2ecf20Sopenharmony_ci ps3_flash_workaround.disk_open = 0; 688c2ecf20Sopenharmony_ci ps3_flash_workaround.disk_sbd = NULL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int ps3stor_probe_access(struct ps3_storage_device *dev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int res, error; 788c2ecf20Sopenharmony_ci unsigned int i; 798c2ecf20Sopenharmony_ci unsigned long n; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) { 828c2ecf20Sopenharmony_ci /* special case: CD-ROM is assumed always accessible */ 838c2ecf20Sopenharmony_ci dev->accessible_regions = 1; 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci error = -EPERM; 888c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_regions; i++) { 898c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, 908c2ecf20Sopenharmony_ci "%s:%u: checking accessibility of region %u\n", 918c2ecf20Sopenharmony_ci __func__, __LINE__, i); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci dev->region_idx = i; 948c2ecf20Sopenharmony_ci res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1, 958c2ecf20Sopenharmony_ci 0); 968c2ecf20Sopenharmony_ci if (res) { 978c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: read failed, " 988c2ecf20Sopenharmony_ci "region %u is not accessible\n", __func__, 998c2ecf20Sopenharmony_ci __LINE__, i); 1008c2ecf20Sopenharmony_ci continue; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n", 1048c2ecf20Sopenharmony_ci __func__, __LINE__, i); 1058c2ecf20Sopenharmony_ci set_bit(i, &dev->accessible_regions); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* We can access at least one region */ 1088c2ecf20Sopenharmony_ci error = 0; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci if (error) 1118c2ecf20Sopenharmony_ci return error; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci n = hweight_long(dev->accessible_regions); 1148c2ecf20Sopenharmony_ci if (n > 1) 1158c2ecf20Sopenharmony_ci dev_info(&dev->sbd.core, 1168c2ecf20Sopenharmony_ci "%s:%u: %lu accessible regions found. Only the first " 1178c2ecf20Sopenharmony_ci "one will be used\n", 1188c2ecf20Sopenharmony_ci __func__, __LINE__, n); 1198c2ecf20Sopenharmony_ci dev->region_idx = __ffs(dev->accessible_regions); 1208c2ecf20Sopenharmony_ci dev_info(&dev->sbd.core, 1218c2ecf20Sopenharmony_ci "First accessible region has index %u start %llu size %llu\n", 1228c2ecf20Sopenharmony_ci dev->region_idx, dev->regions[dev->region_idx].start, 1238c2ecf20Sopenharmony_ci dev->regions[dev->region_idx].size); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/** 1308c2ecf20Sopenharmony_ci * ps3stor_setup - Setup a storage device before use 1318c2ecf20Sopenharmony_ci * @dev: Pointer to a struct ps3_storage_device 1328c2ecf20Sopenharmony_ci * @handler: Pointer to an interrupt handler 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Returns 0 for success, or an error code 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ciint ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int error, res, alignment; 1398c2ecf20Sopenharmony_ci enum ps3_dma_page_size page_size; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci error = ps3stor_open_hv_device(&dev->sbd); 1428c2ecf20Sopenharmony_ci if (error) { 1438c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 1448c2ecf20Sopenharmony_ci "%s:%u: ps3_open_hv_device failed %d\n", __func__, 1458c2ecf20Sopenharmony_ci __LINE__, error); 1468c2ecf20Sopenharmony_ci goto fail; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY, 1508c2ecf20Sopenharmony_ci &dev->irq); 1518c2ecf20Sopenharmony_ci if (error) { 1528c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 1538c2ecf20Sopenharmony_ci "%s:%u: ps3_sb_event_receive_port_setup failed %d\n", 1548c2ecf20Sopenharmony_ci __func__, __LINE__, error); 1558c2ecf20Sopenharmony_ci goto fail_close_device; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci error = request_irq(dev->irq, handler, 0, 1598c2ecf20Sopenharmony_ci dev->sbd.core.driver->name, dev); 1608c2ecf20Sopenharmony_ci if (error) { 1618c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n", 1628c2ecf20Sopenharmony_ci __func__, __LINE__, error); 1638c2ecf20Sopenharmony_ci goto fail_sb_event_receive_port_destroy; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci alignment = min(__ffs(dev->bounce_size), 1678c2ecf20Sopenharmony_ci __ffs((unsigned long)dev->bounce_buf)); 1688c2ecf20Sopenharmony_ci if (alignment < 12) { 1698c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 1708c2ecf20Sopenharmony_ci "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n", 1718c2ecf20Sopenharmony_ci __func__, __LINE__, dev->bounce_size, dev->bounce_buf); 1728c2ecf20Sopenharmony_ci error = -EINVAL; 1738c2ecf20Sopenharmony_ci goto fail_free_irq; 1748c2ecf20Sopenharmony_ci } else if (alignment < 16) 1758c2ecf20Sopenharmony_ci page_size = PS3_DMA_4K; 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci page_size = PS3_DMA_64K; 1788c2ecf20Sopenharmony_ci dev->sbd.d_region = &dev->dma_region; 1798c2ecf20Sopenharmony_ci ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size, 1808c2ecf20Sopenharmony_ci PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size); 1818c2ecf20Sopenharmony_ci res = ps3_dma_region_create(&dev->dma_region); 1828c2ecf20Sopenharmony_ci if (res) { 1838c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n", 1848c2ecf20Sopenharmony_ci __func__, __LINE__); 1858c2ecf20Sopenharmony_ci error = -ENOMEM; 1868c2ecf20Sopenharmony_ci goto fail_free_irq; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf)); 1908c2ecf20Sopenharmony_ci dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf, 1918c2ecf20Sopenharmony_ci dev->bounce_size, DMA_BIDIRECTIONAL); 1928c2ecf20Sopenharmony_ci if (dma_mapping_error(&dev->sbd.core, dev->bounce_dma)) { 1938c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n", 1948c2ecf20Sopenharmony_ci __func__, __LINE__); 1958c2ecf20Sopenharmony_ci error = -ENODEV; 1968c2ecf20Sopenharmony_ci goto fail_free_dma; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci error = ps3stor_probe_access(dev); 2008c2ecf20Sopenharmony_ci if (error) { 2018c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n", 2028c2ecf20Sopenharmony_ci __func__, __LINE__); 2038c2ecf20Sopenharmony_ci goto fail_unmap_dma; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cifail_unmap_dma: 2088c2ecf20Sopenharmony_ci dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, 2098c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 2108c2ecf20Sopenharmony_cifail_free_dma: 2118c2ecf20Sopenharmony_ci ps3_dma_region_free(&dev->dma_region); 2128c2ecf20Sopenharmony_cifail_free_irq: 2138c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 2148c2ecf20Sopenharmony_cifail_sb_event_receive_port_destroy: 2158c2ecf20Sopenharmony_ci ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); 2168c2ecf20Sopenharmony_cifail_close_device: 2178c2ecf20Sopenharmony_ci ps3stor_close_hv_device(&dev->sbd); 2188c2ecf20Sopenharmony_cifail: 2198c2ecf20Sopenharmony_ci return error; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3stor_setup); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * ps3stor_teardown - Tear down a storage device after use 2268c2ecf20Sopenharmony_ci * @dev: Pointer to a struct ps3_storage_device 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_civoid ps3stor_teardown(struct ps3_storage_device *dev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int error; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, 2338c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 2348c2ecf20Sopenharmony_ci ps3_dma_region_free(&dev->dma_region); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); 2398c2ecf20Sopenharmony_ci if (error) 2408c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 2418c2ecf20Sopenharmony_ci "%s:%u: destroy event receive port failed %d\n", 2428c2ecf20Sopenharmony_ci __func__, __LINE__, error); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci error = ps3stor_close_hv_device(&dev->sbd); 2458c2ecf20Sopenharmony_ci if (error) 2468c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 2478c2ecf20Sopenharmony_ci "%s:%u: ps3_close_hv_device failed %d\n", __func__, 2488c2ecf20Sopenharmony_ci __LINE__, error); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3stor_teardown); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * ps3stor_read_write_sectors - read/write from/to a storage device 2558c2ecf20Sopenharmony_ci * @dev: Pointer to a struct ps3_storage_device 2568c2ecf20Sopenharmony_ci * @lpar: HV logical partition address 2578c2ecf20Sopenharmony_ci * @start_sector: First sector to read/write 2588c2ecf20Sopenharmony_ci * @sectors: Number of sectors to read/write 2598c2ecf20Sopenharmony_ci * @write: Flag indicating write (non-zero) or read (zero) 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Returns 0 for success, -1 in case of failure to submit the command, or 2628c2ecf20Sopenharmony_ci * an LV1 status value in case of other errors 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ciu64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, 2658c2ecf20Sopenharmony_ci u64 start_sector, u64 sectors, int write) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci unsigned int region_id = dev->regions[dev->region_idx].id; 2688c2ecf20Sopenharmony_ci const char *op = write ? "write" : "read"; 2698c2ecf20Sopenharmony_ci int res; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", 2728c2ecf20Sopenharmony_ci __func__, __LINE__, op, sectors, start_sector); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci init_completion(&dev->done); 2758c2ecf20Sopenharmony_ci res = write ? lv1_storage_write(dev->sbd.dev_id, region_id, 2768c2ecf20Sopenharmony_ci start_sector, sectors, 0, lpar, 2778c2ecf20Sopenharmony_ci &dev->tag) 2788c2ecf20Sopenharmony_ci : lv1_storage_read(dev->sbd.dev_id, region_id, 2798c2ecf20Sopenharmony_ci start_sector, sectors, 0, lpar, 2808c2ecf20Sopenharmony_ci &dev->tag); 2818c2ecf20Sopenharmony_ci if (res) { 2828c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, 2838c2ecf20Sopenharmony_ci __LINE__, op, res); 2848c2ecf20Sopenharmony_ci return -1; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci wait_for_completion(&dev->done); 2888c2ecf20Sopenharmony_ci if (dev->lv1_status) { 2898c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, 2908c2ecf20Sopenharmony_ci __LINE__, op, dev->lv1_status); 2918c2ecf20Sopenharmony_ci return dev->lv1_status; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__, 2958c2ecf20Sopenharmony_ci op); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3stor_read_write_sectors); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * ps3stor_send_command - send a device command to a storage device 3048c2ecf20Sopenharmony_ci * @dev: Pointer to a struct ps3_storage_device 3058c2ecf20Sopenharmony_ci * @cmd: Command number 3068c2ecf20Sopenharmony_ci * @arg1: First command argument 3078c2ecf20Sopenharmony_ci * @arg2: Second command argument 3088c2ecf20Sopenharmony_ci * @arg3: Third command argument 3098c2ecf20Sopenharmony_ci * @arg4: Fourth command argument 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * Returns 0 for success, -1 in case of failure to submit the command, or 3128c2ecf20Sopenharmony_ci * an LV1 status value in case of other errors 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ciu64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, 3158c2ecf20Sopenharmony_ci u64 arg2, u64 arg3, u64 arg4) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci int res; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%llx\n", __func__, 3208c2ecf20Sopenharmony_ci __LINE__, cmd); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci init_completion(&dev->done); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1, 3258c2ecf20Sopenharmony_ci arg2, arg3, arg4, &dev->tag); 3268c2ecf20Sopenharmony_ci if (res) { 3278c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 3288c2ecf20Sopenharmony_ci "%s:%u: send_device_command 0x%llx failed %d\n", 3298c2ecf20Sopenharmony_ci __func__, __LINE__, cmd, res); 3308c2ecf20Sopenharmony_ci return -1; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci wait_for_completion(&dev->done); 3348c2ecf20Sopenharmony_ci if (dev->lv1_status) { 3358c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx failed 0x%llx\n", 3368c2ecf20Sopenharmony_ci __func__, __LINE__, cmd, dev->lv1_status); 3378c2ecf20Sopenharmony_ci return dev->lv1_status; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx completed\n", __func__, 3418c2ecf20Sopenharmony_ci __LINE__, cmd); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3stor_send_command); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3498c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PS3 Storage Bus Library"); 3508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sony Corporation"); 351