Linux驱动——sd type card(七)

Linux驱动——sd type card(七)

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

概述

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

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

数据结构

mmc_sd_ops

static const struct mmc_bus_ops mmc_sd_ops = {
	.remove = mmc_sd_remove,
	.detect = mmc_sd_detect,
	.runtime_suspend = mmc_sd_runtime_suspend,
	.runtime_resume = mmc_sd_runtime_resume,
	.suspend = mmc_sd_suspend,
	.resume = mmc_sd_resume,
	.alive = mmc_sd_alive,
	.shutdown = mmc_sd_suspend,
	.hw_reset = mmc_sd_hw_reset,
};

sd_type

static struct attribute *sd_std_attrs[] = {
	&dev_attr_cid.attr,
	&dev_attr_csd.attr,
	&dev_attr_scr.attr,
	&dev_attr_ssr.attr,
	&dev_attr_date.attr,
	&dev_attr_erase_size.attr,
	&dev_attr_preferred_erase_size.attr,
	&dev_attr_fwrev.attr,
	&dev_attr_hwrev.attr,
	&dev_attr_manfid.attr,
	&dev_attr_name.attr,
	&dev_attr_oemid.attr,
	&dev_attr_serial.attr,
	&dev_attr_ocr.attr,
	&dev_attr_rca.attr,
	&dev_attr_dsr.attr,
	NULL,
};
ATTRIBUTE_GROUPS(sd_std);

struct device_type sd_type = {
	.groups = sd_std_groups,
};

核心接口说明

sd type card匹配相关

mmc_attach_sd

提供给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_sd_init_card)
  • 为card构造对应的mmc_card并且注册到mmc_bus中

代码如下:

/*
 * Starting point for SD card init.
 */
int mmc_attach_sd(struct mmc_host *host)
{
	int err;
	u32 ocr, rocr;

	WARN_ON(!host->claimed);
	/*
	 * 
	 * 以下部分,连同mmc_rescan_try_freq中的
	 * mmc_go_idle和mmc_send_if_cond一起构成了
	 * “尝试获取一个合适的工作电压” 的任务 
	 */

	// host发送参数为0的ACMD41命令,提取response中的VHS,
	// 得到card支持的工作电压范围
	err = mmc_send_app_op_cond(host, 0, &ocr);
	if (err)
		return err;

	//创建sd_ops,host->bus_ops指向mmc_sd_ops
	mmc_attach_bus(host, &mmc_sd_ops);
	if (host->ocr_avail_sd)
		host->ocr_avail = host->ocr_avail_sd;

	/*
	 * We need to get OCR a different way for SPI.
	 */
	// mmc_spi,则先发送cmd0进入idle模式,再获取ocr
	if (mmc_host_is_spi(host)) {
		mmc_go_idle(host);

		err = mmc_spi_read_ocr(host, 0, &ocr);
		if (err)
			goto err;
	}

	/*
	 * Some SD cards claims an out of spec VDD voltage range. Let's treat
	 * these bits as being in-valid and especially also bit7.
	 */
	ocr &= ~0x7FFF;

	// host选择一个card和host都支持的最低的工作电压,
	// 并将host提供给card的工作电压设置为这个值。
	// 后续就以host->ocr作为工作电压对sd card进行初始化
	rocr = mmc_select_voltage(host, ocr);

	/*
	 * Can we support the voltage(s) of the card(s)?
	 */
	if (!rocr) {
		err = -EINVAL;
		goto err;
	}

	/*
	 * Detect and init the card.
	 */
	/** 上述已经完成了card的识别操作,并且为card选择了一个合适的工作电压 **/
	/** 后续调用mmc_sd_init_card对sd card进行初始化,也就是代码核心 **/
	err = mmc_sd_init_card(host, rocr, NULL);
	if (err)
		goto err;

	mmc_release_host(host);
	// 将sd card添加到device中
	err = mmc_add_card(host->card);
	if (err)
		goto remove_card;

	mmc_claim_host(host);
	return 0;

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

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

	return err;
}
mmc_sd_init_card

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

  • 重新复位,完成card的内部初始化
  • 设置信号电压,包括card和host的设置
  • 获取card的CID值
  • 获取card的RCA值
  • 获取sd card的特殊数据寄存器
  • 切换到transfer state模式
  • 获取sd card的配置寄存器和状态寄存器
  • 读取card 的switch状态,也就是其支持的function
  • 切换总线宽度,包括card和host的设置
  • 选择合适的总线速度模式、驱动强度、以及限流并进行设置,包括card和host的设置
  • 执行tuning操作
