目录

1. 简介

1.1 mmc card的几种类型

1.2 mmc子系统如何区分使用哪种card的?(mmc_rescan)

2. mmc type card协议相关操作

3. 一些重要的API函数

3.1 mmc_attach_mmc

3.2 mmc_init_card

4. mmc_ops结构体

5. mmc ops.c文件接口说明

5.1 mmc_send_status(典型)

5.2 mmc_send_op_cond(特殊)

5.3 mmc_switch


1. 简介

1.1 mmc card的几种类型

(1)mmc core——card相关模块为对应card实现相应的操作,包括初始化操作、以及对应的总线操作集合。负责和对应card协议层相关的东西。
       主要包括三种类型的card,分别是mmc type card、sd type card和sdio type card。

#define MMC_TYPE_MMC     0       /* MMC card */
#define MMC_TYPE_SD     1       /* SD card */
#define MMC_TYPE_SDIO       2       /* SDIO card */
#define MMC_TYPE_SD_COMBO   3       /* SD combo (IO+mem) card */

这里先学习mmc type card。后续再学习sd type card。
       对应代码:

drivers/mmc/core/mmc.c(提供接口),
drivers/mmc/core/mmc-ops.c(提供和mmc type card协议相关的操作),
drivers/mmc/core/mmc-ops.h

(2)另外,这里继续强调一下mmc的概念
       mmc core是指mmc subsystem的核心实现,这里的mmc是表示mmc总线、接口、设备相关的一种统称,可以理解为一种软件架构。
      而mmc type card则是指mmc卡或者emmc。
      总之,这里的mmc是两种概念概念,需要自己先消化一下。

(3)mmc总线和mmc_bus
       在本文里面这两个是不同的概念。
       mmc_bus是指mmc core抽象出来的虚拟总线,和mmc设备对应的硬件总线无关,是一种软件概念。
       而本文的mmc总线是一种物理概念,是实际的总线,是和host controller直接相关联的。

1.2 mmc子系统如何区分使用哪种card的?(mmc_rescan)

在前文mmc core关于host的描述中,mmc_alloc_host函数中,有关于这方面的介绍。

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{.......INIT_DELAYED_WORK(&host->detect, mmc_rescan);........return host;
}

当检测到mmc card(mmc 、sdio、sd卡)插入时,mmc_rescan函数会被调用。

void mmc_rescan(struct work_struct *work)
{struct mmc_host *host =container_of(work, struct mmc_host, detect.work);............mmc_bus_get(host);.........for (i = 0; i < ARRAY_SIZE(freqs); i++) {if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))break;if (freqs[i] <= host->f_min)break;}........
}

最终调用mmc_rescan_try_freq函数

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{.......mmc_go_idle(host);if (!(host->caps2 & MMC_CAP2_NO_SD))mmc_send_if_cond(host, host->ocr_avail);/* Order's important: probe SDIO, then SD, then MMC */if (!(host->caps2 & MMC_CAP2_NO_SDIO))if (!mmc_attach_sdio(host))return 0;if (!(host->caps2 & MMC_CAP2_NO_SD))if (!mmc_attach_sd(host))return 0;if (!(host->caps2 & MMC_CAP2_NO_MMC))if (!mmc_attach_mmc(host))return 0;mmc_power_off(host);return -EIO;
}

会根据host的caps2进行判断是哪种type的card。

2. mmc type card协议相关操作

mmc_ops提供了部分和mmc type card协议相关操作,这些操作会在mmc.c中mmc的初始化过程中被使用到。
      建议先简单了解一下mmc协议的内容。后续会进行总结。

mmc_go_idle
发送CMD0指令,GO_IDLE_STATE
使mmc card进入idle state。
虽然进入到了Idle State,但是上电复位过程并不一定完成了,这主要靠读取OCR的busy位来判断,而流程归结为下一步。

mmc_send_op_cond
发送CMD1指令,SEND_OP_COND
这里会设置card的工作电压寄存器OCR,并且通过busy位(bit31)来判断card的上电复位过程是否完成,如果没有完成的话需要重复发送。
完成之后,mmc card进入ready state。

mmc_all_send_cid
这里会发送CMD2指令,ALL_SEND_CID
广播指令,使card回复对应的CID寄存器的值。在这里就相应获得了CID寄存器的值了,存储在cid中。
完成之后,MMC card会进入Identification State。

