emmc: add kernel wr interface: rk_emmc_transfer
authorkfx <kfx@rock-chips.com>
Fri, 9 Aug 2013 09:00:37 +0000 (17:00 +0800)
committerkfx <kfx@rock-chips.com>
Wed, 28 Aug 2013 01:54:20 +0000 (09:54 +0800)
drivers/mmc/card/block.c
drivers/mmc/host/Makefile
drivers/mmc/host/rkemmc_ops.c [new file with mode: 0644]

index 462098047bbf9f4b3ae4064a200c0b83b3177759..38b0c374f7caf8affdd275096eb8eb7e0b6f8294 100644 (file)
@@ -1963,6 +1963,9 @@ static const struct mmc_fixup blk_fixups[] =
        END_FIXUP
 };
 
+#ifdef CONFIG_EMMC_RK
+extern struct mmc_card *this_card;
+#endif
 static int mmc_blk_probe(struct mmc_card *card)
 {
        struct mmc_blk_data *md, *part_md;
@@ -2005,6 +2008,9 @@ static int mmc_blk_probe(struct mmc_card *card)
                if (mmc_add_disk(part_md))
                        goto out;
        }
+#ifdef CONFIG_EMMC_RK
+       this_card = card;
+#endif
        return 0;
 
  out:
@@ -2017,6 +2023,9 @@ static void mmc_blk_remove(struct mmc_card *card)
 {
        struct mmc_blk_data *md = mmc_get_drvdata(card);
 
+#ifdef CONFIG_EMMC_RK
+       this_card = NULL;
+#endif
        mmc_blk_remove_parts(card, md);
        mmc_claim_host(card->host);
        mmc_blk_part_switch(card, md);
index adba95929ef72922e7d13bddc02fe75db6f4880d..f8117e72e1b472fe266515aa054f79facc7a6bcf 100644 (file)
@@ -8,7 +8,7 @@ else
 obj-$(CONFIG_SDMMC_RK29)       += rk29_sdmmc.o
 endif
 
-obj-$(CONFIG_EMMC_RK)          += rkemmc.o
+obj-$(CONFIG_EMMC_RK)           += rkemmc.o rkemmc_ops.o
 obj-$(CONFIG_MMC_ARMMMCI)      += mmci.o
 obj-$(CONFIG_MMC_PXA)          += pxamci.o
 obj-$(CONFIG_MMC_IMX)          += imxmmc.o
diff --git a/drivers/mmc/host/rkemmc_ops.c b/drivers/mmc/host/rkemmc_ops.c
new file mode 100644 (file)
index 0000000..8d8d01f
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *  linux/drivers/mmchost/rkemmc_ops.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/slab.h>
+
+#include <linux/scatterlist.h>
+#include <linux/swap.h>                /* For nr_free_buffer_pages() */
+#include <linux/list.h>
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+
+#define BLKSZ          512
+
+struct mmc_card        *this_card = NULL;
+/*
+ * Fill in the mmc_request structure given a set of transfer parameters.
+ */
+static void rk_emmc_prepare_mrq(struct mmc_request *mrq, struct scatterlist *sg, 
+               unsigned sg_len, unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
+{
+       BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
+
+       if (blocks > 1) {
+               mrq->cmd->opcode = write ?
+                       MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+       } else {
+               mrq->cmd->opcode = write ?
+                       MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+       }
+
+       mrq->cmd->arg = dev_addr;
+       if (!mmc_card_blockaddr(this_card))
+               mrq->cmd->arg <<= 9;
+
+       mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       if (blocks == 1)
+               mrq->stop = NULL;
+       else {
+               mrq->stop->opcode = MMC_STOP_TRANSMISSION;
+               mrq->stop->arg = 0;
+               mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+       }
+
+       mrq->data->blksz = blksz;
+       mrq->data->blocks = blocks;
+       mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+       mrq->data->sg = sg;
+       mrq->data->sg_len = sg_len;
+       mmc_set_data_timeout(mrq->data, this_card);
+}
+
+static int rk_emmc_busy(struct mmc_command *cmd)
+{
+       return !(cmd->resp[0] & R1_READY_FOR_DATA) ||
+               (R1_CURRENT_STATE(cmd->resp[0]) == 7);
+}
+
+/*
+ * Wait for the card to finish the busy state
+ */
+static int rk_emmc_wait_busy(void)
+{
+       int ret, busy;
+       struct mmc_command cmd = {0};
+
+       busy = 0;
+       do {
+               memset(&cmd, 0, sizeof(struct mmc_command));
+
+               cmd.opcode = MMC_SEND_STATUS;
+               cmd.arg = this_card->rca << 16;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+               ret = mmc_wait_for_cmd(this_card->host, &cmd, 0);
+               if (ret)
+                       break;
+
+               if (!busy && rk_emmc_busy(&cmd)) {
+                       busy = 1;
+                       if (this_card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+                               pr_info("%s: Warning: Host did not "
+                                       "wait for busy state to end.\n",
+                                       mmc_hostname(this_card->host));
+               }
+       } while (rk_emmc_busy(&cmd));
+
+       return ret;
+}
+
+/*
+ * Transfer a single sector of kernel addressable data
+ */
+int rk_emmc_transfer(u8 *buffer, unsigned addr, unsigned blksz, int write)
+{
+       int ret = 0;
+
+       struct mmc_request mrq = {0};
+       struct mmc_command cmd = {0};
+       struct mmc_command stop = {0};
+       struct mmc_data data = {0};
+
+       struct scatterlist sg;
+
+       if(!this_card)
+               return -EIO;
+
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+       mrq.stop = &stop;
+
+       sg_init_one(&sg, buffer, blksz);
+
+       rk_emmc_prepare_mrq(&mrq, &sg, 1, addr, 1, blksz, write);
+
+       mmc_claim_host(this_card->host);
+       mmc_wait_for_req(this_card->host, &mrq);
+
+       if (cmd.error){
+               ret = cmd.error;
+               goto exit;
+       }
+       if (data.error){
+               ret =  data.error;
+               goto exit;
+       }
+
+       ret = rk_emmc_wait_busy();
+exit:
+       mmc_release_host(this_card->host);
+       return ret;
+}
+EXPORT_SYMBOL(rk_emmc_transfer);