您的位置:首页 > 其它

mmc驱动框架分析2

2015-06-22 22:52 711 查看
mmc驱动框架分析1中,分析了sd卡的初始化流程,本文主要分析sd卡的读写过程。先贴一张图,网上网友画的,非常清晰,表示感谢。





从mmc驱动框架分析1中,可以看到mmcblock设备的请求处理函数为mmc_request,先从这个函数分析

static void mmc_request(struct request_queue *q)
{
struct mmc_queue *mq = q->queuedata;
struct request *req;

if (!mq) {
while ((req = blk_fetch_request(q)) != NULL) {
req->cmd_flags |= REQ_QUIET;
__blk_end_request_all(req, -EIO);
}
return;
}

if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
wake_up_process(mq->thread);
}
static int mmc_queue_thread(void *d)
{
struct mmc_queue *mq = d;
struct request_queue *q = mq->queue;

current->flags |= PF_MEMALLOC;

down(&mq->thread_sem);
do {
struct request *req = NULL;
struct mmc_queue_req *tmp;

spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE);
req = blk_fetch_request(q);	//取出一个请求
mq->mqrq_cur->req = req;
spin_unlock_irq(q->queue_lock);

if (req || mq->mqrq_prev->req) {
set_current_state(TASK_RUNNING);
mq->issue_fn(mq, req);	//调用issue_fn接口函数,对应mmc_blk_issue_rq
} else {
if (kthread_should_stop()) {
set_current_state(TASK_RUNNING);
break;
}
up(&mq->thread_sem);
schedule();
down(&mq->thread_sem);
}

/* Current request becomes previous request and vice versa. */
mq->mqrq_prev->brq.mrq.data = NULL;
mq->mqrq_prev->req = NULL;
tmp = mq->mqrq_prev;
mq->mqrq_prev = mq->mqrq_cur;
mq->mqrq_cur = tmp;
} while (1);
up(&mq->thread_sem);

return 0;
}
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
int ret;
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;

if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
mmc_claim_host(card->host);

ret = mmc_blk_part_switch(card, md);
if (ret) {
ret = 0;
goto out;
}

if (req && req->cmd_flags & REQ_DISCARD) {
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
if (req->cmd_flags & REQ_SECURE)
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
} else if (req && req->cmd_flags & REQ_FLUSH) {
/* complete ongoing async transfer before issuing flush */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
ret = mmc_blk_issue_flush(mq, req);
} else {
ret = mmc_blk_issue_rw_rq(mq, req);	//对于普通的读写,调用这个函数
}

out:
if (!req)
/* release host only when there are no more requests */
mmc_release_host(card->host);
return ret;
}
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
int ret = 1, disable_multi = 0, retry = 0;
enum mmc_blk_status status;
struct mmc_queue_req *mq_rq;
struct request *req;
struct mmc_async_req *areq;

if (!rqc && !mq->mqrq_prev->req)
return 0;

do {
if (rqc) {
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
areq = &mq->mqrq_cur->mmc_active;	//取得异步request结构体
} else
areq = NULL;
areq = mmc_start_req(card->host, areq, (int *) &status);	//正式开始请求
if (!areq)
return 0;

mq_rq = container_of(areq, struct mmc_queue_req, mmc_active);
brq = &mq_rq->brq;
req = mq_rq->req;
mmc_queue_bounce_post(mq_rq);

switch (status) {	//对完成状态进行判断
case MMC_BLK_SUCCESS:
case MMC_BLK_PARTIAL:
/*
* A block was successfully transferred.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0,
brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
if (status == MMC_BLK_SUCCESS && ret) {
/*
* The blk_end_request has returned non zero
* even though all data is transfered and no
* erros returned by host.
* If this happen it's a bug.
*/
printk(KERN_ERR "%s BUG rq_tot %d d_xfer %d\n",
__func__, blk_rq_bytes(req),
brq->data.bytes_xfered);
rqc = NULL;
goto cmd_abort;
}
break;
case MMC_BLK_CMD_ERR:
goto cmd_err;
case MMC_BLK_RETRY_SINGLE:
disable_multi = 1;
break;
case MMC_BLK_RETRY:
if (retry++ < 5)
break;
case MMC_BLK_ABORT:
goto cmd_abort;
case MMC_BLK_DATA_ERR:
/*
* After an error, we redo I/O one sector at a
* time, so we only reach here after trying to
* read a single sector.
*/
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, -EIO,
brq->data.blksz);
spin_unlock_irq(&md->lock);
if (!ret)
goto start_new_req;
break;
}

