MTD 概述
MTD subsystem (stands for Memory Technology Devices) provides an abstraction layer for raw flash devices. It makes it possible to use the same API when working with different flash types and technologies, e.g. NAND, OneNAND, NOR, AG-AND, ECC’d NOR, etc.
MTD subsystem does not deal with block devices like MMC, eMMC, SD, CompactFlash, etc. These devices are not raw flashes but they have a Flash Translation layer inside, which makes them look like block devices. These devices are the subject of the Linux block subsystem, not MTD. Please, refer to this FAQ section for a short list of the main differences between block and MTD devices. And the raw flash vs. FTL devices UBIFS section discusses this in more details.
MTD subsystem has the following interfaces.
- MTD character devices - usually referred to as /dev/mtd0, /dev/mtd1, and so on. These character devices provide I/O access to the raw flash. They support a number of ioctl calls for erasing eraseblocks, marking them as bad or checking if an eraseblock is bad, getting information about MTD devices, etc.
- The sysfs interface is relatively newer and it provides full information about each MTD device in the system. This interface is easily extensible and developers are encouraged to use the sysfs interface instead of older ioctl or /proc/mtd interfaces, when possible. The sysfs interface for the mtd subsystem is documentated in the kernel, and currently can be found at Documentation/ABI/testing/sysfs-class-mtd.
- The /proc/mtd proc file system file provides general MTD information. This is a legacy interface and the sysfs interface provides more information.
MTD subsystem supports bare NAND flashes with software and hardware ECC, OneNAND flashes, CFI (Common Flash Interface) NOR flashes, and other flash types.
Additionally, MTD supports legacy FTL/NFTL “translation layers”, M-Systems’ DiskOnChip 2000 and Millennium chips, and PCMCIA flashes (pcmciamtd driver). But the corresponding drivers are very old and not maintained very much.
- ECC 有三种常用的算法:汉明码(Hamming Code)、RS 码(Reed Solomon Code)和 BCH 码。可以分为硬件ECC和软件ECC。硬件ECC又可以分为:“CPU内置ECC”和“Flash内置ECC”。如果是CPU内置ECC,则得到 ECC 值后,我们需要将原数据包和 ECC 数值都写入 NAND 里面。当原始数据包从 NAND中读取时,ECC 值将重新计算,如果新计算的 ECC 不同于先前写入 NAND 器件的 ECC,那么表明数据在读写过程中发生了错误。汉明码编码算法只能够保证更正单一比特的错误,如果两个或是更多的比特出错,汉明码就无能为力了(可以检测出错误,但无法修正)。不过,一般情况 SLC NAND 器件出现 2bit 及以上的错误非常罕见,所以,使用的汉明码基本上够用。
- NAND FLASH 的 page 由 2 部分组成:数据存储区(data area)和备用区域(spare area),对 MX35LF2G14AC来说,数据存储区大小为 2K 字节,备用区域大小为 64 字节。有效数据一般都是存储在数据存储区(data area)。备用区域(spare area),一般用来存放 ECC(ErrorChecking and Correcting)校验值和坏块标志。
- NAND FLASH 的地址分为三类:块地址(Block Address)、页地址(Page Address)和列地址(Column Address)。CA0~CA11 为列地址(Column Address),用于在一个 Page 内部寻址,如果一个 Page 大小为 2112 字节,需要 12 个地址线寻址;PA0~PA5 为页地址(Page Address),用于在一个 Block 内部寻址,如果 一个 Block 大小为 64 个 Page,需要 6 个地址线寻址;BA6~BA17 为块地址(Block Address),用于块寻址,如果器件有 2048 个 Block,需要11根地址线寻址。一般的,代码中的row_addr是块地址+页地址,column_addr为页内的字节地址
- NAND FLASH 的坏块识别有几种方式:1,NAND 厂家出厂的时候,会在每个 Block 的第一个 page 和第二个 page 的 spare 区的第一个字节写入非 0XFF 的值来表示,我们可以通过这个判断该块是否为坏块;2,通过给每个 Block 写入数值(0XFF/0X00),然后读取出来,判断写入的数据和读取的数据是否完全一样,来识别坏块;3,通过读取数据时,校验 ECC 错误,来识别坏块。
- Nand Flash出厂时就会存在一定数量的坏块。在块擦除前,必需先判断是否是坏块,不能对坏块执行擦除操作,因为块擦除操作有可能会清除坏块标志。
- 在读写Nand Flash时,需要检查坏块,跳过坏块。
- 好块的数据在擦除后,全为FF(包括spare area)。
- 一般的,为了更好的管理坏块,节省重复检查坏块的时间,建议建立一个坏块表,在read/write/erase过程中,通过查询坏块表即确认块的好与坏。
- partial page的概念:一个page的一部分。例如:1个page 2048字节,可以分为4part,每part为512字节。对于spare area也有相似的概念:例如:64Byte的spare area可以分为4part,每part为16字节。
- OOB(out of band)与spare area是同一个概念。spare area与ECC的关系是:spare are负责存放ECC,举例:MX35L2GE4AD0Z4I,如果使能Flash内置ECC,page size:为(2048+64) byte,block size:为(128K+4K) byte;如果禁用Flash内置ECC,page size 扩展为2KB+128B,block size扩展为128KB+8KB per block (for 2Gb),每页多出的64Byte用于存放ECC,如下图所示:

- 读raw数据:直接读取raw数据是Flash存储的原始数据(可能有位反正),不进行任何的ECC操作
- 4bit ECC、8bit ECC 和 8bit/1k ECC、16bit/1k ECC的区别:由于历史原因,用户可能会对 4bit ECC 和 8bit/1K ECC,8bit ECC 和 16bit/1k ECC 产生
混淆。器件说的 4bit ECC 和 8bit ECC 每个纠错单元是 512B,而 HiFMCv100 的 8bit 和16bit 每个纠错单元是 1KB(1KB 指 1KB 数量级,并不是严格意义上的 1024Byte;512B 指 512 数量级,并不是严格的 512Byte,一般是 526Byte)。所以,其实 4bit/512B等价于 8bit/1K,8bit/512B 等价于 16bit/1K。建议我们的开发和支撑人员在跟客户交流 ECC 的时候,把纠错单元的大小也指明,免得造成误解。
HI3516A
uboot
核心数据结构:下面的核心结构主要为了解决统一CPU下有多个不同类型的Flash,每个Flash又有不同的分区方式
struct mtdids { //一个mtdids结构体代表一个flashstruct list_head link; //因为需要管理多个flash,所以需要有一个链表u8 type; /* device type */ //类型常见的有:NAND, OneNAND, NORu8 num; /* device number */ //同一个设备下可以有多个device,因此需要有一个编号,例如:nand0,nand1,nand2u32 size; /* device size */ //flash的存储空间的大小char *mtd_id; /* linux kernel device id */ //唯一的标志名称};struct mtd_device { //一个mtd_device结构体表示一个flashstruct list_head link; //因为需要管理多个flash,所以需要有一个链表struct mtdids *id; /* parent mtd id entry */ // mtd_device与mtdids是意义绑定的关系u16 num_parts; /* number of partitions on this device */ //表示一个flash设备有多少个分区struct list_head parts; /* partitions */ //多个分区需要用链表来维护};struct part_info { //一个结构体表示一个分区struct list_head link;char *name; /* partition name */ //分区的名称u8 auto_name; /* set to 1 for generated name */u32 size; /* total size of the partition */ //分区的大小u32 offset; /* offset within device */ //分区的偏移地址void *jffs2_priv; /* used internaly by jffs2 */u32 mask_flags; /* kernel MTD mask flags */ //只读、可读写分区标志u32 sector_size; /* size of sector */ //块大小struct mtd_device *dev; /* parent device */ //记录parent device,方便知道这个分区属于哪个mtd_device};
下面对于:partition/mtdids/mtdparts环境变量的描述:
Three environment variables are used by the parsing routines:'partition' - keeps current partition identifierpartition := <part-id><part-id> := <dev-id>,part_num'mtdids' - linux kernel mtd device id <-> u-boot device id mappingmtdids=<idmap>[,<idmap>,...]<idmap> := <dev-id>=<mtd-id><dev-id> := 'nand'|'nor'|'onenand'<dev-num><dev-num> := mtd device number, 0...<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)'mtdparts' - partition listmtdparts=mtdparts=<mtd-def>[;<mtd-def>...]<mtd-def> := <mtd-id>:<part-def>[,<part-def>...]<mtd-id> := unique device tag used by linux kernel to find mtd device (mtd->name)<part-def> := <size>[@<offset>][<name>][<ro-flag>]<size> := standard linux memsize OR '-' to denote all remaining space<offset> := partition start offset within the device<name> := '(' NAME ')'<ro-flag> := when set to 'ro' makes partition read-only (not used, passed to kernel)Notes:- each <mtd-id> used in mtdparts must also exist in 'mtddis' mapping- if the above variables are not set defaults for a given target are used
一些示例:
示例1:
1 NOR Flash, with 1 single writable partition:
mtdids=nor0=edb7312-nor 说明:对应一个struct mtdids结构体
mtdparts=mtdparts=edb7312-nor:- 说明:这个会对应一个struct mtd_device和一个struct part_info
示例2:
1 NOR Flash with 2 partitions, 1 NAND with one
mtdids=nor0=edb7312-nor,nand0=edb7312-nand 说明:2个flash,会对应两个struct mtdids结构体
mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) 说明:2个flash,会对应两个struct mtd_device结构体,edb7312-nor会有两个struct part_info,edb7312-nand会有一个struct part_info
示例3:
mtddevnum=0
partition=nand0,0
mtdids=nand0=nand0
mtdparts=mtdparts=nand0:384k@0x140000(IPL0),384k(IPL1),384k(IPL_CUST0),384k(IPL_CUST1),768k(UBOOT0),768k(UBOOT1),256k(ENV),256k(ENV1),128k(KEY_CUST),384k(LOGO),3m(KERNEL),3m(RECOVERY),16m(rootfs),512k(lbcmd),512k(lbflash),512k(lbflash2),512k(lbcfg),512k(lbcfg2),5m(backup),55m(factory),100m(ro),-(rw)
示例4:
mtdids=nand0=hinand
partition=nand0,0
mtddevnum=0
mtddevname=boot
mtdparts=mtdparts=hinand:512K(boot),512K(env),4M(kernel),16M(rootfs),1M(lbcmd),2M(lbflash),2M(lbflash2),80M(factory),-(rwfs)
SSC335DE

