Linux驱动——mmc type card(六)

Linux驱动——mmc type card(六)

备注:
  1. Kernel版本:5.4
  2. 使用工具:Source Insight 4.0
  3. 参考博客:
[mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)

概述

  card相关模块为对应card实现相应的操作,包括初始化操作、以及对应的总线操作集合。负责和对应card协议层相关的东西。
mmc type card相关代码:

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

数据结构

mmc_ops

  struct mmc_bus_ops表示mmc host在总线上的操作集合,由host的card 设备来决定,mmc type card、sd type card相应的操作集合是不一样的。
对应的代码:drivers/mmc/core/mmc.c

static const struct mmc_bus_ops mmc_ops = {
	.remove = mmc_remove,					// 释放mmc type card
	.detect = mmc_detect,					// 检测mmc总线的mmc type card是否拔出
	.suspend = mmc_suspend,					// suspend调mmc总线上的mmc type card,注意不仅仅会使card进入sleep state,还会对clock以及mmc cache进行操作
	.resume = mmc_resume,					// resume上mmc总线上的mmc type card
	.runtime_suspend = mmc_runtime_suspend,
	.runtime_resume = mmc_runtime_resume,	
	.alive = mmc_alive,						// 检测mmc总线上的mmc type card状态是否正常
	.shutdown = mmc_shutdown,
	.hw_reset = _mmc_hw_reset,				// mmc type card的hw reset接口
};

mmc_type

struct device_type mmc_type中为mmc_card定义了很多属性,可以在sysfs中进行查看。

/sys/class/mmc_host/mmc0/mmc0:0001 或者
/sys/bus/mmc/devices/mmc0:0001下可以查看到如下属性 block cid csd date driver enhanced_area_offset enhanced_area_size erase_size fwrev hwrev manfid name oemid power preferred_erase_size prv raw_rpmb_size_mult rel_sectors runtime_pm_timeout serial subsystem type uevent

mmc_type对应实现如下:

static struct attribute *mmc_std_attrs[] = {
	&dev_attr_cid.attr,
	&dev_attr_csd.attr,
	&dev_attr_date.attr,
	&dev_attr_erase_size.attr,
	&dev_attr_preferred_erase_size.attr,
	&dev_attr_fwrev.attr,
	&dev_attr_ffu_capable.attr,
	&dev_attr_hwrev.attr,
	&dev_attr_manfid.attr,
	&dev_attr_name.attr,
	&dev_attr_oemid.attr,
	&dev_attr_prv.attr,
	&dev_attr_rev.attr,
	&dev_attr_pre_eol_info.attr,
	&dev_attr_life_time.attr,
	&dev_attr_serial.attr,
	&dev_attr_enhanced_area_offset.attr,
	&dev_attr_enhanced_area_size.attr,
	&dev_attr_raw_rpmb_size_mult.attr,
	&dev_attr_rel_sectors.attr,
	&dev_attr_ocr.attr,
	&dev_attr_rca.attr,
	&dev_attr_dsr.attr,
	&dev_attr_cmdq_en.attr,
	NULL,
};
ATTRIBUTE_GROUPS(mmc_std);

static struct device_type mmc_type = {
	.groups = mmc_std_groups,
};
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
	card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
	card->raw_csd[2], card->raw_csd[3]);
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
MMC_DEV_ATTR(rev, "0x%x\n", card->ext_csd.rev);
MMC_DEV_ATTR(pre_eol_info, "0x%02x\n", card->ext_csd.pre_eol_info);
MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n",
	card->ext_csd.device_life_time_est_typ_a,
	card->ext_csd.device_life_time_est_typ_b);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
		card->ext_csd.enhanced_area_offset);
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);

核心接口说明

mmc type card匹配相关

mmc_attach_mmc

  提供给mmc core主模块使用,用于绑定card到host bus上(也就是card和host的绑定)。通过mmc_host获取mmc type card信息,初始化mmc_card,并进行部分驱动,最后将其注册到mmc_bus上。

主要工作:

  • 设置总线模式
  • 选择一个card和host都支持的最低工作电压
  • 对于不同type的card,相应mmc总线上的操作协议也可能有所不同。所以需要设置相应的总线操作集合(mmc_host->bus_ops)
  • 初始化card使其进入工作状态(mmc_init_card)
  • 为card构造对应的mmc_card并且注册到mmc_bus中

代码如下:

