这里以u-boot-2009.08版本介绍U-Boot的NAND Flash驱动的实现。要明白U-Boot的NAND Flash驱动的实现,首先要了解NAND Flash的基础知识。
一、NAND Flash简介
关于NAND Flash的介绍,可以参看下面两篇博文:
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://www.cnblogs.com/chd-zhangbo/p/5407754.html
https://blog.csdn.net/liukun321/article/details/6598921