mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
emmc: add kernel wr interface: rk_emmc_transfer
This commit is contained in:
parent
183af6915a
commit
c2a7dca71c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
147
drivers/mmc/host/rkemmc_ops.c
Normal file
147
drivers/mmc/host/rkemmc_ops.c
Normal file
|
|
@ -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);
|
||||
Loading…
Reference in New Issue
Block a user