/*
 * 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_sd_init_card(struct mmc_host *host, u32 ocr,
	struct mmc_card *oldcard)
{
	struct mmc_card *card;
	int err;
	u32 cid[4];
	u32 rocr = 0;
	bool v18_fixup_failed = false;

	WARN_ON(!host->claimed);
retry:
	/** 在mmc_sd_get_cid中完成如下工作::: **/
	/** 重新复位,完成card的内部初始化 **/
	/** 设置信号电压,包括card和host的设置 **/
	/** 获取card的CID值 **/
	// 调用mmc_sd_get_cid进行复位、内部初始化,
	// 设置信号电压,然后获取CID值,
	// 最终card进入了identification state。
	err = mmc_sd_get_cid(host, ocr, cid, &rocr);// 获取cid和ocr
	if (err)
		return err;

	if (oldcard) {
		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
			pr_debug("%s: Perhaps the card was replaced\n",
				mmc_hostname(host));
			return -ENOENT;
		}

		card = oldcard;
	} else {
		/*
		 * Allocate card structure.
		 */
		card = mmc_alloc_card(host, &sd_type);
		if (IS_ERR(card))
			return PTR_ERR(card);

		card->ocr = ocr;
		card->type = MMC_TYPE_SD;
		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:  get card RCA and quit open drain mode.
	 */
	/** 获取card的RCA值 **/
	if (!mmc_host_is_spi(host)) {
		 // 调用mmc_send_relative_addr发送CMD3命令,要求card回复其RCA值。
		// 一旦card通过response返回这个RCA之后,进入stand-by state
		err = mmc_send_relative_addr(host, &card->rca);
		if (err)
			goto free_card;
	}

	if (!oldcard) {
		// csd寄存器中存储了sd card的一些信息。
		// host发送CMD9命令,要求card回去其CSD寄存器(card specific data)的值
		// 此时card扔处于stand-by state
		err = mmc_sd_get_csd(host, card);
		if (err)
			goto free_card;

		mmc_decode_cid(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.
	 */
	/** 选中sdcard,切换到transfer state模式 **/
	if (!mmc_host_is_spi(host)) {
		// 后续的初始化操作需要在transfer state下进行,
		// 所以需要发送CMD7命令选中对应的card,
		// 将card切换到transfer state
		err = mmc_select_card(card);
		if (err)
			goto free_card;
	}

	/** 获取sd card的配置寄存器和状态寄存器 **/
	/** 读取card 的switch状态,也就是其支持的function **/
	// host发送ACMD51命令,要求card回复其SCR寄存器(SD configuration register)的值
	// host发送ACMD13命令,要求card回复其SSR寄存器(SD status regiter)的值
	// host发送CMD6命令来读取card switch status。
	// 通过card switch status可以得到card支持的总线速度模式以及驱动强度类型。
	err = mmc_sd_setup_card(host, card, oldcard != NULL);
	if (err)
		goto free_card;

	/*
	 * If the card has not been power cycled, it may still be using 1.8V
	 * signaling. Detect that situation and try to initialize a UHS-I (1.8V)
	 * transfer mode.
	 */
	if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) &&
	    mmc_sd_card_using_v18(card) &&
	    host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
		/*
		 * Re-read switch information in case it has changed since
		 * oldcard was initialized.
		 */
		if (oldcard) {
			err = mmc_read_switch(card);
			if (err)
				goto free_card;
		}
		if (mmc_sd_card_using_v18(card)) {
			if (mmc_host_set_uhs_voltage(host) ||
			    mmc_sd_init_uhs_card(card)) {
				v18_fixup_failed = true;
				mmc_power_cycle(host, ocr);
				if (!oldcard)
					mmc_remove_card(card);
				goto retry;
			}
			goto done;
		}
	}

	/** 切换总线宽度,包括card和host的设置 **/
	/** 选择合适的总线速度模式、驱动强度、以及限流并进行设置,包括card和host的设置 **/
	/** 执行tuning操作 **/
	/* Initialization sequence for UHS-I cards */
	if (rocr & SD_ROCR_S18A && mmc_host_uhs(host)) {
		err = mmc_sd_init_uhs_card(card);
		if (err)
			goto free_card;
	} else {
		/*
		 * Attempt to change to high-speed (if supported)
		 */
		// 对于非uhs card来说,直接尝试切换到HS模式
		// 对于非uhs card,不需要切换其信号电压,因为其一直工作在3.3V
		// 也不需要切换其信号驱动类型、执行tuning操作等等
		err = mmc_sd_switch_hs(card); // 发送CMD6命令尝试将card的总线速度模式切换到HS模式
		if (err > 0)
			mmc_set_timing(card->host, MMC_TIMING_SD_HS);// 如果切换成功,将host的总线速度模式也切换到HS模式
		else if (err)
			goto free_card;

		/*
		 * Set bus speed.
		 */
		// 设置时钟为相应总线速度模式下支持的最大频率
		mmc_set_clock(host, mmc_sd_get_max_clock(card));

		/*
		 * Switch to wider bus (if supported).
		 */
		if ((host->caps & MMC_CAP_4_BIT_DATA) &&
			(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
			// 如果需要切换到4bit总线宽度模式,发送ACMD11通知card准备切换到4bit模式
			err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
			if (err)
				goto free_card;
			 // 设置host自身的总线宽度模式
			mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
		}
	}

	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;
	}
done:
	host->card = card;
	return 0;

free_card:
	if (!oldcard)
		mmc_remove_card(card);

	return err;
}
mmc_sd_get_cid

