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