180071802SGeert Uytterhoeven /* 280071802SGeert Uytterhoeven * PS3 Storage Library 380071802SGeert Uytterhoeven * 480071802SGeert Uytterhoeven * Copyright (C) 2007 Sony Computer Entertainment Inc. 580071802SGeert Uytterhoeven * Copyright 2007 Sony Corp. 680071802SGeert Uytterhoeven * 780071802SGeert Uytterhoeven * This program is free software; you can redistribute it and/or modify it 880071802SGeert Uytterhoeven * under the terms of the GNU General Public License as published 980071802SGeert Uytterhoeven * by the Free Software Foundation; version 2 of the License. 1080071802SGeert Uytterhoeven * 1180071802SGeert Uytterhoeven * This program is distributed in the hope that it will be useful, but 1280071802SGeert Uytterhoeven * WITHOUT ANY WARRANTY; without even the implied warranty of 1380071802SGeert Uytterhoeven * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1480071802SGeert Uytterhoeven * General Public License for more details. 1580071802SGeert Uytterhoeven * 1680071802SGeert Uytterhoeven * You should have received a copy of the GNU General Public License along 1780071802SGeert Uytterhoeven * with this program; if not, write to the Free Software Foundation, Inc., 1880071802SGeert Uytterhoeven * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 1980071802SGeert Uytterhoeven */ 2080071802SGeert Uytterhoeven 2180071802SGeert Uytterhoeven #include <linux/dma-mapping.h> 2280071802SGeert Uytterhoeven 2380071802SGeert Uytterhoeven #include <asm/lv1call.h> 2480071802SGeert Uytterhoeven #include <asm/ps3stor.h> 2580071802SGeert Uytterhoeven 2680071802SGeert Uytterhoeven 2780071802SGeert Uytterhoeven static int ps3stor_probe_access(struct ps3_storage_device *dev) 2880071802SGeert Uytterhoeven { 2980071802SGeert Uytterhoeven int res, error; 3080071802SGeert Uytterhoeven unsigned int i; 3180071802SGeert Uytterhoeven unsigned long n; 3280071802SGeert Uytterhoeven 3380071802SGeert Uytterhoeven if (dev->sbd.match_id == PS3_MATCH_ID_STOR_ROM) { 3480071802SGeert Uytterhoeven /* special case: CD-ROM is assumed always accessible */ 3580071802SGeert Uytterhoeven dev->accessible_regions = 1; 3680071802SGeert Uytterhoeven return 0; 3780071802SGeert Uytterhoeven } 3880071802SGeert Uytterhoeven 3980071802SGeert Uytterhoeven error = -EPERM; 4080071802SGeert Uytterhoeven for (i = 0; i < dev->num_regions; i++) { 4180071802SGeert Uytterhoeven dev_dbg(&dev->sbd.core, 4280071802SGeert Uytterhoeven "%s:%u: checking accessibility of region %u\n", 4380071802SGeert Uytterhoeven __func__, __LINE__, i); 4480071802SGeert Uytterhoeven 4580071802SGeert Uytterhoeven dev->region_idx = i; 4680071802SGeert Uytterhoeven res = ps3stor_read_write_sectors(dev, dev->bounce_lpar, 0, 1, 4780071802SGeert Uytterhoeven 0); 4880071802SGeert Uytterhoeven if (res) { 4980071802SGeert Uytterhoeven dev_dbg(&dev->sbd.core, "%s:%u: read failed, " 5080071802SGeert Uytterhoeven "region %u is not accessible\n", __func__, 5180071802SGeert Uytterhoeven __LINE__, i); 5280071802SGeert Uytterhoeven continue; 5380071802SGeert Uytterhoeven } 5480071802SGeert Uytterhoeven 5580071802SGeert Uytterhoeven dev_dbg(&dev->sbd.core, "%s:%u: region %u is accessible\n", 5680071802SGeert Uytterhoeven __func__, __LINE__, i); 5780071802SGeert Uytterhoeven set_bit(i, &dev->accessible_regions); 5880071802SGeert Uytterhoeven 5980071802SGeert Uytterhoeven /* We can access at least one region */ 6080071802SGeert Uytterhoeven error = 0; 6180071802SGeert Uytterhoeven } 6280071802SGeert Uytterhoeven if (error) 6380071802SGeert Uytterhoeven return error; 6480071802SGeert Uytterhoeven 6580071802SGeert Uytterhoeven n = hweight_long(dev->accessible_regions); 6680071802SGeert Uytterhoeven if (n > 1) 6780071802SGeert Uytterhoeven dev_info(&dev->sbd.core, 6880071802SGeert Uytterhoeven "%s:%u: %lu accessible regions found. Only the first " 69898eb71cSJoe Perches "one will be used\n", 7080071802SGeert Uytterhoeven __func__, __LINE__, n); 7180071802SGeert Uytterhoeven dev->region_idx = __ffs(dev->accessible_regions); 7280071802SGeert Uytterhoeven dev_info(&dev->sbd.core, 73a9dad6e5SStephen Rothwell "First accessible region has index %u start %llu size %llu\n", 7480071802SGeert Uytterhoeven dev->region_idx, dev->regions[dev->region_idx].start, 7580071802SGeert Uytterhoeven dev->regions[dev->region_idx].size); 7680071802SGeert Uytterhoeven 7780071802SGeert Uytterhoeven return 0; 7880071802SGeert Uytterhoeven } 7980071802SGeert Uytterhoeven 8080071802SGeert Uytterhoeven 8180071802SGeert Uytterhoeven /** 8280071802SGeert Uytterhoeven * ps3stor_setup - Setup a storage device before use 8380071802SGeert Uytterhoeven * @dev: Pointer to a struct ps3_storage_device 8480071802SGeert Uytterhoeven * @handler: Pointer to an interrupt handler 8580071802SGeert Uytterhoeven * 8680071802SGeert Uytterhoeven * Returns 0 for success, or an error code 8780071802SGeert Uytterhoeven */ 8880071802SGeert Uytterhoeven int ps3stor_setup(struct ps3_storage_device *dev, irq_handler_t handler) 8980071802SGeert Uytterhoeven { 9080071802SGeert Uytterhoeven int error, res, alignment; 9180071802SGeert Uytterhoeven enum ps3_dma_page_size page_size; 9280071802SGeert Uytterhoeven 9380071802SGeert Uytterhoeven error = ps3_open_hv_device(&dev->sbd); 9480071802SGeert Uytterhoeven if (error) { 9580071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 9680071802SGeert Uytterhoeven "%s:%u: ps3_open_hv_device failed %d\n", __func__, 9780071802SGeert Uytterhoeven __LINE__, error); 9880071802SGeert Uytterhoeven goto fail; 9980071802SGeert Uytterhoeven } 10080071802SGeert Uytterhoeven 10180071802SGeert Uytterhoeven error = ps3_sb_event_receive_port_setup(&dev->sbd, PS3_BINDING_CPU_ANY, 10280071802SGeert Uytterhoeven &dev->irq); 10380071802SGeert Uytterhoeven if (error) { 10480071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 10580071802SGeert Uytterhoeven "%s:%u: ps3_sb_event_receive_port_setup failed %d\n", 10680071802SGeert Uytterhoeven __func__, __LINE__, error); 10780071802SGeert Uytterhoeven goto fail_close_device; 10880071802SGeert Uytterhoeven } 10980071802SGeert Uytterhoeven 11080071802SGeert Uytterhoeven error = request_irq(dev->irq, handler, IRQF_DISABLED, 11180071802SGeert Uytterhoeven dev->sbd.core.driver->name, dev); 11280071802SGeert Uytterhoeven if (error) { 11380071802SGeert Uytterhoeven dev_err(&dev->sbd.core, "%s:%u: request_irq failed %d\n", 11480071802SGeert Uytterhoeven __func__, __LINE__, error); 11580071802SGeert Uytterhoeven goto fail_sb_event_receive_port_destroy; 11680071802SGeert Uytterhoeven } 11780071802SGeert Uytterhoeven 11880071802SGeert Uytterhoeven alignment = min(__ffs(dev->bounce_size), 11980071802SGeert Uytterhoeven __ffs((unsigned long)dev->bounce_buf)); 12080071802SGeert Uytterhoeven if (alignment < 12) { 12180071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 12280071802SGeert Uytterhoeven "%s:%u: bounce buffer not aligned (%lx at 0x%p)\n", 12380071802SGeert Uytterhoeven __func__, __LINE__, dev->bounce_size, dev->bounce_buf); 12480071802SGeert Uytterhoeven error = -EINVAL; 12580071802SGeert Uytterhoeven goto fail_free_irq; 12680071802SGeert Uytterhoeven } else if (alignment < 16) 12780071802SGeert Uytterhoeven page_size = PS3_DMA_4K; 12880071802SGeert Uytterhoeven else 12980071802SGeert Uytterhoeven page_size = PS3_DMA_64K; 13080071802SGeert Uytterhoeven dev->sbd.d_region = &dev->dma_region; 13180071802SGeert Uytterhoeven ps3_dma_region_init(&dev->sbd, &dev->dma_region, page_size, 13280071802SGeert Uytterhoeven PS3_DMA_OTHER, dev->bounce_buf, dev->bounce_size); 13380071802SGeert Uytterhoeven res = ps3_dma_region_create(&dev->dma_region); 13480071802SGeert Uytterhoeven if (res) { 13580071802SGeert Uytterhoeven dev_err(&dev->sbd.core, "%s:%u: cannot create DMA region\n", 13680071802SGeert Uytterhoeven __func__, __LINE__); 13780071802SGeert Uytterhoeven error = -ENOMEM; 13880071802SGeert Uytterhoeven goto fail_free_irq; 13980071802SGeert Uytterhoeven } 14080071802SGeert Uytterhoeven 14180071802SGeert Uytterhoeven dev->bounce_lpar = ps3_mm_phys_to_lpar(__pa(dev->bounce_buf)); 14280071802SGeert Uytterhoeven dev->bounce_dma = dma_map_single(&dev->sbd.core, dev->bounce_buf, 14380071802SGeert Uytterhoeven dev->bounce_size, DMA_BIDIRECTIONAL); 14480071802SGeert Uytterhoeven if (!dev->bounce_dma) { 14580071802SGeert Uytterhoeven dev_err(&dev->sbd.core, "%s:%u: map DMA region failed\n", 14680071802SGeert Uytterhoeven __func__, __LINE__); 14780071802SGeert Uytterhoeven error = -ENODEV; 14880071802SGeert Uytterhoeven goto fail_free_dma; 14980071802SGeert Uytterhoeven } 15080071802SGeert Uytterhoeven 15180071802SGeert Uytterhoeven error = ps3stor_probe_access(dev); 15280071802SGeert Uytterhoeven if (error) { 15380071802SGeert Uytterhoeven dev_err(&dev->sbd.core, "%s:%u: No accessible regions found\n", 15480071802SGeert Uytterhoeven __func__, __LINE__); 15580071802SGeert Uytterhoeven goto fail_unmap_dma; 15680071802SGeert Uytterhoeven } 15780071802SGeert Uytterhoeven return 0; 15880071802SGeert Uytterhoeven 15980071802SGeert Uytterhoeven fail_unmap_dma: 16080071802SGeert Uytterhoeven dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, 16180071802SGeert Uytterhoeven DMA_BIDIRECTIONAL); 16280071802SGeert Uytterhoeven fail_free_dma: 16380071802SGeert Uytterhoeven ps3_dma_region_free(&dev->dma_region); 16480071802SGeert Uytterhoeven fail_free_irq: 16580071802SGeert Uytterhoeven free_irq(dev->irq, dev); 16680071802SGeert Uytterhoeven fail_sb_event_receive_port_destroy: 16780071802SGeert Uytterhoeven ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); 16880071802SGeert Uytterhoeven fail_close_device: 16980071802SGeert Uytterhoeven ps3_close_hv_device(&dev->sbd); 17080071802SGeert Uytterhoeven fail: 17180071802SGeert Uytterhoeven return error; 17280071802SGeert Uytterhoeven } 17380071802SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3stor_setup); 17480071802SGeert Uytterhoeven 17580071802SGeert Uytterhoeven 17680071802SGeert Uytterhoeven /** 17780071802SGeert Uytterhoeven * ps3stor_teardown - Tear down a storage device after use 17880071802SGeert Uytterhoeven * @dev: Pointer to a struct ps3_storage_device 17980071802SGeert Uytterhoeven */ 18080071802SGeert Uytterhoeven void ps3stor_teardown(struct ps3_storage_device *dev) 18180071802SGeert Uytterhoeven { 18280071802SGeert Uytterhoeven int error; 18380071802SGeert Uytterhoeven 18480071802SGeert Uytterhoeven dma_unmap_single(&dev->sbd.core, dev->bounce_dma, dev->bounce_size, 18580071802SGeert Uytterhoeven DMA_BIDIRECTIONAL); 18680071802SGeert Uytterhoeven ps3_dma_region_free(&dev->dma_region); 18780071802SGeert Uytterhoeven 18880071802SGeert Uytterhoeven free_irq(dev->irq, dev); 18980071802SGeert Uytterhoeven 19080071802SGeert Uytterhoeven error = ps3_sb_event_receive_port_destroy(&dev->sbd, dev->irq); 19180071802SGeert Uytterhoeven if (error) 19280071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 19380071802SGeert Uytterhoeven "%s:%u: destroy event receive port failed %d\n", 19480071802SGeert Uytterhoeven __func__, __LINE__, error); 19580071802SGeert Uytterhoeven 19680071802SGeert Uytterhoeven error = ps3_close_hv_device(&dev->sbd); 19780071802SGeert Uytterhoeven if (error) 19880071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 19980071802SGeert Uytterhoeven "%s:%u: ps3_close_hv_device failed %d\n", __func__, 20080071802SGeert Uytterhoeven __LINE__, error); 20180071802SGeert Uytterhoeven } 20280071802SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3stor_teardown); 20380071802SGeert Uytterhoeven 20480071802SGeert Uytterhoeven 20580071802SGeert Uytterhoeven /** 20680071802SGeert Uytterhoeven * ps3stor_read_write_sectors - read/write from/to a storage device 20780071802SGeert Uytterhoeven * @dev: Pointer to a struct ps3_storage_device 20880071802SGeert Uytterhoeven * @lpar: HV logical partition address 20980071802SGeert Uytterhoeven * @start_sector: First sector to read/write 21080071802SGeert Uytterhoeven * @sectors: Number of sectors to read/write 21180071802SGeert Uytterhoeven * @write: Flag indicating write (non-zero) or read (zero) 21280071802SGeert Uytterhoeven * 21380071802SGeert Uytterhoeven * Returns 0 for success, -1 in case of failure to submit the command, or 21480071802SGeert Uytterhoeven * an LV1 status value in case of other errors 21580071802SGeert Uytterhoeven */ 21680071802SGeert Uytterhoeven u64 ps3stor_read_write_sectors(struct ps3_storage_device *dev, u64 lpar, 21780071802SGeert Uytterhoeven u64 start_sector, u64 sectors, int write) 21880071802SGeert Uytterhoeven { 21980071802SGeert Uytterhoeven unsigned int region_id = dev->regions[dev->region_idx].id; 22080071802SGeert Uytterhoeven const char *op = write ? "write" : "read"; 22180071802SGeert Uytterhoeven int res; 22280071802SGeert Uytterhoeven 223a9dad6e5SStephen Rothwell dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", 22480071802SGeert Uytterhoeven __func__, __LINE__, op, sectors, start_sector); 22580071802SGeert Uytterhoeven 22680071802SGeert Uytterhoeven init_completion(&dev->done); 22780071802SGeert Uytterhoeven res = write ? lv1_storage_write(dev->sbd.dev_id, region_id, 22880071802SGeert Uytterhoeven start_sector, sectors, 0, lpar, 22980071802SGeert Uytterhoeven &dev->tag) 23080071802SGeert Uytterhoeven : lv1_storage_read(dev->sbd.dev_id, region_id, 23180071802SGeert Uytterhoeven start_sector, sectors, 0, lpar, 23280071802SGeert Uytterhoeven &dev->tag); 23380071802SGeert Uytterhoeven if (res) { 23480071802SGeert Uytterhoeven dev_dbg(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, 23580071802SGeert Uytterhoeven __LINE__, op, res); 23680071802SGeert Uytterhoeven return -1; 23780071802SGeert Uytterhoeven } 23880071802SGeert Uytterhoeven 23980071802SGeert Uytterhoeven wait_for_completion(&dev->done); 24080071802SGeert Uytterhoeven if (dev->lv1_status) { 241a9dad6e5SStephen Rothwell dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, 24280071802SGeert Uytterhoeven __LINE__, op, dev->lv1_status); 24380071802SGeert Uytterhoeven return dev->lv1_status; 24480071802SGeert Uytterhoeven } 24580071802SGeert Uytterhoeven 24680071802SGeert Uytterhoeven dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, __LINE__, 24780071802SGeert Uytterhoeven op); 24880071802SGeert Uytterhoeven 24980071802SGeert Uytterhoeven return 0; 25080071802SGeert Uytterhoeven } 25180071802SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3stor_read_write_sectors); 25280071802SGeert Uytterhoeven 25380071802SGeert Uytterhoeven 25480071802SGeert Uytterhoeven /** 25580071802SGeert Uytterhoeven * ps3stor_send_command - send a device command to a storage device 25680071802SGeert Uytterhoeven * @dev: Pointer to a struct ps3_storage_device 25780071802SGeert Uytterhoeven * @cmd: Command number 25880071802SGeert Uytterhoeven * @arg1: First command argument 25980071802SGeert Uytterhoeven * @arg2: Second command argument 26080071802SGeert Uytterhoeven * @arg3: Third command argument 26180071802SGeert Uytterhoeven * @arg4: Fourth command argument 26280071802SGeert Uytterhoeven * 26380071802SGeert Uytterhoeven * Returns 0 for success, -1 in case of failure to submit the command, or 26480071802SGeert Uytterhoeven * an LV1 status value in case of other errors 26580071802SGeert Uytterhoeven */ 26680071802SGeert Uytterhoeven u64 ps3stor_send_command(struct ps3_storage_device *dev, u64 cmd, u64 arg1, 26780071802SGeert Uytterhoeven u64 arg2, u64 arg3, u64 arg4) 26880071802SGeert Uytterhoeven { 26980071802SGeert Uytterhoeven int res; 27080071802SGeert Uytterhoeven 271a9dad6e5SStephen Rothwell dev_dbg(&dev->sbd.core, "%s:%u: send device command 0x%llx\n", __func__, 27280071802SGeert Uytterhoeven __LINE__, cmd); 27380071802SGeert Uytterhoeven 27480071802SGeert Uytterhoeven init_completion(&dev->done); 27580071802SGeert Uytterhoeven 27680071802SGeert Uytterhoeven res = lv1_storage_send_device_command(dev->sbd.dev_id, cmd, arg1, 27780071802SGeert Uytterhoeven arg2, arg3, arg4, &dev->tag); 27880071802SGeert Uytterhoeven if (res) { 27980071802SGeert Uytterhoeven dev_err(&dev->sbd.core, 280a9dad6e5SStephen Rothwell "%s:%u: send_device_command 0x%llx failed %d\n", 28180071802SGeert Uytterhoeven __func__, __LINE__, cmd, res); 28280071802SGeert Uytterhoeven return -1; 28380071802SGeert Uytterhoeven } 28480071802SGeert Uytterhoeven 28580071802SGeert Uytterhoeven wait_for_completion(&dev->done); 28680071802SGeert Uytterhoeven if (dev->lv1_status) { 287a9dad6e5SStephen Rothwell dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx failed 0x%llx\n", 28880071802SGeert Uytterhoeven __func__, __LINE__, cmd, dev->lv1_status); 28980071802SGeert Uytterhoeven return dev->lv1_status; 29080071802SGeert Uytterhoeven } 29180071802SGeert Uytterhoeven 292a9dad6e5SStephen Rothwell dev_dbg(&dev->sbd.core, "%s:%u: command 0x%llx completed\n", __func__, 29380071802SGeert Uytterhoeven __LINE__, cmd); 29480071802SGeert Uytterhoeven 29580071802SGeert Uytterhoeven return 0; 29680071802SGeert Uytterhoeven } 29780071802SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3stor_send_command); 29880071802SGeert Uytterhoeven 29980071802SGeert Uytterhoeven 30080071802SGeert Uytterhoeven MODULE_LICENSE("GPL"); 30180071802SGeert Uytterhoeven MODULE_DESCRIPTION("PS3 Storage Bus Library"); 30280071802SGeert Uytterhoeven MODULE_AUTHOR("Sony Corporation"); 303