在上面mmc_sd_init_card中被调用。从idle state到identification state的一个过程。
在mmc_sd_get_cid中的重要工作如下:

  • 重新复位,完成card的内部初始化
  • 设置信号电压,包括card和host的设置
  • 获取card的CID值
/*
 * Fetch CID from card.
 */
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{
	int err;
	u32 max_current;
	int retries = 10;
	u32 pocr = ocr;

/** 重新复位,完成card的内部初始化 **/
try_again:
	if (!retries) {
		ocr &= ~SD_OCR_S18R;
		pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
	}

	/*
	 * 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(host);// 发送cmd0,使card进入idle模式

	/*
	 * If SD_SEND_IF_COND indicates an SD 2.0
	 * compliant card and we should set bit 30
	 * of the ocr to indicate that we can handle
	 * block-addressed SDHC cards.
	 */
	// host发送CMD8命令,告诉card,host可以支持SD2.0。
	// card收到CMD8命令之后会使能自己符合SD2.0的一些新功能
	// 同时获取card的ocr值
	err = mmc_send_if_cond(host, ocr);
	if (!err)
		ocr |= SD_OCR_CCS; // 当前代码是支持SDHC和SDXC的处理的,所以这里设置ocr中的HCS位

	/*
	 * If the host supports one of UHS-I modes, request the card
	 * to switch to 1.8V signaling level. If the card has failed
	 * repeatedly to switch however, skip this.
	 */
	if (retries && mmc_host_uhs(host))
		ocr |= SD_OCR_S18R; // 如果host支持UHS模式,那么自然就支持1.8的信号电压的输出了

	/*
	 * If the host can supply more than 150mA at current voltage,
	 * XPC should be set to 1.
	 */
	max_current = sd_get_host_max_current(host);
	if (max_current > 150)
		ocr |= SD_OCR_XPC;

	// host根据host是否支持SDHC来设置ocr的HCS、是否支持1.8V来设置ocr的S18R,
	// 将设置好的ocr作为ACMD41的参数,发送给card。
	// 当ACMD41处理完成值,card就进入到了ready state了。
	err = mmc_send_app_op_cond(host, ocr, rocr);
	if (err)
		return err;

	/*
	 * In case CCS and S18A in the response is set, start Signal Voltage
	 * Switch procedure. SPI mode doesn't support CMD11.
	 */
	/** 设置信号电压,包括card和host的设置 **/
	if (!mmc_host_is_spi(host) && rocr &&
	   ((*rocr & 0x41000000) == 0x41000000)) {
		// 调用mmc_set_uhs_voltage切换信号电压到1.8V
		// 会先发送CMD11来通知card准备切换到信号电压为1.8V的模式下
		// 然后调用host->ops->start_signal_voltage_switch
		// 来切换host输出的信号电压
		err = mmc_set_uhs_voltage(host, pocr);
		if (err == -EAGAIN) { // host读取ACMD41的busy位来判断card的内部初始化是否完成,如果没有完成继续发送ACMD41
			retries--;
			goto try_again;
		} else if (err) {
			retries = 0;
			goto try_again;
		}
	}

	/** 获取card的CID值 **/
	err = mmc_send_cid(host, cid);
	return err;
}
mmc_sd_init_uhs_card

