* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
#include <linux/module.h>
#include <asm/firmware.h>
#define DEVICE_NAME "ps3disk"
#define PS3DISK_MAX_DISKS 16
#define PS3DISK_MINORS 16
#define PS3DISK_NAME "ps3d%c"
spinlock_t lock; /* Request queue spinlock */
struct request_queue *queue;
struct blk_mq_tag_set tag_set;
unsigned int blocking_factor;
unsigned char model[ATA_ID_PROD_LEN+1];
#define LV1_STORAGE_SEND_ATA_COMMAND (2)
#define LV1_STORAGE_ATA_HDDOUT (0x23)
memcpy(dev->bounce_buf+offset, buf, size);
memcpy(buf, dev->bounce_buf+offset, size);
flush_kernel_dcache_page(bvec.bv_page);
bvec_kunmap_irq(buf, &flags);
static int ps3disk_submit_request_sg(struct ps3_storage_device *dev,
static blk_status_t ps3disk_submit_request_sg(struct ps3_storage_device *dev,
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
int write = rq_data_dir(req), res;
const char *op = write ? "write" : "read";
u64 start_sector, sectors;
unsigned int region_id = dev->regions[dev->region_idx].id;
start_sector, sectors, 0,
dev->bounce_lpar, &dev->tag);
res = lv1_storage_read(dev->sbd.dev_id, region_id,
start_sector, sectors, 0,
dev->bounce_lpar, &dev->tag);
dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__,
__blk_end_request_all(req, BLK_STS_IOERR);
static int ps3disk_submit_flush_request(struct ps3_storage_device *dev,
static blk_status_t ps3disk_submit_flush_request(struct ps3_storage_device *dev,
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__);
res = lv1_storage_send_device_command(dev->sbd.dev_id,
LV1_STORAGE_ATA_HDDOUT, 0, 0, 0,
dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%llx\n",
__func__, __LINE__, res);
__blk_end_request_all(req, BLK_STS_IOERR);
static void ps3disk_do_request(struct ps3_storage_device *dev,
static blk_status_t ps3disk_do_request(struct ps3_storage_device *dev,
dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
while ((req = blk_fetch_request(q))) {
if (ps3disk_submit_flush_request(dev, req))
if (ps3disk_submit_request_sg(dev, req))
blk_dump_rq_flags(req, DEVICE_NAME " bad request");
__blk_end_request_all(req, BLK_STS_IOERR);
return ps3disk_submit_flush_request(dev, req);
return ps3disk_submit_request_sg(dev, req);
blk_dump_rq_flags(req, DEVICE_NAME " bad request");
static void ps3disk_request(struct request_queue *q)
static blk_status_t ps3disk_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
struct request_queue *q = hctx->queue;
struct ps3_storage_device *dev = q->queuedata;
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__);
blk_mq_start_request(bd->rq);
spin_lock_irq(&priv->lock);
ret = ps3disk_do_request(dev, bd->rq);
spin_unlock_irq(&priv->lock);
ps3disk_do_request(dev, q);
static irqreturn_t ps3disk_interrupt(int irq, void *data)
struct ps3_storage_device *dev = data;
struct ps3disk_private *priv;
dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__,
ps3disk_scatter_gather(dev, req, 0);
__blk_end_request_all(req, error);
ps3disk_do_request(dev, priv->queue);
blk_mq_end_request(req, error);
spin_unlock(&priv->lock);
blk_mq_run_hw_queues(priv->queue, true);
static int ps3disk_sync_cache(struct ps3_storage_device *dev)
dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__);
res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0);
/* All we're interested in are raw capacity and model name */
priv->raw_capacity = ata_id_n_sectors(id);
ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model));
static unsigned long ps3disk_mask;
static DEFINE_MUTEX(ps3disk_mask_mutex);
static const struct blk_mq_ops ps3disk_mq_ops = {
.queue_rq = ps3disk_queue_rq,
static int ps3disk_probe(struct ps3_system_bus_device *_dev)
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct ps3disk_private *priv;
struct request_queue *queue;
if (dev->blk_size < 512) {
error = ps3stor_setup(dev, ps3disk_interrupt);
queue = blk_init_queue(ps3disk_request, &priv->lock);
dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n",
queue = blk_mq_init_sq_queue(&priv->tag_set, &ps3disk_mq_ops, 1,
dev_err(&dev->sbd.core, "%s:%u: blk_mq_init_queue failed\n",
blk_queue_max_hw_sectors(queue, dev->bounce_size >> 9);
blk_queue_segment_boundary(queue, -1UL);
blk_queue_dma_alignment(queue, dev->blk_size-1);
blk_queue_logical_block_size(queue, dev->blk_size);
"%s is a %s (%llu MiB total, %lu MiB for OtherOS)\n",
gendisk->disk_name, priv->model, priv->raw_capacity >> 11,
get_capacity(gendisk) >> 11);
device_add_disk(&dev->sbd.core, gendisk, NULL);
blk_cleanup_queue(queue);
blk_mq_free_tag_set(&priv->tag_set);
ps3_system_bus_set_drvdata(_dev, NULL);
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(devidx, &ps3disk_mask);
struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
struct ps3disk_private *priv = ps3_system_bus_get_drvdata(&dev->sbd);
mutex_lock(&ps3disk_mask_mutex);
__clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS,
mutex_unlock(&ps3disk_mask_mutex);
del_gendisk(priv->gendisk);
blk_cleanup_queue(priv->queue);
blk_mq_free_tag_set(&priv->tag_set);
dev_notice(&dev->sbd.core, "Synchronizing disk cache\n");
ps3_system_bus_set_drvdata(_dev, NULL);