/*
 * 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_host的总线操作集合mmc_ops */
	// 设置host->bus_ops,也就是会为host的bus选择一个操作集
	mmc_attach_bus(host, &mmc_ops);

	/* 为card选择一个HOST和card都支持的最低电压 */
	if (host->ocr_avail_mmc)
		host->ocr_avail = host->ocr_avail_mmc;// 选择mmc的可用ocr值作为host的ocr_avail值

	/*
	 * We need to get OCR a different way for SPI.
	 */
	// mmc_spi需用不同的方法再次获取ocr
	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 */
	err = mmc_init_card(host, rocr, NULL);
	if (err)
		goto err;

	mmc_release_host(host);
	// 调用到mmc_add_card,将card注册到设备驱动模型中。
	// 这时候该mmc_card就挂在了mmc_bus上,
	// 会和mmc_bus上的block这类mmc driver匹配起来。
	if (err)
		goto remove_card;

	mmc_claim_host(host);
	return 0;

remove_card:
	mmc_remove_card(host->card);
	mmc_claim_host(host);
	host->card = NULL;
err:
	mmc_detach_bus(host);

	pr_err("%s: error %d whilst initialising MMC card\n",
		mmc_hostname(host), err);

	return err;
}

重点说明
(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的时候再说明。

mmc_init_card

  mmc_init_card用于对mmc type card进行实质性的初始化,并为其分配和初始化一个对应的mmc_card。这部分和协议相关,需要先学习一下mmc协议。
主要工作:

  • 根据协议初始化mmc type card,使其进入相应状态(standby state)
  • 为mmc type card构造对应mmc_card并进行设置
  • 从card的csd寄存器以及ext_csd寄存器获取card信息并设置到mmc_card的相应成员中
  • 根据host属性以及一些需求修改ext_csd寄存器的值设置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_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) {
			pr_debug("%s: Perhaps the card was replaced\n",
				mmc_hostname(host));
			err = -ENOENT;
			goto err;
		}

		card = oldcard;
	} else {
		/*
		 * Allocate card structure.
		 */
		/* 调用mmc_alloc_card分配一个mmc_card并进行部分设置 */
		// 为card配分一个struct mmc_card结构体并进行初始化,
		// 在mmc_type中为mmc定义了大量的属性。
		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.
	 */
	if (!mmc_host_is_spi(host)) {
		err = mmc_set_relative_addr(card);
		if (err)
			goto free_card;

		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
	}

	/* 从card的csd寄存器以及ext_csd寄存器获取信息并设置到mmc_card的相应成员中 */
	if (!oldcard) {
		/*
		 * Fetch CSD from card.
		 */
		// 发送CMD9指令,MMC_SEND_CSD
		// 要求mmc card发送csd寄存器,存储到card->raw_csd中,
		// 也就是原始的csd寄存器的值。
		// 此时mmc card还是处于standby state
		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
		// 选择或者断开指定的card
		// 这时卡进入transfer state。
		// 后续可以通过各种指令进入到receive-data state或者sending-data state
		// 依次来进行数据的传输
		err = mmc_select_card(card);
		if (err)
			goto free_card;
	}

	if (!oldcard) {
		/* Read extended CSD. */
		// 发送CMD8指令,SEND_EXT_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);
	}

	/* 根据host属性以及一些需求修改ext_csd寄存器的值 */

	/* Enable ERASE_GRP_DEF. This bit is lost after a reset or power off. */
	if (card->ext_csd.rev >= 3) {
		// 发送CMD6命令,MMC_SWITCH
		// 用于设置ext_csd寄存器的某些bit
		// 当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;
		// 发送CMD6命令,MMC_SWITCH
		// 用于设置ext_csd寄存器的某些bit
		// 设置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) {
		// 发送CMD6命令,MMC_SWITCH
		// 用于设置ext_csd寄存器的某些bit
		// 设置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;
	}

	/* set erase_arg */
	if (mmc_can_discard(card))
		card->erase_arg = MMC_DISCARD_ARG;
	else if (mmc_can_trim(card))
		card->erase_arg = MMC_TRIM_ARG;
	else
		card->erase_arg = MMC_ERASE_ARG;

	/*
	 * Select timing interface
	 */
	// 根据mmc card类型选择时序及时钟速度
	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 */
		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;
		}
	}

	/*
	 * Enable Command Queue if supported. Note that Packed Commands cannot
	 * be used with Command Queue.
	 */
	card->ext_csd.cmdq_en = false;
	if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) {
		err = mmc_cmdq_enable(card);
		if (err && err != -EBADMSG)
			goto free_card;
		if (err) {
			pr_warn("%s: Enabling CMDQ failed\n",
				mmc_hostname(card->host));
			card->ext_csd.cmdq_support = false;
			card->ext_csd.cmdq_depth = 0;
			err = 0;
		}
	}
	/*
	 * 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 (card->ext_csd.cmdq_en && !host->cqe_enabled) {
		err = host->cqe_ops->cqe_enable(host, card);
		if (err) {
			pr_err("%s: Failed to enable CQE, error %d\n",
				mmc_hostname(host), err);
		} else {
			host->cqe_enabled = true;
			pr_info("%s: Command Queue Engine enabled\n",
				mmc_hostname(host));
		}
	}

	if (host->caps2 & MMC_CAP2_AVOID_3_3V &&
	    host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
		pr_err("%s: Host failed to negotiate down from 3.3V\n",
			mmc_hostname(host));
		err = -EINVAL;
		goto free_card;
	}

	if (!oldcard)
		host->card = card;

	return 0;

free_card:
	if (!oldcard)
		mmc_remove_card(card);
err:
	return err;
}

mmc_ops相关函数

static const struct mmc_bus_ops mmc_ops = {
	.remove = mmc_remove,					// 释放mmc type card
	.detect = mmc_detect,					// 检测mmc总线的mmc type card是否拔出
	.suspend = mmc_suspend,					// suspend调mmc总线上的mmc type card,注意不仅仅会使card进入sleep state,还会对clock以及mmc cache进行操作
	.resume = mmc_resume,					// resume上mmc总线上的mmc type card
	.runtime_suspend = mmc_runtime_suspend,
	.runtime_resume = mmc_runtime_resume,	
	.alive = mmc_alive,						// 检测mmc总线上的mmc type card状态是否正常
	.shutdown = mmc_shutdown,
	.hw_reset = _mmc_hw_reset,				// mmc type card的hw reset接口
};
mmc_remove
/*
 * Host is being removed. Free up the current card.
 */
