351 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 Storage Library4 *5 * Copyright (C) 2007 Sony Computer Entertainment Inc.6 * Copyright 2007 Sony Corp.7 */8 9#include <linux/dma-mapping.h>10#include <linux/module.h>11 12#include <asm/lv1call.h>13#include <asm/ps3stor.h>14 15/*16 * A workaround for flash memory I/O errors when the internal hard disk17 * has not been formatted for OtherOS use. Delay disk close until flash18 * memory is closed.19 */20 21static struct ps3_flash_workaround {22 int flash_open;23 int disk_open;24 struct ps3_system_bus_device *disk_sbd;25} ps3_flash_workaround;26 27static int ps3stor_open_hv_device(struct ps3_system_bus_device *sbd)28{29 int error = ps3_open_hv_device(sbd);30 31 if (error)32 return error;33 34 if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH)35 ps3_flash_workaround.flash_open = 1;36 37 if (sbd->match_id == PS3_MATCH_ID_STOR_DISK)38 ps3_flash_workaround.disk_open = 1;39 40 return 0;41}42 43static int ps3stor_close_hv_device(struct ps3_system_bus_device *sbd)44{45 int error;46 47 if (sbd->match_id == PS3_MATCH_ID_STOR_DISK48 && ps3_flash_workaround.disk_open49 && ps3_flash_workaround.flash_open) {50 ps3_flash_workaround.disk_sbd = sbd;51 return 0;52 }53 54 error = ps3_close_hv_device(sbd);55 56 if (error)57 return error;58 59 if (sbd->match_id == PS3_MATCH_ID_STOR_DISK)60 ps3_flash_workaround.disk_open = 0;61 62 if (sbd->match_id == PS3_MATCH_ID_STOR_FLASH) {63 ps3_flash_workaround.flash_open = 0;64 65 if (ps3_flash_workaround.disk_sbd) {66 ps3_close_hv_device(ps3_flash_workaround.disk_sbd);67 ps3_flash_workaround.disk_open = 0;68 ps3_flash_workaround.disk_sbd = NULL;69 }70 }71 72 return 0;73}74 75static int ps3stor_probe_access(struct ps3_storage_device *dev)76{77 int res, error;78 unsigned int i;79 unsigned long n;80 81 if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) {82 /* special case: CD-ROM is assumed always accessible */83 dev->accessible_regions = 1;84 return 0;85 }86 87 error = -EPERM;88 for (i = 0; i < dev->num_regions; i++) {89 dev_dbg(&dev->sbd.core,90 "%s:%u: checking accessibility of region %u\n",91 __func__, __LINE__, i);92 93 dev->region_idx = i;94 res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1,95 0);96 if (res) {97 dev_dbg(&dev->sbd.core, "%s:%u: read failed, "98 "region %u is not accessible\n", __func__,99 __LINE__, i);100 continue;101 }102 103 dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n",104 __func__, __LINE__, i);105 set_bit(i, &dev->accessible_regions);106 107 /* We can access at least one region */108 error = 0;109 }110 if (error)111 return error;112 113 n = hweight_long(dev->accessible_regions);114 if (n > 1)115 dev_info(&dev->sbd.core,116 "%s:%u: %lu accessible regions found. Only the first "117 "one will be used\n",118 __func__, __LINE__, n);119 dev->region_idx = __ffs(dev->accessible_regions);120 dev_info(&dev->sbd.core,121 "First accessible region has index %u start %llu size %llu\n",122 dev->region_idx, dev->regions[dev->region_idx].start,123 dev->regions[dev->region_idx].size);124 125 return 0;126}127 128 129/**130 * ps3stor_setup - Setup a storage device before use131 * @dev: Pointer to a struct ps3_storage_device132 * @handler: Pointer to an interrupt handler133 *134 * Returns 0 for success, or an error code135 */136int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler)137{138 int error, res, alignment;139 enum ps3_dma_page_size page_size;140 141 error = ps3stor_open_hv_device(&dev->sbd);142 if (error) {143 dev_err(&dev->sbd.core,144 "%s:%u: ps3_open_hv_device failed %d\n", __func__,145 __LINE__, error);146 goto fail;147 }148 149 error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY,150 &dev->irq);151 if (error) {152 dev_err(&dev->sbd.core,153 "%s:%u: ps3_sb_event_receive_port_setup failed %d\n",154 __func__, __LINE__, error);155 goto fail_close_device;156 }157 158 error = request_irq(dev->irq, handler, 0,159 dev->sbd.core.driver->name, dev);160 if (error) {161 dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n",162 __func__, __LINE__, error);163 goto fail_sb_event_receive_port_destroy;164 }165 166 alignment = min(__ffs(dev->bounce_size),167 __ffs((unsigned long)dev->bounce_buf));168 if (alignment < 12) {169 dev_err(&dev->sbd.core,170 "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n",171 __func__, __LINE__, dev->bounce_size, dev->bounce_buf);172 error = -EINVAL;173 goto fail_free_irq;174 } else if (alignment < 16)175 page_size = PS3_DMA_4K;176 else177 page_size = PS3_DMA_64K;178 dev->sbd.d_region = &dev->dma_region;179 ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size,180 PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size);181 res = ps3_dma_region_create(&dev->dma_region);182 if (res) {183 dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n",184 __func__, __LINE__);185 error = -ENOMEM;186 goto fail_free_irq;187 }188 189 dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf));190 dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf,191 dev->bounce_size, DMA_BIDIRECTIONAL);192 if (dma_mapping_error(&dev->sbd.core, dev->bounce_dma)) {193 dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n",194 __func__, __LINE__);195 error = -ENODEV;196 goto fail_free_dma;197 }198 199 error = ps3stor_probe_access(dev);200 if (error) {201 dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n",202 __func__, __LINE__);203 goto fail_unmap_dma;204 }205 return 0;206 207fail_unmap_dma:208 dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size,209 DMA_BIDIRECTIONAL);210fail_free_dma:211 ps3_dma_region_free(&dev->dma_region);212fail_free_irq:213 free_irq(dev->irq, dev);214fail_sb_event_receive_port_destroy:215 ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq);216fail_close_device:217 ps3stor_close_hv_device(&dev->sbd);218fail:219 return error;220}221EXPORT_SYMBOL_GPL(ps3stor_setup);222 223 224/**225 * ps3stor_teardown - Tear down a storage device after use226 * @dev: Pointer to a struct ps3_storage_device227 */228void ps3stor_teardown(struct ps3_storage_device *dev)229{230 int error;231 232 dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size,233 DMA_BIDIRECTIONAL);234 ps3_dma_region_free(&dev->dma_region);235 236 free_irq(dev->irq, dev);237 238 error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq);239 if (error)240 dev_err(&dev->sbd.core,241 "%s:%u: destroy event receive port failed %d\n",242 __func__, __LINE__, error);243 244 error = ps3stor_close_hv_device(&dev->sbd);245 if (error)246 dev_err(&dev->sbd.core,247 "%s:%u: ps3_close_hv_device failed %d\n", __func__,248 __LINE__, error);249}250EXPORT_SYMBOL_GPL(ps3stor_teardown);251 252 253/**254 * ps3stor_read_write_sectors - read/write from/to a storage device255 * @dev: Pointer to a struct ps3_storage_device256 * @lpar: HV logical partition address257 * @start_sector: First sector to read/write258 * @sectors: Number of sectors to read/write259 * @write: Flag indicating write (non-zero) or read (zero)260 *261 * Returns 0 for success, -1 in case of failure to submit the command, or262 * an LV1 status value in case of other errors263 */264u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar,265 u64 start_sector, u64 sectors, int write)266{267 unsigned int region_id = dev->regions[dev->region_idx].id;268 const char *op = write ? "write" : "read";269 int res;270 271 dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n",272 __func__, __LINE__, op, sectors, start_sector);273 274 init_completion(&dev->done);275 res = write ? lv1_storage_write(dev->sbd.dev_id, region_id,276 start_sector, sectors, 0, lpar,277 &dev->tag)278 : lv1_storage_read(dev->sbd.dev_id, region_id,279 start_sector, sectors, 0, lpar,280 &dev->tag);281 if (res) {282 dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,283 __LINE__, op, res);284 return -1;285 }286 287 wait_for_completion(&dev->done);288 if (dev->lv1_status) {289 dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__,290 __LINE__, op, dev->lv1_status);291 return dev->lv1_status;292 }293 294 dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__,295 op);296 297 return 0;298}299EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors);300 301 302/**303 * ps3stor_send_command - send a device command to a storage device304 * @dev: Pointer to a struct ps3_storage_device305 * @cmd: Command number306 * @arg1: First command argument307 * @arg2: Second command argument308 * @arg3: Third command argument309 * @arg4: Fourth command argument310 *311 * Returns 0 for success, -1 in case of failure to submit the command, or312 * an LV1 status value in case of other errors313 */314u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1,315 u64 arg2, u64 arg3, u64 arg4)316{317 int res;318 319 dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%llx\n", __func__,320 __LINE__, cmd);321 322 init_completion(&dev->done);323 324 res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1,325 arg2, arg3, arg4, &dev->tag);326 if (res) {327 dev_err(&dev->sbd.core,328 "%s:%u: send_device_command 0x%llx failed %d\n",329 __func__, __LINE__, cmd, res);330 return -1;331 }332 333 wait_for_completion(&dev->done);334 if (dev->lv1_status) {335 dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx failed 0x%llx\n",336 __func__, __LINE__, cmd, dev->lv1_status);337 return dev->lv1_status;338 }339 340 dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx completed\n", __func__,341 __LINE__, cmd);342 343 return 0;344}345EXPORT_SYMBOL_GPL(ps3stor_send_command);346 347 348MODULE_LICENSE("GPL");349MODULE_DESCRIPTION("PS3 Storage Bus Library");350MODULE_AUTHOR("Sony Corporation");351