uboot移植之修改支持NandFlash识别篇6(超详细)

uboot移植之前期准备篇1

uboot移植之Makefile分析概述篇2

boot移植之init_sequence_f函数数组分析(番外篇)

uboot移植之源码流程分析篇3(超详细!)

uboot移植之修改支持SDRAM篇4

uboot移植之修改支持NorFlash篇5


目录

1、预备知识

2、开发文件对象2410 -> 2440

3、修改分析代码

4、遗留问题

5、遗留问题“NAND write to offset 0 failed -5”解决方法


1、预备知识

参考《K9F2G08U0C》手册章节“Pin Description”,可知道Nand Flash各引脚的使用说明如下图:

uboot移植之修改支持NandFlash识别篇6(超详细)_第1张图片

在操作Nand的时候,只需要往特定的寄存器中写值/读值,就可以实现发地址,发数据,读数据的功能了,中间一些CLE,ALE引脚电平控制操作都通过Nand Flash控制器帮我们搞定了。如果没有Nand控制器,那么这些工作都需要我们编程完成。

①发地址         NFADDR=地址值

②发数据         NFDATA=数据值

③发命令         NFCMMD=命令值     

④读数据         val=NFDATA

存储芯片的编程一般可分为这几个过程:1、初始化;2、识别;3、读;4、写;5、擦除;

而对于我们的Nand编程,则:

1、Nand芯片的Nand Flash控制器

2、读取ID

3、一次读一个页

4、一次写一个页

5、一次擦除一个块(页是块的n倍)

nand的底层程序框架可以分为两部分:一部分是通用的协议层,另一部分是和单板硬件相关。比方说:xxx函数知道发出90命令,接着再发出0地址,然后读出的第一个数据是厂家id,第二个数据是设备id。这是所有nand都有的一个特点,但是至于怎样发命令,怎样发地址等等,这些都是协议层中的代码 使用底层提供的函数 去实现(后面分析代码可以知道,其实就是构造好mtd_info结构体提供给协议层使用,里面含有各种底层函数,如果我们没有去定义新函数,那么将会使用默认函数去发命令,发地址...)。所以我们应该重点去修改,查看底层相应的功能函数,比方说:选中、发命令、发地址、发数据、读数据、判断状态。另外,由于2440和2410的寄存器存在差异性,所以,在程序中当涉及寄存器操作时候,一定要认真查看相应的手册,确定操作无误。

2、开发文件对象2410 -> 2440

在drivers\mtd\nand\目录下,将s3c2410_nand.c复制为s3c2440_nand.c,并修改该目录下的Makefile

在include\configs\smdk2440.h中修改2410对应的宏为2440

3、修改分析代码

uboot启动输出信息中“NAND: 0 MiB”,在工程中搜索“NAND: ”,定位common\board_r.c的initr_nand()函数,并通过两次的函数嵌套调用,最终在nand_init_chip函数确定了Nand的大小并输出。

uboot移植之修改支持NandFlash识别篇6(超详细)_第2张图片

uboot移植之修改支持NandFlash识别篇6(超详细)_第3张图片

base_addr=0x4E000000(nand的NFCONF寄存器地址)。nand的IO读写地址会在 board_nand_init 函数中重新设置。

在drivers\mtd\nand\s3c2440_nand.c的 board_nand_init 函数中

uboot移植之修改支持NandFlash识别篇6(超详细)_第4张图片

nand_reg指向Nand控制器的第一个寄存器NFCONF(0x4E000000),Nand的io读写地址是在该函数中被重新设置为NFDATA的地址。nand->select_chip所指向的片选函数为NULL,那么将会在drivers\mtd\nand\nand_base.c的函数nand_set_defaults 中被设置为使用默认函数。这是底层相关的函数,默认提供的函数可能并不符合我们的要求,后面有必要去修改它。

tacls twrph0 twrph1简单来说就是脉冲时间参数,需要配置NFCONF寄存器。当发生①②③④操作时候,就让它按照设置好的时间参数去控制Nand Flash给它发地址、发命令等等,因为我们外接的Nand Flash可能是不同种类的,所以就要按照特定的类别要求去设置好,让控制器发出符合该型号nand的时序控制信号。对比2440和2410手册我们可以知道,操作的位已经发生变化,需要重新来设置。


翻阅2440手册,找到对应的时间参数配置寄存器NFCONF,以及Nand Flash的内存定时说明图:

uboot移植之修改支持NandFlash识别篇6(超详细)_第5张图片

nWE:低电平有效。

uboot移植之修改支持NandFlash识别篇6(超详细)_第6张图片

翻阅《K9F2G08U0C》Nand手册,找到该型号nand在特定电压(3.3v)下的最小时间参数:

uboot移植之修改支持NandFlash识别篇6(超详细)_第7张图片

我们可以借助命令锁存周期说明图(Command Latch Cycle)来理解:

uboot移植之修改支持NandFlash识别篇6(超详细)_第8张图片

根据AC Timing表可以知道,t(CLS)和t(WP)的最小值为12us,所以tacls最小为0。

