这里以u-boot-2009.08版本介绍U-Boot的NAND Flash驱动的实现。要明白U-Boot的NAND Flash驱动的实现,首先要了解NAND Flash的基础知识。

一、NAND Flash简介

关于NAND Flash的介绍,可以参看下面两篇博文:

https://blog.csdn.net/shengnan_wu/article/details/8116861?utm_medium=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control

https://blog.csdn.net/qq_38880380/article/details/78884522

这里根据上面两篇博文,总结一下NAND Flash的关键知识:

1、NAND Flash和NOR Flash主要区别:

(1)NOR Flash是芯片内执行(XIP, eXecute In Place),采用了内存的随机读取技术。这样应用程序可以直接在NOR flash内运行,不必再把代码读到系统RAM中。

NAND Flash没有采取内存的随机读取技术,它的读取是以一次读取一块的形式来进行的。用户不能直接运行 NAND Flash上的代码。

(2)NOR Flash的传输(读取)效率很高,但是写入和擦除速度很慢(擦除64~128KB的块约5秒钟)。NAND Flash的读取速度比NOR Flash稍慢,但是NAND Flash的写入和擦除速度比NOR Flash快很多(擦除8~32KB的块约4ms)。

(3)NAND flash的单元尺寸几乎是NOR器件的一半,由于生产过程更为简单,NAND结构可以在给定的模具尺寸内提供更高的容量,也就相应地降低了价格。NOR flash占据了容量为1~16MB闪存市场的大部分,而NAND flash只是用在8~128MB的产品当中,这也说明NOR主要应用在代码存储介质中,NAND适合于数据存储,NAND在CompactFlash、Secure Digital、PC Cards和MMC存储卡市场上所占份额最大。

(4)NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节。NAND器件使用复杂的I/O口来串行地存取数据,各个产品或厂商的方法可能各不相同。8个引脚用来传送控制、地址和数据信息。

(5)NAND Flash中每个块的最大擦写次数是一百万次,而NOR Flash的擦写次数是十万次。NAND Flash的位交换概率比NOR Flash的大,必须使用EDC/ECC系统以确保可靠性。

(6)NAND Flash的坏块是随机分布的,需要对其进行初始化扫描以发现坏块,并将坏块标记为不可用。

(7)NOR Flash较之NAND Flash方便使用很多。可以非常直接地使用基于NOR的闪存,可以像其他存储器那样连接,并可以在上面直接运行代码。由于需要I/O接口,NAND要复杂得多。各种NAND器件的存取方法因厂家而异。在使用NAND器件时,必须先写入驱动程序,才能继续执行其他操作。向NAND器件写入信息需要相当的技巧,因为设计师绝不能向坏块写入,这就意味着在NAND器件上自始至终都必须进行虚拟映射。

(8)在NOR器件上运行代码不需要任何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD),NAND和NOR器件在进行写入和擦除操作时都需要MTD。使用NOR器件时所需要的MTD要相对少一些,许多厂商都提供用于NOR器件的更高级软件,这其中包括M-System的TrueFFS驱动,该驱动被Wind River System、Microsoft、QNX Software System、Symbian和Intel等厂商所采用。驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理,包括纠错、坏块处理和损耗平衡。

2、NAND Flash的地址分为三部分:块号,块内页号,页内字节号;正因为如此,NAND的一次数据访问,要经过3次寻址,先后确定块号,块内页号,页内字节号,至少占用了三个时间周期。

3、Nand flash的数据是以bit的方式保存在memory cell,一个cell里面只能存储一个bit。这些cell以8个或者16个为单元,连成bit line,形成所谓的byte(X8)/word(X16),这就是NAND Device的位宽。这些line会再组成page,page又分为main area(一般用来做普通数据的存储区)和spare area(一般用于存放ECC校验码、坏块标记等信息),最后再又多个page形成一个block。

4、NAND flash以页为单位读写数据,而以块为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址:

――Block address――page address――column address

对于NANDFLASH来说,地址,命令和数据都只能在I/O[7:0]上传递,数据宽度为8bits或16bits。