mmc_set_relative_addr
发送CMD3指令,SET_RELATIVE_ADDR
设置该mmc card的关联地址为card->rca,也就是0x0001
完成之后,该MMC card进入standby模式。

mmc_send_csd
发送CMD9指令,MMC_SEND_CSD
要求mmc card发送csd寄存器,存储到card->raw_csd中,也就是原始的csd寄存器的值。
此时mmc card还是处于standby state

mmc_select_card & mmc_deselect_cards
发送CMD7指令,SELECT/DESELECT CARD
选择或者断开指定的card
这时卡进入transfer state。后续可以通过各种指令进入到receive-data state或者sending-data state依次来进行数据的传输

mmc_get_ext_csd
发送CMD8指令,SEND_EXT_CSD
这里要求处于transfer state的card发送ext_csd寄存器,这里获取之后存放在ext_csd寄存器中
这里会使card进入sending-data state,完成之后又退出到transfer state。

mmc_switch
发送CMD6命令,MMC_SWITCH
用于设置ext_csd寄存器的某些bit

mmc_send_status
发送CMD13命令,MMC_SEND_STATUS
要求card发送自己当前的状态寄存器

mmc_send_cid
发送CMD10命令,MMC_SEND_CID
要求mmc card回复cid寄存器

mmc_card_sleepawake
发送CMD5命令,MMC_SLEEP_AWAKE
使card进入或者退出sleep state,由参数决定。关于sleep state是指card的一种状态,具体参考emmc 5.1协议。

3. 一些重要的API函数

3.1 mmc_attach_mmc

/** Starting point for MMC card init.*/
int mmc_attach_mmc(struct mmc_host *host)
{int err;u32 ocr, rocr;WARN_ON(!host->claimed);/* Set correct bus mode for MMC before attempting attach *//* 在尝试匹配之前,先设置正确的总线模式 */if (!mmc_host_is_spi(host))mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);/* 获取card的ocr寄存器 *//** 发送CMD1命令(MMC_SEND_OP_COND),并且参数为0;* 这里获取OCR(Operation condition register)32位* 的OCR包含卡设备支持的工作电压表,存储到ocr变量中* 如果Host的IO电压可调整,那调整前需要读取OCR。为了* 不使卡误进入Inactive State,可以给MMC卡发送不带* 参数的CMD1,这样可以仅获取OCR寄存器,而不会改变卡的状态。*/err = mmc_send_op_cond(host, 0, &ocr);if (err)return err;/* mmc总线上的操作协议 */mmc_attach_bus(host, &mmc_ops);/* 为card选择一个HOST和card都支持的最低电压 */if (host->ocr_avail_mmc)host->ocr_avail = host->ocr_avail_mmc;/** We need to get OCR a different way for SPI.*/if (mmc_host_is_spi(host)) {err = mmc_spi_read_ocr(host, 1, &ocr);if (err)goto err;}/* 通过OCR寄存器选择一个HOST和card都支持的最低电压 */rocr = mmc_select_voltage(host, ocr);/** Can we support the voltage of the card?*/if (!rocr) {err = -EINVAL;goto err;}/** Detect and init the card.*//* 调用mmc_init_card初始化该mmc type card,这里是核心函数,后续会继续说明 */// 初始化该mmc type card,并为其分配和初始化一个对应的mmc_carderr = mmc_init_card(host, rocr, NULL);if (err)goto err;mmc_release_host(host);     // 先释放掉host,可能是在mmc_add_card中会获取这个host/* 将分配到的mmc_card注册到mmc_bus中 */err = mmc_add_card(host->card);if (err)goto remove_card;mmc_claim_host(host);  // 再次申请hostreturn 0;......
}

用于通过mmc_host获取mmc type card信息,初始化mmc_card,并进行部分驱动,最后将其注册到mmc_bus上。

主要工作:

(1)设置总线模式
(2)选择一个card和host都支持的最低工作电压
(3)设置相应的总线操作集合(mmc_host->bus_ops)
(4)初始化card使其进入工作状态(mmc_init_card)
(5)为card构造对应的mmc_card并且注册到mmc_bus中(mmc_add_card,具体参考《mmc core——bus模块说明》)

