在支持Nand Flash操作之前,首先要对Nand Flash的读写方法有一定的了解,参考文章:
1. 去除nand flash屏蔽
在之前初步移植uboot时,发现开启nand flash之后编译不通过,所以屏蔽了nand flash的使用,在单板配置文件include/configs/smdk2440.h
中开启:
然后编译,改正编译错误。
2. 定位编译出错问题所在
首先来修复第一个问题:
查看s3c2410_nand.c文件的72行:
这个指针有问题的话,就是nand这个结构体变量的定义问题,找到nand变量的定义:
struct s3c2410_nand *nand = s3c2410_get_base_nand();
接下来问题就变为struct s3c2410_nand
这个结构体定义有问题,继续寻找该定义,果然,在文件arch/arm/include/asm/arch-s3c24x0/s3c24x0.h
中,我们定义的是CONFIG_S3C2440,所以有struct s3c2440_nand的定义,没有struct s3c2410_nand的定义:
3. 修复编译错误 — 添加s3c2440_nand.c文件
3.1. 添加文件到工程中
这里涉及到将所有定义全部改变,所以复制一份s3c2410_nand.c文件,重命名为s3c2440_nand.c:
修改该目录下的makefile文件,将s3c2440_nand.c文件加入编译:
再去单板配置文件include/configs/smdk2440.h
中配置宏定义CONFIG_NAND_S3C2440:
3.2. 修改文件
修改宏定义
首先借助VS Code文件替换全部内容的功能,将宏定义替换为2440的:
宏定义全部替换之后,如下:
再对照数据手册中,修改NFCONF寄存器和NFCONT寄存器中这些位的值:
全局替换
借助VS Code文件替换全部内容的功能,将涉及到2410的变量和函数都替换为2440的,如下图(替换之后的结果之一):
4. uboot中的nand flash操作框架
在uboot中,这些对于nand_flash的操作是基于一套操作框架的,类似于Linux中的设备驱动,所以首先分析一下这套框架,然后修改代码。
4.1. nand_init函数
故事要从uboot的第二阶段说起,在 board_init_r 函数中(文件arch/arm/lib/board.c
)调用了 nand_iint 初始化函数:
接下来从该函数入手,分析nand flash的操作框架,该函数的源码在drivers/mtd/nand/nand.c
文件中,定义如下:
nand_init_chip函数同样在该文件中,定义如下:
在此处定义了 nand_chip 结构体和 mtd_info 结构体,并分别将指针传递传参给 board_nand_init 函数和 nand_scan 函数,接下来分别分析这两个函数。
4.2. board_nand_init函数
board_nand_init 函数定义在上面我们自己添加的文件drivers/mtd/nand/s3c2440_nand.c
中,该函数中主要使能了nand flash内存控制器,初始化nand flash控制器中的时序参数,最重要的是:
初始化传入的nand_chip结构体中的成员。
struct nand_chip
结构体在include/linux/mtd/nand.h
文件中定义,这个结构体中的成员非常多,其中有非常多的函数指针,这些函数指针在初始化时被设置为指向底层实现的函数,在调用时拉起底层函数。
在board_nand_init 中初始化该结构体时,别的函数指针都被传入了相应的值,只有 select_chip 函数未被传入具体实现,而是NULL:
接下来继续分析这套框架,看一下 select_chip 函数的调用情况。
4.3. nand_scan 函数
该函数定义在drivers/mtd/nand/nand_base.c
文件中,其定义代码如下:
这个函数将mtd结构体指针继续传给了nand_scan_ident函数,接着分析,这个函数同样在该文件中定义,根据函数注释可知,nand_scan_ident
函数会读取Flash ID,并根据读出的ID设置MTD信息,存入mtd结构体中。
进而查看该函数源码:
至此,是不是对nand_flash的整套操作框架有所了解了?可以画出这样一个调用关系图,帮助理解:
接下来分析nand_scan_ident函数中调用的两个函数:
- nand_set_defaults
- nand_get_flash_type
4.4. nand_set_defaults函数
该函数同样定义在文件drivers/mtd/nand/nand_base.c
中,主要作用是设置 nand_chip结构体中的默认调用的函数,设置方法如下:
如果nand_chip结构体中的函数指针已经有值了,则不进行任何操作;
如果nand_chip结构体中的函数指针为NULL,则赋予默认值。
经过4.2节的分析,select_chip 指针被设置为NULL,所以在该函数中会被设置默认值:
默认函数 nand_select_chip 也同样定义在该文件中,定义如下:
从该函数中可以找到问题,片选函数被调用时,什么都不干,导致nand Flash芯片根本不工作。
解决方法就是:自己重新实现一个正常工作的 nand_select_chip 函数并将函数指针传给nand_flash结构体中的对应成员。
4.5. nand_get_flash_type函数
该函数主要是读取ID值,根据根据读取到的ID值分析出nand flash类型,存储到 mtd_info 结构体中,该函数同样定义drivers/mtd/nand/nand_base.c
文件中:
在该函数中,调用select_chip时传入的第二个参数为0,进一步验证了默认的片选函数什么都不干的分析结果。
6. 解决问题 — 自己实现select_chip函数
在drivers/mtd/nand/s3c2440_nand.c
文件中自定义片选函数,函数原型如下:
static void nand_select_chip(struct mtd_info *mtd, int chipnr);
实现如下:
static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
switch (chipnr)
{
case -1:
/* 取消片选 */
writel(readl(&nand->nfcont) | (1 << 1), &nand->nfcont);
break;
case 0:
/* 开启片选 */
writel(readl(&nand->nfcont) & (~(1 << 1)), &nand->nfcont);
break;
default:
BUG();
}
}
然后在 board_nand_init 函数中使用该函数指针初始化nand_chip结构体成员,这样就避免它使用不能正常工作的默认值:
7. 修改底层实现代码
7.1. board_nand_init函数
这部分代码是重中之重,根据之前的文章来修改这段代码:
首先设置nand flash控制器时序参数:
接下来修改寄存器设置代码:
7.2. s3c2440_hwcontrol函数
该函数是所有发命令、发地址、发数据都会回调的函数,修改实现如下:
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if(ctrl & NAND_CLE)
{
//发命令
writeb(cmd, &(nand->nfcmd));
}
else if(ctrl & NAND_ALE)
{
//发地址
writeb(cmd, &(nand->nfaddr));
}
else
{
//发数据
writeb(cmd, &(nand->nfdata));
}
}
8. 关掉硬件ECC,使用软件ECC
这部分代码是使用宏定义CONFIG_S3C2440_NAND_HWECC
来控制的:
所以在单板文件中去掉该宏定义:
至此,添加对Nand Flash的支持完成,编译,下载到开发板,在串口查看结果,成功检测出了Nand Flash,并分析出大小为256MB:
接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。
来源:CSDN
作者:Mculover666
链接:https://blog.csdn.net/Mculover666/article/details/104531452