static void mmc_remove(struct mmc_host *host)
{
	mmc_remove_card(host->card);
	host->card = NULL;
}
mmc_alive
/*
 * Card detection - card is alive.
 */
static int mmc_alive(struct mmc_host *host)
{
	return mmc_send_status(host->card, NULL);
}
mmc_detect
/*
 * Card detection callback from host.
 */
/**********************检测mmc总线的mmc type card是否拔出************************/
static void mmc_detect(struct mmc_host *host)
{
	int err;

	// 申请host使用权
	mmc_get_card(host->card, NULL);

	/*
	 * Just check if our card has been removed.
	 */
	/* 检测card是否被拔出 */
	err = _mmc_detect_card_removed(host);

	// 释放host使用权
	mmc_put_card(host->card, NULL);

	/* card确实被拔出,正常释放card */
	if (err) {
		mmc_remove(host);

		mmc_claim_host(host);
		mmc_detach_bus(host);
		mmc_power_off(host);
		mmc_release_host(host);
	}
}
_mmc_hw_reset
static int _mmc_hw_reset(struct mmc_host *host)
{
	struct mmc_card *card = host->card;

	/*
	 * In the case of recovery, we can't expect flushing the cache to work
	 * always, but we have a go and ignore errors.
	 */
	mmc_flush_cache(host->card);

	if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset &&
	     mmc_can_reset(card)) {
		/* If the card accept RST_n signal, send it. */
		mmc_set_clock(host, host->f_init);
		host->ops->hw_reset(host);
		/* Set initial state and call mmc_set_ios */
		mmc_set_initial_state(host);
	} else {
		/* Do a brute force power cycle */
		mmc_power_cycle(host, card->ocr);
		mmc_pwrseq_reset(host);
	}
	return mmc_init_card(host, card->ocr, card);
}

mmc ops相关函数

  • mmc_ops提供了部分和mmc type card协议相关操作,这些操作会在mmc.c中mmc的初始化过程中被使用到。
  • 这些操作都会发起mmc请求,因此会调用mmc core主模块的mmc请求API,会在mmc core中进行说明。
mmc_go_idle
  • 发送CMD0指令,GO_IDLE_STATE
  • 使mmc card进入idle state。
  • 虽然进入到了Idle State,但是上电复位过程并不一定完成了,这主要靠读取OCR的busy位来判断,而流程归结为下一步。