(1)在attach过程中,有一个很重要的函数mmc_init_card,第四节就要围绕这个函数进行展开。
      (2)调用了mmc_add_card之后mmc_card就挂在了mmc_bus上,会和mmc_bus上的block(mmc_driver)匹配起来。相应block(mmc_driver)就会进行probe,驱动card,实现card的实际功能(也就是存储设备的功能)。会对接到块设备子系统中。具体在学习mmc card driver的时候再说明。

3.2 mmc_init_card

在第3节中,可以看出mmc_attach_mmc中的一个核心函数就是mmc_init_card,用于对mmc type card进行实质性的初始化,并为其分配和初始化一个对应的mmc_card。这部分和协议相关,需要先学习一下mmc协议。

/** Handle the detection and initialisation of a card.** In the case of a resume, "oldcard" will contain the card* we're trying to reinitialise.*/
static int mmc_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)
{// struct mmc_host *host:该mmc card使用的host// ocr:表示了host要使用的电压,在mmc_attach_mmc中,// 已经得到了一个HOST和card都支持的最低电压 struct mmc_card *card;int err;u32 cid[4];u32 rocr;WARN_ON(!host->claimed);/* Set correct bus mode for MMC before attempting init */// 设置总线模式为开漏模式if (!mmc_host_is_spi(host))mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);/** Since we're changing the OCR value, we seem to* need to tell some cards to go back to the idle* state.  We wait 1ms to give cards time to* respond.* mmc_go_idle is needed for eMMC that are asleep*//* 根据mmc协议从mmc总线上选中一张card(协议的初始化流程) *//* 发送CMD0指令,GO_IDLE_STATE*  使mmc card进入idle state。*  虽然进入到了Idle State,但是上电复位过程并不一定完成了,*  这主要靠读取OCR的busy位来判断,而流程归结为下一步。*/mmc_go_idle(host);/* The extra bit indicates that we support high capacity *//*发送CMD1指令,SEND_OP_COND* 这里会设置card的工作电压寄存器OCR,并且通过busy位(bit31)* 来判断card的上电复位过程是否完成,如果没有完成的话需要重复发送。* 完成之后,mmc card进入ready state。*/err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);if (err)goto err;/** For SPI, enable CRC as appropriate.*/if (mmc_host_is_spi(host)) {err = mmc_spi_set_crc(host, use_spi_crc);if (err)goto err;}/** Fetch CID from card.*//* 这里会发送CMD2指令,ALL_SEND_CID*  广播指令,使card回复对应的CID寄存器的值。*     在这里就相应获得了CID寄存器的值了,存储在cid中。* 成之后,MMC card会进入Identification State。*/err = mmc_send_cid(host, cid);if (err)goto err;if (oldcard) {if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {err = -ENOENT;goto err;}card = oldcard;} else {/** Allocate card structure.*//* 调用mmc_alloc_card分配一个mmc_card并进行部分设置 */card = mmc_alloc_card(host, &mmc_type);if (IS_ERR(card)) {err = PTR_ERR(card);goto err;}card->ocr = ocr;card->type = MMC_TYPE_MMC;  // 设置card的type为MMC_TYPE_MMCcard->rca = 1;         // 设置card的RCA地址为1// 将读到的CID存储到card->raw_cid,也就是原始CID值中memcpy(card->raw_cid, cid, sizeof(card->raw_cid));}/** Call the optional HC's init_card function to handle quirks.*/if (host->ops->init_card)host->ops->init_card(host, card);/** For native busses:  set card RCA and quit open drain mode.*//* 设置card RCA地址 */if (!mmc_host_is_spi(host)) {// 设置置该mmc card的关联地址为card->rca,也就是0x0001err = mmc_set_relative_addr(card);if (err)goto free_card;// 设置总线模式为MMC_BUSMODE_PUSHPULLmmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);}/* 从card的csd寄存器以及ext_csd寄存器获取信息并设置到mmc_card的相应成员中 */if (!oldcard) {/** Fetch CSD from card.*/// 求mmc card发送csd寄存器,存储到card->raw_csd中,也就是原始的csd寄存器的值。err = mmc_send_csd(card, card->raw_csd);if (err)goto free_card;// 解析raw_csd,获取到各个bit的值并设置到card->csd中的相应成员上err = mmc_decode_csd(card);if (err)goto free_card;// 解析raw_cid,获取到各个bit的值并设置到card->cid中的相应成员上err = mmc_decode_cid(card);if (err)goto free_card;}/** handling only for cards supporting DSR and hosts requesting* DSR configuration*/if (card->csd.dsr_imp && host->dsr_req)mmc_set_dsr(host);/** Select card, as all following commands rely on that.*/if (!mmc_host_is_spi(host)) {// 发送CMD7指令,SELECT/DESELECT CARD// 这时卡进入transfer state// 后续可以通过各种指令进入到receive-data state// 或者sending-data state依次来进行数据的传输err = mmc_select_card(card);if (err)goto free_card;}if (!oldcard) {/* Read extended CSD. *//* 这里要求处于transfer state的card发送ext_csd寄存器,* 这里获取之后存放在ext_csd寄存器中 * 这里会使card进入sending-data state,完成之后又退出到transfer state。*/ err = mmc_read_ext_csd(card);if (err)goto free_card;/** If doing byte addressing, check if required to do sector* addressing.  Handle the case of <2GB cards needing sector* addressing.  See section 8.1 JEDEC Standard JED84-A441;* ocr register has bit 30 set for sector addressing.*/if (rocr & BIT(30))mmc_card_set_blockaddr(card);/* Erase size depends on CSD and Extended CSD */// 设置card的erase_size,扇区里面的擦除字节数,读出来是512Kmmc_set_erase_size(card);}/* Enable ERASE_GRP_DEF. This bit is lost after a reset or power off. */if (card->ext_csd.rev >= 3) {//当enhanced_area_en 被设置的时候,host需要去设置ext_csd// 寄存器中的EXT_CSD_ERASE_GROUP_DEF位为1err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_ERASE_GROUP_DEF, 1,card->ext_csd.generic_cmd6_time);if (err && err != -EBADMSG)goto free_card;if (err) {err = 0;/** Just disable enhanced area off & sz* will try to enable ERASE_GROUP_DEF* during next time reinit*/card->ext_csd.enhanced_area_offset = -EINVAL;card->ext_csd.enhanced_area_size = -EINVAL;} else {card->ext_csd.erase_group_def = 1;/** enable ERASE_GRP_DEF successfully.* This will affect the erase size, so* here need to reset erase size*/mmc_set_erase_size(card);}}/** Ensure eMMC user default partition is enabled*/if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) {card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;// 设置ext_csd寄存器中的EXT_CSD_CMD_SET_NORMAL位为EXT_CSD_PART_CONFIGerr = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,card->ext_csd.part_config,card->ext_csd.part_time);if (err && err != -EBADMSG)goto free_card;}/** Enable power_off_notification byte in the ext_csd register*/if (card->ext_csd.rev >= 6) {//设置ext_csd寄存器中的EXT_CSD_POWER_OFF_NOTIFICATION位为EXT_CSD_POWER_ONerr = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_POWER_OFF_NOTIFICATION,EXT_CSD_POWER_ON,card->ext_csd.generic_cmd6_time);if (err && err != -EBADMSG)goto free_card;/** The err can be -EBADMSG or 0,* so check for success and update the flag*/if (!err)card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;}/** Select timing interface*/err = mmc_select_timing(card);if (err)goto free_card;if (mmc_card_hs200(card)) {err = mmc_hs200_tuning(card);if (err)goto free_card;err = mmc_select_hs400(card);if (err)goto free_card;} else if (!mmc_card_hs400es(card)) {/* Select the desired bus width optionally *//* 设置mmc总线时钟频率以及位宽 */err = mmc_select_bus_width(card);if (err > 0 && mmc_card_hs(card)) {err = mmc_select_hs_ddr(card);if (err)goto free_card;}}/** Choose the power class with selected bus interface*/mmc_select_powerclass(card);/** Enable HPI feature (if supported)*/if (card->ext_csd.hpi) {err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_HPI_MGMT, 1,card->ext_csd.generic_cmd6_time);if (err && err != -EBADMSG)goto free_card;if (err) {pr_warn("%s: Enabling HPI failed\n",mmc_hostname(card->host));card->ext_csd.hpi_en = 0;err = 0;} else {card->ext_csd.hpi_en = 1;}}/** If cache size is higher than 0, this indicates the existence of cache* and it can be turned on. Note that some eMMCs from Micron has been* reported to need ~800 ms timeout, while enabling the cache after* sudden power failure tests. Let's extend the timeout to a minimum of* DEFAULT_CACHE_EN_TIMEOUT_MS and do it for all cards.*/if (card->ext_csd.cache_size > 0) {unsigned int timeout_ms = MIN_CACHE_EN_TIMEOUT_MS;timeout_ms = max(card->ext_csd.generic_cmd6_time, timeout_ms);err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,EXT_CSD_CACHE_CTRL, 1, timeout_ms);if (err && err != -EBADMSG)goto free_card;/** Only if no error, cache is turned on successfully.*/if (err) {pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",mmc_hostname(card->host), err);card->ext_csd.cache_ctrl = 0;err = 0;} else {card->ext_csd.cache_ctrl = 1;}}/** In some cases (e.g. RPMB or mmc_test), the Command Queue must be* disabled for a time, so a flag is needed to indicate to re-enable the* Command Queue.*/card->reenable_cmdq = card->ext_csd.cmdq_en;if (!oldcard)host->card = card;return 0;free_card:if (!oldcard)mmc_remove_card(card);
err:return err;
}

