mmc驱动框架分析2
2015-06-22 22:52
711 查看
mmc驱动框架分析1中,分析了sd卡的初始化流程,本文主要分析sd卡的读写过程。先贴一张图,网上网友画的,非常清晰,表示感谢。
从mmc驱动框架分析1中,可以看到mmcblock设备的请求处理函数为mmc_request,先从这个函数分析
上面是card层和core层对请求的处理,接下来看host层的处理:
再来看下数据发送:
从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; }
相关文章推荐
- 移动端调自适应的方法
- Java-马士兵设计模式学习笔记-建造者模式
- ApiDemo/FragmentRetainInstance 解析
- python特殊函数(id, dir, type, isinstance, issubclass, is)
- 21.判断字符串是否为回文
- 【深入JVM】JVM工具之JMAP
- 第七十五天至第八十一天 how can I 坚持
- 企业口碑营销推广策略
- 企业口碑营销推广策略
- samba之实例
- 【万年老坑】斐波那契钟计划
- java学习之旅08--浮点数_浮点数误差问题
- (数据结构)顺序表的建立,增删查改
- samba之基础知识
- 9.MyBatis 关联映射(多对多)
- csc命令使用的一些参数
- 为什么要使用Spring?
- leetcode-35-Search Insert Position
- [MVC]Controller
- 网络交换机设备