int mmc_go_idle(struct mmc_host *host)
{
	int err;
	struct mmc_command cmd = {};

	/*
	 * Non-SPI hosts need to prevent chipselect going active during
	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
	 *
	 * SPI hosts ignore ios.chip_select; it's managed according to
	 * rules that must accommodate non-MMC slaves which this layer
	 * won't even know about.
	 */
	if (!mmc_host_is_spi(host)) {
		mmc_set_chip_select(host, MMC_CS_HIGH);
		mmc_delay(1);
	}

	cmd.opcode = MMC_GO_IDLE_STATE;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

	err = mmc_wait_for_cmd(host, &cmd, 0);

	mmc_delay(1);

	if (!mmc_host_is_spi(host)) {
		mmc_set_chip_select(host, MMC_CS_DONTCARE);
		mmc_delay(1);
	}

	host->use_spi_crc = 0;

	return err;
}
mmc_send_op_cond
  • 发送CMD1指令,SEND_OP_COND
  • 这里会设置card的工作电压寄存器OCR,并且通过busy位(bit31)来判断card的上电复位过程是否完成,如果没有完成的话需要重复发送。
  • 完成之后,mmc card进入ready state。
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
	struct mmc_command cmd = {};
	int i, err = 0;

	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;
		
		/* 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);

		/*
		 * According to eMMC specification v5.1 section 6.4.3, we
		 * should issue CMD1 repeatedly in the idle state until
		 * the eMMC is ready. Otherwise some eMMC devices seem to enter
		 * the inactive mode after mmc_init_card() issued CMD0 when
		 * the eMMC device is busy.
		 */
		if (!ocr && !mmc_host_is_spi(host))
			cmd.arg = cmd.resp[0] | BIT(30);
	}

	if (rocr && !mmc_host_is_spi(host))
		*rocr = cmd.resp[0];

	return err;
}
mmc_all_send_cid
  • 这里会发送CMD2指令,ALL_SEND_CID
  • 广播指令,使card回复对应的CID寄存器的值。在这里就相应获得了CID寄存器的值了,存储在cid中。完成之后,MMC card会进入Identification State。
int mmc_send_cid(struct mmc_host *host, u32 *cid)
{
	if (mmc_host_is_spi(host))
		return mmc_spi_send_cid(host, cid);

	return mmc_send_cxd_native(host, 0, cid, MMC_ALL_SEND_CID);
}

static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
	int err;
	struct mmc_command cmd = {};

	cmd.opcode = opcode;
	cmd.arg = arg;
	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;

	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
	if (err)
		return err;

	memcpy(cxd, cmd.resp, sizeof(u32) * 4);

	return 0;
}
mmc_set_relative_addr
  • 发送CMD3指令,SET_RELATIVE_ADDR
  • 设置该mmc card的关联地址为card->rca,也就是0x0001完成之后,该MMC card进入standby模式。
int mmc_set_relative_addr(struct mmc_card *card)
{
	struct mmc_command cmd = {};

	cmd.opcode = MMC_SET_RELATIVE_ADDR;
	cmd.arg = card->rca << 16;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;

	return mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
}
mmc_send_csd
  • 发送CMD9指令,MMC_SEND_CSD
  • 要求mmc card发送csd寄存器,存储到card->raw_csd中,也就是原始的csd寄存器的值。此时mmc card还是处于standby state
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
	if (mmc_host_is_spi(card->host))
		return mmc_spi_send_csd(card, csd);

	return mmc_send_cxd_native(card->host, card->rca << 16,	csd,
				MMC_SEND_CSD);
}
mmc_select_card & mmc_deselect_cards
  • 发送CMD7指令,SELECT/DESELECT CARD
  • 选择或者断开指定的card
  • 这时卡进入transfer state。后续可以通过各种指令进入到receive-data state或者sending-data state依次来进行数据的传输
int mmc_select_card(struct mmc_card *card)
{

	return _mmc_select_card(card->host, card);
}

int mmc_deselect_cards(struct mmc_host *host)
{
	return _mmc_select_card(host, NULL);
}