前面篇节已经设置HCLK=100Hz,根据该手册中指定的最小时间以及2440中的参数公式可以反推出,twrph0 twrph1的最小设置分别为:1 , 0。所以,使用程序提供的默认参数完全没有问题。


在该函数对nand控制器的初始化中并没有读写操作Nand Flash,为了避免误操作,可以取消选中。重新添加使能nand,以及初始化ECC编码/解码器,我们需要启用ecc校验 (软件生成),以保证数据安全可靠。因为Nand存在坏块缺陷,无法避免。数据写到页中,并且生成ECC校验码存放在页后紧跟的OOB区;当开始读数据的时候,先读页内容,生成所读页的ECC校验码,和原先存放在OOB区的校验码进行比较,如果匹配表明数据没有问题,否则,会通过某种方法去修改,使得数据正确。

所以,需修改drivers\mtd\nand\s3c2440_nand.c中定义的宏如下,并且将程序中的宏名字替换为2440,再注释掉137行,并模仿nfconf,配置好nfcont:

uboot移植之修改支持NandFlash识别篇6(超详细)_第9张图片

接着分析s3c24x0_hwcontrol函数,在66~71行,2410操作的是NFCONF寄存器选中/取消片选,而2440操作的是NFCONT寄存器,所以,需要修改寄存器和宏。

uboot移植之修改支持NandFlash识别篇6(超详细)_第10张图片

至此,我们通过分析观察可以知道,在 board_nand_init 函数中,主要就是构造好nand->chip结构体,设置一些底层函数,这些函数将会在nand_scan函数中排上用场。

uboot移植之修改支持NandFlash识别篇6(超详细)_第11张图片

在nand_scan调用nand_scan_ident函数,设置默认函数。然后在nand_get_flash_type函数中选中片选,复位,发出90命令,再发出0地址,接着读取id。上面也曾分析过,这是协议层函数,调用的是应该是我们底层提供给它的片选函数,默认提供的函数并不能满足我们的要求,所以有必要重新写一个片选函数。

uboot移植之修改支持NandFlash识别篇6(超详细)_第12张图片

先修改片选函数:重写一个片选函数,而不是在默认函数基础上去修改,虽然可以这样做,但是为了代码规整,不提倡。

uboot移植之修改支持NandFlash识别篇6(超详细)_第13张图片

另外,因为之前并没有设置chip->cmdfunc,所以,发命令使用的也是默认函数nand_command。

跟踪进函数nand_command,发现所谓的发命令,发地址就是调用chip->cmd_ctrl。

uboot移植之修改支持NandFlash识别篇6(超详细)_第14张图片

而chip->cmd_ctrl在函数board_nand_init就被指向了s3c24x0_hwcontrol,所以s3c24x0_hwcontrol还可以用来发命令,发地址(行地址:哪一页  列地址:页内偏移),那么该函数怎么区分是发命令?还是发地址?还是操作片选信号?

阅读nand_command函数,发现就是通过传入s3c24x0_hwcontrol的第三个参数ctrl来区分。

重现分析s3c24x0_hwcontrol是怎样实现发命令和发地址的:(原来代码)

uboot移植之修改支持NandFlash识别篇6(超详细)_第15张图片

查阅2410芯片手册,发现0x4E000008是NFADDR寄存器,0x4E000004是NFCMD寄存器。

而在2410芯片手册,发现0x4E00000C是NFADDR寄存器,0x4E000008是NFCMD寄存器。

所以有必要修改对应宏,类似上面去修改就行了,不再重复。此处仅放出修改后的代码:

uboot移植之修改支持NandFlash识别篇6(超详细)_第16张图片

同样地,查看前面代码也发现读写函数也是使用默认的提供函数。那么busw到底有没有设置呢?

一直追查,发现busw就是默认设置函数的第二个参数,而chip->options并没有被设置,所以使用的读写函数是基于8位的,符合我们的Nand Flash接口情况,所以不必去修改了。

5、总结&&结果

我们主要修改了底层的发命令,发地址,片选操作函数。在修改的过程最大限度保留了代码的编写作风,当然,你也可以直接操作寄存器达到目的。最后,一定要结合nand_command命令处理函数思考,在当发生命令调用时,代入整个流程分析,这里就不展开讨论了。另外,若要打印调试信息,可以在drivers\mtd\nand\s3c2440_nand.c开头定义:

虽然会有警告信息,但是不能忽略_DEBUG。

uboot移植之修改支持NandFlash识别篇6(超详细)_第17张图片

编译,烧写,测试(关调试):

uboot移植之修改支持NandFlash识别篇6(超详细)_第18张图片

编译,烧写,测试(开调试):

uboot移植之修改支持NandFlash识别篇6(超详细)_第19张图片

4、遗留问题

写操作有点问题,但是目前不知道问题到底出在哪了。

①开调试打印,在uboot菜单中输入:

SMDK2440 # nand erase 0 80 (没问题)

uboot移植之修改支持NandFlash识别篇6(超详细)_第20张图片

SMDK2440 # nand write 0 0 80 (输出错误信息如下)

SMDK2440 # nand write 0 0 80