在上面mmc_sd_init_card中被调用。用来初始化uhs card的总线工作模式。
主要工作如下:

  • 切换总线宽度,包括card和host的设置
  • 选择合适的总线速度模式、驱动强度、以及限流并进行设置,包括card和-host的设置
  • 执行tuning操作
/*
 * UHS-I specific initialization procedure
 */
static int mmc_sd_init_uhs_card(struct mmc_card *card)
{
	int err;
	u8 *status;

	if (!(card->csd.cmdclass & CCC_SWITCH))
		return 0;

	status = kmalloc(64, GFP_KERNEL);
	if (!status)
		return -ENOMEM;

	/** 切换总线宽度,包括card和host的设置 **/
	/* Set 4-bit bus width */
	// uhs都是工作在4bit的总线位宽的模式下,
	// 因此,在设置uhs的总线速度模式之前,
	// 必须先切换到4bit总线宽度模式
	err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);// 发送ACMD11,告诉card准备切换的4bit总线宽度模式
	if (err)
		goto out;

	// 切换host的总线宽度为4bit模式
	mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);

	/*
	 * Select the bus speed mode depending on host
	 * and card capability.
	 */
	/** 选择合适的总线速度模式、驱动强度、以及限流并进行设置,包括card和host的设置 **/
	// 根据card和host都支持的总线速度模式,选择一个最佳的总线速度模式
	sd_update_bus_speed_mode(card);

	/* Set the driver strength for the card */
	/ 根据要选择的总线速度模式,切换驱动信号强度
	// 发送CMD6,告诉card准备切换驱动信号强度,命令格式如下:
	// mmc_sd_switch(card, 1, 2, drive_strength, status),属于group2
	// 然后就是设置host的驱动信号强度
	// mmc_set_driver_type(card->host, drive_strength);
	err = sd_select_driver_type(card, status);
	if (err)
		goto out;

	/* Set current limit for the card */
	// 根据要选择的总线速度模式,切换限流值
	// 发送CMD6,告诉card准备切换限流值,命令格式如下:
	// mmc_sd_switch(card, 1, 3, current_limit, status),属于group3
	err = sd_set_current_limit(card, status);
	if (err)
		goto out;

	/* Set bus speed mode of the card */
	// 这里进行总线速度模式的切换,先查询host关于该总线速度模式对应的时序模式
	// card——》总线速度模式,card->sd_bus_speed = host——》时序,host->ios.timing,二者是对应的
	// 发送CMD6,告诉card准备切换总线速度模式,命令格式如下:
	//  mmc_sd_switch(card, 1, 0, card->sd_bus_speed, status),属于group0
	// 然后就是设置host的时序模式以及时钟
	// mmc_set_timing(card->host, timing);
	// mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
	err = sd_set_bus_speed_mode(card, status);
	if (err)
		goto out;

	/*
	 * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
	 * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
	 */
	/** 执行tuning操作 **/
	if (!mmc_host_is_spi(card->host) &&
		(card->host->ios.timing == MMC_TIMING_UHS_SDR50 ||
		 card->host->ios.timing == MMC_TIMING_UHS_DDR50 ||
		 card->host->ios.timing == MMC_TIMING_UHS_SDR104)) {
		err = mmc_execute_tuning(card);

		/*
		 * As SD Specifications Part1 Physical Layer Specification
		 * Version 3.01 says, CMD19 tuning is available for unlocked
		 * cards in transfer state of 1.8V signaling mode. The small
		 * difference between v3.00 and 3.01 spec means that CMD19
		 * tuning is also available for DDR50 mode.
		 */
		if (err && card->host->ios.timing == MMC_TIMING_UHS_DDR50) {
			pr_warn("%s: ddr50 tuning failed\n",
				mmc_hostname(card->host));
			err = 0;
		}
	}