static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
	struct mmc_command cmd = {};

	cmd.opcode = MMC_SELECT_CARD;

	if (card) {
		cmd.arg = card->rca << 16;
		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
	} else {
		cmd.arg = 0;
		cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
	}

	return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
}
mmc_get_ext_csd
  • 发送CMD8指令,SEND_EXT_CSD
  • 这里要求处于transfer state的card发送ext_csd寄存器,这里获取之后存放在ext_csd寄存器中这里会使card进入sending-data state,完成之后又退出到transfer state。
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
{
	int err;
	u8 *ext_csd;

	if (!card || !new_ext_csd)
		return -EINVAL;

	// 判断是否支持ext_csd
	if (!mmc_can_ext_csd(card))
		return -EOPNOTSUPP;

	/*
	 * As the ext_csd is so large and mostly unused, we don't store the
	 * raw block in mmc_card.
	 */
	ext_csd = kzalloc(512, GFP_KERNEL);
	if (!ext_csd)
		return -ENOMEM;

	// 发送cmd8,通过DATA线获取ext_csd数据
	err = mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ext_csd,
				512);
	if (err)
		kfree(ext_csd);
	else
		*new_ext_csd = ext_csd;

	return err;
}
mmc_switch

用于设置ext_csd寄存器的某些bit

/**
 *	__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. Note, some hosts requires R1B,
	 * which also means they are on their own when it comes to deal with the
	 * busy timeout.
	 */
	if (!(host->caps & MMC_CAP_NEED_RSP_BUSY) && timeout_ms &&
	    host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
		use_r1b_resp = false;

	/* 主要是根据对应命令构造struct mmc_command */
	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. */
	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;
}


int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
		unsigned int timeout_ms)
{
	return __mmc_switch(card, set, index, value, timeout_ms, 0,
			true, true, false);
}
EXPORT_SYMBOL_GPL(mmc_switch);

mmc_send_status
  • 发送CMD13命令,MMC_SEND_STATUS
  • 要求card发送自己当前的状态寄存器
int mmc_send_status(struct mmc_card *card, u32 *status)
{
	return __mmc_send_status(card, status, MMC_CMD_RETRIES);
}
EXPORT_SYMBOL_GPL(mmc_send_status);


int __mmc_send_status(struct mmc_card *card, u32 *status, unsigned int retries)
{
	int err;
	struct mmc_command cmd = {};

	/* 主要是根据对应命令构造struct mmc_command */
	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)
		*status = cmd.resp[0];  // 依照协议,response[0]存储了status,因此这里提取cmd.resp[0]

	return 0;
}
mmc_send_cid
  • 发送CMD10命令,MMC_SEND_CID
  • 要求mmc card回复cid寄存器
static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{
	int err;
	struct mmc_command cmd = {};

	cmd.opcode = opcode;
	cmd.arg = arg;
	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;

	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
	if (err)
		return err;

	memcpy(cxd, cmd.resp, sizeof(u32) * 4);

	return 0;
}

int mmc_send_cid(struct mmc_host *host, u32 *cid)
{
	if (mmc_host_is_spi(host))
		return mmc_spi_send_cid(host, cid);

	return mmc_send_cxd_native(host, 0, cid, MMC_ALL_SEND_CID);
}
mmc_sleep
  • 发送CMD5命令,MMC_SLEEP_AWAKE
  • 使card进入或者退出sleep state,由参数决定。关于sleep state是指card的一种状态,具体参考emmc 5.1协议。
static int mmc_sleep(struct mmc_host *host)
{
	struct mmc_command cmd = {};
	struct mmc_card *card = host->card;
	unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
	int err;

	/* Re-tuning can't be done once the card is deselected */
	mmc_retune_hold(host);

	err = mmc_deselect_cards(host);
	if (err)
		goto out_release;

	cmd.opcode = MMC_SLEEP_AWAKE;
	cmd.arg = card->rca << 16;
	cmd.arg |= 1 << 15;

	/*
	 * If the max_busy_timeout of the host is specified, validate it against
	 * the sleep cmd timeout. 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. Note, some hosts requires R1B, which also
	 * means they are on their own when it comes to deal with the busy
	 * timeout.
	 */
	if (!(host->caps & MMC_CAP_NEED_RSP_BUSY) && host->max_busy_timeout &&
	    (timeout_ms > host->max_busy_timeout)) {
		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
	} else {
		cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
		cmd.busy_timeout = timeout_ms;
	}

	err = mmc_wait_for_cmd(host, &cmd, 0);
	if (err)
		goto out_release;

	/*
	 * If the host does not wait while the card signals busy, then we will
	 * will have to wait the sleep/awake timeout.  Note, we cannot use the
	 * SEND_STATUS command to poll the status because that command (and most
	 * others) is invalid while the card sleeps.
	 */
	if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
		mmc_delay(timeout_ms);

out_release:
	mmc_retune_release(host);
	return err;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值