NAND write: device 0 offset 0x0, size 0x80
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80 //将参数代入hwcontrol()分析,功能是:取消选中
hwcontrol(): 0xffffffff 0x81 //选中片选
hwcontrol(): 0x70 0x83  //选中片选,发命令0x70 读状态
hwcontrol(): 0xffffffff 0x81 //选中片选
hwcontrol(): 0x80 0x83  //选中片选,发命令0x80 
hwcontrol(): 0x00 0x85  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发地址0x00
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0x10 0x83  //选中片选,发命令0x10 启动写操作 单位:1页
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83  //选中片选,发命令0x70 查询写操作是否完成
hwcontrol(): 0xffffffff 0x81
dev_ready     //检查状态引脚
hwcontrol(): 0xffffffff 0x80 
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x81 
hwcontrol(): 0x00 0x83  //选中片选,发命令0x00,正好对应读操作的第一个访问周期命令
hwcontrol(): 0x00 0x85  //选中片选,发地址0x00
hwcontrol(): 0x00 0x05  //选中片选,发命令0x00
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x30 0x83 //发命令0x30  正好对应读操作的第二个访问周期命令
hwcontrol(): 0xffffffff 0x81
dev_ready
dev_ready
hwcontrol(): 0x05 0x83   //结束写操作,和读一样。
hwcontrol(): 0x828 0x85
hwcontrol(): 0x08 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xe0 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
NAND write to offset 0 failed -5
 0 bytes written: ERROR

写操作:(操作单位:/1页)

uboot移植之修改支持NandFlash识别篇6(超详细)_第21张图片

读操作:(操作单位:/1页)

uboot移植之修改支持NandFlash识别篇6(超详细)_第22张图片

SMDK2440 # nand read 0x30000000 0 80

uboot移植之修改支持NandFlash识别篇6(超详细)_第23张图片

②关调试打印,在uboot菜单中输入:

uboot移植之修改支持NandFlash识别篇6(超详细)_第24张图片

附:命令集表

uboot移植之修改支持NandFlash识别篇6(超详细)_第25张图片

5、遗留问题“NAND write to offset 0 failed -5”解决方法

由于nand存在位反转缺陷,当写入页数据的时候,需要根据页数据生成ECC校验码,并写入当前页后面的oob区。当开始读该页的数据的时候,由于nand的位反转缺陷,可能导致读出的数据某位发生了错误。所以需要继续读出oob区的校验码,根据校验码修正数据中的错误位。

根据输出的错误信息,在工程寻找出错位置函数,发现于cmd\nand.c的nand_write_skip_bad函数,返回值错误。

uboot移植之修改支持NandFlash识别篇6(超详细)_第26张图片

跟踪进该函数,加入打印调试信息,根据输出信息发现其实是在调用nand_verify函数验证写入数据和缓冲区的数据的过程中出错,导致写入不成功。校验码其实早在nand_write的时候就已经写入oob区了,读取数据的时候自然会根据oob区的校验码修改数据,为什么还要和缓冲区的数据校验呢?(缓冲区的数据其实就是源数据)另外,我发现在旧版的uboot中,并没有这个判断选项,所以暂且先将其关闭,测试是否能成功。(测试证明没问题)

uboot移植之修改支持NandFlash识别篇6(超详细)_第27张图片

修改前uboot输出:(添加详细注释)

SMDK2440 # nand write 0 0 800
argv[0]=nand  //该参数是我添加打印的,源码没有
argv[1]=write
argv[2]=0
argv[3]=0
argv[4]=800

NAND write: device 0 offset 0x0, size 0x800
in else raw size=2048,off=0,dev=0  //自行添加打印的,源码没有
after else raw size=2048,off=0,dev=0  //自行添加打印的,源码没有
before nand_write_skip_bad addr=0,off=0,rwsize=0,maxsize=2048,ret=0  //自行添加打印的,源码没有
inskip blocksize=131072,length = 2048,offset=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip actual=0,need_skip=0  //自行添加打印的,源码没有
need_skip = 0  //自行添加打印的,源码没有
inskip before write buffer=0,length = 2048,offset=0,rval=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x80 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x10 0x83
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x70 0x83
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip after write buffer=0,length = 2048,offset=0,rval=0  //自行添加打印的,源码没有
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x00 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0x00 0x05
hwcontrol(): 0xffffffff 0x81
hwcontrol(): 0x30 0x83
hwcontrol(): 0xffffffff 0x81
dev_ready
dev_ready
hwcontrol(): 0xffffffff 0x80
hwcontrol(): 0xffffffff 0x80
inskip after verify buffer=0,length = 2048,offset=0,rval=-5   //自行添加打印的,源码没有
NAND write to offset 0 failed -5
after nand_write_skip_bad addr=0,off=0,rwsize=0,maxsize=0,ret=0   //自行添加打印的,源码没有
 0 bytes written: ERROR

修改后uboot输出:(添加详细注释)

uboot移植之修改支持NandFlash识别篇6(超详细)_第28张图片

参考资料:

《嵌入式Linux应用开发完全手册》 韦东山著

《S3C2440A_UserManual_Rev13》

《K9F2G08U0C(NAND FLASH)》

 

 

你可能感兴趣的:(2440,uboot)