Linux MTD下获取Nand flash各个参数的过程的详细解析

本文详细解析了 Linux MTD 中获取 NAND Flash 型号及各项参数的过程,包括页大小、块大小、OOB 大小等关键信息,并介绍了相关术语如编程、数据手册等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

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 ;

2416         int tmp_id , tmp_manf ;

2417

/* 选中芯片,才能对其操作。 */

2418         /* Select the device */

2419         chip -> select_chip ( mtd , 0);

2420

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);

2426

/* 发送 ReadID 的命令: 0x90 ,去驱动芯片的 ID 信息 */

2427         /* Send the command for reading device ID */

2428         chip -> cmdfunc ( mtd , NAND_CMD_READID , 0x00, -1);

2429

/* 根据 datasheet 中的定义,第一个字节,简称 byte1 ,是生产厂商的信息,不同的厂商,对应不同的数字。而 byte2 是芯片类型,不同的 nand flash 芯片,对应不同的设备 ID ,也就是一个字节的数字。

关于读取出来的 ID 的具体含义,可以参考三星 K9K8G08U0A datasheet 中解释:

Linux MTD下获取Nand flash各个参数的过程的详细解析 - 东东 - work and job

1.Nand Flash 读取出来的各个 ID 的含义

  */

2430         /* Read manufacturer and device IDs */

2431         * maf_id = chip -> read_byte ( mtd );

2432         dev_id = chip -> read_byte ( mtd );

2433

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          */

2439

/* 再次发送 ReadID 命令,其目的,上面注释代码中说了,有些特殊的系统中,第一次读取的信息,看起来是很正常,但是实际是错的,所以这里读两次,正常的设备,肯定都会一样的,如果两次不一样,那么说明设备有问题,也就直接函数返回了。 */

2440         chip -> cmdfunc ( mtd , NAND_CMD_READID , 0x00, -1);

2441

2442         /* Read manufacturer and device IDs */

2443

2444         tmp_manf = chip -> read_byte ( mtd );

2445         tmp_id = chip -> read_byte ( mtd );

2446

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         }

2453

/* 下面根据读取出来的 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         }

2461

2462         if (! type )

2463                 return ERR_PTR (- ENODEV );

2464

2465         if (! mtd -> name )

2466                 mtd -> name = type -> name ;

2467

/* 此处由于上面表中的 chipsize MB 2^10 为单位的,所以要左移 20 位,换算成 byte 单位 */

2468         chip -> chipsize = ( uint64_t ) type -> chipsize << 20;

2469

2470         /* Newer devices have all the information in additional id bytes */

2471         if (! type -> pagesize ) {

2472                 int extid ;

/* 解释下面代码第三个字节之前,要先把图标帖出来,才更容易看得懂具体的解释:

Linux MTD下获取Nand flash各个参数的过程的详细解析 - 东东 - work and job

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 。如图:

Linux MTD下获取Nand flash各个参数的过程的详细解析 - 东东 - work and job

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 的含义,如图

Linux MTD下获取Nand flash各个参数的过程的详细解析 - 东东 - work and job

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);

2479                 extid >>= 2;

/*

(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);

2482                 extid >>= 2;

/*

(3)Block Size

具体算法很清楚,算出是 64KB 的多少倍,得出总大小。

*/

2483                 /* Calc blocksize. Blocksize is multiples of 64KiB */

2484                 mtd -> erasesize = (64 * 1024) << ( extid & 0x03);

2485                 extid >>= 2;

/*

(4)Organization

X8/X16 ,表示的是,硬件 I/O 位宽( Bus Width )是 8 位的还是 16 位的。

目前大多数,都是 X8 的。

*/

2486                 /* Get buswidth information */

2487                 busw = ( extid & 0x01) ? NAND_BUSWIDTH_16 : 0;

2488

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         }

2498

/*

根据读取出来的生长厂商的 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         }

2504

/*

检测你的驱动中的关于位宽的定义,适合和硬件所一致。

*/

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,

2515                        busw ? 16 : 8);

2516                 return ERR_PTR (- EINVAL );

2517         }

2518

/*

此处计算的 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;

2523

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;

2530

/*

设置坏块的标记位置。

关于 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 ;

2534

/*

获得上面 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 ;

2538

/*

自动增加页数??? 没太搞懂啥意思,估计是 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 ;

2543

/*

如果有 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 ;

2549

/*

一种特殊的 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 ;

2555

/*

如果 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 ;

2559

/*

终于检测完所有需要的信息了。最后打印出 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 );

2563

/*

活干完了,就可以 return 回家了,呵呵。

*/

2564         return type ;

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值