在一个块内,对每一个页进行编程(写操作)的话,必须是顺序的,而不能是随机的。比如,一个块中有128个页,那么你只能先对page0编程,再对page1编程,。。。。,而不能随机的,比如先对page3,再page1,page2.,page0,page4,.。。。

5、Nand Flash中,一个块中含有1个或多个位是坏的,就成为其为坏块。坏块的稳定性是无法保证的,也就是说,不能保证你写入的数据是对的,或者写入对了,读出来也不一定对的。而正常的块,肯定是写入读出都是正常的。坏块有两种:

(1)一种是出厂的时候,也就是,你买到的新的,还没用过的Nand Flash,就可以包含了坏块。此类出厂时就有的坏块,被称作factory (masked)bad block或initial bad/invalid block,在出厂之前,就会做对应的标记,标为坏块。具体标记的地方是,对于现在常见的页大小为2K的NandFlash,是块中第一个页列地址为2048的位置(旧的小页面,pagesize是512B甚至256B的nandflash,坏块标记是spare area的第6个字节),如果不是0xFF,就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是0xFF的。

(2)第二类叫做在使用过程中产生的,由于使用过程时间长了,在擦块除的时候,出错了,说明此块坏了,也要在程序运行过程中,发现,并且标记成坏块的。具体标记的位置,和上面一样。这类块叫做worn-out bad block。对于坏块的管理,在Linux系统中,叫做坏块管理(BBM,Bad Block Managment),对应的会有一个表去记录好块,坏块的信息,以及坏块是出厂就有的,还是后来使用产生的,这个表叫做坏块表(BBT,Bad Block Table)。在Linux内核MTD架构下的Nand Flash驱动,和Uboot中Nand Flash驱动中,在加载完驱动之后,如果你没有加入参数主动要求跳过坏块扫描的话,那么都会去主动扫描坏块,建立必要的BBT的,以备后面坏块管理所使用。(这样每次使用之之前,都会自动扫描一下,建立BBT,这样就可以跳过怀块进行别的方面的处理了)

关于好的,可以使用的块的数目达到一定的数目,比如三星的K9G8G08U0M,整个flash一共有4096个块,出厂的时候,保证好的块至少大于3996个,也就是意思是,你新买到这个型号的nand flash,最坏的可能,有3096-3996=100个坏块。不过,事实上,现在出厂时的坏块,比较少,绝大多数,都是使用时间长了,在使用过程中出现的。
       保证第一个块是好的,并且一般相对来说比较耐用。做此保证的主要原因是,很多Nand Flash坏块管理方法中,就是将第一个块,用来存储上面提到的BBT,否则,都是出错几率一样的块,那么也就不太好管理了,连放BBT的地方,都不好找了,^_^。一般来说,不同型号的Nand Flash的数据手册中,也会提到,自己的这个nand flash,最多允许多少个坏块。就比如上面提到的,三星的K9G8G08U0M,最多有100个坏块。 对于坏块的标记,本质上,也只是对应的flash上的某些字节的数据是非0xFF而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。uboot中有个命令是“nand scrub”就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。一般来说,不建议用这个。不过,我倒是经常用,其实也没啥大碍,呵呵。最好用“nand erase”只擦除好的块,对于已经标记坏块的块,不擦除。

如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把块里其他好的面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完块备份之后,再将这个坏块擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!

6、NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。
      ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1。(512生成两组ECC,共6字节) 
      当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的spare area中。校验的时候,根据上述ECC生成原理不难推断:将从spare area中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示spare area出错;其他情况均表示出现了无法纠正的错误。

7、很多Nand flash支持一个叫做CE don’t-care的技术,字面意思就是,不关心是否片选,那有人会问了,如果不片选,那还能对其操作吗?答案就是,这个技术,主要用在当时是不需要选中芯片却还可以继续操作的这些情况:在某些应用,比如录音,音频播放等应用中,外部使用的微秒(us)级的时钟周期,此处假设是比较少的2us,在进行读取一页或者对页编程时,是对Nand Flash操作,这样的串行(SerialAccess)访问的周期都是20/30/50ns,都是纳秒(ns)级的,此处假设是50ns,当你已经发了对应的读或写的命令之后,接下来只是需要Nand Flash内部去自己操作,将数据读取或写入进去到内部的数据寄存器中而已,此处,如果可以把片选取消,CE#是低电平有效,取消片选就是拉高电平,这样会在下一个外部命令发送过来之前,即微秒量级的时间里面,即2us-50ns≈2us,这段时间的取消片选,可以降低很少的系统功耗,但是多次的操作,就可以在很大程度上降低整体的功耗了。总结起来简单解释就是:由于某些外部应用的频率比较低。

