硬件方面介绍
emmc的擦写次数是有限的,一般闪存分为三种类型:TLC,MLC,SLC。TLC的擦写次数约都为500~1000次,寿命和速度都不行;MLC的擦写次数是1W次,SLC最好了为10W次。
转载 https://blog.csdn.net/mengxp/article/details/88342310
最近有一个项目,买了几片 镁光、东芝、佰维、江波龙、SK海力士、三星的EMMC
其中东芝、三星、SK海力士的是MLC颗粒,镁光、佰维、江波龙都是TLC
东芝的 64GB 120元/pcs ,镁光 64GB 120元/pcs,样片还是那种LOGO被激光打掉的那种,不知道是什么级别的垃圾
佰维江波龙都很便宜,75元和80元。 SK海力士64G 95元
测下来发现TLC颗粒都有一个问题,在写了一部分之后(30~40%),写入速度从60~70MB/s下降到 0KB~12MB/s 左右。
这应该是和SSD的原理基本一致,一开始主控将芯片从TLC切到SLC模式,写入速度很快,但实际容量变为1/3,随后空间不足了,必须切回TLC模式,此时切换带来的后果就是数据要从SLC复制到TLC,内部的数据读写已经占了很多时间片,因此外部的写入速度降低就是必然。当内部正在搬移数据的时候,芯片会很热,大概50度左右,即使外部并没有任何读写,高温仍然持续很久,直到内部复制完毕。温度降下来后再次对EMMC进行写入,发现速度已经提高。由此依据可证实上述猜测。
而东芝的MLC颗粒则速度一直保持60~70MB/s恒定。
如果是更注重性能的话,还是要选择MLC颗粒的EMMC。
温度方面,东芝的温度最低,50度,SK海力士70度。
一、命令说明
mmc_go_idle
发送CMD0指令,GO_IDLE_STATE
使mmc card进入idle state。
虽然进入到了Idle State,但是上电复位过程并不一定完成了,这主要靠读取OCR的busy位来判断,而流程归结为下一步。
mmc_send_op_cond
发送CMD1指令,SEND_OP_COND
这里会设置card的工作电压寄存器OCR(好像是host也可以通过这条命令获取card支持的电压信息OCR),并且通过busy位(bit31)来判断card的上电复位过程是否完成,如果没有完成的话需要重复发送。
完成之后,mmc card进入ready state。
mmc_all_send_cid
这里会发送CMD2指令,ALL_SEND_CID
广播指令,使card回复对应的CID寄存器的值。在这里就相应获得了CID寄存器的值了,存储在cid中。
完成之后,MMC card会进入Identification State。
mmc_set_relative_addr
发送CMD3指令,SET_RELATIVE_ADDR
设置该mmc card的关联地址RCA(Relative Device Address)为card->rca,也就是0x0001 (似乎是0x10000)
完成之后,该MMC card进入standby模式。
mmc_send_csd
发送CMD9指令,MMC_SEND_CSD
获取CSD信息,要求mmc card发送csd寄存器,存储到card->raw_csd中,也就是原始的csd寄存器的值。
此时mmc card还是处于standby state
mmc_select_card & mmc_deselect_cards
发送CMD7指令,SELECT/DESELECT CARD
选择或者断开指定的card
这时卡进入transfer state。后续可以通过各种指令进入到receive-data state或者sending-data state依次来进行数据的传输
mmc_get_ext_csd
发送CMD8指令,SEND_EXT_CSD
这里要求处于transfer state的card发送ext_csd寄存器,这里获取之后存放在ext_csd寄存器中
这里会使card进入sending-data state,完成之后又退出到transfer state。
mmc_switch
发送CMD6命令,MMC_SWITCH
用于设置ext_csd寄存器的某些bit
mmc_send_status
发送CMD13命令,MMC_SEND_STATUS
要求card发送自己当前的状态寄存器
mmc_send_cid
发送CMD10命令,MMC_SEND_CID
要求mmc card回复cid寄存器
mmc_card_sleepawake
发送CMD5命令,MMC_SLEEP_AWAKE
使card进入或者退出sleep state,由参数决定。
mmc_set_ios
设置card 的clock ,bus_width, timing
__mmc_set_signal_voltage
设置电压
二、初始化顺序
1.power up emmc
2.设置电压,配置阶段为400khz
3.设置位宽为1bit
4.cmd0
5.cmd1循环一直到检测到emmc返回的电源状态稳定
6.cmd2获取cid
7.cmd3 设置rca,如果获取到的数值不正确,可以手动设置为0x01
8.cmd9 获取csd
9.可以重新配置工作速率
10.cmd7 设置为transfer mode
11.cmd8获取额外的csd信息
12.cmd 16设置block len
13.cmd6 设置工作bit模式
14读cmd17.18
15.写cmd24.25
三、EMMC的主机配置
主机的配置最先要注意的就是IO的配置,很多复用的管脚,是否是EMMC的CMD和DATA功能配置
四、 log
15:33:28[ 2.996019] Freeing unused kernel memory: 512K (ffffffc000930000 - ffffffc0009b0000)
15:33:28[ 3.004090] mmc0: starting CMD3 arg 00010000 flags 00000015
15:33:28This script just[ 3.010175] mmc0: starting CMD9 arg 00010000 flags 00000007
15:33:28 mounts and boot[ 3.017587] mmc0: starting CMD7 arg 00010000 flags 00000015
15:33:28s the rootfs!
15:33:28[ 3.024536] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.031715] cyx mmc_init_card R1_STATE_TRAN OK!
15:33:28 [ 3.036488] mmc0: starting CMD8 arg 00000000 flags 000000b5 // failed at here
15:33:28[ 3.052912] mmc0: starting CMD6 arg 03220101 flags 0000049d
15:33:28[ 3.058993] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.065152] mmc0: starting CMD6 arg 03b90101 flags 0000049d
15:33:28[ 3.071238] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.077404] mmc0: starting CMD6 arg 03b70101 flags 0000049d
15:33:28[ 3.083240] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.089105] mmc0: starting CMD8 arg 00000000 flags 000000b5
15:33:28[ 3.094977] mmc0: starting CMD6 arg 03a10101 flags 0000049d
15:33:28[ 3.100810] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.106668] mmc0: starting CMD6 arg 03210101 flags 0000049d
15:33:28[ 3.112501] mmc0: starting CMD13 arg 00010000 flags 00000195
15:33:28[ 3.118384] mmc0: new high speed MMC card at address 0001
15:33:28[ 3.124330] mmcblk0: mmc0:0001 AJTD4R 14.6 GiB
15:33:28[ 3.129157] mmcblk0boot0: mmc0:0001 AJTD4R partition 1 4.00 MiB
15:33:28[ 3.135410] mmcblk0boot1: mmc0:0001 AJTD4R partition 2 4.00 MiB
15:33:28[ 3.141655] mmcblk0rpmb: mmc0:0001 AJTD4R partition 3 4.00 MiB
15:33:28[ 3.147990] mmc0: starting CMD18 arg 00000000 flags 000000b5
15:33:28[ 3.154266] mmc0: starting CMD18 arg 00000008 flags 000000b5
15:33:28[ 3.160517] mmc0: starting CMD18 arg 00000010 flags 000000b5
15:33:28[ 3.166757] mmc0: starting CMD18 arg 00000018 flags 000000b5
驱动代码流程
1. probe->sdhci_pltfm_init-> sdhci_alloc_host -> mmc_alloc_host -> INIT_DELAYED_WORK(&host->detect, mmc_rescan);
sdhci_add_host -> sdhci_setup_host 读取host里寄存器,设置host flag, 然后初始化mmc->caps
-> __sdhci_add_host-> sdhci_init(host, 0); 初始化phy寄存器
->mmc_add_host->创建设备节点,并且_mmc_detect_change(host, 0, false);
2. mmc_rescan->4次mmc_rescan_try_freq->
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
#ifdef CONFIG_MMC_DEBUG
pr_info("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init);
#endif
mmc_power_up(host, host->ocr_avail);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host);
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
* Skip it if we already know that we do not support SDIO commands
*/
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
mmc_go_idle(host);
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
/* 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 int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
mmc_go_idle(host);
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
/*
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
/*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
if (err)
goto free_card;
。。。。。。
}