mmc驱动框架分析2

来源:互联网 发布:纳粹党卫军军服淘宝 编辑:程序博客网 时间:2024/05/29 09:58

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);elseret = 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结构体} elseareq = 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 voidmmc_start_request(struct mmc_host *host, struct mmc_request *mrq){#ifdef CONFIG_MMC_DEBUGunsigned int i, sz;struct scatterlist *sg;#endifpr_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_DEBUGsz = 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);#endifmrq->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);} elses3cmci_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))//使用DMAres = s3cmci_prepare_dma(host, cmd->data);elseres = s3cmci_prepare_pio(host, cmd->data);//不适用DMAif (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;elsehost->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);//写数据,激活tasklethost->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);//读数据,激活tasklethost->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);//触发taskletgoto 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);} elses3cmci_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);//开始DMAreturn 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);//触发taskletspin_unlock_irqrestore(&host->complete_lock, iflags);return;fail_request:host->mrq->data->error = -EINVAL;host->complete_what = COMPLETION_FINALIZE;clear_imask(host);goto out;}

0 0
原创粉丝点击