ps3stor_lib.c (9227B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * PS3 Storage Library 4 * 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 disk 17 * has not been formatted for OtherOS use. Delay disk close until flash 18 * 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_DISK 48 && ps3_flash_workaround.disk_open 49 && 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 use 131 * @dev: Pointer to a struct ps3_storage_device 132 * @handler: Pointer to an interrupt handler 133 * 134 * Returns 0 for success, or an error code 135 */ 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 else 177 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 use 226 * @dev: Pointer to a struct ps3_storage_device 227 */ 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 device 255 * @dev: Pointer to a struct ps3_storage_device 256 * @lpar: HV logical partition address 257 * @start_sector: First sector to read/write 258 * @sectors: Number of sectors to read/write 259 * @write: Flag indicating write (non-zero) or read (zero) 260 * 261 * Returns 0 for success, -1 in case of failure to submit the command, or 262 * an LV1 status value in case of other errors 263 */ 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 device 304 * @dev: Pointer to a struct ps3_storage_device 305 * @cmd: Command number 306 * @arg1: First command argument 307 * @arg2: Second command argument 308 * @arg3: Third command argument 309 * @arg4: Fourth command argument 310 * 311 * Returns 0 for success, -1 in case of failure to submit the command, or 312 * an LV1 status value in case of other errors 313 */ 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");