rendered paste body#include <linux/init.h>
#include <linux/module.h>
#include <linux/mmc/card.h>
#include <linux/mmc/protocol.h>
#include <linux/mtd/mtd.h>
#include <linux/scatterlist.h>
/*
* erase a range of erase groups aligned to mtd->erase_size
*/
static int mmc_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mmc_card *card = mtd->priv;
struct mmc_command cmd[3] = { {
.opcode = MMC_ERASE_GROUP_START,
.arg = instr->addr,
.flags = MMC_RSP_R1 | MMC_CMD_AC,
}, {
.opcode = MMC_ERASE_GROUP_END,
.arg = instr->addr + instr->len,
.flags = MMC_RSP_R1 | MMC_CMD_AC,
}, {
.opcode = MMC_ERASE,
.flags = MMC_RSP_R1B | MMC_CMD_AC,
},
};
int err, i;
dev_dbg(card->dev, "%s: from %ld len %ld\n", __FUNCTION__, from, len);
instr->state = MTD_ERASING;
err = mmc_card_claim_host(card);
if (err)
goto error;
for (i=0; i<3; i++) {
err = mmc_wait_for_cmd(card->host, cmd, 5);
if (err) {
dev_err(&card->dev, "%s: error %d in stage %d\n",
__FUNCTION__, err, i);
break;
}
}
mmc_card_release_host(card);
mtd_erase_callback(instr);
error:
instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
return err;
}
/*
* check if a write command was completed correctly
*/
static int mmc_mtd_get_status(struct mmc_card *card)
{
int err;
struct mmc_command cmd;
err = mmc_card_claim_host(card);
if (err)
goto error;
do {
cmd = (struct mmc_command) {
.opcode = MMC_SEND_STATUS,
.arg = card->rca << 16,
.flags = MMC_RSP_R1 | MMC_CMD_AC,
};
err = mmc_wait_for_cmd(card->host, &cmd, 5);
if (err) {
dev_err(&card->dev, "error %d requesting status\n", err);
break;
}
} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
mmc_card_release_host(card);
error:
return err;
}
/*
* transfer a block to/from the card. The block needs to be aligned
* to mtd->writesize. If we want to implement an mtd_writev method,
* this needs to use stream operations with an appropriate stop
* command as well.
*/
static int mmc_mtd_transfer_low(struct mmc_card *card, loff_t off, size_t len,
size_t *retlen, u_char *buf, int write)
{
struct scatterlist sg;
struct mmc_data data = {
.blksz = 1 << card->csd.read_blkbits,
.blocks = len >> card->csd.read_blkbits,
.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ,
.sg = &sg,
.sg_len = 1,
};
struct mmc_command cmd = {
.arg = off,
.flags = MMC_RSP_R1 | MMC_CMD_ADTC,
.opcode = write ? MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK,
};
struct mmc_request mrq = {
.cmd = &cmd,
.data = &data,
};
int ret;
dev_dbg(&card->dev, "%s off %ld, len %ld\n",
write ? "write" : "read", off, len);
sg_init_one(&sg, buf, len);
mmc_set_data_timeout(&data, card, write);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error || data.error) {
dev_err(&card->dev, "error %d/%d sending read/write command\n",
cmd.error, data.error);
ret = -EIO;
}
/* copied from the block driver, don't understand why this is needed */
if (write)
ret = mmc_mtd_get_status(card);
if (!ret)
*retlen = len;
return ret;
}
/*
* do some common sanity checks and locking for the actual transfer
* function.
*/
static int mmc_mtd_transfer(struct mmc_card *card, loff_t off, size_t len,
size_t *retlen, u_char *buf, int write)
{
int ret;
if (from > mtd->size)
return -EINVAL;
if (from + len > mtd->size)
len = mtd->size - from;
if (retlen)
*retlen = 0;
ret = mmc_card_claim_host(card);
if (ret) {
dev_warn(&card->dev, "%s: mmc_card_claim_host returned %d\n",
__FUNCTION__, ret);
ret = -EIO;
goto error;
}
ret = mmc_mtd_transfer_low(card, from, len, retlen, buf, write);
mmc_card_release_host(card);
error:
return ret;
}
static int mmc_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
return mmc_mtd_transfer(mtd->priv, from, len, retlen, buf, 0);
}
static int mmc_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
return mmc_mtd_transfer(mtd->priv, to, len, retlen, (u_char *)buf, 1);
}
/*
* Initialize an mmc card. We create a new MTD device for each
* MMC card we find. The operations are rather straightforward,
* so we don't even need our own data structure to contain the
* mtd_info.
*/
static int mmc_mtd_probe(struct mmc_card *card)
{
struct mtd_info *mtd;
int ret;
if (!(card->csd.cmdclass & CCC_ERASE))
return -ENODEV;
dev_info(&card->dev, "mmc card %s found\n", card->dev.bus_id);
mtd = kzalloc(GFP_KERNEL, sizeof *mtd);
if (!mtd)
return -ENOMEM;
sprintf(mtd->name, "mmc: %s", card->dev.bus_id);
/* Don't know if these computations are correct */
mtd->size = card->csd.capacity << (card->csd.read_blkbits - 9);
mtd->writesize = 1 << card->csd.write_blkbits;
mtd->erasesize = mtd->writesize * card->csd.erase_blksize;
dev_info(&card->dev, "size %d bytes, writesize %d, readsize %d\n",
mtd->size, mtd->writesize, mtd->erasesize);
/*
* NAND is a relatively good approximation, but with the current
* MTD scheme, we might need to get our own type
*/
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
/*
* operations that may be worthwhile implementing:
* writev, sync, lock, unlock, suspend, resume
* In the mean time, these should be sufficient.
*/
mtd->erase = mmc_mtd_erase;
mtd->write = mmc_mtd_write;
mtd->writev = default_mtd_writev;
mtd->read = mmc_mtd_read;
mtd->owner = THIS_MODULE;
mtd->priv = card;
mmc_set_drvdata(card, mtd);
ret = add_mtd_device(mtd);
if (ret)
kfree(mtd);
return ret;
}
/*
* Not sure if this is safe. Can we call del_mtd_device while
* the device is still in use? Do we have a choice?
*/
static void mmc_mtd_remove(struct mmc_card *card)
{
struct mtd_info *mtd;
mtd = mmc_get_drvdata(card);
mmc_mtd_sync(mtd);
del_mtd_device(mtd);
kfree(mtd);
}
/*
* This driver will match any card, so it conflicts with the
* mmc block driver. It's only possible to load one of them
* at a time.
*/
static struct mmc_driver mmc_mtd_driver = {
.drv = {
.name = "mmcmtd",
},
.probe = mmc_mtd_probe,
.remove = mmc_mtd_remove,
};
static int __init mmc_blk_init(void)
{
return mmc_register_driver(&mmc_mtd_driver);
}
module_init(mmc_blk_init);
static void __exit mmc_blk_exit(void)
{
mmc_unregister_driver(&mmc_mtd_driver);
}
module_exit(mmc_blk_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) MTD device driver");
MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");