最近看了下mmc读写,起由是Vendor发来eMMC固件升级要求,说如果使用了CMD18 + CMD12,就要升级,因为不知道OEM使用情况,建议都升级。
我们来确认下内核的情况, 内核版本3.18,高通平台。
#define MMC_READ_DAT_UNTIL_STOP 11 #define MMC_STOP_TRANSMISSION 12 #define MMC_SEND_STATUS 13 #define MMC_BUS_TEST_R 14 #define MMC_GO_INACTIVE_STATE 15 #define MMC_BUS_TEST_W 19 #define MMC_SPI_READ_OCR 58 #define MMC_SPI_CRC_ON_OFF 59 #define MMC_SET_BLOCKLEN 16 #define MMC_READ_SINGLE_BLOCK 17 #define MMC_READ_MULTIPLE_BLOCK 18 #define MMC_SEND_TUNING_BLOCK 19 #define MMC_SEND_TUNING_BLOCK_HS200 21
CMD18就是MMC_READ_MULTIPLE_BLOCK,CMD12就是MMC_STOP_TRANSMISSION。
查下相关代码在mmc_test_prepare_mrq中使用。
static void mmc_test_prepare_mrq (struct mmc_test_card *test, 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(test->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, test->card); }
这个具体是在mmc_test里,先看看log情况。
mmc_test默认是模块CONFIG_MMC_TEST=m,insmod有如下签名问题。
insmod: failed to load mmc_test.ko: Required key not available
先略过签名改成y后,如下路径会多出mmc_test目录。
ok,用mmc0会直接Oops,先用mmc1 (SD card)试下,操作步骤是:
echo mmc1:0001 > /sys/bus/mmc/drivers/mmcblk/unbindecho mmc1:0001 > /sys/bus/mmc/drivers/mmc_test/bind
这时候到如下路径就能看到多出test和testlist两个文件。
xxx:/sys/kernel/debug/mmc1/mmc1:0001 state status test testlist
其中6就是多块读测试:
把CONFIG_MMC_DEBUG放开,太多打印了,能看到这项测试的log
[ 137.468637] mmc1: starting CMD18 arg 00000000 flags 00000035 [ 137.468644] mmc1: blksz 512 blocks 16 flags 00000200 tsac 100 ms nsac 0 [ 137.468650] mmc1: CMD12 arg 00000000 flags 0000001d [ 137.468696] sdhci [sdhci_irq()]: *** mmc1 got interrupt: 0x00000001 [ 137.469279] sdhci [sdhci_irq()]: *** mmc1 got interrupt: 0x00000002 [ 137.469309] sdhci [sdhci_irq()]: *** mmc1 got interrupt: 0x00000003 [ 137.469323] mmc1: accumulated busy time is 33456 usec [ 137.469331] mmc1: req done (CMD18): 0: 00000900 00000000 00000000 00000000 [ 137.469336] mmc1: 8192 bytes transferred: 0 [ 137.469343] mmc1: (CMD12): 0: 00000b00 00000000 00000000 00000000
ok, 那mmc0到底用了没,放开debug,确是没有发现CMD18 + CMD12的打印,偶尔看了下LK的代码:
uint32_t mmc_sdhci_read (struct mmc_device *dev, void *dest, uint64_t blk_addr, uint32_t num_blocks) { uint32_t mmc_ret = 0 ; struct mmc_command cmd ; struct mmc_card *card = &dev->card; memset ((struct mmc_command *)&cmd, 0 , sizeof (struct mmc_command)); if (num_blocks == 1 ) cmd.cmd_index = CMD17_READ_SINGLE_BLOCK; else cmd.cmd_index = CMD18_READ_MULTIPLE_BLOCK; ... if (mmc_ret && num_blocks > 1 ) { return mmc_stop_command(dev); } return mmc_parse_response(cmd.resp[0 ]); }
正常的使用应该在这类读写接口里,kernel的mmc0/mmc1读写是怎么样的,从上面的log能看出在mmc_blk_rw_rq_prep中:
static void mmc_blk_rw_rq_prep (struct mmc_queue_req *mqrq, struct mmc_card *card, int disable_multi, struct mmc_queue *mq) { u32 readcmd, writecmd; struct mmc_blk_request *brq = &mqrq->brq; struct request *req = mqrq->req; struct mmc_blk_data *md = mq->data; bool do_data_tag; bool do_rel_wr = ((req->cmd_flags & REQ_FUA) || (req->cmd_flags & REQ_META)) && (rq_data_dir(req) == WRITE) && (md->flags & MMC_BLK_REL_WR); ... brq->cmd.arg = blk_rq_pos(req); if (!mmc_card_blockaddr(card)) brq->cmd.arg <<= 9 ; brq->cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; brq->data.blksz = 512 ; brq->stop.opcode = MMC_STOP_TRANSMISSION; brq->stop.arg = 0 ; ... if (brq->data.blocks > 1 || do_rel_wr) { if (!mmc_host_is_spi(card->host) || rq_data_dir(req) == READ) brq->mrq.stop = &brq->stop; readcmd = MMC_READ_MULTIPLE_BLOCK; writecmd = MMC_WRITE_MULTIPLE_BLOCK; } else { brq->mrq.stop = NULL ; readcmd = MMC_READ_SINGLE_BLOCK; writecmd = MMC_WRITE_BLOCK; } ...
流程调用大概看下:
mmc_blk_probe | mmc_blk_alloc/mmc_blk_alloc_part | mmc_blk_alloc_req (md->queue.issue_fn = mmc_blk_issue_rq) | mmc_blk_issue_rq | mmc_blk_issue_rw_rq | mmc_blk_rw_rq_prep
请求发出主要是通过一个线程 call issue_fn。
static int mmc_queue_thread (void *d) { struct mmc_queue *mq = d; struct request_queue *q = mq->queue ; struct mmc_card *card = mq->card; current->flags |= PF_MEMALLOC; if (card->host->wakeup_on_idle) set_wake_up_idle(true ); down(&mq->thread_sem); do { struct request *req = NULL ; struct mmc_queue_req *tmp ; unsigned int cmd_flags = 0 ; 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); cmd_flags = req ? req->cmd_flags : 0 ; mq->issue_fn(mq, req);
线程的创建是在mmc_init_queue:
int mmc_init_queue (struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock, const char *subname, int area_type) { struct mmc_host *host = card->host; u64 limit = BLK_BOUNCE_HIGH; int ret; struct mmc_queue_req *mqrq_cur = &mq->mqrq[0 ]; struct mmc_queue_req *mqrq_prev = &mq->mqrq[1 ]; ... success: sema_init(&mq->thread_sem, 1 ); if (card->host->ops->init) card->host->ops->init(card->host); mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s" , host->index, subname ? subname : "" );
ps看下:
root 288 2 0 0 irq_thread 00000000 S irq/145-mmc0 root 290 2 0 0 irq_thread 00000000 S irq/147-mmc1 root 292 2 0 0 mmc_cmdq_t 00000000 D mmc-cmdqd/0 root 293 2 0 0 mmc_queue_ 00000000 S mmcqd/0rpmb
插上SD card,
root 288 2 0 0 irq_thread 00000000 S irq/145-mmc0 root 290 2 0 0 irq_thread 00000000 S irq/147-mmc1 root 291 2 0 0 mmc_cmdq_t 00000000 D mmc-cmdqd/0 root 293 2 0 0 mmc_queue_ 00000000 S mmcqd/0rpmb root 303 2 0 0 mmc_start_ 00000000 S mmcqd/1
上面创建的线程应该就是mmcqd/0rpmb和mmcqd/1,mmcqd/0rpmb是mmc0的rpmb区分,mmcqd/1才是for mmc1(SD card)。
那还有一个线程mmc-cmdqd/0是啥,线程创建往上再细看下:
int mmc_init_queue (struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock, const char *subname, int area_type) { struct mmc_host *host = card->host; u64 limit = BLK_BOUNCE_HIGH; int ret; struct mmc_queue_req *mqrq_cur = &mq->mqrq[0 ]; struct mmc_queue_req *mqrq_prev = &mq->mqrq[1 ]; if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT; mq->card = card; if (card->ext_csd.cmdq_support && (area_type == MMC_BLK_DATA_AREA_MAIN)) { mq->queue = blk_init_queue(mmc_cmdq_dispatch_req, lock); if (!mq->queue ) return -ENOMEM; mmc_cmdq_setup_queue(mq, card); ret = mmc_cmdq_init(mq, card); if (ret) { pr_err("%s: %d: cmdq: unable to set-up\n" , mmc_hostname(card->host), ret); blk_cleanup_queue(mq->queue ); } else { sema_init(&mq->thread_sem, 1 ); if (card->host->cmdq_ops->init) card->host->cmdq_ops->init(card->host); mq->queue ->queuedata = mq; mq->thread = kthread_run(mmc_cmdq_thread, mq, "mmc-cmdqd/%d%s" , host->index, subname ? subname : "" ); if (IS_ERR(mq->thread)) { pr_err("%s: %d: cmdq: failed to start mmc-cmdqd thread\n" , mmc_hostname(card->host), ret); ret = PTR_ERR(mq->thread); } return ret; } } mq->queue = blk_init_queue(mmc_request_fn, lock); if (!mq->queue ) return -ENOMEM;
如果card->ext_csd.cmdq_support
为真,也就是说支持cmdq,如果创建mmc-cmdqd/0成功就返回了,ok, cmdq是啥。
看到ext_csd,很显然应该是个硬件特性,再看下开机mmc0/mmc1相关log:
[ 8.332885] mmc0: SDHCI controller on 7824900.sdhci [7824900.sdhci] using 32-bit ADMA in CMDQ mode ... [ 8.504130] mmc1: SDHCI controller on 7864900.sdhci [7864900.sdhci] using 32-bit ADMA in legacy mode
看到这里似乎知道了,mmc0用的是CMDQ方式工作,而mmc1仍然使用的是过去的旧方式。
我们再对比看下CMDQ的线程mmc_cmdq_thread,它对应请求是mmc_blk_cmdq_issue_rq/mmc_blk_cmdq_rw_prep,所有相关的cmdq的接口都在cmdq_hci.c里。
来看下git log:
commit 72dfcb7c9f6134282b8077beea420d5f0d736cc9 Author: Venkat Gopalakrishnan <venkatg@codeaurora.org> Date: Fri May 29 17:25:46 2015 -0700 mmc: cmdq: support for command queue enabled host This patch adds CMDQ support for command-queue compatible hosts. Command queue is added in eMMC-5.1 specification. This enables the controller to process upto 32 requests at a time. Change-Id: I0486495ef57c64bf8427e917daeb184c69b8dc73 Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> drivers/mmc/host/Kconfig | 13 ++++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/cmdq_hci.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/cmdq_hci.h | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 12 ++++
log commit df118e6a7fd85f1f649e2b35aed1d2b18cc29b94 Author: Asutosh Das <asutoshd@codeaurora.org> Date: Fri Oct 17 16:36:47 2014 +0530 mmc: sdhci: add command queue support to sdhci Adds command-queue support to SDHCi compliant drivers. Change-Id: I1efee7f1c86e102364083e9158e4d45c887dd06e Signed-off-by: Asutosh Das <asutoshd@codeaurora.org> Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org> Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> drivers/mmc/host/sdhci.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/host/sdhci.h | 3 +++ include/linux/mmc/sdhci.h | 2 ++ 3 files changed, 144 insertions(+), 3 deletions(-) @@ -293,6 +293,8 @@ struct sdhci_host { u32 auto_cmd_err_sts; struct ratelimit_state dbg_dump_rs; + struct cmdq_host *cq_host; + unsigned long private[0] ____cacheline_aligned; }; static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result = IRQ_NONE; @@ -2948,6 +2963,15 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } do { + if (host->mmc->card && mmc_card_cmdq(host->mmc->card) && + !mmc_host_halt(host->mmc)) { + pr_debug("*** %s: cmdq intr: 0x%08x\n", + mmc_hostname(host->mmc), + intmask); + result = sdhci_cmdq_irq(host->mmc, intmask); + goto out; + } + if (intmask & SDHCI_INT_AUTO_CMD_ERR) host->auto_cmd_err_sts = sdhci_readw(host, SDHCI_AUTO_CMD_ERR); @@ -3918,12 +4042,24 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } - pr_info("%s: SDHCI controller on %s [%s] using %s\n", + if (mmc->caps2 & MMC_CAP2_CMD_QUEUE) { + bool dma64 = (host->flags & SDHCI_USE_ADMA_64BIT) ? + true : false; + ret = sdhci_cmdq_init(host, mmc, dma64); + if (ret) + pr_err("%s: CMDQ init: failed (%d)\n", + mmc_hostname(host->mmc), ret); + else + host->cq_host->ops = &sdhci_cmdq_ops; + } + pr_info("%s: SDHCI controller on %s [%s] using %s in %s mode\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), (host->flags & SDHCI_USE_ADMA) ? ((host->flags & SDHCI_USE_ADMA_64BIT) ? "64-bit ADMA" : "32-bit ADMA") : - ((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO")); + ((host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"), + ((mmc->caps2 & MMC_CAP2_CMD_QUEUE) && !ret) ? + "CMDQ" : "legacy");
那基本上就是这样了,还有一个地方:
static void sdhci_finish_data (struct sdhci_host *host) { ... if (data->stop && (data->error || !host->mrq->sbc)) { if (data->error) { sdhci_do_reset(host, SDHCI_RESET_CMD); sdhci_do_reset(host, SDHCI_RESET_DATA); } sdhci_send_command(host, data->stop); } else
so it should not be for CQ xfer.
btw: 高通总是不正面答复我,给个差评:]
版权声明: 本站所有文章均采用 CC BY-NC-SA 4.0 CN 许可协议。转载请注明原文链接!