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;
}