二、Linux MTD系统解析

       U-Boot的NAND Flash驱动是从Linux的MTD系统里移植过来的。所以必须首先理解Linux的MTD系统,才能理解U-Boot的NAND Flash驱动。

1、Linux MTD系统框架

MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。

Linux内核的MTD框架如图1所示。

图1 Linux内核的MTD框架

如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。

(1)设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备。

(2)MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c 是MTD字符设备接口相关实现,mtdblock.c是MTD块设备接口相关实现。

(3)MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c是MTD原始设备接口相关实现,mtdpart.c 是MTD分区接口相关实现。

(4)硬件驱动层:硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。

实现各层的关键文件、数据结构和之间联系如图2所示。

图2 实现各层的关键文件、数据结构和之间联系

U-Boot只使用了Linux内核中的MTD原始设备层和硬件驱动层。

2、Linux MTD各层数据结构及接口函数

(1)MTD设备层       

mtd字符设备接口:/drivers/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它,可以直接访问Flash设备,与前面的字符驱动一样,通过file_operations结构体里面的open()、read()、write()、ioctl()可以读写Flash,通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL) 。

mtd块设备接口:/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash上的block,将内存中的映射块写入Flash块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。

(2)MTD原始设备层

       mtdcore.c文件实现了MTD原始设备相关接口,mtdpart.c文件实现了MTD分区相关接口。

用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:

struct mtd_info *mtd_table[MAX_MTD_DEVICES];  

Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。

struct mtd_part {
2.    struct mtd_info mtd;        /* 分区信息, 大部分由master决定 */
3.    struct mtd_info *master;    /* 分区的主分区 */
4.    uint64_t offset;            /* 分区的偏移地址 */
5.    int index;                  /* 分区号 (Linux3.0后不存在该字段) */
6.    struct list_head list;      /* 将mtd_part链成一个链表mtd_partitons */
7.    int registered;
8.};

下面重点介绍下数据结构是mtd_info。为了便于观察,将重要的数据放在前面,不大重要的编写在后面。

struct mtd_info {
2.    u_char type;         /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
3.    uint32_t flags;      /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
4.    uint64_t size;       /* mtd设备的大小 */
5.    uint32_t erasesize;  /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
6.    uint32_t writesize;  /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
7.    uint32_t oobsize;    /* OOB字节数 */
8.    uint32_t oobavail;   /* 可用的OOB字节数 */
9.    unsigned int erasesize_shift;   /* 默认为0,不重要 */
10.    unsigned int writesize_shift;   /* 默认为0,不重要 */
11.    unsigned int erasesize_mask;    /* 默认为1,不重要 */
12.    unsigned int writesize_mask;    /* 默认为1,不重要 */
13.    const char *name;               /* 名字,   不重要*/
14.    int index;                      /* 索引号,不重要 */
15.    int numeraseregions;            /* 通常为1 */
16.    struct mtd_erase_region_info *eraseregions; /* 可变擦除区域 */
17.
18.    void *priv;     /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
19.    struct module *owner;   /* 一般设置为THIS_MODULE */
20.
21.    /* 擦除函数 */
22.    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
23.
24.    /* 读写flash函数 */
25.    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
26.    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
27.
28.    /* 带oob读写Flash函数 */
29.    int (*read_oob) (struct mtd_info *mtd, loff_t from,
30.             struct mtd_oob_ops *ops);
31.    int (*write_oob) (struct mtd_info *mtd, loff_t to,
32.             struct mtd_oob_ops *ops);
33.
34.    int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
35.    int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
36.    int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
37.    int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
38.    int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
39.    int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
40.
41.    int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
42.    int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
43.    /* Sync */
44.    void (*sync) (struct mtd_info *mtd);
45.
46.    /* Chip-supported device locking */
47.    int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
48.    int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
49.
50.    /* 电源管理函数 */
51.    int (*suspend) (struct mtd_info *mtd);
52.    void (*resume) (struct mtd_info *mtd);
53.
54.    /* 坏块管理函数 */
55.    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
56.    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
57.
58.    void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
59.    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
60.                        unsigned long len,
61.                        unsigned long offset,
62.                        unsigned long flags);
63.    struct backing_dev_info *backing_dev_info;
64.    struct notifier_block reboot_notifier;  /* default mode before reboot */
65.
66.    /* ECC status information */
67.    struct mtd_ecc_stats ecc_stats;
68.    int subpage_sft;
69.    struct device dev;
70.    int usecount;
71.    int (*get_device) (struct mtd_info *mtd);
72.    void (*put_device) (struct mtd_info *mtd);
73.};

mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。

如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。

int add_mtd_device(struct mtd_info *mtd)
int del_mtd_device (struct mtd_info *mtd)

如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
int del_mtd_partitions(struct mtd_info *master)

其中mtd_partition结构体表示分区的信息。

struct mtd_partition {
2.    char *name;             /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
3.    uint64_t size;          /* 分区大小 */
4.    uint64_t offset;        /* 分区偏移值 */
5.    uint32_t mask_flags;    /* 掩码标识,不重要 */
6.    struct nand_ecclayout *ecclayout;   /* OOB布局 */
7.    struct mtd_info **mtdp;     /* pointer to store the MTD object */
8.};

(3)MTD硬件驱动层       

Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。

MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。

struct nand_chip {
2.    void  __iomem   *IO_ADDR_R;     /* 读8位I/O线地址 */
3.    void  __iomem   *IO_ADDR_W;     /* 写8位I/O线地址 */
4.
5.    /* 从芯片中读一个字节 */
6.    uint8_t (*read_byte)(struct mtd_info *mtd);
7.    /* 从芯片中读一个字 */
8.    u16     (*read_word)(struct mtd_info *mtd);
9.    /* 将缓冲区内容写入芯片 */
10.    void    (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
11.    /* 读芯片读取内容至缓冲区/ */
12.    void    (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
13.    /* 验证芯片和写入缓冲区中的数据 */
14.    int     (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
15.    /* 选中芯片 */
16.    void    (*select_chip)(struct mtd_info *mtd, int chip);
17.    /* 检测是否有坏块 */
18.    int     (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
19.    /* 标记坏块 */
20.    int     (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
21.    /* 命令、地址、数据控制函数 */
22.    void    (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
23.    /* 设备是否就绪 */
24.    int     (*dev_ready)(struct mtd_info *mtd);
25.    /* 实现命令发送 */
26.    void    (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
27.    int     (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
28.    /* 擦除命令的处理 */
29.    void    (*erase_cmd)(struct mtd_info *mtd, int page);
30.    /* 扫描坏块 */
31.    int     (*scan_bbt)(struct mtd_info *mtd);
32.    int     (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
33.    /* 写一页 */
34.    int     (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
35.                      const uint8_t *buf, int page, int cached, int raw);
36.
37.    int     chip_delay;         /* 由板决定的延迟时间 */
38.    /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
39.    unsigned int    options;
40.
41.    /* 用位表示的NAND芯片的page大小,如某片NAND芯片
42.     * 的一个page有512个字节,那么page_shift就是9
43.     */
44.    int      page_shift;
45.    /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
46.     * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14
47.     */
48.    int      phys_erase_shift;
49.    /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
50.     * 所以bbt_erase_shift通常与phys_erase_shift相等
51.     */
52.    int      bbt_erase_shift;
53.    /* 用位表示的NAND芯片的容量 */
54.    int      chip_shift;
55.    /* NADN FLASH芯片的数量 */
56.    int      numchips;
57.    /* NAND芯片的大小 */
58.    uint64_t chipsize;
59.    int      pagemask;
60.    int      pagebuf;
61.    int      subpagesize;
62.    uint8_t  cellinfo;
63.    int      badblockpos;
64.    nand_state_t    state;
65.    uint8_t     *oob_poi;
66.    struct nand_hw_control  *controller;
67.    struct nand_ecclayout   *ecclayout; /* ECC布局 */
68.
69.    struct nand_ecc_ctrl ecc;   /* ECC校验结构体,里面有大量的函数进行ECC校验 */
70.    struct nand_buffers *buffers;
71.    struct nand_hw_control hwcontrol;
72.    struct mtd_oob_ops ops;
73.    uint8_t     *bbt;
74.    struct nand_bbt_descr   *bbt_td;
75.    struct nand_bbt_descr   *bbt_md;
76.    struct nand_bbt_descr   *badblock_pattern;
77.    void        *priv;
78.};

三、U-BOOT的NAND Flash驱动的实现

       U-BOOT的NAND Flash驱动实现的关键是对数据结构mtd_info和nand_chip各个成员的实现。

1、数据结构mtd_info的实现

数据结构mtd_info各个成员已经由Linux内核实现,不需要自己单独实现。数据结构mtd_info各个成员的实现主要在mtd/nand/nand_base.c的函数nand_init_chip、函数nand_get_flash_type、函数nand_scan_ident、函数nand_scan_tail中实现的。比如,数据结构mtd_info的read()、write()、read_oob()、write_oob()等成员函数在函数nand_scan_tail中赋值,且这些函数都已经在mtd/nand/nand_base.c中实现了。

/*** nand_scan_tail - [NAND Interface] Scan for the NAND device* @mtd:      MTD device structure* @maxchips:       Number of chips to scan for** This is the second phase of the normal nand_scan() function. It* fills out all the uninitialized function pointers with the defaults* and scans for a bad block table if appropriate.*/
int nand_scan_tail(struct mtd_info *mtd)
{....../* Fill in remaining MTD driver data */mtd->type = MTD_NANDFLASH;mtd->flags = MTD_CAP_NANDFLASH;mtd->erase = nand_erase;mtd->point = NULL;mtd->unpoint = NULL;mtd->read = nand_read;mtd->write = nand_write;mtd->read_oob = nand_read_oob;mtd->write_oob = nand_write_oob;mtd->sync = nand_sync;mtd->lock = NULL;mtd->unlock = NULL;mtd->suspend = nand_suspend;mtd->resume = nand_resume;mtd->block_isbad = nand_block_isbad;mtd->block_markbad = nand_block_markbad;.......
}

2、数据结构nand_chip的实现       

       数据结构nand_chip里的绝大部分成员都由内核已经实现好了,少数成员需要根据控制器和NAND FLASH芯片单独实现,这些成员有:

(1)IO_ADDR_R、IO_ADDR_W

读8位I/O线地址、写8位I/O线地址

(2)cmd_ctrl

命令、地址、数据控制函数

(3)dev_ready

设备是否就绪函数

(4)erase_cmd

擦除命令的处理函数

(5)chip_delay

由板决定的延迟时间

(6)options

与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等。

(7)ecc

如果ecc计算等由硬件完成,则也需要根据具体的处理器实现ecc成员的某些成员。

四、U-BOOT的NAND Flash驱动的使用

       U-BOOT的NAND Flash驱动的使用范例可以参看common/cmd_nand.c文件里的函数do_nand。

五、U-BOOT的NAND Flash驱动的移植

 U-BOOT的NAND Flash驱动的移植可以参看博文https://blog.csdn.net/kunkliu/article/details/82183311。

参考博文:

https://blog.csdn.net/qq_38880380/article/details/78884522

https://blog.csdn.net/shengnan_wu/article/details/8116861?utm_medium=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control

https://www.cnblogs.com/chd-zhangbo/p/5407754.html

https://blog.csdn.net/liukun321/article/details/6598921

U-Boot源码之NAND Flash驱动相关推荐

  1. Nand flash驱动的编写与移植

    1 Nand flash工作原理     S3C2410板的Nand Flash支持由两部分组成:Nand Flash控制器(集成在S3C2410 CPU)和Nand Flash存储 芯片(K9F12 ...

  2. Linux MTD架构下的nand flash驱动详解

    转载自:http://blog.csdn.net/wang_zheng_kai/article/details/18988521 有了前面的基础(Nandflash详解:https://blog.cs ...

  3. linux下nand flash驱动工作原理,1.3.4. Nand flash驱动工作原理

    1.3.4. Nand flash驱动工作原理 在介绍具体如何写Nand Flash驱动之前,我们先要了解,大概的整个系统,和Nand Flash相关的部分的驱动工作流程,这样,对于后面的驱动实现,才 ...

  4. nand flash 经典 全面 ------如何编写Linux下Nand Flash驱动

    Crifan Li 摘要 本文先解释了Nand Flash相关的一些名词,再从Flash硬件机制开始,介绍到Nand Flash的常见的物理特性,且深入介绍了Nand Flash的一些高级功能,然后开 ...

  5. 15.NAND FLASH驱动

    NAND FLASH 原理以及操作详见:https://blog.csdn.net/qq_16933601/article/details/100001443 一.基本的问题 NAND FLASH是一 ...

  6. 如何编写linux下nand flash驱动

    http://www.cnblogs.com/sankye/articles/1638852.html 向作者Sankye致敬 [编写驱动之前要了解的知识] 1.       硬件特性: [Flash ...

  7. ARM9 2410移植之Nand flash 驱动的编写与移植

    1 Nand flash 工作原理 S3C2410 板的Nand Flash 支持由两部分组成:Nand Flash 控制器(集成在S3C2410 CPU)和Nand Flash 存储 芯片(K9F1 ...

  8. [转]ARM9 2410移植之Nand flash 驱动的编写与移植

    1 Nand flash 工作原理 S3C2410 板的Nand Flash 支持由两部分组成:Nand Flash 控制器(集成在S3C2410 CPU)和Nand Flash 存储 芯片(K9F1 ...

  9. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  10. linux nand 驱动,Linux NAND FLASH驱动分析(一)

    最近一直在忙着工作上的事情,好久都没有更新博客了,发现最近思想是比较混乱的.学任何东西都坚持不下去,既然选择驱动开发这条路就要坚持下去. 之前分析了Linux块设备驱动,是以内存块来模拟的虚拟块设备. ...

最新文章

  1. 基于wincc的虚拟电梯设计_基于WINCC的模拟电梯设计
  2. windows网络负载平衡
  3. 如何设置网站标题更有利于网站SEO优化?
  4. python垃圾回收离职_谈谈python垃圾回收机制
  5. 含有“外骨骼”的电影和游戏
  6. java同步锁实例_Java lock同步锁使用实例解析
  7. angular 使用data-bs-datepicker时的一个小问题及解决
  8. 放大镜制作(2)—此方法比较容易理解
  9. C语言 | 赋值与运算符
  10. 【第二届】Erlang Fans交流会(补充事宜)
  11. 加载文件流_未关闭的文件流会引起内存泄露么?
  12. 【排序算法】插入排序-常规方法
  13. 三年JAVA开发经验如何做到年薪35万
  14. moodle 页面联动效果
  15. Javaweb常见面试题
  16. excel--text(双坐标图表)
  17. 网上赚钱的平台哪个好?7个方式总有你喜欢的!
  18. 再传即刻搜索和盘古搜索合并,真假已不重要
  19. Matlab 基于遗传算法优化的VMD信号去噪算法 创新点:基于样本熵作为适应度函数
  20. “英语”有多少个字母?

热门文章

  1. matlab把数据乘,【excel怎么相乘】如何把EXCLE数据导入到MATLAB中
  2. 离线pandas安装教程
  3. 项目经理应该具备的四种能力
  4. Canvas API
  5. 微软智能云Azure在华新增数据中心区域正式启用
  6. Java求指定精度的开根号运算
  7. clustMD r语言_R语言聚类分析-层次聚类分析
  8. pyLDA系列︱gensim中带'监督味'的作者-主题模型(Author-Topic Model)
  9. 关于金融学和计量金融学的好书
  10. arcgis中python计算面积的表达式_ArcGIS字段计算器中的python函数