out:
	kfree(status);

	return err;
}
mmc_sd_setup_card
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
	bool reinit)
{
	int err;

	if (!reinit) {
		/*
		 * Fetch SCR from card.
		 */
		// 发送cmd51 获取 card的scr
		err = mmc_app_send_scr(card);
		if (err)
			return err;

		err = mmc_decode_scr(card);
		if (err)
			return err;

		/*
		 * Fetch and process SD Status register.
		 */
		err = mmc_read_ssr(card);
		if (err)
			return err;

		/* Erase init depends on CSD and SSR */
		mmc_init_erase(card);

		/*
		 * Fetch switch information from card.
		 */
		err = mmc_read_switch(card);
		if (err)
			return err;
	}

	/*
	 * For SPI, enable CRC as appropriate.
	 * This CRC enable is located AFTER the reading of the
	 * card registers because some SDHC cards are not able
	 * to provide valid CRCs for non-512-byte blocks.
	 */
	if (mmc_host_is_spi(host)) {
		err = mmc_spi_set_crc(host, use_spi_crc);
		if (err)
			return err;
	}

	/*
	 * Check if read-only switch is active.
	 */
	if (!reinit) {
		int ro = mmc_sd_get_ro(host);

		if (ro < 0) {
			pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
				mmc_hostname(host));
		} else if (ro > 0) {
			mmc_card_set_readonly(card);
		}
	}

	return 0;
}

sd ops相关函数

