eMMC
eMMC (Embedded Multi Media Card)是MMC协会订立、主要针对手机或平板电脑等产品的内嵌式存储器标准规格。eMMC在封装中集成了一个控制器,提供标准接口并管理闪存,通常厂家出来的eMMC 主要由这几个部分组成:
1. BOOT Area Partition 1 & 2
2. RPMB (Replay Protected Memory Block) Partition
3. User Data Area
4. Vender private area
eMMC的分区地址和大小有一些是不能修改的(如BOOT1、BOOT2和RPMB分区),User Data Area分区可以通过特定的命令和寄存器就可以修改的(如划分为Enhanced Partition和GPAP)。下面就来具体说明一下:
图 eMMC存储组织结构
Boot分区
两个Boot分区是由厂家在生产过程中配置好了的,并且其大小是不能由软件进行配置的, Boot 1 和Boot 2这两个区域在存储的稳定性、可靠性及擦除次数上都远比UDA要好,所以很多主控芯片上都会使用这两个区域来存放一下关键数据,如boot程序,默认配置参数等等。当然不同的主控芯片的配置方法也不尽相同,这个可以找芯片厂商的工程师询问。
另外,不同的eMMC版本一般对Boot area和RPMB的容量大小需求也不同,如下作一个简单的参考:
RPMB分区
RPMB是Replay Protected Memory Block的缩写,他的存在目的是用来给系统存放一些特殊的、需要进行访问授权的数据;他的请求及回应类型如下所:
UDA分区
User Data Area就是用户可以进行读写存储的区域,通常其大小为整块EMMC表示大小的93%左右,即4GB的eMMC UDA的区域只有4GB*93%=3809MB。
之前说的BOOT1&2、RPMB和UDA区域我们都可以认为他们在物理上是独立的(当然都是存在于同一块flash晶片上),即他们各自的物理起始地址都是0x0,这个在出厂的时候就会设置完成。下面我们说两种可以在物理上进行独立分区的方式。
GPAP
GPAP即General Purpose Area Partitions,eMMC 的spec上定义每个eMMC 最多可以通过配置寄存器来定义4个GPAP。GPAP配置定义完成之后每一个GPAP的起始地址都为0x0;即可以相应地将其认为是独立的一块区域。只是在存放数据的时候会需要从新根据他的起始地址进行计算然后再存储数据。这样必然会增加一定的工作量。当一个设备有多个CPU的时候并且他们的功能还不同时,这个时候使用GPAP这个功能就非常方便了。
Enhanced Partition
我们知道eMMC只是指他的接口标准,而他真正的存储介质还是NAND Flash, 而NAND又分为SLC、MLC和TLC,他们的稳定性、可靠性和擦除次数又有很大区别。目前市场上主流的eMMC还是以MLC的NAND 存储介质为主,而TLC的eMMC也在逐渐的增加,而我们这里的Enhanced Partition的主要功能就是将MLC配置成为SLC,增强eMMC稳定性代价是容量只有原来的一半。
Vender Private Area
在eMMC里面除了主控芯片能操作(即可识别并且可以通过地址进行访问)的boot 1&2、RPMB和UDA之外,其实还有一小部分区域是主控芯片看不见也不能进行操作的。这部分区域是由生产厂家预留的,主要是用来存放这样一些内容:eMMC的固件,eMMC的boot程序,FTL(Flash Translation Layer)以及在厂家生产过程中产生的坏块等等。
eMMC寄存器
eMMC规范定义了OCR, CID, CSD, EXT_CSD, RCA 以及DSR 6组寄存器,这里离线烧写需要配置的是EXT_CSD(扩展寄存器)这组寄存器,用来确定MMC数据总线位宽、启动分区、启动模式等。
Ext_CSD[162]
Ext_CSD[162]表示RST_n信号配置寄存器,默认RST_n_ENABLE值是0表示RST_n信号临时关闭,当烧写工具配置这个寄存器0x1(永久打开)或者0x2(永久关闭)后这个寄存器不能被改写。当RST_n信号关闭后,eMMC将会忽略输入的RST_n信号。
Ext_CSD[177]
Ext_CSD[177]表示启动时MMC总线位宽、速率与模式配置寄存器,这个寄存器是非易失(nonvolatile),离线烧写固化在内部掉电不丢失。这里关键的是BOOT_BUS_WIDTH表示MMC数据总线位宽,若主控芯片与eMMC连接方式与BOOT_BUS_WIDTH不匹配,那离线烧写后eMMC起不来。
Ext_CSD[179]
Ext_CSD[179]表示启动分区选择寄存器,其中PARTITION_ACCESS是配置特殊分区的访问权限,若没有配置,主控芯片启动后无法对这些分区进行读写操作。BOOT_PARTITION_ENABLE是选择从哪个分区启动,这个与主控芯片启动方式有关。
Ext_CSD[167]
Ext_CSD[167]表示写可靠配置寄存器,对应的bit位写1后能够保护之前写入的数据不丢失,但相对的会降低eMMC写操作性能。这个寄存器能够对User Data Area和General Purpose Area Partitions单独配置写可靠功能,修改此寄存器需要将HS_CTRL_REL(Ext_CSD[166],bit0)写1,否则是只读寄存器。
确定配置字方法
在工厂产线上离线烧写eMMC是批量进行的,不同的主控芯片需要写入的配置字是不一样的,一旦写入错误数据,贴片生产后设备无法正常启动,只能使用主控芯片烧写工具手动一台台重新烧写。新的eMMC导入需要与主控芯片厂商、烧录器厂商、eMMC厂商沟通制定烧写方案来配置寄存器,涉及多个厂商效率很低。这里提供一个简单高效的方法快速确定配置字,从内核MMC驱动程序里将Ext_CSD寄存器组整个打印出来,找到对应的数据配置到烧写工具里。
在MMC驱动程序里(drivers/mmc/core/mmc.c),函数mmc_init_card执行流程中会一次将整个Ext_CSD寄存器组读取出来。
static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
//……
/*
* Fetch and process extended CSD.
*/
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
goto free_card;
err = mmc_read_ext_csd(card, ext_csd);
if (err)
goto free_card;
// …
}
在mmc_get_ext_csd执行完成后ext_csd包含了整组寄存器值,简单修改后即可将值打印到调试串口上。
static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
//……
/*
* Fetch and process extended CSD.
*/
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
goto free_card;
{
int i, j;
printk("[ext_csd] data:\n");
for (i=0;i<16;i++) {
for (j=0;j<16;j++)
printk("%02x ", ext_csd[16*i+j]);
printk("\n");
}
}
err = mmc_read_ext_csd(card, ext_csd);
if (err)
goto free_card;
// …
}
设备启动后立即就能获得当前eMMC配置寄存器值。
[20180530_20:56:22]IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[20180530_20:56:22]IP-Config: Gateway not on directly connected network
[20180530_20:56:22][ext_csd] data:
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]39 03 00 80 74 00 00 80 74 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 0a 00 00 01
[20180530_20:56:22]00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00
[20180530_20:56:22]00 00 00 00 00 00 00 00 00 00 00 01 00 a4 03 00
[20180530_20:56:22]07 00 01 00 00 00 15 1f 20 00 00 00 00 00 00 00
[20180530_20:56:22]00 02 00 38 00 00 00 00 01 00 00 00 00 00 00 00
[20180530_20:56:22]08 00 02 00 57 1f 0a 0a aa aa 44 44 00 1e 00 46
[20180530_20:56:22]00 78 00 01 00 00 e9 00 10 14 0a 09 07 01 01 07
[20180530_20:56:22]08 08 20 00 07 ff fb 55 01 00 64 00 bb bb aa 55
[20180530_20:56:22]01 1e 00 00 00 00 00 32 0a 00 10 00 00 cc 03 00
[20180530_20:56:22]mmc0: BKOPS_EN bit is not set
[20180530_20:56:22]: switch to mmc0 failed
[20180530_20:56:22]mmc0: new MMC card at address 0001