主要工作:

(1)根据协议初始化mmc type card,使其进入相应状态(standby state)
(2)为mmc type card构造对应mmc_card并进行设置
(3)从card的csd寄存器以及ext_csd寄存器获取card信息并设置到mmc_card的相应成员中
(4)根据host属性以及一些需求修改ext_csd寄存器的值
(5)设置mmc总线时钟频率以及位宽

4. mmc_ops结构体

static const struct mmc_bus_ops mmc_ops = {.remove = mmc_remove,.detect = mmc_detect,.suspend = mmc_suspend,.resume = mmc_resume,.runtime_suspend = mmc_runtime_suspend,.runtime_resume = mmc_runtime_resume,.alive = mmc_alive,.shutdown = mmc_shutdown,.reset = mmc_reset,
};

5. mmc ops.c文件接口说明

mmc_ops.c提供了部分和mmc type card协议相关操作,这些操作会在mmc.c中mmc的初始化过程中被使用到。
      这些操作都会发起mmc请求,因此会调用mmc core主模块的mmc请求API,会在mmc core中进行说明。
      建议先简单了解一下mmc协议的内容。后续会进行总结。

5.1 mmc_send_status(典型)

int mmc_send_status(struct mmc_card *card, u32 *status)
{return __mmc_send_status(card, status, MMC_CMD_RETRIES);
}int __mmc_send_status(struct mmc_card *card, u32 *status, unsigned int retries)
{int err;struct mmc_command cmd = {};/* 主要是根据对应命令构造struct mmc_command */// 设置命令操作码opcode,这里设置为MMC_SEND_STATUS,也就是CMD13cmd.opcode = MMC_SEND_STATUS;if (!mmc_host_is_spi(card->host))cmd.arg = card->rca << 16;  // 设置命令的对应参数,这里设置为card的RCA地址// 设置请求的一些标识,包括命令类型,response类型等等cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;/* 调用mmc_wait_for_cmd发送命令请求并且等待命令处理完成。 */err = mmc_wait_for_cmd(card->host, &cmd, retries);if (err)return err;/* NOTE: callers are required to understand the difference* between "native" and SPI format status words!*//* 对response的处理 */if (status)// 依照协议,response[0]存储了status,因此这里提取cmd.resp[0]*status = cmd.resp[0];return 0;
}

