u-boot移植--4、对于NAND FLASH的支持
常常说自己是老年人记忆,因为看过的东西很快就忘。秉承着看自己写的东西肯定要比看别人写东西要好理解的原则。写下这个系列的u-boot移植,同时也加深自己的理解。其实到现在网上大神很多,给出了很多的解决方案。我也以他们为参考。与之不同我会加上一些我自己对某些地方的理解来进行处理。希望对大家有帮助。 NAND FLASH型号:K9F2G08U0A 对于NAND FLASH,我们需要了解如下的信息。K9F2G08UXA 是一个具有128K页,2048块,每一块64页,每一页具有(2K+64)byte,2K是主区,64是信息区域,每一块是(128K+4K)byte的空间。所有的操作都是通过一个8位的IO实现。对每一页的访问通过列地址进行访问,2K空间一共需要11个位宽,因此列访问需要两次,第一次前8位, 首先我们在include/configs/TQ2440.h文件中的CONFIG_COMMANDS中增加CFG_CMD_NAND的命令,如下所示。 #define CONFIG_COMMANDS (CONFIG_CMD_DFL | CFG_CMD_CACHE | CFG_CMD_NAND | CFG_CMD_REGINFO | CFG_CMD_DATE | CFG_CMD_PING | CFG_CMD_ELF) 定义的CFG_CMD_NAND实际上就相当于u-boot对nand支持的总开关,定义之后u-boot对于NAND的应用就被打开了。我们先编译一下,看看现在是什么状态。make all,发现了如下的报错信息。 /work/system/u-boot-1.1.6/include/linux/mtd/nand.h:412: error: `NAND_MAX_CHIPS' undeclared here (not in a function) nand.c:35: error: `CFG_MAX_NAND_DEVICE' undeclared here (not in a function) nand.c:38: error: `CFG_NAND_BASE' undeclared here (not in a function) nand.c:35: error: storage size of `nand_info' isn't known nand.c:37: error: storage size of `nand_chip' isn't known nand.c:38: error: storage size of `base_address' isn't known nand.c:37: warning: 'nand_chip' defined but not used nand.c:38: warning: 'base_address' defined but not used 可以看到实际上虽然我们打开了对于NAND FLASH的支持,但是我们并没有添加NAND的信息,这样NAND肯定是有意见的。我们没有定义这三个宏定义,也缺少一些基本信息。那么我们首先在include/configs/TQ2440.h中添加三个宏定义信息。如下所示 /*----------------------------------------------------------------------- * NAND FLASH environment organization */ #define CFG_NAND_BASE 0x4E000000 /* define the base address */ #define CFG_MAX_NAND_DEVICE 1 /* the count of device */ #define NAND_MAX_CHIPS 1 /* every device is consist of 1 chip */可以看到我们定义的CFG_NAND_BASE是使用S3C2410_NAND_BASE来定义的,这是因为S3C2440的NAND控制器首地址是与S3C2410相同的,另外定义CFG_NAND_BASE实际上就是指的S3C2440的NAND控制器的所有寄存器的首地址。所以这里定义的CFG_NAND_BASE即为S3C2410_NAND_BASE所代表的0x4E000000. 对于u-boot来说,他的启动分两个阶段,第一阶段的文件为cpu/arm920t/start.s和board/TQ2440/low_level_init.s文件,前者是和平台相关的,后者是和开发板相关的。第二阶段是从lib_arm/board.c中的start_armboot函数开始的,如下图所示 在start_armboot函数中flash_init完成之后,如果定了的nand则会初始化nand,函数为nand_init(),nand_init()是drivers/nand/nand.c文件中函数,在nand_init()中又调用了同文件中的nand_init_chip();,然后nand_init_chip()又调用同文件中的board_nand_init()函数,可以看到这个函数也在nand.c文件中,只不过只有个名字,没有被实现。我们需要自己来实现这个函数。 在编写nand_init_chip()之前我们应该知道s3c2410和s3c2440还是有差异存在。这其中就包括了nand控制寄存器的差异。在s3c2410中,nand控制寄存器地址范围为0x4E000000到0x4E000014(6个32位寄存器)。而在s3c2440在nand控制寄存器地址范围为0x4E000000到0x4E00003C(12个32位寄存器)。s3c2410的寄存器的定义是在include/S3c24x0.h中定义的(S3c2410.h包含这个头文件),代码中寄存器的定义是按照2410的寄存器定义的,因为s3c2440的nand控制器的寄存器更多一些,我们新建一个S3C2440_NAND结构体来定义我们要用的关于S3C2440的寄存器。具体添加什么寄存器,及对应顺序可以去s3c2440的数据手册里去查。添加完成后的两个片子的nand控制器寄存器结构体如下所示。 /* NAND FLASH (see S3C2410 manual chapter 6) */ typedef struct { S3C24X0_REG32 NFCONF; S3C24X0_REG32 NFCMD; S3C24X0_REG32 NFADDR; S3C24X0_REG32 NFDATA; S3C24X0_REG32 NFSTAT; S3C24X0_REG32 NFECC; } /*__attribute__((__packed__))*/ S3C2410_NAND; /* NAND FLASH (see S3C2440) */ typedef struct { S3C24X0_REG32 NFCONF; S3C24X0_REG32 NFCONT; S3C24X0_REG32 NFCMD; S3C24X0_REG32 NFADDR; S3C24X0_REG32 NFDATA; S3C24X0_REG32 NFMECCD0; S3C24X0_REG32 NFMECCD1; S3C24X0_REG32 NFSECCD; S3C24X0_REG32 NFSTAT; S3C24X0_REG32 NFESTAT0; S3C24X0_REG32 NFESTAT1; S3C24X0_REG32 NFMECC0; S3C24X0_REG32 NFMECC1; S3C24X0_REG32 NFSECC; S3C24X0_REG32 NFSBLK; S3C24X0_REG32 NFEBLK; } /*__attribute__((__packed__))*/ S3C2440_NAND; 好接下来我们只要实现board_nand_init()函数就好了。为了能够更好地层次化处理我们代码我们在单独编写一个s3c2440_nand.c文件来单独实现一个s3c2440_nand_init()函数以及其他几个用到的函数。这样我们就可以直接在drivers/nand/nand.c中用board_nand_init()直接调用函数s3c2440_nand_init函数就可以了。 首先为了更好地编写s3c2440_nand.c文件,我们在include/s3c2410.h中添加对于获取nand地址的函数,即仿照S3C2410_GetBase_NAND函数定义实现一个函数名为S3C2410_GetBase_NAND的函数来获取地址,如下所示 static inline S3C2410_NAND * const S3C2410_GetBase_NAND(void) { return (S3C2410_NAND * const)S3C2410_NAND_BASE; } static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void) { return (S3C2440_NAND * const)S3C2440_NAND_BASE; } 这里的S3C2440_NAND_BASE我们需要在前面的宏定义添加一下,这个地址是芯片寄存器已经定义好的。宏定义如下所示 #define S3C2440_NAND_BASE 0x4E000000 (这里需要说明一下,这里定义的S3C2440_NAND_BASE地址和我们之前定义的CFG_NAND_BASE一样都指向了S3C2440片子的NAND控制器寄存器的首地址。) 然后编写的s3c2440_nand.c文件代码为(s3c2440_nand.c的文件路径是/cpu/arm920t/s3c24x0/s3c2440_nand.c) /* * Nand flash interface of s3c2410/s3c2440,by www.100ask.net * Changed from drivers/mtd/nand/s3c2410.c of kernel 2.6.13 */ #include <common.h> #if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #include <s3c2410.h> #include <nand.h> DECLARE_GLOBAL_DATA_PTR; #define S3C2440_NFSTAT_READY (1<<0) #define S3C2440_NFCONT_nFCE (1<<1) /* select chip,for s3c2440 */ static void s3c2440_nand_select_chip(struct mtd_info *mtd,int chip) { S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND(); if (chip == -1) { s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE; } else { s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE; } } /* command and control functions */ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd,int cmd) { S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND(); struct nand_chip *chip = mtd->priv; switch (cmd) { case NAND_CTL_SETNCE: case NAND_CTL_CLRNCE: printf("%s: called for NCEn",__FUNCTION__); break; case NAND_CTL_SETCLE: chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD; break; case NAND_CTL_SETALE: chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR; break; /* NAND_CTL_CLRCLE: */ /* NAND_CTL_CLRALE: */ default: chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA; break; } } /* s3c2440_nand_devready() * * returns 0 if the nand is busy,1 if it is ready */ static int s3c2440_nand_devready(struct mtd_info *mtd) { S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND(); return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY); } /* * Nand flash hardware initialization: * Set the timing,enable NAND flash controller */ static void s3c2440_nand_inithw(void) { S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND(); #define TACLS 0 #define TWRPH0 4 #define TWRPH1 2 /* Set flash memory timing */ s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* Initialize ECC,enable chip select,NAND flash controller enable */ s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0); } /* * Called by drivers/nand/nand.c,initialize the interface of nand flash */ void s3c2440_nand_init(struct nand_chip *chip) { S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND(); s3c2440_nand_inithw(); chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA; chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA; chip->hwcontrol = s3c2440_nand_hwcontrol; chip->dev_ready = s3c2440_nand_devready; chip->select_chip = s3c2440_nand_select_chip; chip->options = 0; chip->eccmode = NAND_ECC_SOFT; } #endif(这段代码是直接由韦东山老师的代码改的) (详细的研究下这段代码的作用,多研究http://blog.csdn.net/ying_seven/article/details/6888923) 接着修改drivers/nand/nand.c文件在其中建立函数board_nand_init(),其代码为 void board_nand_init(struct nand_chip *nand) { s3c2440_nand_init(nand); } 最后修改将s3c2440_nand.c文件加入到编译链中,就可以了。修改cpu/arm920t/s3c24x0/Makefile文件 修改前: COBJS = i2c.o interrupts.o serial.o speed.o usb_ohci.o 修改后: COBJS = i2c.o interrupts.o serial.o speed.o usb_ohci.o s3c2440_nand.o 这样就大功告成了,我们编译一下make all,然后编译通过,就可以了 最后说一下关于nand中的调用关系实际上是比较复杂,也比较完善的。我这里并没有提及这些东西,因为我也不是搞的很清楚,本博文的移植实际上也只是皮毛的移植。如果想深入了解,这里有一篇好博文大家可以去看。我自己也深受其益。http://blog.csdn.net/ying_seven/article/details/6888923
参考文献:1、《嵌入式linux开发完全手册》、韦东山 2、http://blog.csdn.NET/zhaocj (这位博主下面的u-boot系列) 3、http://blog.csdn.net/ying_seven/article/details/6888923 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |