--- /dev/null
+/*
+ * 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);