if (ret) {
/*
* In case of a none complete request
* prepare it again and resend.
*/
mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq);
mmc_start_req(card->host, &mq_rq->mmc_active, NULL);
}
} while (ret);

return 1;

cmd_err:
/*
* If this is an SD card and we're writing, we can first
* mark the known good sectors as ok.
*
* If the card is not SD, we can still ok written sectors
* as reported by the controller (which might be less than
* the real number of written sectors, but never more).
*/
if (mmc_card_sd(card)) {
u32 blocks;

blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
spin_lock_irq(&md->lock);
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
spin_unlock_irq(&md->lock);
}

cmd_abort:
spin_lock_irq(&md->lock);
while (ret)
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
spin_unlock_irq(&md->lock);

start_new_req:
if (rqc) {
mmc_blk_rw_rq_prep(mq->mqrq_cur, card, 0, mq);
mmc_start_req(card->host, &mq->mqrq_cur->mmc_active, NULL);
}

return 0;
}
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
struct mmc_async_req *areq, int *error)
{
int err = 0;
struct mmc_async_req *data = host->areq;

/* Prepare a new request */
if (areq)
mmc_pre_req(host, areq->mrq, !host->areq);
//如果上次传递未完成,需要等待
if (host->areq) {
mmc_wait_for_req_done(host, host->areq->mrq);
err = host->areq->err_check(host->card, host->areq);
if (err) {
mmc_post_req(host, host->areq->mrq, 0);
if (areq)
mmc_post_req(host, areq->mrq, -EINVAL);

host->areq = NULL;
goto out;
}
}

if (areq)
__mmc_start_req(host, areq->mrq);	//开始处理请求

if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);

host->areq = areq;
out:
if (error)
*error = err;
return data;
}
static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
init_completion(&mrq->completion);
mrq->done = mmc_wait_done;
mmc_start_request(host, mrq);
}
static void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
struct scatterlist *sg;
#endif

pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode,
mrq->cmd->arg, mrq->cmd->flags);

if (mrq->data) {
pr_debug("%s:     blksz %d blocks %d flags %08x "
"tsac %d ms nsac %d\n",
mmc_hostname(host), mrq->data->blksz,
mrq->data->blocks, mrq->data->flags,
mrq->data->timeout_ns / 1000000,
mrq->data->timeout_clks);
}

if (mrq->stop) {
pr_debug("%s:     CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->stop->opcode,
mrq->stop->arg, mrq->stop->flags);
}

WARN_ON(!host->claimed);

mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
if (mrq->data) {	//对于有数据传输要求的命令,要对data结构错误判断
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
BUG_ON(mrq->data->blocks * mrq->data->blksz >
host->max_req_size);

#ifdef CONFIG_MMC_DEBUG
sz = 0;
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)
sz += sg->length;
BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);
#endif

mrq->cmd->data = mrq->data;
mrq->data->error = 0;
mrq->data->mrq = mrq;
if (mrq->stop) {
mrq->data->stop = mrq->stop;
mrq->stop->error = 0;
mrq->stop->mrq = mrq;
}
}
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
host->ops->request(host, mrq);	//调用host端的request函数,对应于s3cmci_request
}

上面是card层和core层对请求的处理,接下来看host层的处理:

static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct s3cmci_host *host = mmc_priv(mmc);

host->status = "mmc request";
host->cmd_is_stop = 0;
host->mrq = mrq;

if (s3cmci_card_present(mmc) == 0) {
dbg(host, dbg_err, "%s: no medium present\n", __func__);
host->mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
} else
s3cmci_send_request(mmc);
}
static void s3cmci_send_request(struct mmc_host *mmc)
{
struct s3cmci_host *host = mmc_priv(mmc);
struct mmc_request *mrq = host->mrq;
struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;

host->ccnt++;
prepare_dbgmsg(host, cmd, host->cmd_is_stop);

/* Clear command, data and fifo status registers
Fifo clear only necessary on 2440, but doesn't hurt on 2410
*/
writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);

if (cmd->data) {	//对于数据的处理
int res = s3cmci_setup_data(host, cmd->data);

host->dcnt++;

if (res) {
dbg(host, dbg_err, "setup data error %d\n", res);
cmd->error = res;
cmd->data->error = res;

mmc_request_done(mmc, mrq);
return;
}

if (s3cmci_host_usedma(host))	//使用DMA
res = s3cmci_prepare_dma(host, cmd->data);
else
res = s3cmci_prepare_pio(host, cmd->data);	//不适用DMA

if (res) {
dbg(host, dbg_err, "data prepare error %d\n", res);
cmd->error = res;
cmd->data->error = res;

mmc_request_done(mmc, mrq);
return;
}
}