mmc_go_idle、mmc_select_card、mmc_all_send_cid、mmc_set_relative_addr、     mmc_send_cxd_native等等的实现方法和其类似。主要差异在于命令的构造区别以及对response的数据的处理。

5.2 mmc_send_op_cond(特殊)

发送CMD1指令,SEND_OP_COND
       在idle状态时,向卡传送Host支持的电压范围,卡回复OCR的值以及上电复位的状态。如果发送的电压参数为0,则卡仅传回OCR的值,并不进行判断。如果发送的电压参数存在,则和卡本身的OCR对比,若不符合,则卡进入Inactive State,符合,则返回OCR寄存器的值。
       其实和典型的接口类似,但是其特殊之处在于需要通过busy位(bit31)来判断card的上电复位过程是否完成,如果没有完成的话需要重复发送。

int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{struct mmc_command cmd = {};int i, err = 0;/* 主要是根据对应命令构造struct mmc_command */cmd.opcode = MMC_SEND_OP_COND;cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;/* 需要判断status的busy(bit31)来判断上电复位是否完成,如果没有完成的话需要重复发送。 */for (i = 100; i; i--) {err = mmc_wait_for_cmd(host, &cmd, 0);if (err)break;/* if we're just probing, do a single pass */// ocr为0,说明只是读取ocr寄存器的值,不进行判断if (ocr == 0)break;/* otherwise wait until reset completes */// 如果发送的电压参数存在,则和卡本身的OCR对比,// 若不符合,则卡进入Inactive State,符合,则返回OCR寄存器的值。// 同时,需要判断OCR寄存器的busy位来判断上电复位是否完成if (mmc_host_is_spi(host)) {if (!(cmd.resp[0] & R1_SPI_IDLE))break;} else {if (cmd.resp[0] & MMC_CARD_BUSY)break;}err = -ETIMEDOUT;mmc_delay(10);}if (rocr && !mmc_host_is_spi(host))*rocr = cmd.resp[0];return err;
}

5.3 mmc_switch

发送CMD6命令,MMC_SWITCH
      用于设置ext_csd寄存器的某些bit。
      特殊之处在于:在__mmc_switch中会发起CMD6命令,会导致card进入programming state,因此,在__mmc_switch中必须去获取card的status,直到card退出programming state。这部分就是通过CMD13来实现的。

/*** __mmc_switch - modify EXT_CSD register* @card: the MMC card associated with the data transfer* @set: cmd set values*  @index: EXT_CSD register index*    @value: value to program into EXT_CSD register*    @timeout_ms: timeout (ms) for operation performed by register write,*                   timeout of zero implies maximum possible timeout*  @timing: new timing to change to*  @use_busy_signal: use the busy signal as response type*    @send_status: send status cmd to poll for busy*    @retry_crc_err: retry when CRC errors when polling with CMD13 for busy**   Modifies the EXT_CSD register for selected card.*/
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,unsigned int timeout_ms, unsigned char timing,bool use_busy_signal, bool send_status,    bool retry_crc_err)
{struct mmc_host *host = card->host;int err;struct mmc_command cmd = {};bool use_r1b_resp = use_busy_signal;unsigned char old_timing = host->ios.timing;mmc_retune_hold(host);/** If the cmd timeout and the max_busy_timeout of the host are both* specified, let's validate them. A failure means we need to prevent* the host from doing hw busy detection, which is done by converting* to a R1 response instead of a R1B.*/if (timeout_ms && host->max_busy_timeout &&(timeout_ms > host->max_busy_timeout))use_r1b_resp = false;cmd.opcode = MMC_SWITCH;cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |(index << 16) |(value << 8) |set;cmd.flags = MMC_CMD_AC;if (use_r1b_resp) {cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;/** A busy_timeout of zero means the host can decide to use* whatever value it finds suitable.*/cmd.busy_timeout = timeout_ms;} else {cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;}if (index == EXT_CSD_SANITIZE_START)cmd.sanitize_busy = true;/* 调用mmc_wait_for_cmd发送命令请求并且等待命令处理完成。 */err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);if (err)goto out;/* No need to check card status in case of unblocking command */if (!use_busy_signal)goto out;/*If SPI or used HW busy detection above, then we don't need to poll. */if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||mmc_host_is_spi(host))goto out_tim;/* Let's try to poll to find out when the command is completed. *//* 调用mmc_send_status发送CMD13获取card status,等待card退出programming state。 */err = mmc_poll_for_busy(card, timeout_ms, send_status, retry_crc_err);if (err)goto out;out_tim:/* Switch to new timing before check switch status. */if (timing)mmc_set_timing(host, timing);if (send_status) {err = mmc_switch_status(card);if (err && timing)mmc_set_timing(host, old_timing);}
out:mmc_retune_release(host);return err;
}

