emmc 总结

  • Post author:
  • Post category:其他



硬件方面介绍

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;

。。。。。。

}