/* Send command */
s3cmci_send_command(host, cmd);	//对于命令的处理

/* Enable Interrupt */
s3cmci_enable_irq(host, true);
}
s3cmci_send_request函数分为对数据和命令的处理,先看对命令的处理

static void s3cmci_send_command(struct s3cmci_host *host,
struct mmc_command *cmd)
{
u32 ccon, imsk;

imsk  = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
S3C2410_SDIIMSK_RESPONSECRC;

enable_imask(host, imsk);

if (cmd->data)
host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
else if (cmd->flags & MMC_RSP_PRESENT)
host->complete_what = COMPLETION_RSPFIN;
else
host->complete_what = COMPLETION_CMDSENT;	//任务标记

writel(cmd->arg, host->base + S3C2410_SDICMDARG);

ccon  = cmd->opcode & S3C2410_SDICMDCON_INDEX;
ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;

if (cmd->flags & MMC_RSP_PRESENT)
ccon |= S3C2410_SDICMDCON_WAITRSP;

if (cmd->flags & MMC_RSP_136)
ccon |= S3C2410_SDICMDCON_LONGRSP;

writel(ccon, host->base + S3C2410_SDICMDCON);	//配置命令控制寄存器
}
static irqreturn_t s3cmci_irq(int irq, void *dev_id)
{
struct s3cmci_host *host = dev_id;
struct mmc_command *cmd;
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
u32 mci_cclear = 0, mci_dclear;
unsigned long iflags;

mci_dsta = readl(host->base + S3C2410_SDIDSTA);
mci_imsk = readl(host->base + host->sdiimsk);

if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {	//sdio相关
if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
writel(mci_dclear, host->base + S3C2410_SDIDSTA);

mmc_signal_sdio_irq(host->mmc);
return IRQ_HANDLED;
}
}

spin_lock_irqsave(&host->complete_lock, iflags);

mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
mci_dclear = 0;

if ((host->complete_what == COMPLETION_NONE) ||
(host->complete_what == COMPLETION_FINALIZE)) {
host->status = "nothing to complete";
clear_imask(host);
goto irq_out;
}

if (!host->mrq) {
host->status = "no active mrq";
clear_imask(host);
goto irq_out;
}

cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;

if (!cmd) {
host->status = "no active cmd";
clear_imask(host);
goto irq_out;
}

if (!s3cmci_host_usedma(host)) {
if ((host->pio_active == XFER_WRITE) &&
(mci_fsta & S3C2410_SDIFSTA_TFDET)) {

disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
tasklet_schedule(&host->pio_tasklet);	//写数据,激活tasklet
host->status = "pio tx";
}

if ((host->pio_active == XFER_READ) &&
(mci_fsta & S3C2410_SDIFSTA_RFDET)) {

disable_imask(host,
S3C2410_SDIIMSK_RXFIFOHALF |
S3C2410_SDIIMSK_RXFIFOLAST);

tasklet_schedule(&host->pio_tasklet);	//读数据,激活tasklet
host->status = "pio rx";
}
}

if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {	//命令超时
dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
cmd->error = -ETIMEDOUT;
host->status = "error: command timeout";
goto fail_transfer;
}

if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {	//命令发出
if (host->complete_what == COMPLETION_CMDSENT) {
host->status = "ok: command sent";
goto close_transfer;
}

mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
}

if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
if (cmd->flags & MMC_RSP_CRC) {
if (host->mrq->cmd->flags & MMC_RSP_136) {
dbg(host, dbg_irq,
"fixup: ignore CRC fail with long rsp\n");
} else {
/* note, we used to fail the transfer
* here, but it seems that this is just
* the hardware getting it wrong.
*
* cmd->error = -EILSEQ;
* host->status = "error: bad command crc";
* goto fail_transfer;
*/
}
}

mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
}

if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {	//收到响应
if (host->complete_what == COMPLETION_RSPFIN) {
host->status = "ok: command response received";
goto close_transfer;
}

if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
host->complete_what = COMPLETION_XFERFINISH;

mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
}

/* errors handled after this point are only relevant
when a data transfer is in progress */

if (!cmd->data)
goto clear_status_bits;