Linux内核4.14版本——mmc core(4)——card相关模块(mmc type card)相关推荐

  1. Linux内核4.14版本:ARM64的内核启动过程(二)——start_kernel

    目录 1. rest_init 2. init 进程(kernel_init) 2.1 kernel_init_freeable 2.1.1 do_basic_setup 2.1.2 prepare_ ...

  2. Linux内核4.14版本——mmc core(3)——host模块

    1. 前言 2. MMC host驱动介绍 3. 主要数据结构 3.1 struct mmc_host 3.2 struct mmc_host_ops 3.2.1 数据传输有关的函数 3.2.2 总线 ...

  3. Linux内核4.14版本——mmc框架_软件总体架构

    目录 1. 前言 2. 软件架构 3. 工作流程 4. mmc设备 4.1 mmc type card 4.2 sd type card 4.3 sdio type card 5. mmc协议 5.1 ...

  4. Linux内核4.14版本——mmc core(7)——mmc core主模块(3)总线io setting相关(struct mmc_ios)

    目录 1. mmc_ios说明 2. mmc_set_ios 3. mmc_set_bus_mode & mmc_set_bus_width 1. mmc_ios说明 struct mmc_i ...

  5. Linux内核4.14版本——mmc core(10)——mmc core主模块(6)mmc请求相关

    目录 1. 数据结构说明 1.1 struct mmc_command 1.2 struct mmc_data 1.3 struct mmc_request 1.4 struct mmc_async_ ...

  6. Linux内核4.14版本——watchdog看门狗框架分析

    目录 0 简介 1. 设备的注册 1.1 dw_wdt_drv_probe 1.2 watchdog_register_device 1.3 __watchdog_register_device 1. ...

  7. Linux内核4.14版本——DMA Engine框架分析(6)-实战(测试dma驱动)

    1. dw-axi-dmac驱动 2. dma的测试程序 2.1 内核程序 2.2 用户测试程序 1. dw-axi-dmac驱动 dw-axi-dmac驱动4.14版本没有,是从5.4版本移植的,基 ...

  8. Linux内核4.14版本——alsa框架分析(1)—alsa简介

    目录 一,ALSA声音编程介绍 二,ALSA历史 三,数字音频基础 四,ALSA基础 五,ALSA体系结构 六,设备命名 七,声音缓存和数据传输 八,Over and Under Run 九,一个典型 ...

  9. Linux内核4.14版本——drm框架分析(1)——drm简介

    目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...

