备注:
1. Kernel版本:5.4
2. 使用工具:Source Insight 4.0
3. 参考博客:
[mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)
- Linux驱动——mmc type card(六)
- 概述
- 数据结构
- mmc_ops
- mmc_type
- 核心接口说明
- mmc type card匹配相关
- mmc_attach_mmc
- mmc_init_card
- mmc_ops相关函数
- mmc_remove
- mmc_alive
- mmc_detect
- _mmc_hw_reset
- mmc ops相关函数
- mmc_go_idle
- mmc_send_op_cond
- mmc_all_send_cid
- mmc_set_relative_addr
- mmc_send_csd
- mmc_select_card & mmc_deselect_cards
- mmc_get_ext_csd
- mmc_switch
- mmc_send_status
- mmc_send_cid
- mmc_sleep
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%08xn", card->raw_cid[0], card->raw_cid[1], card->raw_cid[2], card->raw_cid[3]); MMC_DEV_ATTR(csd, "%08x%08x%08x%08xn", card->raw_csd[0], card->raw_csd[1], card->raw_csd[2], card->raw_csd[3]); MMC_DEV_ATTR(date, "%02d/%04dn", card->cid.month, card->cid.year); MMC_DEV_ATTR(erase_size, "%un", card->erase_size << 9); MMC_DEV_ATTR(preferred_erase_size, "%un", card->pref_erase << 9); MMC_DEV_ATTR(ffu_capable, "%dn", card->ext_csd.ffu_capable); MMC_DEV_ATTR(hwrev, "0x%xn", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06xn", card->cid.manfid); MMC_DEV_ATTR(name, "%sn", card->cid.prod_name); MMC_DEV_ATTR(oemid, "0x%04xn", card->cid.oemid); MMC_DEV_ATTR(prv, "0x%xn", card->cid.prv); MMC_DEV_ATTR(rev, "0x%xn", card->ext_csd.rev); MMC_DEV_ATTR(pre_eol_info, "0x%02xn", card->ext_csd.pre_eol_info); MMC_DEV_ATTR(life_time, "0x%02x 0x%02xn", card->ext_csd.device_life_time_est_typ_a, card->ext_csd.device_life_time_est_typ_b); MMC_DEV_ATTR(serial, "0x%08xn", card->cid.serial); MMC_DEV_ATTR(enhanced_area_offset, "%llun", card->ext_csd.enhanced_area_offset); MMC_DEV_ATTR(enhanced_area_size, "%un", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#xn", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#xn", card->ext_csd.rel_sectors); MMC_DEV_ATTR(ocr, "0x%08xn", card->ocr); MMC_DEV_ATTR(rca, "0x%04xn", card->rca); MMC_DEV_ATTR(cmdq_en, "%dn", 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中
代码如下:
int mmc_attach_mmc(struct mmc_host *host)
{
int err;
u32 ocr, rocr;
WARN_ON(!host->claimed);
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
// 发送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;
// 设置host->bus_ops,也就是会为host的bus选择一个操作集
mmc_attach_bus(host, &mmc_ops);
if (host->ocr_avail_mmc)
host->ocr_avail = host->ocr_avail_mmc;// 选择mmc的可用ocr值作为host的ocr_avail值
// 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);
if (!rocr) {
err = -EINVAL;
goto err;
}
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 cardn",
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 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总线时钟频率以及位宽
代码如下:
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);
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
// 发送CMD0指令,GO_IDLE_STATE
// 使mmc card进入idle state。
// 虽然进入到了Idle State,但是上电复位过程并不一定完成了,
// 这主要靠读取OCR的busy位来判断,而流程归结为下一步。
mmc_go_idle(host);
// 发送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;
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto err;
}
// 这里会发送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 replacedn",
mmc_hostname(host));
err = -ENOENT;
goto err;
}
card = oldcard;
} else {
// 为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));
}
if (host->ops->init_card)
host->ops->init_card(host, card);
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);
}
if (!oldcard) {
// 发送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;
}
if (card->csd.dsr_imp && host->dsr_req)
mmc_set_dsr(host);
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) {
// 发送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 (rocr & BIT(30))
mmc_card_set_blockaddr(card);
// 设置card的erase_size,扇区里面的擦除字节数,读出来是512K
mmc_set_erase_size(card);
}
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;
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
} else {
card->ext_csd.erase_group_def = 1;
mmc_set_erase_size(card);
}
}
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;
}
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;
if (!err)
card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
}
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;
// 根据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)) {
err = mmc_select_bus_width(card);
if (err > 0 && mmc_card_hs(card)) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
}
}
mmc_select_powerclass(card);
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 failedn",
mmc_hostname(card->host));
card->ext_csd.hpi_en = 0;
err = 0;
} else {
card->ext_csd.hpi_en = 1;
}
}
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;
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;
}
}
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 failedn",
mmc_hostname(card->host));
card->ext_csd.cmdq_support = false;
card->ext_csd.cmdq_depth = 0;
err = 0;
}
}
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 %dn",
mmc_hostname(host), err);
} else {
host->cqe_enabled = true;
pr_info("%s: Command Queue Engine enabledn",
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.3Vn",
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
static void mmc_remove(struct mmc_host *host)
{
mmc_remove_card(host->card);
host->card = NULL;
}
mmc_alive
static int mmc_alive(struct mmc_host *host)
{
return mmc_send_status(host->card, NULL);
}
mmc_detect
static void mmc_detect(struct mmc_host *host)
{
int err;
// 申请host使用权
mmc_get_card(host->card, NULL);
err = _mmc_detect_card_removed(host);
// 释放host使用权
mmc_put_card(host->card, NULL);
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;
mmc_flush_cache(host->card);
if ((host->caps & MMC_CAP_HW_RESET) && host->ops->hw_reset &&
mmc_can_reset(card)) {
mmc_set_clock(host, host->f_init);
host->ops->hw_reset(host);
mmc_set_initial_state(host);
} else {
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中进行说明。
- 发送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 = {};
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;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
break;
// 如果发送的电压参数存在,则和卡本身的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 (!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;
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
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 (!(host->caps & MMC_CAP_NEED_RSP_BUSY) && 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;
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;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
goto out;
if (!use_busy_signal)
goto out;
if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||
mmc_host_is_spi(host))
goto out_tim;
err = mmc_poll_for_busy(card, timeout_ms, send_status, retry_crc_err);
if (err)
goto out;
out_tim:
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 = {};
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;
err = mmc_wait_for_cmd(card->host, &cmd, retries);
if (err)
return err;
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;
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 (!(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 (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(timeout_ms);
out_release:
mmc_retune_release(host);
return err;
}



