diff options
Diffstat (limited to 'drivers/mmc/mmc_block.c')
| -rw-r--r-- | drivers/mmc/mmc_block.c | 522 |
1 files changed, 0 insertions, 522 deletions
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c deleted file mode 100644 index d91fcf7c317..00000000000 --- a/drivers/mmc/mmc_block.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Block driver for media (i.e., flash cards) - * - * Copyright 2002 Hewlett-Packard Company - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * Many thanks to Alessandro Rubini and Jonathan Corbet! - * - * Author: Andrew Christian - * 28 May 2002 - */ -#include <linux/moduleparam.h> -#include <linux/module.h> -#include <linux/init.h> - -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/hdreg.h> -#include <linux/kdev_t.h> -#include <linux/blkdev.h> -#include <linux/devfs_fs_kernel.h> - -#include <linux/mmc/card.h> -#include <linux/mmc/protocol.h> - -#include <asm/system.h> -#include <asm/uaccess.h> - -#include "mmc_queue.h" - -/* - * max 8 partitions per card - */ -#define MMC_SHIFT 3 - -static int major; - -/* - * There is one mmc_blk_data per slot. - */ -struct mmc_blk_data { - spinlock_t lock; - struct gendisk *disk; - struct mmc_queue queue; - - unsigned int usage; - unsigned int block_bits; -}; - -static DECLARE_MUTEX(open_lock); - -static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) -{ - struct mmc_blk_data *md; - - down(&open_lock); - md = disk->private_data; - if (md && md->usage == 0) - md = NULL; - if (md) - md->usage++; - up(&open_lock); - - return md; -} - -static void mmc_blk_put(struct mmc_blk_data *md) -{ - down(&open_lock); - md->usage--; - if (md->usage == 0) { - put_disk(md->disk); - mmc_cleanup_queue(&md->queue); - kfree(md); - } - up(&open_lock); -} - -static inline int mmc_blk_readonly(struct mmc_card *card) -{ - return mmc_card_readonly(card) || - !(card->csd.cmdclass & CCC_BLOCK_WRITE); -} - -static int mmc_blk_open(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md; - int ret = -ENXIO; - - md = mmc_blk_get(inode->i_bdev->bd_disk); - if (md) { - if (md->usage == 2) - check_disk_change(inode->i_bdev); - ret = 0; - - if ((filp->f_mode & FMODE_WRITE) && - mmc_blk_readonly(md->queue.card)) - ret = -EROFS; - } - - return ret; -} - -static int mmc_blk_release(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; - - mmc_blk_put(md); - return 0; -} - -static int -mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct block_device *bdev = inode->i_bdev; - - if (cmd == HDIO_GETGEO) { - struct hd_geometry geo; - - memset(&geo, 0, sizeof(struct hd_geometry)); - - geo.cylinders = get_capacity(bdev->bd_disk) / (4 * 16); - geo.heads = 4; - geo.sectors = 16; - geo.start = get_start_sect(bdev); - - return copy_to_user((void __user *)arg, &geo, sizeof(geo)) - ? -EFAULT : 0; - } - - return -ENOTTY; -} - -static struct block_device_operations mmc_bdops = { - .open = mmc_blk_open, - .release = mmc_blk_release, - .ioctl = mmc_blk_ioctl, - .owner = THIS_MODULE, -}; - -struct mmc_blk_request { - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_command stop; - struct mmc_data data; -}; - -static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - int stat = BLKPREP_OK; - - /* - * If we have no device, we haven't finished initialising. - */ - if (!md || !mq->card) { - printk(KERN_ERR "%s: killing request - no device/host\n", - req->rq_disk->disk_name); - stat = BLKPREP_KILL; - } - - return stat; -} - -static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - struct mmc_card *card = md->queue.card; - int ret; - - if (mmc_card_claim_host(card)) - goto cmd_err; - - do { - struct mmc_blk_request brq; - struct mmc_command cmd; - - memset(&brq, 0, sizeof(struct mmc_blk_request)); - brq.mrq.cmd = &brq.cmd; - brq.mrq.data = &brq.data; - - brq.cmd.arg = req->sector << 9; - brq.cmd.flags = MMC_RSP_R1; - brq.data.timeout_ns = card->csd.tacc_ns * 10; - brq.data.timeout_clks = card->csd.tacc_clks * 10; - brq.data.blksz_bits = md->block_bits; - brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); - brq.stop.opcode = MMC_STOP_TRANSMISSION; - brq.stop.arg = 0; - brq.stop.flags = MMC_RSP_R1B; - - if (rq_data_dir(req) == READ) { - brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; - brq.data.flags |= MMC_DATA_READ; - } else { - brq.cmd.opcode = MMC_WRITE_BLOCK; - brq.data.flags |= MMC_DATA_WRITE; - brq.data.blocks = 1; - } - brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL; - - brq.data.sg = mq->sg; - brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); - - mmc_wait_for_req(card->host, &brq.mrq); - if (brq.cmd.error) { - printk(KERN_ERR "%s: error %d sending read/write command\n", - req->rq_disk->disk_name, brq.cmd.error); - goto cmd_err; - } - - if (brq.data.error) { - printk(KERN_ERR "%s: error %d transferring data\n", - req->rq_disk->disk_name, brq.data.error); - goto cmd_err; - } - - if (brq.stop.error) { - printk(KERN_ERR "%s: error %d sending stop command\n", - req->rq_disk->disk_name, brq.stop.error); - goto cmd_err; - } - - do { - int err; - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - if (err) { - printk(KERN_ERR "%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - goto cmd_err; - } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); - -#if 0 - if (cmd.resp[0] & ~0x00000900) - printk(KERN_ERR "%s: status = %08x\n", - req->rq_disk->disk_name, cmd.resp[0]); - if (mmc_decode_status(cmd.resp)) - goto cmd_err; -#endif - - /* - * A block was successfully transferred. - */ - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - if (!ret) { - /* - * The whole request completed successfully. - */ - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req); - } - spin_unlock_irq(&md->lock); - } while (ret); - - mmc_card_release_host(card); - - return 1; - - cmd_err: - mmc_card_release_host(card); - - /* - * This is a little draconian, but until we get proper - * error handling sorted out here, its the best we can - * do - especially as some hosts have no idea how much - * data was transferred before the error occurred. - */ - spin_lock_irq(&md->lock); - do { - ret = end_that_request_chunk(req, 0, - req->current_nr_sectors << 9); - } while (ret); - - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req); - spin_unlock_irq(&md->lock); - - return 0; -} - -#define MMC_NUM_MINORS (256 >> MMC_SHIFT) - -static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; - -static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int devidx, ret; - - devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); - if (devidx >= MMC_NUM_MINORS) - return ERR_PTR(-ENOSPC); - __set_bit(devidx, dev_use); - - md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); - if (md) { - memset(md, 0, sizeof(struct mmc_blk_data)); - - md->disk = alloc_disk(1 << MMC_SHIFT); - if (md->disk == NULL) { - kfree(md); - md = ERR_PTR(-ENOMEM); - goto out; - } - - spin_lock_init(&md->lock); - md->usage = 1; - - ret = mmc_init_queue(&md->queue, card, &md->lock); - if (ret) { - put_disk(md->disk); - kfree(md); - md = ERR_PTR(ret); - goto out; - } - md->queue.prep_fn = mmc_blk_prep_rq; - md->queue.issue_fn = mmc_blk_issue_rq; - md->queue.data = md; - - md->disk->major = major; - md->disk->first_minor = devidx << MMC_SHIFT; - md->disk->fops = &mmc_bdops; - md->disk->private_data = md; - md->disk->queue = md->queue.queue; - md->disk->driverfs_dev = &card->dev; - - /* - * As discussed on lkml, GENHD_FL_REMOVABLE should: - * - * - be set for removable media with permanent block devices - * - be unset for removable block devices with permanent media - * - * Since MMC block devices clearly fall under the second - * case, we do not set GENHD_FL_REMOVABLE. Userspace - * should use the block device creation/destruction hotplug - * messages to tell when the card is present. - */ - - sprintf(md->disk->disk_name, "mmcblk%d", devidx); - sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); - - md->block_bits = card->csd.read_blkbits; - - blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); - set_capacity(md->disk, card->csd.capacity); - } - out: - return md; -} - -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) -{ - struct mmc_command cmd; - int err; - - mmc_card_claim_host(card); - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 1 << card->csd.read_blkbits; - cmd.flags = MMC_RSP_R1; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - mmc_card_release_host(card); - - if (err) { - printk(KERN_ERR "%s: unable to set block size to %d: %d\n", - md->disk->disk_name, cmd.arg, err); - return -EINVAL; - } - - return 0; -} - -static int mmc_blk_probe(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int err; - - /* - * Check that the card supports the command class(es) we need. - */ - if (!(card->csd.cmdclass & CCC_BLOCK_READ)) - return -ENODEV; - - if (card->csd.read_blkbits < 9) { - printk(KERN_WARNING "%s: read blocksize too small (%u)\n", - mmc_card_id(card), 1 << card->csd.read_blkbits); - return -ENODEV; - } - - md = mmc_blk_alloc(card); - if (IS_ERR(md)) - return PTR_ERR(md); - - err = mmc_blk_set_blksize(md, card); - if (err) - goto out; - - printk(KERN_INFO "%s: %s %s %dKiB %s\n", - md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), - (card->csd.capacity << card->csd.read_blkbits) / 1024, - mmc_blk_readonly(card)?"(ro)":""); - - mmc_set_drvdata(card, md); - add_disk(md->disk); - return 0; - - out: - mmc_blk_put(md); - - return err; -} - -static void mmc_blk_remove(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - int devidx; - - del_gendisk(md->disk); - - /* - * I think this is needed. - */ - md->disk->queue = NULL; - - devidx = md->disk->first_minor >> MMC_SHIFT; - __clear_bit(devidx, dev_use); - - mmc_blk_put(md); - } - mmc_set_drvdata(card, NULL); -} - -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_queue_suspend(&md->queue); - } - return 0; -} - -static int mmc_blk_resume(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_blk_set_blksize(md, card); - mmc_queue_resume(&md->queue); - } - return 0; -} -#else -#define mmc_blk_suspend NULL -#define mmc_blk_resume NULL -#endif - -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmcblk", - }, - .probe = mmc_blk_probe, - .remove = mmc_blk_remove, - .suspend = mmc_blk_suspend, - .resume = mmc_blk_resume, -}; - -static int __init mmc_blk_init(void) -{ - int res = -ENOMEM; - - res = register_blkdev(major, "mmc"); - if (res < 0) { - printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", - major, res); - goto out; - } - if (major == 0) - major = res; - - devfs_mk_dir("mmc"); - return mmc_register_driver(&mmc_driver); - - out: - return res; -} - -static void __exit mmc_blk_exit(void) -{ - mmc_unregister_driver(&mmc_driver); - devfs_remove("mmc"); - unregister_blkdev(major, "mmc"); -} - -module_init(mmc_blk_init); -module_exit(mmc_blk_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); - -module_param(major, int, 0444); -MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); |
