糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > Linux内核4.14版本——mmc core(4)——card相关模块(mmc type card)

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

时间:2021-09-16 07:37:39

相关推荐

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

目录

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.1mmc_send_status(典型)

5.2 mmc_send_op_cond(特殊)

5.3mmc_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_MMC0/* MMC card */#define MMC_TYPE_SD1/* SD card */#define MMC_TYPE_SDIO2/* SDIO card */#define MMC_TYPE_SD_COMBO3/* 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.1mmc_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.3mmc_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)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。