18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PS3 Disk Storage Driver 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/ata.h> 108c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/lv1call.h> 158c2ecf20Sopenharmony_ci#include <asm/ps3stor.h> 168c2ecf20Sopenharmony_ci#include <asm/firmware.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define DEVICE_NAME "ps3disk" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define BOUNCE_SIZE (64*1024) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define PS3DISK_MAX_DISKS 16 248c2ecf20Sopenharmony_ci#define PS3DISK_MINORS 16 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PS3DISK_NAME "ps3d%c" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct ps3disk_private { 318c2ecf20Sopenharmony_ci spinlock_t lock; /* Request queue spinlock */ 328c2ecf20Sopenharmony_ci struct request_queue *queue; 338c2ecf20Sopenharmony_ci struct blk_mq_tag_set tag_set; 348c2ecf20Sopenharmony_ci struct gendisk *gendisk; 358c2ecf20Sopenharmony_ci unsigned int blocking_factor; 368c2ecf20Sopenharmony_ci struct request *req; 378c2ecf20Sopenharmony_ci u64 raw_capacity; 388c2ecf20Sopenharmony_ci unsigned char model[ATA_ID_PROD_LEN+1]; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define LV1_STORAGE_SEND_ATA_COMMAND (2) 438c2ecf20Sopenharmony_ci#define LV1_STORAGE_ATA_HDDOUT (0x23) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct lv1_ata_cmnd_block { 468c2ecf20Sopenharmony_ci u16 features; 478c2ecf20Sopenharmony_ci u16 sector_count; 488c2ecf20Sopenharmony_ci u16 LBA_low; 498c2ecf20Sopenharmony_ci u16 LBA_mid; 508c2ecf20Sopenharmony_ci u16 LBA_high; 518c2ecf20Sopenharmony_ci u8 device; 528c2ecf20Sopenharmony_ci u8 command; 538c2ecf20Sopenharmony_ci u32 is_ext; 548c2ecf20Sopenharmony_ci u32 proto; 558c2ecf20Sopenharmony_ci u32 in_out; 568c2ecf20Sopenharmony_ci u32 size; 578c2ecf20Sopenharmony_ci u64 buffer; 588c2ecf20Sopenharmony_ci u32 arglen; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cienum lv1_ata_proto { 628c2ecf20Sopenharmony_ci NON_DATA_PROTO = 0, 638c2ecf20Sopenharmony_ci PIO_DATA_IN_PROTO = 1, 648c2ecf20Sopenharmony_ci PIO_DATA_OUT_PROTO = 2, 658c2ecf20Sopenharmony_ci DMA_PROTO = 3 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cienum lv1_ata_in_out { 698c2ecf20Sopenharmony_ci DIR_WRITE = 0, /* memory -> device */ 708c2ecf20Sopenharmony_ci DIR_READ = 1 /* device -> memory */ 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int ps3disk_major; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic const struct block_device_operations ps3disk_fops = { 778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void ps3disk_scatter_gather(struct ps3_storage_device *dev, 828c2ecf20Sopenharmony_ci struct request *req, int gather) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci unsigned int offset = 0; 858c2ecf20Sopenharmony_ci struct req_iterator iter; 868c2ecf20Sopenharmony_ci struct bio_vec bvec; 878c2ecf20Sopenharmony_ci unsigned int i = 0; 888c2ecf20Sopenharmony_ci size_t size; 898c2ecf20Sopenharmony_ci void *buf; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci rq_for_each_segment(bvec, req, iter) { 928c2ecf20Sopenharmony_ci unsigned long flags; 938c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: bio %u: %u sectors from %llu\n", 948c2ecf20Sopenharmony_ci __func__, __LINE__, i, bio_sectors(iter.bio), 958c2ecf20Sopenharmony_ci iter.bio->bi_iter.bi_sector); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci size = bvec.bv_len; 988c2ecf20Sopenharmony_ci buf = bvec_kmap_irq(&bvec, &flags); 998c2ecf20Sopenharmony_ci if (gather) 1008c2ecf20Sopenharmony_ci memcpy(dev->bounce_buf+offset, buf, size); 1018c2ecf20Sopenharmony_ci else 1028c2ecf20Sopenharmony_ci memcpy(buf, dev->bounce_buf+offset, size); 1038c2ecf20Sopenharmony_ci offset += size; 1048c2ecf20Sopenharmony_ci flush_kernel_dcache_page(bvec.bv_page); 1058c2ecf20Sopenharmony_ci bvec_kunmap_irq(buf, &flags); 1068c2ecf20Sopenharmony_ci i++; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic blk_status_t ps3disk_submit_request_sg(struct ps3_storage_device *dev, 1118c2ecf20Sopenharmony_ci struct request *req) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 1148c2ecf20Sopenharmony_ci int write = rq_data_dir(req), res; 1158c2ecf20Sopenharmony_ci const char *op = write ? "write" : "read"; 1168c2ecf20Sopenharmony_ci u64 start_sector, sectors; 1178c2ecf20Sopenharmony_ci unsigned int region_id = dev->regions[dev->region_idx].id; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#ifdef DEBUG 1208c2ecf20Sopenharmony_ci unsigned int n = 0; 1218c2ecf20Sopenharmony_ci struct bio_vec bv; 1228c2ecf20Sopenharmony_ci struct req_iterator iter; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rq_for_each_segment(bv, req, iter) 1258c2ecf20Sopenharmony_ci n++; 1268c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, 1278c2ecf20Sopenharmony_ci "%s:%u: %s req has %u bvecs for %u sectors\n", 1288c2ecf20Sopenharmony_ci __func__, __LINE__, op, n, blk_rq_sectors(req)); 1298c2ecf20Sopenharmony_ci#endif 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci start_sector = blk_rq_pos(req) * priv->blocking_factor; 1328c2ecf20Sopenharmony_ci sectors = blk_rq_sectors(req) * priv->blocking_factor; 1338c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s %llu sectors starting at %llu\n", 1348c2ecf20Sopenharmony_ci __func__, __LINE__, op, sectors, start_sector); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (write) { 1378c2ecf20Sopenharmony_ci ps3disk_scatter_gather(dev, req, 1); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci res = lv1_storage_write(dev->sbd.dev_id, region_id, 1408c2ecf20Sopenharmony_ci start_sector, sectors, 0, 1418c2ecf20Sopenharmony_ci dev->bounce_lpar, &dev->tag); 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci res = lv1_storage_read(dev->sbd.dev_id, region_id, 1448c2ecf20Sopenharmony_ci start_sector, sectors, 0, 1458c2ecf20Sopenharmony_ci dev->bounce_lpar, &dev->tag); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (res) { 1488c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, 1498c2ecf20Sopenharmony_ci __LINE__, op, res); 1508c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci priv->req = req; 1548c2ecf20Sopenharmony_ci return BLK_STS_OK; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic blk_status_t ps3disk_submit_flush_request(struct ps3_storage_device *dev, 1588c2ecf20Sopenharmony_ci struct request *req) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 1618c2ecf20Sopenharmony_ci u64 res; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci res = lv1_storage_send_device_command(dev->sbd.dev_id, 1668c2ecf20Sopenharmony_ci LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 1678c2ecf20Sopenharmony_ci 0, &dev->tag); 1688c2ecf20Sopenharmony_ci if (res) { 1698c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n", 1708c2ecf20Sopenharmony_ci __func__, __LINE__, res); 1718c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci priv->req = req; 1758c2ecf20Sopenharmony_ci return BLK_STS_OK; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic blk_status_t ps3disk_do_request(struct ps3_storage_device *dev, 1798c2ecf20Sopenharmony_ci struct request *req) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci switch (req_op(req)) { 1848c2ecf20Sopenharmony_ci case REQ_OP_FLUSH: 1858c2ecf20Sopenharmony_ci return ps3disk_submit_flush_request(dev, req); 1868c2ecf20Sopenharmony_ci case REQ_OP_READ: 1878c2ecf20Sopenharmony_ci case REQ_OP_WRITE: 1888c2ecf20Sopenharmony_ci return ps3disk_submit_request_sg(dev, req); 1898c2ecf20Sopenharmony_ci default: 1908c2ecf20Sopenharmony_ci blk_dump_rq_flags(req, DEVICE_NAME " bad request"); 1918c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic blk_status_t ps3disk_queue_rq(struct blk_mq_hw_ctx *hctx, 1968c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct request_queue *q = hctx->queue; 1998c2ecf20Sopenharmony_ci struct ps3_storage_device *dev = q->queuedata; 2008c2ecf20Sopenharmony_ci struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 2018c2ecf20Sopenharmony_ci blk_status_t ret; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci blk_mq_start_request(bd->rq); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci spin_lock_irq(&priv->lock); 2068c2ecf20Sopenharmony_ci ret = ps3disk_do_request(dev, bd->rq); 2078c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->lock); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic irqreturn_t ps3disk_interrupt(int irq, void *data) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct ps3_storage_device *dev = data; 2158c2ecf20Sopenharmony_ci struct ps3disk_private *priv; 2168c2ecf20Sopenharmony_ci struct request *req; 2178c2ecf20Sopenharmony_ci int res, read; 2188c2ecf20Sopenharmony_ci blk_status_t error; 2198c2ecf20Sopenharmony_ci u64 tag, status; 2208c2ecf20Sopenharmony_ci const char *op; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (tag != dev->tag) 2258c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 2268c2ecf20Sopenharmony_ci "%s:%u: tag mismatch, got %llx, expected %llx\n", 2278c2ecf20Sopenharmony_ci __func__, __LINE__, tag, dev->tag); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (res) { 2308c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%llx\n", 2318c2ecf20Sopenharmony_ci __func__, __LINE__, res, status); 2328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci priv = ps3_system_bus_get_drvdata(&dev->sbd); 2368c2ecf20Sopenharmony_ci req = priv->req; 2378c2ecf20Sopenharmony_ci if (!req) { 2388c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, 2398c2ecf20Sopenharmony_ci "%s:%u non-block layer request completed\n", __func__, 2408c2ecf20Sopenharmony_ci __LINE__); 2418c2ecf20Sopenharmony_ci dev->lv1_status = status; 2428c2ecf20Sopenharmony_ci complete(&dev->done); 2438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (req_op(req) == REQ_OP_FLUSH) { 2478c2ecf20Sopenharmony_ci read = 0; 2488c2ecf20Sopenharmony_ci op = "flush"; 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci read = !rq_data_dir(req); 2518c2ecf20Sopenharmony_ci op = read ? "read" : "write"; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci if (status) { 2548c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%llx\n", __func__, 2558c2ecf20Sopenharmony_ci __LINE__, op, status); 2568c2ecf20Sopenharmony_ci error = BLK_STS_IOERR; 2578c2ecf20Sopenharmony_ci } else { 2588c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, 2598c2ecf20Sopenharmony_ci __LINE__, op); 2608c2ecf20Sopenharmony_ci error = 0; 2618c2ecf20Sopenharmony_ci if (read) 2628c2ecf20Sopenharmony_ci ps3disk_scatter_gather(dev, req, 0); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_lock(&priv->lock); 2668c2ecf20Sopenharmony_ci priv->req = NULL; 2678c2ecf20Sopenharmony_ci blk_mq_end_request(req, error); 2688c2ecf20Sopenharmony_ci spin_unlock(&priv->lock); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci blk_mq_run_hw_queues(priv->queue, true); 2718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int ps3disk_sync_cache(struct ps3_storage_device *dev) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci u64 res; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0); 2818c2ecf20Sopenharmony_ci if (res) { 2828c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n", 2838c2ecf20Sopenharmony_ci __func__, __LINE__, res); 2848c2ecf20Sopenharmony_ci return -EIO; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* ATA helpers copied from drivers/ata/libata-core.c */ 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void swap_buf_le16(u16 *buf, unsigned int buf_words) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 2958c2ecf20Sopenharmony_ci unsigned int i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci for (i = 0; i < buf_words; i++) 2988c2ecf20Sopenharmony_ci buf[i] = le16_to_cpu(buf[i]); 2998c2ecf20Sopenharmony_ci#endif /* __BIG_ENDIAN */ 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic u64 ata_id_n_sectors(const u16 *id) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci if (ata_id_has_lba(id)) { 3058c2ecf20Sopenharmony_ci if (ata_id_has_lba48(id)) 3068c2ecf20Sopenharmony_ci return ata_id_u64(id, 100); 3078c2ecf20Sopenharmony_ci else 3088c2ecf20Sopenharmony_ci return ata_id_u32(id, 60); 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci if (ata_id_current_chs_valid(id)) 3118c2ecf20Sopenharmony_ci return ata_id_u32(id, 57); 3128c2ecf20Sopenharmony_ci else 3138c2ecf20Sopenharmony_ci return id[1] * id[3] * id[6]; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, 3188c2ecf20Sopenharmony_ci unsigned int len) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci unsigned int c; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci while (len > 0) { 3238c2ecf20Sopenharmony_ci c = id[ofs] >> 8; 3248c2ecf20Sopenharmony_ci *s = c; 3258c2ecf20Sopenharmony_ci s++; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci c = id[ofs] & 0xff; 3288c2ecf20Sopenharmony_ci *s = c; 3298c2ecf20Sopenharmony_ci s++; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ofs++; 3328c2ecf20Sopenharmony_ci len -= 2; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, 3378c2ecf20Sopenharmony_ci unsigned int len) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci unsigned char *p; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci WARN_ON(!(len & 1)); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ata_id_string(id, s, ofs, len - 1); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci p = s + strnlen(s, len - 1); 3468c2ecf20Sopenharmony_ci while (p > s && p[-1] == ' ') 3478c2ecf20Sopenharmony_ci p--; 3488c2ecf20Sopenharmony_ci *p = '\0'; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int ps3disk_identify(struct ps3_storage_device *dev) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 3548c2ecf20Sopenharmony_ci struct lv1_ata_cmnd_block ata_cmnd; 3558c2ecf20Sopenharmony_ci u16 *id = dev->bounce_buf; 3568c2ecf20Sopenharmony_ci u64 res; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci dev_dbg(&dev->sbd.core, "%s:%u: identify disk\n", __func__, __LINE__); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci memset(&ata_cmnd, 0, sizeof(struct lv1_ata_cmnd_block)); 3618c2ecf20Sopenharmony_ci ata_cmnd.command = ATA_CMD_ID_ATA; 3628c2ecf20Sopenharmony_ci ata_cmnd.sector_count = 1; 3638c2ecf20Sopenharmony_ci ata_cmnd.size = ata_cmnd.arglen = ATA_ID_WORDS * 2; 3648c2ecf20Sopenharmony_ci ata_cmnd.buffer = dev->bounce_lpar; 3658c2ecf20Sopenharmony_ci ata_cmnd.proto = PIO_DATA_IN_PROTO; 3668c2ecf20Sopenharmony_ci ata_cmnd.in_out = DIR_READ; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci res = ps3stor_send_command(dev, LV1_STORAGE_SEND_ATA_COMMAND, 3698c2ecf20Sopenharmony_ci ps3_mm_phys_to_lpar(__pa(&ata_cmnd)), 3708c2ecf20Sopenharmony_ci sizeof(ata_cmnd), ata_cmnd.buffer, 3718c2ecf20Sopenharmony_ci ata_cmnd.arglen); 3728c2ecf20Sopenharmony_ci if (res) { 3738c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: identify disk failed 0x%llx\n", 3748c2ecf20Sopenharmony_ci __func__, __LINE__, res); 3758c2ecf20Sopenharmony_ci return -EIO; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci swap_buf_le16(id, ATA_ID_WORDS); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* All we're interested in are raw capacity and model name */ 3818c2ecf20Sopenharmony_ci priv->raw_capacity = ata_id_n_sectors(id); 3828c2ecf20Sopenharmony_ci ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model)); 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic unsigned long ps3disk_mask; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ps3disk_mask_mutex); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct blk_mq_ops ps3disk_mq_ops = { 3918c2ecf20Sopenharmony_ci .queue_rq = ps3disk_queue_rq, 3928c2ecf20Sopenharmony_ci}; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int ps3disk_probe(struct ps3_system_bus_device *_dev) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); 3978c2ecf20Sopenharmony_ci struct ps3disk_private *priv; 3988c2ecf20Sopenharmony_ci int error; 3998c2ecf20Sopenharmony_ci unsigned int devidx; 4008c2ecf20Sopenharmony_ci struct request_queue *queue; 4018c2ecf20Sopenharmony_ci struct gendisk *gendisk; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (dev->blk_size < 512) { 4048c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, 4058c2ecf20Sopenharmony_ci "%s:%u: cannot handle block size %llu\n", __func__, 4068c2ecf20Sopenharmony_ci __LINE__, dev->blk_size); 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG); 4118c2ecf20Sopenharmony_ci mutex_lock(&ps3disk_mask_mutex); 4128c2ecf20Sopenharmony_ci devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS); 4138c2ecf20Sopenharmony_ci if (devidx >= PS3DISK_MAX_DISKS) { 4148c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__, 4158c2ecf20Sopenharmony_ci __LINE__); 4168c2ecf20Sopenharmony_ci mutex_unlock(&ps3disk_mask_mutex); 4178c2ecf20Sopenharmony_ci return -ENOSPC; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci __set_bit(devidx, &ps3disk_mask); 4208c2ecf20Sopenharmony_ci mutex_unlock(&ps3disk_mask_mutex); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (!priv) { 4248c2ecf20Sopenharmony_ci error = -ENOMEM; 4258c2ecf20Sopenharmony_ci goto fail; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ps3_system_bus_set_drvdata(_dev, priv); 4298c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci dev->bounce_size = BOUNCE_SIZE; 4328c2ecf20Sopenharmony_ci dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); 4338c2ecf20Sopenharmony_ci if (!dev->bounce_buf) { 4348c2ecf20Sopenharmony_ci error = -ENOMEM; 4358c2ecf20Sopenharmony_ci goto fail_free_priv; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci error = ps3stor_setup(dev, ps3disk_interrupt); 4398c2ecf20Sopenharmony_ci if (error) 4408c2ecf20Sopenharmony_ci goto fail_free_bounce; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ps3disk_identify(dev); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci queue = blk_mq_init_sq_queue(&priv->tag_set, &ps3disk_mq_ops, 1, 4458c2ecf20Sopenharmony_ci BLK_MQ_F_SHOULD_MERGE); 4468c2ecf20Sopenharmony_ci if (IS_ERR(queue)) { 4478c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: blk_mq_init_queue failed\n", 4488c2ecf20Sopenharmony_ci __func__, __LINE__); 4498c2ecf20Sopenharmony_ci error = PTR_ERR(queue); 4508c2ecf20Sopenharmony_ci goto fail_teardown; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci priv->queue = queue; 4548c2ecf20Sopenharmony_ci queue->queuedata = dev; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9); 4578c2ecf20Sopenharmony_ci blk_queue_dma_alignment(queue, dev->blk_size-1); 4588c2ecf20Sopenharmony_ci blk_queue_logical_block_size(queue, dev->blk_size); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci blk_queue_write_cache(queue, true, false); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci blk_queue_max_segments(queue, -1); 4638c2ecf20Sopenharmony_ci blk_queue_max_segment_size(queue, dev->bounce_size); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci gendisk = alloc_disk(PS3DISK_MINORS); 4668c2ecf20Sopenharmony_ci if (!gendisk) { 4678c2ecf20Sopenharmony_ci dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, 4688c2ecf20Sopenharmony_ci __LINE__); 4698c2ecf20Sopenharmony_ci error = -ENOMEM; 4708c2ecf20Sopenharmony_ci goto fail_cleanup_queue; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci priv->gendisk = gendisk; 4748c2ecf20Sopenharmony_ci gendisk->major = ps3disk_major; 4758c2ecf20Sopenharmony_ci gendisk->first_minor = devidx * PS3DISK_MINORS; 4768c2ecf20Sopenharmony_ci gendisk->fops = &ps3disk_fops; 4778c2ecf20Sopenharmony_ci gendisk->queue = queue; 4788c2ecf20Sopenharmony_ci gendisk->private_data = dev; 4798c2ecf20Sopenharmony_ci snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME, 4808c2ecf20Sopenharmony_ci devidx+'a'); 4818c2ecf20Sopenharmony_ci priv->blocking_factor = dev->blk_size >> 9; 4828c2ecf20Sopenharmony_ci set_capacity(gendisk, 4838c2ecf20Sopenharmony_ci dev->regions[dev->region_idx].size*priv->blocking_factor); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_info(&dev->sbd.core, 4868c2ecf20Sopenharmony_ci "%s is a %s (%llu MiB total, %llu MiB for OtherOS)\n", 4878c2ecf20Sopenharmony_ci gendisk->disk_name, priv->model, priv->raw_capacity >> 11, 4888c2ecf20Sopenharmony_ci get_capacity(gendisk) >> 11); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci device_add_disk(&dev->sbd.core, gendisk, NULL); 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cifail_cleanup_queue: 4948c2ecf20Sopenharmony_ci blk_cleanup_queue(queue); 4958c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&priv->tag_set); 4968c2ecf20Sopenharmony_cifail_teardown: 4978c2ecf20Sopenharmony_ci ps3stor_teardown(dev); 4988c2ecf20Sopenharmony_cifail_free_bounce: 4998c2ecf20Sopenharmony_ci kfree(dev->bounce_buf); 5008c2ecf20Sopenharmony_cifail_free_priv: 5018c2ecf20Sopenharmony_ci kfree(priv); 5028c2ecf20Sopenharmony_ci ps3_system_bus_set_drvdata(_dev, NULL); 5038c2ecf20Sopenharmony_cifail: 5048c2ecf20Sopenharmony_ci mutex_lock(&ps3disk_mask_mutex); 5058c2ecf20Sopenharmony_ci __clear_bit(devidx, &ps3disk_mask); 5068c2ecf20Sopenharmony_ci mutex_unlock(&ps3disk_mask_mutex); 5078c2ecf20Sopenharmony_ci return error; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int ps3disk_remove(struct ps3_system_bus_device *_dev) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); 5138c2ecf20Sopenharmony_ci struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci mutex_lock(&ps3disk_mask_mutex); 5168c2ecf20Sopenharmony_ci __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS, 5178c2ecf20Sopenharmony_ci &ps3disk_mask); 5188c2ecf20Sopenharmony_ci mutex_unlock(&ps3disk_mask_mutex); 5198c2ecf20Sopenharmony_ci del_gendisk(priv->gendisk); 5208c2ecf20Sopenharmony_ci blk_cleanup_queue(priv->queue); 5218c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&priv->tag_set); 5228c2ecf20Sopenharmony_ci put_disk(priv->gendisk); 5238c2ecf20Sopenharmony_ci dev_notice(&dev->sbd.core, "Synchronizing disk cache\n"); 5248c2ecf20Sopenharmony_ci ps3disk_sync_cache(dev); 5258c2ecf20Sopenharmony_ci ps3stor_teardown(dev); 5268c2ecf20Sopenharmony_ci kfree(dev->bounce_buf); 5278c2ecf20Sopenharmony_ci kfree(priv); 5288c2ecf20Sopenharmony_ci ps3_system_bus_set_drvdata(_dev, NULL); 5298c2ecf20Sopenharmony_ci return 0; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic struct ps3_system_bus_driver ps3disk = { 5338c2ecf20Sopenharmony_ci .match_id = PS3_MATCH_ID_STOR_DISK, 5348c2ecf20Sopenharmony_ci .core.name = DEVICE_NAME, 5358c2ecf20Sopenharmony_ci .core.owner = THIS_MODULE, 5368c2ecf20Sopenharmony_ci .probe = ps3disk_probe, 5378c2ecf20Sopenharmony_ci .remove = ps3disk_remove, 5388c2ecf20Sopenharmony_ci .shutdown = ps3disk_remove, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic int __init ps3disk_init(void) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci int error; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 5478c2ecf20Sopenharmony_ci return -ENODEV; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci error = register_blkdev(0, DEVICE_NAME); 5508c2ecf20Sopenharmony_ci if (error <= 0) { 5518c2ecf20Sopenharmony_ci printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, 5528c2ecf20Sopenharmony_ci __LINE__, error); 5538c2ecf20Sopenharmony_ci return error; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci ps3disk_major = error; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci pr_info("%s:%u: registered block device major %d\n", __func__, 5588c2ecf20Sopenharmony_ci __LINE__, ps3disk_major); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci error = ps3_system_bus_driver_register(&ps3disk); 5618c2ecf20Sopenharmony_ci if (error) 5628c2ecf20Sopenharmony_ci unregister_blkdev(ps3disk_major, DEVICE_NAME); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return error; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void __exit ps3disk_exit(void) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci ps3_system_bus_driver_unregister(&ps3disk); 5708c2ecf20Sopenharmony_ci unregister_blkdev(ps3disk_major, DEVICE_NAME); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cimodule_init(ps3disk_init); 5748c2ecf20Sopenharmony_cimodule_exit(ps3disk_exit); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PS3 Disk Storage Driver"); 5788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sony Corporation"); 5798c2ecf20Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK); 580