、协议流程
1、整体框图
2、框图描述
从上图可以看出:
(1)设备上电或者设备接收到参数为0的CMD0命令,便会进入idle状态
(2)当设备进入idle状态后,通过CMD1(SEND_OP_COND)命令来判断设备是否处于忙状态,而且可以获取到OCR寄存器的值,即可以获取到emmc支持的电压范围,和控制进行比较,判断是否有匹配的电压
CMD1需要重复发送直到返回不忙状态,设备进入ready状态
(3)进入ready状态后,发送广播命令CMD2(ALL_SEND_CID),该命令请求所有设备发送其唯一设备标识(CID)号,所有
未识别的(即处于Ready状态的)设备同时开始串行地发送其CID号,成功后设备进入Identification状态
(4)主机发送CMD3(SET_RELATIVE_ADDR),给改设备一个相对设备地址(RCA),该地址用于数据传输模式,设备一旦接收到RCA,设备就变为Stand-by状态。设备将其输出驱动器从开漏切换到推拉模式。
RCA寄存器模式是0x0001
二、裸驱配置流程
1、SRS11.SRFA ( Software Reset For All) 复位整个芯片,循环等待该bit为0,为0代表复位成功
2、SRS10.BP=1 SRS10.BVS=101(1.8V)
3、SRS13 使能或者SRS14关闭各种中断
4、phy初始化
5、SRS10.EDTW=0 SRS10.DTW=0 配置位宽为1bit
6、SRS11.SDCFSL=(200M/400KHz)配置clk为400Hz
7、开始发送CMD0,延时2ms
8、发送CMD1,根据应答判断OCR的bit31是否busy,1代表空闲,0代表忙,如果忙,再次发送,连续尝试3次
9、发送CMD2,根据应答获取到CID
10、发送CMD3,分配一个rca值,根据应答获取到rca
三、linux的流程
drivers/mmc/host/sdhci-xxxx.c
sdhci_add_host
__sdhci_add_host
host->complete_wq = alloc_workqueue("sdhci", flags, 0);
timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
sdhci_init(host, 0);
sdhci_do_reset(host, SDHCI_RESET_ALL);
sdhci_set_default_irqs(host);
ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,IRQF_SHARED,>mmc_hostname(mmc), host);
ret = mmc_add_host(mmc);
err = device_add(&host->class_dev);
mmc_start_host(host);
mmc_gpiod_request_cd_irq(host)
_mmc_detect_change(host, 0, false);
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay); // 触发mmc_rescan
driver/mmc/core/core.c
subsys_initcall(mmc_init);
__init mmc_init(void)
ret = mmc_register_bus();
bus_register(&mmc_bus_type);
ret = mmc_register_host_class();
class_register(&mmc_host_class);
ret = sdio_register_bus();
bus_register(&sdio_bus_type);
drivers/mmc/core/host.c
drivers/mmc/host/mmci.c
module_amba_driver(mmci_driver);
#define module_amba_driver(__amba_drv) \
module_driver(__amba_drv, amba_driver_register, amba_driver_unregister)
static struct amba_driver mmci_driver = {
...
.probe = mmci_probe,
...
}
static struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.pre_req = mmci_pre_request,
.post_req = mmci_post_request,
.set_ios = mmci_set_ios,
.get_ro = mmc_gpio_get_ro,
.get_cd = mmci_get_cd,
.start_signal_voltage_switch = mmci_sig_volt_switch,
};
mmci_probe
mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
ret = mmci_of_parse(np, mmc);
alias_id = of_alias_get_id(dev->of_node, "mmc");
dev_set_name(&host->class_dev, "mmc%d", host->index);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
timer_setup(&host->retune_timer, mmc_retune_timer, 0);
host->mmc_ops = &mmci_ops;
mmc->ops = &mmci_ops;
host->clk = devm_clk_get(&dev->dev, NULL);
ret = clk_prepare_enable(host->clk);
host->mclk = clk_get_rate(host->clk)
host->base = devm_ioremap_resource(&dev->dev, &dev->res);
host->rst = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
ret = mmc_regulator_get_supply(mmc);
ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq,mmci_irq_thread, IRQF_SHARED,DRIVER_NAME " (cmd)", host);
mmci_dma_setup(host);
mmc_add_host(mmc);
mmc_rescan // 在sdhci_add_host中被触发
if (!mmc_card_is_removable(host) && host->rescan_entered) return
if (mmc_card_is_removable(host) && host->ops->get_cd && host->ops->get_cd(host) == 0) {
mmc_power_off(host);
mmc_release_host(host);
}
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
unsigned int freq = freqs[i];
if (freq > host->f_max) {
if (i + 1 < ARRAY_SIZE(freqs))
continue;
freq = host->f_max;
}
if (!mmc_rescan_try_freq(host, max(freq, host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
mmc_release_host(host);
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
mmc_power_up(host, host->ocr_avail);
mmc_pwrseq_pre_power_on(host);
mmc_delay(host->ios.power_delay_ms);
mmc_pwrseq_post_power_on(host);
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
mmc_delay(host->ios.power_delay_ms); // This delay must be at least 74 clock sizes, or 1 ms,
mmc_hw_reset_for_init(host);
mmc_pwrseq_reset(host);
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
mmc_go_idle(host);
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0
err = mmc_wait_for_cmd(host, &cmd, 0)
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
err = mmc_wait_for_cmd(host, &cmd, 0);
result_pattern = cmd.resp[0] & 0xFF;
/* Order's important: probe SDIO, then SD, then MMC */
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_SD))
if (!mmc_attach_sd(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_MMC))
if (!mmc_attach_mmc(host)) // 见下
return 0;
mmc_power_off(host);
return -EIO;
}
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.hw_reset = _mmc_hw_reset,
};
mmc_attach_mmc
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
host->ios.bus_mode = mode;
mmc_set_ios(host);
err = mmc_send_op_cond(host, 0, &ocr);
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
mmc_delay(10); // ms
cmd.arg = cmd.resp[0] | BIT(30);
}
*rocr = cmd.resp[0];
mmc_attach_bus(host, &mmc_ops);
host->bus_ops = ops;
rocr = mmc_select_voltage(host, ocr);
err = mmc_init_card(host, rocr, NULL);
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
mmc_go_idle(host);
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
err = mmc_send_cid(host, cid);
card = mmc_alloc_card(host, &mmc_type);
card->ocr = ocr;
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
err = mmc_set_relative_addr(card);
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = card->rca << 16;
mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES)
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
err = mmc_send_csd(card, card->raw_csd);
mmc_send_cxd_native(card->host, card->rca << 16,>csd,MMC_SEND_CSD);
err = mmc_decode_csd(card);
err = mmc_decode_cid(card);
err = mmc_select_card(card);
_mmc_select_card(card->host, card);
cmd.opcode = MMC_SELECT_CARD;
cmd.arg = card->rca << 16;
mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES)
err = mmc_read_ext_csd(card);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,
card->ext_csd.part_config,
card->ext_csd.part_time);
err = mmc_select_timing(card);
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
err = mmc_select_hs400es(card);
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)
err = mmc_select_bus_width(card);
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
card->ext_csd.generic_cmd6_time, 0,
false, true);
mmc_set_timing(host, MMC_TIMING_MMC_HS);
err = mmc_switch_status(card, true);
mmc_set_clock(host, card->ext_csd.hs_max_dtr);
val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
val,
card->ext_csd.generic_cmd6_time);
mmc_select_driver_type(card);
val = EXT_CSD_TIMING_HS400 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, 0,
false, true);
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
err = mmc_switch_status(card, true);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
err = mmc_select_hs200(card);
mmc_select_driver_type(card);
err = mmc_select_bus_width(card);
val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time, 0,
false, true);
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
err = mmc_switch_status(card, false);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
err = mmc_select_hs(card);
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
card->ext_csd.generic_cmd6_time, MMC_TIMING_MMC_HS,
true, true);
if (mmc_card_hs200(card)) { // card->host->ios.timing == MMC_TIMING_MMC_HS200;
err = mmc_hs200_tuning(card);
err = host->ops->execute_tuning(host, opcode);
err = mmc_select_hs400(card);
} else if (!mmc_card_hs400es(card)) { // card->host->ios.enhanced_strobe;
err = mmc_select_bus_width(card);
if (err > 0 && mmc_card_hs(card)) { // card->host->ios.timing == MMC_TIMING_SD_HS || card->host->ios.timing == MMC_TIMING_MMC_HS;
err = mmc_select_hs_ddr(card);
bus_width = host->ios.bus_width;
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
ext_csd_bits,
card->ext_csd.generic_cmd6_time,
MMC_TIMING_MMC_DDR52,
true, true);
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
}
if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) {
err = mmc_cmdq_enable(card);
mmc_cmdq_switch(card, true);
}
mmc_release_host(host);
err = mmc_add_card(host->card);
mmc_claim_host(host);