From: xbw Date: Thu, 20 Mar 2014 05:38:24 +0000 (+0800) Subject: SDMMC: add the new API about direct read-wirte for the eMMC formatted already X-Git-Tag: firefly_0821_release~5970 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=680462b44fe7fd7a2b696d5331c0dd9571b1e210;p=firefly-linux-kernel-4.4.55.git SDMMC: add the new API about direct read-wirte for the eMMC formatted already --- diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 78f9cfe8bc53..98bc2c205259 100755 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2379,6 +2379,10 @@ static const struct mmc_fixup blk_fixups[] = END_FIXUP }; +#if defined(CONFIG_MMC_DW_ROCKCHIP) +extern struct mmc_card *this_card; +#endif + static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; @@ -2428,6 +2432,10 @@ static int mmc_blk_probe(struct mmc_card *card) pm_runtime_set_active(&card->dev); pm_runtime_enable(&card->dev); } +#if defined(CONFIG_MMC_DW_ROCKCHIP) + if(card->host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) + this_card = card; +#endif return 0; @@ -2440,7 +2448,11 @@ static int mmc_blk_probe(struct mmc_card *card) static void mmc_blk_remove(struct mmc_card *card) { struct mmc_blk_data *md = mmc_get_drvdata(card); - + +#if defined(CONFIG_MMC_DW_ROCKCHIP) + if(card->host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) + this_card = NULL; +#endif mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); mmc_claim_host(card->host); diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 9ce558427521..82c471324ca8 100755 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_MMC_DW) += rk_sdmmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o # To Specific Extensions for Synopsys DW Multimedia Card Interface in Rockchip Soc. Added by XBW. -obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o rk_sdmmc_of.o +obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o rk_sdmmc_of.o rk_sdmmc_ops.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o diff --git a/drivers/mmc/host/rk_sdmmc_ops.c b/drivers/mmc/host/rk_sdmmc_ops.c new file mode 100755 index 000000000000..8d8d01fa17b9 --- /dev/null +++ b/drivers/mmc/host/rk_sdmmc_ops.c @@ -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 +#include +#include +#include +#include + +#include +#include /* For nr_free_buffer_pages() */ +#include + +#include +#include +#include +#include +#include + +#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);