/* Check for FIFO failure */
if (host->is2440) {
if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
dbg(host, dbg_err, "FIFO failure\n");
host->mrq->data->error = -EILSEQ;
host->status = "error: 2440 fifo failure";
goto fail_transfer;
}
} else {
if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
dbg(host, dbg_err, "FIFO failure\n");
cmd->data->error = -EILSEQ;
host->status = "error:  fifo failure";
goto fail_transfer;
}
}

if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
dbg(host, dbg_err, "bad data crc (outgoing)\n");
cmd->data->error = -EILSEQ;
host->status = "error: bad data crc (outgoing)";
goto fail_transfer;
}

if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
dbg(host, dbg_err, "bad data crc (incoming)\n");
cmd->data->error = -EILSEQ;
host->status = "error: bad data crc (incoming)";
goto fail_transfer;
}

if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
dbg(host, dbg_err, "data timeout\n");
cmd->data->error = -ETIMEDOUT;
host->status = "error: data timeout";
goto fail_transfer;
}

if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
if (host->complete_what == COMPLETION_XFERFINISH) {
host->status = "ok: data transfer completed";
goto close_transfer;
}

if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
host->complete_what = COMPLETION_RSPFIN;

mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
}

clear_status_bits:
writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
writel(mci_dclear, host->base + S3C2410_SDIDSTA);

goto irq_out;

fail_transfer:
host->pio_active = XFER_NONE;

close_transfer:
host->complete_what = COMPLETION_FINALIZE;

clear_imask(host);
tasklet_schedule(&host->pio_tasklet);	//触发tasklet

goto irq_out;

irq_out:
dbg(host, dbg_irq,
"csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",
mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);

spin_unlock_irqrestore(&host->complete_lock, iflags);
return IRQ_HANDLED;

}
static void pio_tasklet(unsigned long data)
{
struct s3cmci_host *host = (struct s3cmci_host *) data;

s3cmci_disable_irq(host, true);

if (host->pio_active == XFER_WRITE)
do_pio_write(host);

if (host->pio_active == XFER_READ)
do_pio_read(host);

if (host->complete_what == COMPLETION_FINALIZE) {
clear_imask(host);
if (host->pio_active != XFER_NONE) {
dbg(host, dbg_err, "unfinished %s "
"- pio_count:[%u] pio_bytes:[%u]\n",
(host->pio_active == XFER_READ) ? "read" : "write",
host->pio_count, host->pio_bytes);

if (host->mrq->data)
host->mrq->data->error = -EINVAL;
}

s3cmci_enable_irq(host, false);
finalize_request(host);
} else
s3cmci_enable_irq(host, true);
}


再来看下数据发送:

static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
{
int dma_len, i;
int rw = data->flags & MMC_DATA_WRITE;

BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);

s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);	//设置完成回调函数
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE);

if (dma_len == 0)
return -ENOMEM;

host->dma_complete = 0;
host->dmatogo = dma_len;

for (i = 0; i < dma_len; i++) {
int res;

dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));

res = s3c2410_dma_enqueue(host->dma, host,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));	//加载数据到DMA通道

if (res) {
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
return -EBUSY;
}
}

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);	//开始DMA

return 0;
}
static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
void *buf_id, int size,
enum s3c2410_dma_buffresult result)
{
struct s3cmci_host *host = buf_id;
unsigned long iflags;
u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;

mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
mci_dsta = readl(host->base + S3C2410_SDIDSTA);
mci_fsta = readl(host->base + S3C2410_SDIFSTA);
mci_dcnt = readl(host->base + S3C2410_SDIDCNT);

BUG_ON(!host->mrq);
BUG_ON(!host->mrq->data);
BUG_ON(!host->dmatogo);

spin_lock_irqsave(&host->complete_lock, iflags);

if (result != S3C2410_RES_OK) {
dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
mci_csta, mci_dsta, mci_fsta,
mci_dcnt, result, host->dmatogo);

goto fail_request;
}

host->dmatogo--;
if (host->dmatogo) {
dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] "
"DCNT:[%08x] toGo:%u\n",
size, mci_dsta, mci_dcnt, host->dmatogo);

goto out;
}

dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
size, mci_dsta, mci_dcnt);

host->dma_complete = 1;
host->complete_what = COMPLETION_FINALIZE;	//任务标记

out:
tasklet_schedule(&host->pio_tasklet);	//触发tasklet
spin_unlock_irqrestore(&host->complete_lock, iflags);
return;

fail_request:
host->mrq->data->error = -EINVAL;
host->complete_what = COMPLETION_FINALIZE;
clear_imask(host);

goto out;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: