u-boot中NAND flash的MTD驱动移植 四

转载地址:http://blog.sina.com.cn/s/blog_87f8cc4e0102vbxg.html

至此,nand_scan()函数的代码分析全部结束。下面我们来总结一下这个函数都做了些什么:

1、 初始化mtd_info中一个重要的指针priv,使这个指针指向nand_chip变量; 2、 初始化nand_chip中的一些MTD驱动低层函数指针,如:cmdfunc、waitfunc、select_chip、select_chip、read_byte、block_bad、block_markbad、write_buf、read_buf、verify_buf 、read_buf、erase_cmd等;

3、 读芯片ID,并根据读出的设备id比对nand_flash_ids[i]中设备id找到匹配项,根据匹配项中数据和读ID的低4周期数据初始化nandflash芯片相关重要参数,如:chipsize、oobblock、oobsize、erasesize、busw、badblockpos、page_shift、bbt_erase_shift、chip_shift;

4、 查找板上nandflash物理芯片数,并计算出总容量初始化mtd->size;

5、 分配oob_buf、data_buf空间,初始化空间指针;

6、 初始化oob区、ecc校验相关参数和函数指针,如参数:autooob、oobavail、eccsize、eccbytes、eccsteps;函数指针:calculate_ecc、correct_data;

7、 初始化MTD驱动接口函数,如: nand_erase、nand_read、nand_write、nand_read_ecc、nand_write_ecc、nand_read_oob、nand_write_oob、nand_block_isbad、nand_block_markbad

8、 调用nand_bbt()创建坏块表.

总之:函数功能就是初始化,为驱动接口函数提供必要的运行环境。

 

4.3)board_nand_init()分析

该函数在nand_init_chip()先于nand_scan()被调用,它的主要功能就是完成一些与底层

硬件操作相关的初始化。由于这些底层操作大多为寄存器操作,所以与具体nand芯片型号及nand控制器相关,MTD不可能提供通用函数来完成这些底层操作。因此,用户必须自行编写board_nand_init()来完成这些操作的初始化,因此移植MTD驱动时编写board_nand_init()是一项核心工作。nand_scan()中完成的初始化多与通用的接口函数有关,反倒在移植时很少需要修改。

下面列出了需要在board_nand_init()完成初始化的操作:

1、 nandflash硬件初始化,即设置NFCONF、NFCONT 两个寄存器;

2、 寄存器访问接口指针IO_ADDR_R/W初始化;

3、 寄存器访问控制函数hwcontrol()初始化;这个函数没在nand_scan()中初始化,MTD也没有提供通用函数。所以这个函数不但要在board_nand_init()中初始化,还要自行编写该函数;

4、 R/B状态读取函数dev_ready()的初始化,这个函数没在nand_scan()中初始化,MTD也没有提供通用函数。所以这个函数不但要在board_nand_init()中初始化,还要自行编写该函数;

5、 片选函数select_chip()的初始化,这个函数在nand_scan()中初始化了,MTD也提供通用函数。但是该函数不具备完整功能,用户可在通用函数的基础上修改,也可以用户自行编写并在board_nand_init()中初始化;

6、 功能配置参数options初始化,在board_nand_init()中的功能配置参数options仅仅起到配置位宽的作用,初始化为0位宽为8;初始化为2位宽为16;

7、 ECC校验类型参数初始化,初始化为软件校验方式NAND_ECC_SOFT即可。 8、 chip_delay初始化,该值就是tR,这个值在芯片的datasheet中可以查到。在nand_scan()函数中被默认初始化20us。如果需要修改,可以在board_nand_init()初始化。

 

3.4.3)MTD驱动在fl2440上的移植步骤

在移植之前首先必须清楚板上nandflash芯片的具体型号,fl2440开发板上为现代HY27UF082G2B 其设备ID为DA,位宽为8,总容量为256MB。关于该芯片的详细情况在之前介绍过了,下面开始移植步骤:

1、 在drivers/nand/nand_ids.c中的结构体数组struct nand_flash_dev nand_flash_ids[]中找到与板上nandflash芯片的匹配项(至少保证设备id和芯片容量两项匹配),如果没有找到则需要自行添加,注意添加时nand_flash_ids[].options的配置;

2、 在include/configs/fl2440.h中的宏CONFIG_COMMANDS中添加CFG_CMD_NAND,使u-boot支持NAND。前面分析过如果不添加这个宏,nand_init()根本不会编译,也就是说NAND不会被初始化;

3、 在include/configs/fl2440.h中添加宏:

#define CFG_NAND_BASE 0 //nand基址地址,它在nand_scan()被赋值给IO_ADDR_R/W,这没有实际意义。IO_ADDR_R/W会在board_nand_init()重新初始化

#define CFG_MAX_NAND_DEVICE 1 //nandflash设备数

#define NAND_MAX_CHIPS 1 //每个nandflash设备物理片数

4、编写board_nand_init()函数完成上述7项初始化,下面详细分析了如何编写board_nand_init()。

1)在board/fl2440/中添加一个nand_flash.c的文件,在该文件中实现board_nand_init()及其他功能函数;

2)为方便board_nand_init()及其他功能函数对nandflash控制器的寄存器的访问,移植

时使用了一个小技巧:将nandflash控制器组定义成一个结构体,并将该寄存器组的基址地址定义成此结构体的指针,通过该指针对结构体中成员访问就是对nandflash控制器的寄存器的访问(u-boot中大量运用了这样的技巧来实现对各寄存器的访问,对该技巧的详细分析前面有):

a、在include/s3c24x0.h或include/s3c2410.h中添加结构体:

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;

} S3C2440_NAND;

注意该结构体中成员的顺序一定要和s3c2440 nand控制器寄存器组的顺序相同

b、在include/s3c2410.h中添加宏:

#define S3C2440_NAND_BASE 0x4E000000

//nandflash控制器的寄存器组基址地址

及函数:

static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)

{

return (S3C2440_NAND * const)S3C2440_NAND_BASE;

}

该函数将nandflash控制器的寄存器组基址地址作为寄存器组结构体的指针。

 

在nand_flash.c中实现board_nand_init()及其他功能函数代码如下: 3)

 

#include

#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)

#include

#include

 

DECLARE_GLOBAL_DATA_PTR;

 

#define S3C2440_NFSTAT_READY (1<<0)

#define S3C2440_NFCONT_nFCE (1<<1)

 

 

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;

}

}

 

 

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 NCE\n", __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;

 

 

 

default: //默认指向数据寄存器

chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;

break;

}

}

 

 

static int s3c2440_nand_devready(struct mtd_info *mtd)

{

S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

 

return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);

}

 

static void s3c2440_nand_inithw(void)

{

S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

 

#define TACLS 0

#define TWRPH0 1

#define TWRPH1 0

 

s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);

 

s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);

}

 

 

void board_nand_init(struct nand_chip *chip)

{

S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

 

s3c2440_nand_inithw();

//寄存器访问接口指针IO_ADDR_R/W初始化

chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;

chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;

 

//寄存器访问控制函数hwcontrol()初始化

chip->hwcontrol = s3c2440_nand_hwcontrol;

//R/B状态读取函数dev_ready()函数的初始化

chip->dev_ready = s3c2440_nand_devready;

//片选函数初始化

chip->select_chip = s3c2440_nand_select_chip;

chip->options = 0;

chip->eccmode = NAND_ECC_SOFT;

}

#endif

 

有必要对上述代码中的三个参数TACLS、TWRPH0、TWRPH1做一下说明。这个三个参数控制的是nandflash控制器输出信号CLE/ALE与WE的时序关系,必须正确地设置这三个参数才能使得nandflash控制器发出正确的时序控制信号。

TACLS:表征 ALE/CLE信号的建立时间(Time of ALE/CLE Setup),即ALE/CLE上升沿到WE下降沿的时间,见下时序图中时间段1

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四
时间段1=tCLS – tWP。tCLS、tWP的值在nandflash芯片的datasheet中查到,下图是HY27UF082G2A datasheet中查到的tCLS、tWP:

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

tCLS=15ns;tWP=15ns(最小值)所以时间段1的值为0。时间段1的值与TACLS的值关系为:时间段1 < HCLKS周期 * TACLS,不难计算出对HY27UF082G2A而言TACLS的值应大于等于0,源码中取为0。

要特别注意的是:有些芯片的datasheet中表述的tCLS与这里的tCLS – tWP等价。见下图:

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

TWRPH0:表征WE的脉冲宽度,即上时序图中的时间段2。WE的脉冲宽度即datasheet中的tWP(time of WE Pluse),tWP=15ns(最小值)。tWP与TWRPH0的关系为:

tWP < HCLK周期 * (TWRPH0+1) FL2440移植本u-boot后HCLK周期为10ns,所以不难计算出TWRPH0应大于等于1,该源码中取1。

TWRPH1:表征ALE/CLE的保持时间,即上时序图中时间段3。ALE/CLE的保持时间即WE上升沿到CLE/ALE下降沿时间段。在datasheet中即为tCLH或tALH(time of ale/cle hold),tCLH=tALH=5ns。tCLH或tALH与TWRPH1的关系为:

tCLH或tALH < HCLK周期 * (TWRPH1+1) FL2440移植本u-boot后HCLK周期为10ns,所以不难计算出TWRPH1应大于等于0,该源码中取0。

 

4)修改board/fl2440/makefile为:COBJS := fl2440.o flash.o nand_flash.o

 

至此,移植步骤完成。移植结果如下

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

上图中出现了NAND : 256MiB的字样,表示MTD驱动至少对板上的nandflash芯片初始化成功。键入命令:nand info查看nandflash的基本信息。键入命令nand scrub擦除整个nandflash芯片。(这样连出厂的坏块标记也擦除了,这些标记是不可恢复的,这势必导致nandflash读写的不可靠性。可是没办法,用nand erase命令擦除时提示“skipping bad block”擦除不成功,而且nand wirte、nand read命令执行都不成功。用nand scrub擦除整个nandflash芯片后一切都正常了,这是什么原因待查)

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

上图执行了loady命令下载了一个bootloader到0x31000000。然后执行命令nand write 0x31000000 0x0 0x20000 把这个bootloader烧写到nandflash 的地址0x0处,烧写数据长度为0x20000。提示烧写成功了。

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

u-boot中NAND <wbr>flash的MTD驱动移植(转) <wbr>四

从上图可以看出,出现了”Hit any key to antuboot: 0”的字样,所以u-boot自动进入了引导模式;出现了“nand read:device0 offset 0x200000,size 0x200000”的字样,所以bootcmd中的nand read命令被正确执行了;出现了”Booting image at 0x31000000 …”的字样,说明bootcmd中的bootm命令被正确执行了,并且bootm的参数也正确。在红线以上的内容为u-boot打印的调试信息。”Booting image at 0x31000000 …”以下,红线以上为do_bootm()函数打印的调试信息。红线以下为linux内核打印的调试信息,这说明引导内核成功了。但是此时的内核还不能完整地运行,因为还没有根文件系统和设备驱动。为了烧写根文件系统,u-boot还应该支持烧写yaffs根文件系统镜像,因为根文件系统镜像烧写nandflash中,而yaffs格式的镜像文件更适合于nandflash。所以下部是u-boot支持yaffs烧写的功能移植。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值