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

5.2 mmc_send_op_cond(特殊)

5.3 mmc_switch


1. 简介

1.1 mmc card的几种类型

Linux内核4.14版本——mmc core(4)——card相关模块(mmc type card)_第1张图片

      (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_card
	err = 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);  // 再次申请host
	return 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_MMC
		card->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,也就是0x0001
		err = mmc_set_relative_addr(card);
		if (err)
			goto free_card;

		// 设置总线模式为MMC_BUSMODE_PUSHPULL
		mmc_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,扇区里面的擦除字节数,读出来是512K
		mmc_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位为1
		err = 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_CONFIG
		err = 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_ON
		err = 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,也就是CMD13
	cmd.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;
}

你可能感兴趣的:(emmc模块,linux,mmc)