最新文章

  1. dom元素滚动条高度 js_DOM 事件与事件委托
  2. Python 学习笔记: 反射
  3. linux下怎么卸载ogg,OGG在Linux上的安装
  4. [django]模板中自定义变量django模板中的变量
  5. pytorch loss function 总结
  6. 剖析 .NET 托管提供程序
  7. 【数学基础】拉格朗日对偶
  8. Qt自定义数据类型注册meta-object system
  9. 接口报Provisional headers are shown原因和解决方法
  10. .net 使用阿里云RocketMQ
  11. 在matlab中使用spm8,在matlab中同时使用spm2,spm5,spm8
  12. Linux相关图解随记
  13. 利用边缘监督信息加速Mask R-CNN实例分割训练
  14. ofo 押金被强制理财?黄章回应 OPPO 涉嫌抄袭;三星华为折叠手机重名 | 极客头条...
  15. [CF1073E]Segment Sum
  16. ChainOfResponsibilityPattern(23种设计模式之一)
  17. Java程序员必读的10本书籍
  18. 一键快速打开IE的Internet选项->连接->局域网设置
  19. OpenAI注册(ChatGPT)
  20. 少儿python教学_如何教少儿学习Python编程

热门文章

  1. python报考软考哪个比较好_软考考什么比较好?
  2. 北京科技大学 数值计算方法实验代码
  3. 机器学习实战2(决策树篇)
  4. root高级权限怎么弄,root高级权限怎么打开
  5. 微信小程序实现获取当前系统时间
  6. matlab uint8 小数,matlabuint8什么意思
  7. text to image(一):《GENERATING IMAGES FROM CAPTIONS WITH ATTENTION》
  8. QML基础:锚anchors
  9. 如何让novnc/websockify支持tls1.2 (by quqi99)
  10. 青海行--(7月28日)凯旋归程