mmc_app_cmd
//告诉卡,下个命令是特定应用命令,而不是标准命令。
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
{
	int err;
	struct mmc_command cmd = {};

	if (WARN_ON(card && card->host != host))
		return -EINVAL;

	cmd.opcode = MMC_APP_CMD;

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

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

	/* Check that card supported application commands */
	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
		return -EOPNOTSUPP;

	return 0;
}
EXPORT_SYMBOL_GPL(mmc_app_cmd);
mmc_app_set_bus_width
// ACMD6 定义数据总线的宽度(‘00’=1bit,‘10’=4bit)。
// 接受的数据总线定义在 SCR 寄存器中。
int mmc_app_set_bus_width(struct mmc_card *card, int width)
{
	struct mmc_command cmd = {};

	cmd.opcode = SD_APP_SET_BUS_WIDTH;
	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;

	switch (width) {
	case MMC_BUS_WIDTH_1:
		cmd.arg = SD_BUS_WIDTH_1;
		break;
	case MMC_BUS_WIDTH_4:
		cmd.arg = SD_BUS_WIDTH_4;
		break;
	default:
		return -EINVAL;
	}

	return mmc_wait_for_app_cmd(card->host, card, &cmd);
}
mmc_send_app_op_cond
// ACMD41 发送卡的支持信息(HCS),并要求卡通
// 过命令线返回 OCR 寄存器内容。
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
	struct mmc_command cmd = {};
	int i, err = 0;

	cmd.opcode = SD_APP_OP_COND;
	if (mmc_host_is_spi(host))
		cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
	else
		cmd.arg = ocr;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;

	for (i = 100; i; i--) {
		err = mmc_wait_for_app_cmd(host, NULL, &cmd);
		if (err)
			break;

		/* if we're just probing, do a single pass */
		if (ocr == 0)
			break;

		/* otherwise wait until reset completes */
		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 (!i)
		pr_err("%s: card never left busy state\n", mmc_hostname(host));

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

	return err;
}
mmc_send_if_cond
// CMD8 发送 SD 卡接口条件,包含了主机支持的电压信息,
// 并询问卡是否支持。保留位应该设置为 0。
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
	struct mmc_command cmd = {};
	int err;
	static const u8 test_pattern = 0xAA;
	u8 result_pattern;

	/*
	 * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
	 * before SD_APP_OP_COND. This command will harmlessly fail for
	 * SD 1.0 cards.
	 */
	cmd.opcode = SD_SEND_IF_COND;
	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;

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

	if (mmc_host_is_spi(host))
		result_pattern = cmd.resp[1] & 0xFF;
	else
		result_pattern = cmd.resp[0] & 0xFF;

	if (result_pattern != test_pattern)
		return -EIO;

	return 0;
}
mmc_send_relative_addr
// CMD3 通知所有卡发布新 RCA
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
{
	int err;
	struct mmc_command cmd = {};

	cmd.opcode = SD_SEND_RELATIVE_ADDR;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;

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

	*rca = cmd.resp[0] >> 16;

	return 0;
}
mmc_app_send_scr
int mmc_app_send_scr(struct mmc_card *card)
{
	int err;
	struct mmc_request mrq = {};
	struct mmc_command cmd = {};
	struct mmc_data data = {};
	struct scatterlist sg;
	__be32 *scr;

	/* NOTE: caller guarantees scr is heap-allocated */
	// 发送cmd55,切换到APP
	err = mmc_app_cmd(card->host, card);
	if (err)
		return err;

	/* dma onto stack is unsafe/nonportable, but callers to this
	 * routine normally provide temporary on-stack buffers ...
	 */
	scr = kmalloc(sizeof(card->raw_scr), GFP_KERNEL);
	if (!scr)
		return -ENOMEM;

	mrq.cmd = &cmd;
	mrq.data = &data;
	// 发送 CMD15,通过DATA线获取8byte的SCR
	cmd.opcode = SD_APP_SEND_SCR;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;

	data.blksz = 8;
	data.blocks = 1;
	data.flags = MMC_DATA_READ;
	data.sg = &sg;
	data.sg_len = 1;

	sg_init_one(&sg, scr, 8);

	mmc_set_data_timeout(&data, card);

	mmc_wait_for_req(card->host, &mrq);

	card->raw_scr[0] = be32_to_cpu(scr[0]);
	card->raw_scr[1] = be32_to_cpu(scr[1]);

	kfree(scr);

	if (cmd.error)
		return cmd.error;
	if (data.error)
		return data.error;

	return 0;
}
mmc_sd_switch
// 切换命令(CMD6)用于切换或扩展存储卡功能。
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
	u8 value, u8 *resp)
{
	struct mmc_request mrq = {};
	struct mmc_command cmd = {};
	struct mmc_data data = {};
	struct scatterlist sg;

	/* NOTE: caller guarantees resp is heap-allocated */

	mode = !!mode;
	value &= 0xF;

	mrq.cmd = &cmd;
	mrq.data = &data;

	cmd.opcode = SD_SWITCH;
	cmd.arg = mode << 31 | 0x00FFFFFF;
	cmd.arg &= ~(0xF << (group * 4));
	cmd.arg |= value << (group * 4);
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;

	data.blksz = 64;
	data.blocks = 1;
	data.flags = MMC_DATA_READ;
	data.sg = &sg;
	data.sg_len = 1;

	sg_init_one(&sg, resp, 64);

	mmc_set_data_timeout(&data, card);

	mmc_wait_for_req(card->host, &mrq);

	if (cmd.error)
		return cmd.error;
	if (data.error)
		return data.error;

	return 0;
}
mmc_app_sd_status
// ACMD13 发送 SD 状态
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
{
	int err;
	struct mmc_request mrq = {};
	struct mmc_command cmd = {};
	struct mmc_data data = {};
	struct scatterlist sg;

	/* NOTE: caller guarantees ssr is heap-allocated */

	err = mmc_app_cmd(card->host, card);
	if (err)
		return err;

	mrq.cmd = &cmd;
	mrq.data = &data;

	cmd.opcode = SD_APP_SD_STATUS;
	cmd.arg = 0;
	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC;

	data.blksz = 64;
	data.blocks = 1;
	data.flags = MMC_DATA_READ;
	data.sg = &sg;
	data.sg_len = 1;

	sg_init_one(&sg, ssr, 64);

	mmc_set_data_timeout(&data, card);

	mmc_wait_for_req(card->host, &mrq);

	if (cmd.error)
		return cmd.error;
	if (data.error)
		return data.error;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值