EMMC子系统分析-读写流程

EMMC读写操作的调用栈

mmc_queue_thread ->

    mmc_blk_issue_rq ->

        mmc_blk_issue_rw_rq ->

            mmc_start_req ->

                __mmc_start_data_req ->

                    mmc_start_request ->

                        omap_hsmmc_request


mmc_queue_thread

 49 static int mmc_queue_thread(void *d)
 50 {
 51     struct mmc_queue *mq = d;
 52     struct request_queue *q = mq->queue;
 53 
 54     current->flags |= PF_MEMALLOC;
 55 
 56     down(&mq->thread_sem);
 57     do {
 58         struct request *req = NULL;
 59         struct mmc_queue_req *tmp;
 60         unsigned int cmd_flags = 0;
 61 
 62         spin_lock_irq(q->queue_lock);
 63         set_current_state(TASK_INTERRUPTIBLE);
 64         req = blk_fetch_request(q);
 65         mq->mqrq_cur->req = req;
 66         spin_unlock_irq(q->queue_lock);
 67 
 68         if (req || mq->mqrq_prev->req) {
 69             set_current_state(TASK_RUNNING);
 70             cmd_flags = req ? req->cmd_flags : 0;
 71             mq->issue_fn(mq, req);
 72             if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
 73                 mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
 74                 continue; /* fetch again */
 75             }
 76 
 77             /*
 78              * Current request becomes previous request
 79              * and vice versa.
 80              * In case of special requests, current request
 81              * has been finished. Do not assign it to previous
 82              * request.
 83              */
 84             if (cmd_flags & MMC_REQ_SPECIAL_MASK)
 85                 mq->mqrq_cur->req = NULL;
 86 
 87             mq->mqrq_prev->brq.mrq.data = NULL;
 88             mq->mqrq_prev->req = NULL;
 89             tmp = mq->mqrq_prev;
 90             mq->mqrq_prev = mq->mqrq_cur;
 91             mq->mqrq_cur = tmp;
 92         } else {
 93             if (kthread_should_stop()) {
 94                 set_current_state(TASK_RUNNING);
 95                 break;
 96             }
 97             up(&mq->thread_sem);
 98             schedule();
 99             down(&mq->thread_sem);
100         }
101     } while (1);
102     up(&mq->thread_sem);
103 
104     return 0;
105 }

64 blk_fetch_request 从request_queue获取一个request,设置为mmc_request的当前request

68~71 如果从blk_fetch_reuqest获得了新request,或者mmc_request的previous request正在处理当中,那么调用mq->issue_fn处理reuqest

72~75 issue_fn有可能被阻塞在mmc_wait_for_data_req_done,如果此时有新的请求到达,那么有可能会唤醒阻塞的进程 (条件是cur==null, prev!=null)。

87~90 这段代码相当奇怪,看起来把mq->mqrq_prev和mq->mqrq_cur做了置换,有点类似frame buffer ping-pong操作的意思。


mmc_blk_issue_rq

1. 调用mmc_blk_part_switch切换device的分区配置,对于正常的分区操作来说,part_config就是缺省的0值,可以参考Spec5.0中PARTITION_CONFIG (before BOOT_CONFIG) [179]

2. request->cmd_flags标识了REQ_DISCARD, REQ_FLUSH,REQ_WRITE等操作,根据request命令类型不同,分别调用:

2.1 mmc_blk_issue_secdiscard_rq 和mmc_blk_issue_discard_rq

2.2 mmc_blk_issue_flush

2.3 mmc_blk_issue_rw_rq,这个是我们要分析的读写数据流程。


mc_blk_issue_rw_rq

static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)

如果参数req为空(无新request),或者mmc queue的previous request也为空(无未完成的request),那么mmc_blk_issue_rw_rq直接返回。

mmc_blk_prep_packed_list尝试把当前request和队列中的其他request合并,以增强性能。是否可以合并,要依赖于:

    1. 控制器支持packed功能

    2. device的MAX_PACKED_WRITES 大于0

    3. 只对写request进行packed

正常情况下执行mmc_blk_rw_rq_prep函数,从request构造mmc_request,毕竟下发给host请求,是mmc_request,而不是block层通用的request。如果支持packed功能,那么就用pack_list来构造mmc_request

areq表示async req,实际上,只要参数@req存在,就表明这是一个新request,必然是异步传输的。

mmc_start_req 启动一个非阻塞的request,这个函数会等待前一个request完成,然后启动当前request,并立刻返回。

如果mmc_start_req返回的areq不为空,说明完成了上一次的request,

mmc_queue_bounce_post:如果是读操作,并且使用bounce buffer,那么需要把读结果从bounce buffer复制回sg buffer。所谓bounce buffer是因为某些DMA控制器只能处理连续物理内存,此时需要通过bounce buffer来达到物理内存连续性。

检查mmc_start_req返回的状态:

    1. 如果是MMC_BLK_SUCCESS或者MMC_BLK_PARTIAL,需要调用blk_end_request通知block设备层,完成了本次读写request。

    2. 如果是MMC_BLK_CMD_ERR,那么调用mmc_blk_reset复位host。调用mmc_blk_cmd_err尝试blk_end_request,如果发现reuqest未完成,说明本次操作失败,反之成功start_new_req

    3. ....


mmc_start_req

struct mmc_async_req *mmc_start_req(struct mmc_host *host,
                    struct mmc_async_req *areq, int *error)

执行一个非阻塞的reuqest,如果previous request正在执行过程中,这个函数会等待previous request完成,并返回previous request;如果没有正在执行的request,则不会等待并返回NULL。

if (host->areq) {
    err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);
host->areq不为空,说明有正在处理的reuqest,函数mmc_wait_for_data_req_done用来等待这个host->areq,有两个条件会唤醒该MMC上下文: is_done_rcv和is_new_req

    if (!err && areq)
        start_err = __mmc_start_data_req(host, areq->mrq);
上面on-going的request已经执行完毕,如果有新的request,那么调用__mmc_start_data_req执行新的request

把@areq赋值给host->areq,即当前的request变成了previous request。


__mmc_start_data_req->mmc_start_request

mmc_start_request是主流程,

    mrq->done = mmc_wait_data_done;
mmc_wait_data_done会设置context_info->is_done_rcv=true,这正好是唤醒mmc_wait_for_data_req_done的条件之一。

mmc_start_reuqest实际调用host->ops->request方法,进入了平台特定的request函数,对于TI平台,改函数就是omap_hsmmc_request


omap_hsmmc_request

1958     WARN_ON(host->mrq != NULL);
1959     host->mrq = req;
1960     host->clk_rate = clk_get_rate(host->fclk);
1961     err = omap_hsmmc_prepare_data(host, req);
1962     if (err) {
1963         req->cmd->error = err;
1964         if (req->data)
1965             req->data->error = err;
1966         host->mrq = NULL;
1967         mmc_request_done(mmc, req);
1968         return;
1969     }
1970     if (req->sbc && !(host->flags & AUTO_CMD23)) {
1971         omap_hsmmc_start_command(host, req->sbc, NULL);
1972         return;
1973     }
1974 
1975     omap_hsmmc_start_dma_transfer(host);
1976     omap_hsmmc_start_command(host, req->cmd, req->data);

1961 准备数据,配置并启动MMC card的DMA

1970 如果req->sbc存在,即set block count命令存在,并且host不支持AUTO CMD23,那么我们要先发送sbc

1975 启动DMA操作

1976 发送MMC command,这里才是mmc操作的终极启动命令




  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值