Linux MTD 下获取Nand flash 各个参数的过程的详细解析
version: 1.0
date:20090728
Author :crifan
Mail:green-waste(At)163.com
下面是 Linux MTD 中,获取 nand flash 型号 , 各个参数,以及硬件特性的函数,其实也就是
nand_get_flash_type ,下面对其详细解析:
【看此文之前,一些有必要先解释的术语】
1. Program( 编程 ) :此处的编程,不是写软件,写代码,而是对于硬件来说的,可以理解为对硬件编程,只不过其工具是硬件内部的逻辑,而不是你用的软件。对 Nand Flash 的编程,本质上就是实现写操作,将数据写到 Nand Flash 里面去,所以对于 nand flash ,可以简单的理解为 program 编程= write 写(数据)。
2. Datasheet( 数据手册 ) :这个词,本来没啥好说的,接触多了,自然就知道了。但是对于和我类似,最开始接触的时候,就是没搞懂这个词的具体含义。其中文翻译,一般称作,数据手册,意思就是,一个关于描述硬件各个硬件特性,参数以及 / 或者如何操作,如何使用的文档。
3. Erasesize / Writesize :这个是 Linux MTD 中,关于块大小和页大小的别名,第一次见到的时候,把我搞糊涂了,后来才慢慢明白的。因为, nand 操作的写基本单位页,所以, writesize ,对应的就是 pagesize ,页大小。而擦除操作的基本单位是 blocksize ,块大小,所以也叫它 erasesize 。在此简单提一下这几个名词,方便和我遇到类似问题的朋友。
4. Spare Area / Redundant Area / OOB : nand flash 中每一页对应一块区域,用于存放校验的 ECC 数据和其他一些信息,比如上层文件系统放的和自己文件系统相关的数据。这个区域,在 Linux MTD 相关系统中,被称作 oob ( out of band ),可以翻译为带外,也就是 nand flash 的一个页,可以称作一个 band , band 之外,对应的就是指那个多出来的,特殊的区域了。而 nand flash 的 datasheet 中,一般成为 spare area ,可译为空闲区域,另外,在 ID 的含义解释中也叫做 redundant area ,可译为冗余区域,归根结底,都是一个含义。不要被搞糊涂了就好。
5. Page Register( 页寄存器 ) : nand flash 硬件中的一块地方,名字叫做 register ,实际就是一个数据缓存,一个 buffer ,用于存放那些从 flash 读出来或者将要写入到 flash 中的。其实叫做页缓存,更合适,更容易明白其含义。此页寄存器的大小=页大小 + oob 大小,即 pagesize+oob ,对于常见的页是 2KB 的,此页寄存器就是 2KB+64=2112 字节。
6. Chip 和 Plane :对于 chip ,其实任何某个型号的 flash ,都可以称其是一个 chip ,但是实际上,此处的 chip ,是针对内部来说的,也就是某型号的 flash ,内部有几个 chip ,比如下面会举例说到的,三星的 2GB 的 K9WAG08U1A 芯片(可以理解为外部芯片 / 型号)内部装了 2 个单片是 1GB 的 K9K8G08U0A ,此时就称 K9WAG08U1A 内部有 2 个 chip ,而有些单个的 chip ,内部又包含多个 plane ,比如上面的 K9K8G08U0A 内部包含 4 个单片是 2Gb 的 Plane 。只有搞清楚了此处的 chip 和 plane 的关系,才能明白后面提到的多页( Multi Plane / Multi Page )编程和交互( interleave )编程的含义。
7. 编程 (Program) : 此处的编程,不是写软件,写代码,而是对于硬件来说的,可以理解为对硬件编程,只不过其工具是硬件内部的逻辑,而不是你用的软件。对 Nand Flash 的编程,本质上就是实现写操作,将数据写到 Nand Flash 里面去,所以对于 nand flash ,可以简单的理解为 program 编程= write 写(数据)。
详细代码可以在这里找到:
linux/ drivers/ mtd/ nand/ nand_base.c
2407 /*
2408 * Get the flash and manufacturer id and lookup if the type is supported
2409 */
2410 static struct nand_flash_dev * nand_get_flash_type (struct mtd_info * mtd ,
2411 struct nand_chip * chip ,
2412 int busw , int * maf_id )
2413 {
2414 struct nand_flash_dev * type = NULL ;
2415 int i , dev_id , maf_idx ;
/* 选中芯片,才能对其操作。 */
2418 /* Select the device */
2419 chip -> select_chip ( mtd , 0);
2421 /*
2422 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
2423 * after power-up
2424 */
2425 chip -> cmdfunc ( mtd , NAND_CMD_RESET , -1, -1);
/* 发送 ReadID 的命令: 0x90 ,去驱动芯片的 ID 信息 */
2427 /* Send the command for reading device ID */
2428 chip -> cmdfunc ( mtd , NAND_CMD_READID , 0x00, -1);
/* 根据 datasheet 中的定义,第一个字节,简称 byte1 ,是生产厂商的信息,不同的厂商,对应不同的数字。而 byte2 是芯片类型,不同的 nand flash 芯片,对应不同的设备 ID ,也就是一个字节的数字。
关于读取出来的 ID 的具体含义,可以参考三星 K9K8G08U0A 的 datasheet 中解释:
图 1.Nand Flash 读取出来的各个 ID 的含义
*/
2430 /* Read manufacturer and device IDs */
2431 * maf_id = chip -> read_byte ( mtd );
2432 dev_id = chip -> read_byte ( mtd );
2434 /* Try again to make sure, as some systems the bus-hold or other
2435 * interface concerns can cause random data which looks like a
2436 * possibly credible NAND flash to appear. If the two results do
2437 * not match, ignore the device completely.
2438 */
/* 再次发送 ReadID 命令,其目的,上面注释代码中说了,有些特殊的系统中,第一次读取的信息,看起来是很正常,但是实际是错的,所以这里读两次,正常的设备,肯定都会一样的,如果两次不一样,那么说明设备有问题,也就直接函数返回了。 */
2440 chip -> cmdfunc ( mtd , NAND_CMD_READID , 0x00, -1);
2442 /* Read manufacturer and device IDs */
2444 tmp_manf = chip -> read_byte ( mtd );
2445 tmp_id = chip -> read_byte ( mtd );
2447 if ( tmp_manf != * maf_id || tmp_id != dev_id ) {
2448 printk ( KERN_INFO "%s: second ID read did not match "
2449 "%02x,%02x against %02x,%02x/n ", __func__ ,
2450 * maf_id , dev_id , tmp_manf , tmp_id );
2451 return ERR_PTR (- ENODEV );
2452 }
/* 下面根据读取出来的 flash ID ,也就是具体 flash 芯片,或叫做设备 ID ,不同的数值,对应不同的容量和物理参数的 flash 。
其中, nand_flash_ids 是个预先定义好的数组,其定义在:
drivers/mtd/nand/nand_ids.c 中,此处简要摘录如下:
/*
* Chip ID list
*
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, options
*
* Pagesize; 0, 256, 512
* 0 get this information from the extended chip ID
+ 256 256 Byte page size
* 512 512 Byte page size
*/
struct nand_flash_dev nand_flash_ids[] = {
。。。。。
/* 4 Gigabit */
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
/* 8 Gigabit */
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
。。。。。
}
而结构体 nand_flash_dev 的定义如下:
include/linux/mtd/nand.h :
/**
* struct nand_flash_dev - NAND Flash Device ID Structure
* @name: Identify the device type
* @id: device ID code
* @pagesize: Pagesize in bytes. Either 256 or 512 or 0
* If the pagesize is 0, then the real pagesize
* and the eraseize are determined from the
* extended id bytes in the chip
* @erasesize: Size of an erase block in the flash device.
* @chipsize: Total chipsize in Mega Bytes
* @options: Bitfield to store chip relevant options
*/
struct nand_flash_dev {
char *name;
int id;
unsigned long pagesize;
unsigned long chipsize;
unsigned long erasesize;
unsigned long options;
};
在结构体数组 nand_flash_ids[] 中,预先定义了,目前所支持的很多类型 Nand Flash 的具体物理参数,主要是上面结构体中的页大小 pagesize ,芯片大小 chipsize ,块大小 erasesize ,而 id 变量表示此类型的芯片,用哪个数字来表示。
下面代码中,通过刚读取到的设备 ID ,去和预先定义好的那个结构体数组 nand_flash_ids[] 中的每一个 ID 去比较,如果相等,那么说明支持此款 nand falsh ,而其他的信息,就可以直接从后面几项中直接获得了。
其中,有个要注意的是,前面代码注释中也解释了,就是如果 pagesize 是 0 ,那么说明关于 pagesize 和其他一些信息,要通过读取额外的 ID 来获得,这也就是待会下面要详细解释的。
而对于旧的一些 nand flash ,在表项中其 pagesize 不是 0 ,就可以直接可以从上面的预定义的表里面获得了。
比如,对于常见的三星的型号为 K9K8G08U0A 的 nand flash ,其设备号是 0xD3 ,找到匹配的表项就是 ;
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
因此也就知道,其容量是 1024MB ,设备相关物理特性是 1GiB 3,3V 8-bit 了。
而关于 pagesize 和块大小 erasesize 此处都是 0 ,就只能另外从后面读取的 ID 中获得了。
*/
2454 /* Lookup the flash id */
2455 for ( i = 0; nand_flash_ids [ i ]. name != NULL ; i ++) {
2456 if ( dev_id == nand_flash_ids [ i ]. id ) {
2457 type = & nand_flash_ids [ i ];
2458 break;
2459 }
2460 }
2463 return ERR_PTR (- ENODEV );
2466 mtd -> name = type -> name ;
/* 此处由于上面表中的 chipsize 是 MB = 2^10 为单位的,所以要左移 20 位,换算成 byte 单位 */
2468 chip -> chipsize = ( uint64_t ) type -> chipsize << 20;
2470 /* Newer devices have all the information in additional id bytes */
2471 if (! type -> pagesize ) {
/* 解释下面代码第三个字节之前,要先把图标帖出来,才更容易看得懂具体的解释:
图 2.Nand Flash 第三个 ID 的具体含义
由表中定义可以看出:
1. Internal Chip Number
意思是,内部芯片有几颗。有些型号的 Nand Flash ,为了实现更高的容量,在芯片内部封装了多个芯片,比如三星的 K9WAG08U1A 容量是 2GB ,内部是装了 2 个单片是 1GB 的 K9K8G08U0A ,对应地,里面要包含 2 个片选 CE1 和 CE2 (均是低电平有效),而 4GB 的 K9NBG08U5A 包含了 4 片的 K9K8G08U0A 。
2. Cell Type : SLC / MLC
bit2&bit3 表示的是芯片的类型,是 SLC 还是某种 MLC 。
Bit2,bit3=0x00 : SLC ,简单说就是内部单个存储单元,存储一位的数据,所能表示的数值只有 0 , 1 ,也就需要两种不同的电压来表示,所以叫做 2 Level 的 Cell 。
Bit2,bit3=0x01/0x10/0x11 : 4 /8/16 Level Cell ,都叫做 MLC ,其含义是内部单个存储单元设计成可以表示多个,即 4/8/16 个不同的电压,对应地,可以表示 2 , 3 , 4 位的数据。
这类的 MLC 的 nand flash ,由于单个存储单元,要存储更多的数据,所以内部结构更复杂,读取和写入数据的逻辑更复杂,相对数据出错的几率也比 SLC 要大。所以,一般 MLC 的使用,都需要检错和纠错能力更强的硬件或软件算法,以保证数据的正确性。软件实现此类的多位数据的检错和纠错的效率相对较低,一般是硬件本身就已经提供此功能。
对应的其为硬件 ECC ,也就是 Linux 内核 MTD 中的 HW_ECC 。
其他关于 SLC/MLC 的更详细解释,感兴趣的可以去看另一个帖子:
【简介】如何编写 linux 下 nand flash 驱动 v1.0 .pdf
http://www.rayfile.com/zh-cn/files/f513b02b-778b-11de-91d4-0014221b798a/
3. Number of Simultaneously Programmed Pages
可以对几个页同时编程 / 写。此功能简单的说就是,一次性地写多个页的数据到对应的不同的页。对应支持此操作的,硬件上必须要有多个 plane ,而每个 plane ,都有一个自己的页寄存器。比如 K9K8G08U0A 有 4 个 plane ,分别叫做, plane0 , plane1 , plane2 , plane3 。
它们共分成 2 组, plane0 和 plane1 , plane2 和 plane3 。如图:
图 3.Nand Flash 中多页编程对应的多个 Plane 的组织架构
在多页编程时候,只能对某一组中的两个 plane 操作,不允许类似于 plane0 和 plane2 或 plane3 一起去做多页编程。以 plane0 和 plane1 为例,在实现具体的编程动作之前,将你要写入的 2 个页的数据,分别写入 plane0 和 plane1 中的页寄存器,然后才能发命令,去实现具体的编程操作。
正是因为多页编程需要底层的多 plane 支持,底层实现的时候,是同时对多个 plane 编程,所以,也被叫做 Multi Plane Program
4. Interleave Program Between Multiple chips
交错,从字面意思就可以看出,此操作涉及对象就不止一个。交错编程,就是对多个 chip ,交错地进行编程,先对一个编程,充分利用第一个编程过程中需要等待的时间,转去操作另一个,以此实现总体效率的提高。
如果支持 Interleave Program 的话,那么前面的 chip number 必然大于 1 。
5. Cache Program
Cache 读:在开始了一次 cache 读之后,在你把数据读出去的这段时间, nand flash 会自动地把下一页的数据读取出来放到页寄存器。
Cache 写:在你写入数据的时候,对应的内存中的数据,不是直接写到页寄存器中,而是到了 cache buffer 中,然后再发 cache 写的命令,此时,数据才从 cache buffer 中,转递到页寄存器中,然后把数据一点点编程到 nand flash ,此时,你可以去利用页编程的时间,去准备下一次的数据,然后依此地写入下一个页。
Cache 读或写,是充分利用了读一页数据出来,或者将一页数据写到 flash 里面去的时间,去准备新的一页的数据,这样就可以实现连续的读或写,大大提高读写效率。
2.SLC/MLC
*/
2473 /* The 3rd id byte holds MLC / multichip data */
2474 chip -> cellinfo = chip -> read_byte ( mtd );
/* 读取 4th ID */
2475 /* The 4th id byte is the important one */
2476 extid = chip -> read_byte ( mtd );
/* 4th ID 的含义,如图
图 4.Nand Flash 第 4 个 ID 的具体含义
( 1 ) Page Size :
如图,页大小,是 bit0 和 bit1 组合起来所表示的。
extid & 0x3 ,就是取得 bit0 和 bit1 的值,而左移 1024 位,是因为上面表中的单位是 KB = 2^10=1024 。此处关于 1024 << (extid & 0x3) 这样的写法,再多说一下。
我之前也是看了很长时间,都没看懂,后来才看懂具体的意思的。
1024 << (extid & 0x3) 其实就是
1024 × ( 1<< (extid & 0x3) )= 前面的 1024 是因为单位是 KB ,而后面的写法,就是 2 的 extid & 0x3 的次方,比如,如果 extid & 0x3 是 3 ,那么, 1<< (extid & 0x3) 就是 1<<3 = 8 ,对应的上面的 8KB 。
*/
2477 /* Calc pagesize */
2478 mtd -> writesize = 1024 << ( extid & 0x3);
(2)Redundant Area Size(byte/512byte) :
前面介绍过了,此处的 oob ,就是 datasheet 中的 redundant area size 就是 linux 中的 oob 大小。
上面表中的意思是, 512 个 byte ,对应 8 还是 16 个字节的 redundant area 。
之所以是 512 字节对应多少个是因为以前的 nand flash 页大小是 512 (除了最早的好像是 256 之外),所以估计是硬件设计就这样设计了, 512 个字节对应多少个冗余的数据用作 oob ,而后来的页大小,对应的是 512 的整数倍,比如 2K , 4K 等,所以,此处,可以按照每个 512 对应几个字节的 oob ,然后再算页大小是 512 的多少倍,即:
此处的 extid & 0x01 算出来的值,对应上面的 8 或 16 ,而 mtd->writesize >> 9 ,其实就是
td->writesize /512 ,到此,才算清楚,为何此处 oob 是这么算的。
*/
2480 /* Calc oobsize */
2481 mtd -> oobsize = (8 << ( extid & 0x01)) * ( mtd -> writesize >> 9);
/*
(3)Block Size :
具体算法很清楚,算出是 64KB 的多少倍,得出总大小。
*/
2483 /* Calc blocksize. Blocksize is multiples of 64KiB */
2484 mtd -> erasesize = (64 * 1024) << ( extid & 0x03);
/*
(4)Organization :
X8/X16 ,表示的是,硬件 I/O 位宽( Bus Width )是 8 位的还是 16 位的。
目前大多数,都是 X8 的。
*/
2486 /* Get buswidth information */
2487 busw = ( extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
2489 } else {
旧的 nand flash 的一些参数,是知道设备 ID 后,可以直接从表中读取出来的。
*/
2490 /*
2491 * Old devices have chip data hardcoded in the device id table
2492 */
2493 mtd -> erasesize = type -> erasesize ;
2494 mtd -> writesize = type -> pagesize ;
2495 mtd -> oobsize = mtd -> writesize / 32;
2496 busw = type -> options & NAND_BUSWIDTH_16 ;
2497 }
/*
根据读取出来的生长厂商的 ID ,去和表中对应项匹配,找到是哪家的 nand flash 芯片。
其中, nand_manuf_ids 和上面 nand_flash_ids 类似,也是个预先定义好的数组,其定义和 nand_flash_ids 同文件:
drivers/mtd/nand/nand_ids.c 中:
/*
* Manufacturer ID list
*/
struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_TOSHIBA, "Toshiba"},
{NAND_MFR_SAMSUNG, "Samsung"},
{NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD"},
{0x0, "Unknown"}
};
比如最对应的宏和结构体定义在:
include/linux/mtd/nand.h 中:
/*
* NAND Flash Manufacturer ID Codes
*/
#define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_SAMSUNG 0xec
#define NAND_MFR_FUJITSU 0x04
#define NAND_MFR_NATIONAL 0x8f
#define NAND_MFR_RENESAS 0x07
#define NAND_MFR_STMICRO 0x20
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
#define NAND_MFR_AMD 0x01
/**
* struct nand_manufacturers - NAND Flash Manufacturer ID Structure
* @name: Manufacturer name
* @id: manufacturer ID code of device.
*/
struct nand_manufacturers {
int id;
char * name;
};
比如我们读到的是,最常见的 0xEC ,对照上面的定义,就是 Samsung ,表示此款 nand flash 是三星家的。
*/
2499 /* Try to identify manufacturer */
2500 for ( maf_idx = 0; nand_manuf_ids [ maf_idx ]. id != 0x0; maf_idx ++) {
2501 if ( nand_manuf_ids [ maf_idx ]. id == * maf_id )
2502 break;
2503 }
/*
检测你的驱动中的关于位宽的定义,适合和硬件所一致。
*/
2505 /*
2506 * Check, if buswidth is correct. Hardware drivers should set
2507 * chip correct !
2508 */
2509 if ( busw != ( chip -> options & NAND_BUSWIDTH_16 )) {
2510 printk ( KERN_INFO "NAND device: Manufacturer ID: "
2511 " 0x%02x, Chip ID: 0x%02x (%s %s)/n ", * maf_id ,
2512 dev_id , nand_manuf_ids [ maf_idx ]. name , mtd -> name );
2513 printk ( KERN_WARNING "NAND bus width %d instead %d bit/n ",
2514 ( chip -> options & NAND_BUSWIDTH_16 ) ? 16 : 8,
2516 return ERR_PTR (- EINVAL );
2517 }
/*
此处计算的 pagesize , blocksize 等的 shift ,是为了后期的对这些值的除操作更加高效而做的。
对于代码中的除操作,如果直接只是 /pagesize ,则没有直接算出其是 2 的多少次方,然后用位移操作,更加高效。因此,此处直接计算好是多少个 shift ,以后的除于 pagesize , blocksize ,就可以直接用对应的位移操作了。
*/
2519 /* Calculate the address shift from the page size */
2520 chip -> page_shift = ffs ( mtd -> writesize ) - 1;
算出 mask ,为了后期保证传入的地址不越界,所以会对其 mask 一下。
*/
2521 /* Convert chipsize to number of pages per chip -1. */
2522 chip -> pagemask = ( chip -> chipsize >> chip -> page_shift ) - 1;
2524 chip -> bbt_erase_shift = chip -> phys_erase_shift =
2525 ffs ( mtd -> erasesize ) - 1;
/*
这段,貌似是新的 kernel 里面新加的,而且把 chip->chipsize 定义换成 uint64_t 了,支持超过 4GB 大小的 nand flash 了,否则,如果你正好是 4GB ,对于旧的代码 chip->chipsize 是 uint32_t 类型的,那么正好就变成 0 了。
此处之所以去 chip->chipsize & 0xffffffff 判断是超过 4GB ,看起来,估计是 ffs 函数最多支持 32 位,所以,没法计算超过此大小的 ffs 。
简单说一下, ffs 是计算一个数值的第一个被置位的位置,全称好像是 find first set bit ,其简单解释如下:
ffs | 查找第一个已设置的位 | int ffs (int x) | x 为要搜索的字。 |
刚百度了一下,好像还有个对应的函数: ffz ,找到第一个 0 的位置,估计就是 find first zero bit 了。呵呵。
而且,这些,好像是 Linux 提供的基本函数库里面的,自己之前都没怎么听说过呢。汗一个先。。。
*/
2526 if ( chip -> chipsize & 0xffffffff)
2527 chip -> chip_shift = ffs ((unsigned) chip -> chipsize ) - 1;
2528 else
2529 chip -> chip_shift = ffs ((unsigned)( chip -> chipsize >> 32)) + 32 - 1;
/*
设置坏块的标记位置。
关于 nand flash 的 small block 和 large block ,据我了解,好像就是对应的 small pagesize 和 large pagesize ,而此处的大小,是针对于旧的 nand flash ,其页大小 pagesize 是 512 ,所以,
Small block 就是页大小是 512B 的 nand flash ,而 larger block 就是新的,页大小大于 512B 的,比如 2KB , 4KB 等的 nand flash 。
下面的宏定义在
include/linux/mtd/nand.h 中:
/*
* Constants for oob configuration
*/
#define NAND_SMALL_BADBLOCK_POS 5
#define NAND_LARGE_BADBLOCK_POS 0
约定俗成的, small block 的 nand ,坏块标记在 byte5 ,而 large block 的 nand flash 在 byte0 。
关于坏块标记,实际情况更复杂些:
【 Nand Flash 坏块标记位置】
对于 2K 页的 nand flash ,标记位置都是页内 oob 开始处,都是非 0xFF 表示坏块,
但是,对于是第几页,不同 nand flash 就有不同的规定了:
有些 nand flash ,是标记在坏块的第一个页(或者是第二个页,这点是考虑到,万一第一个页是坏的,所以才做此规定的。一般都是在第一个页处做标记),比如三星的多数 SLC , Hynix 等;另一些,是在一个块内的最后一页或倒数第二页做此标记,比如 samsung MLC , Numonyx 等。所以,真正比较完整的检查坏块的做法,至少要检测块内第一,第二,倒数第一,倒数第二页,是否是 0xFF ,才能比较全面的判断是否是坏块的。
*/
2531 /* Set the bad block position */
2532 chip -> badblockpos = mtd -> writesize > 512 ?
2533 NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS ;
获得上面 nand id 表中的默认设置的那些 option : LP_OPTIONS (如果是 X16 则是, LP_OPTIONS16 )。
*/
2535 /* Get chip options, preserve non chip based options */
2536 chip -> options &= ~ NAND_CHIPOPTIONS_MSK ;
2537 chip -> options |= type -> options & NAND_CHIPOPTIONS_MSK ;
自动增加页数??? 没太搞懂啥意思,估计是 cache program/read 相关的吧,目前据我了解的,好像页只有 cache program/read ,能和 auto increment pages 有点关系。
*/
2539 /*
2540 * Set chip as a default. Board drivers can override it, if necessary
2541 */
2542 chip -> options |= NAND_NO_AUTOINCR ;
/*
如果有 extentID 且不是三星的 nand flash ,则清除掉上面我们默认设置的那些参数: LP_OPTIONS 。
*/
2544 /* Check if chip is a not a samsung device. Do not clear the
2545 * options for chips which are not having an extended id.
2546 */
2547 if (* maf_id != NAND_MFR_SAMSUNG && ! type -> pagesize )
2548 chip -> options &= ~ NAND_SAMSUNG_LP_OPTIONS ;
/*
一种特殊的 nand flash , AND chip 。所以,也要赋值给特殊的擦除函数。
具体关于此类 nand flash 的介绍,感兴趣的自己参考上面 drivers/mtd/nand/nand_ids.c 中 nand_flash_ids 数组中的解释。
*/
2550 /* Check for AND chips with 4 page planes */
2551 if ( chip -> options & NAND_4PAGE_ARRAY )
2552 chip -> erase_cmd = multi_erase_cmd ;
2553 else
2554 chip -> erase_cmd = single_erase_cmd ;
如果 nand flash 的页大小是大于 512B ,也就是 2KB , 4KB 等新的,被称作 large block 或 largepage 的 nand flash ,此处的 lp ,应该也就是 large page 的缩写。
此类的 nand flash 比旧的,在发送地址的时候,多一个地址周期。具体参考 datasheet 。
*/
2556 /* Do not replace user supplied command function ! */
2557 if ( mtd -> writesize > 512 && chip -> cmdfunc == nand_command )
2558 chip -> cmdfunc = nand_command_lp ;
/*
终于检测完所有需要的信息了。最后打印出 nand flash 的相关信息。
*/
2560 printk ( KERN_INFO "NAND device: Manufacturer ID: "
2561 " 0x%02x, Chip ID: 0x%02x (%s %s)/n ", * maf_id , dev_id ,
2562 nand_manuf_ids [ maf_idx ]. name , type -> name );
/*
活干完了,就可以 return 回家了,呵呵。
*/
2565 }
问题:现在4KB pagesize的flash,很多ID data定义和之前2KB的都不太一样,linux driver在解析ID时会有错误,这个要怎么解决呢?比如H27UAG8T2ATR
ecjtuboy:我是原帖作者。
这个问题,我之前也注意到了,所以,最后实现了一个版本的代码,具体是在 linux_kernel_src/drivers/mtd/nand/nand_base.c中: 这一行:
chip->chipsize = (uint64_t)type->chipsize << 20; 后面,是那个判断芯片信息的算法,更新为如下:
/* Newer devices have all the information in additional id bytes */
if (!type->pagesize)
{ int erase_bits, page_base, block_base, old_50nm, new_40nm; uint8_t id3rd, id4th, id5th, id6th, id7th;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id3rd = chip->read_byte(mtd);
/* The 4th id byte is the important one */
id4th = chip->read_byte(mtd);
id5th = chip->read_byte(mtd);
id6th = chip->read_byte(mtd);
id7th = chip->read_byte(mtd);
/* printk(KERN_INFO " (ID:%02x %02x %02x %02x %02x %02x %02x) ", id1st, id2nd, id3rd, id4th, id5th, id6th, id7th); */
if (nand_is_mlc(chip->cellinfo)) {
/* * MLC: * 50nm has 5 bytes ID, further read ID will periodically output * 40nm has 6 bytes ID */
/* * the 4th byte is not the same meaning for different manufature */
if (NAND_MFR_SAMSUNG == *maf_id)
{
/* samsung MLC chip has several type ID meanings:
(1)50nm serials, such as K9GAG08U0M (2)40nm serials, such as K9LBG08UXD */
/* old 50nm chip will periodically output if read further ID */
old_50nm = (id1st == id6th) && (id2nd == id7th);
/* is 40nm or newer */
new_40nm = id6th & 0x07;
if ((!old_50nm) && new_40nm) {
/* * Samsang