加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

uboot之nandflash相关程序解释

发布时间:2020-12-15 18:19:11 所属栏目:百科 来源:网络整理
导读:一 在start.S程序中,为了支持nandflash启动,修改添加了了以下程序 #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM ?? ?*/ adr r0,_start /* r0 - current position of code ? */ ldr r1,_TEXT_BASE /* test if we run from flas
一 在start.S程序中,为了支持nandflash启动,修改&添加了了以下程序
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM?? ?*/
adr r0,_start /* r0 <- current position of code ? */
ldr r1,_TEXT_BASE /* test if we run from flash or RAM */
cmp r0,r1 /* don't reloc during debug ? ? ? ? */
beq clear_bss

/*上面代码判断是否从ram中执行uboot',如果不是,则执行下面代码,判断是从nor启动还是nand启动*/
#define BWSCON 0x48000000
ldr r4,=BWSCON
ldr r4,[r4]?
ands r4,r4,#6 ?//判断BWSCON[2:1]是否为00,如果是,Z=1即跳转到nand_boot?
beq nand_boot
? ? 通过判断BWSCON来判断是否从nand启动是根据数据手册中的以下内容:
BWSCON的[2:1]位如下
其中的OM[1:0]的作用如下:
OM[2:1]可以通过跳线或者指拨开关来改变电平高低
如果判断不是nand启动,则将执行下面程序,从norflash启动
ldr r2,_armboot_start
ldr r3,_bss_start
sub r2,r3,r2 /* r2 <- size of armboot ? ? ? ? ? ?*/
add r2,r0,r2 /* r2 <- source end address ? ? ? ? */
copy_loop:
ldmia r0!,{r3-r10} /* copy from source address [r0] ? ?*/
stmia r1!,{r3-r10} /* copy to ? target address [r1] ? ?*/
cmp r0,r2 /* until source end addreee [r2] ? ?*/
ble copy_loop
b clear_bss

#define NAND_SECTOR_SIZE_LP 2048
#define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1)
nand_boot:
ldr r0,=0x33D00000/*传递给C代码的第一个参数:u-boot在RAM中的起始地址*/
? mov r1,#0x0 ? ? ?/*传递给C代码的第二个参数:Nand Flash的起始地址*/
//? mov r2,#0x30000 ?/*传递给C代码的第三个参数:u-boot的长度大小(192k)*/
ldr r2,r2 /* r2 <- size of armboot ? ? ? ? ? ?*/
ldr r4,=NAND_BLOCK_MASK_LP
add r2,r2,r4
bic r2,r4//以上两句是将r2中保存的地址转换为以页大小为单位的整数
bl nand_read_ll //执行nand读程序,将nand中的uboot代码读取到ram中指定位置。
cmp r0,#0x0
beq ok_nand_read

bad_nand_read:
/*若nand_read_nll返回错误,则中间两个led点亮*/
loop2:?
ldr? r0,=GPBDAT
ldr? r1,[r0]
bic? r1,r1,#((1<<6)|(1<<7))
orr? r1,#((1<<5)|(1<<8))
str? r1,[r0]
b loop2 ? ?//infinite loop

ok_nand_read:
mov r0,#0
? ldr r1,=0x33D00000
mov r2,#0x400 ? ? ? ? ? //比较1KB数据

go_next:
ldr r3,[r0],#4
ldr r4,[r1],#4
teq r3,r4
bne notmatch
?
subs r2,#4
beq clear_bss
bne go_next

notmatch:
/*若拷贝到ram中的数据有误,则边上两个led点亮*/
loop3:
ldr? r0,#((1<<5)|(1<<8))
orr? r1,#((1<<7)|(1<<6))
str? r1,[r0]
b loop3 ? ? ? ? ? //infinite loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
clear_bss:
ldr r0,_bss_start /* find start of bss segment ? ? ? ?*/
ldr r1,_bss_end /* stop here ? ? ? ? ? ? ? ? ? ? ? ?*/
mov r2,#0x00000000 /* clear ? ? ? ? ? ? ? ? ? ? ? ? ? ?*/

clbss_l:
????????str r2,[r0]/* clear loop... ? ? ? ? ? ? ? ? ? ?*/
add r0,#4
cmp r0,r1
ble clbss_l

/*如果nand拷贝到ram没有问题,则所有led点亮*/
ldr? r0,#((1<<6)|(1<<7)|(1<<5)|(1<<8))
? ? ?/* orr?r1,#((1<<5)|(1<<8))*/
str? r1,[r0]
二?start_armboot中nand_init函数研究
void nand_init(void)
{
int i;
unsigned int size = 0;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
nand_init_chip(&nand_info[i],&nand_chip[i],base_address[i]);
size += nand_info[i].size / 1024;//这个size是mtd数据的成员变量,调用nand_init_chip函数时,通过nand_info改变
if (nand_curr_device == -1)
nand_curr_device = i;
}
printf("%u MiBn",size / 1024);//把KB转换为MB单位,将nand的总大小打印出来
}
以上函数中
CONFIG_SYS_MAX_NAND_DEVICE宏指nand设备的数量,移植nand支持时,应该在板子头文件中将其设置为1:
#define CONFIG_CMD_NAND
#define CONFIG_SYS_NAND_BASE 0x4e000000
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_MTD_NAND_VERIFY_WRITE 1
#define CONFIG_SYS_NAND_MAX_CHIPS ? 1
(以上几个宏定义是为了添加nand支持在板子头文件中添加的)
nand_info是一个mtd_info类型的数组,数组大小就是上面的宏CONFIG_SYS_MAX_NAND_DEVICE指定。
下面解释一下mtd(百度中找到的)
MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的
memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。CFI接口的
MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

所有组成MTD原始设备的Flash芯片必须是同类型(无论是interleave还是地址相连),在描述MTD原始设备数据结构中采用同一结构描述组成Flash芯片。每个MTD原始设备有一个mtd_info结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip结构的数组。其中_info、map_info和cfi_private结构用于描述MTD原始设备,因为组成MTD原始设备的NOR型Flash相同,cfi_ident结构用于描述Flash芯片信息;而flchip结构用于描述每个Flash芯片专有信息。

nand_chip同样为一个数组,大小就是上面的宏CONFIG_SYS_MAX_NAND_DEVICE指定,数据类型是struct nand_chip结构。主要成员是一些nand操作相关的函数。
base_address保存了nand设备的控制寄存器的起始地址,在上面的宏中可以看到设置为0x4e000000。
nand_init_chip的定义如下:
static void nand_init_chip(struct mtd_info *mtd,struct nand_chip *nand,
?? ulong base_addr)
{
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
int __attribute__((unused)) i = 0;

if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;

nand->IO_ADDR_R = nand->IO_ADDR_W = (void ?__iomem *)base_addr;
if (board_nand_init(nand) == 0) {
if (nand_scan(mtd,maxchips) == 0) {
if (!mtd->name)
mtd->name = (char *)default_nand_name;
#ifndef CONFIG_RELOC_FIXUP_WORKS
else
mtd->name += gd->reloc_off;
#endif
} else
mtd->name = NULL;
} else {
mtd->name = NULL;
mtd->size = 0;
}

}

1.下面具体看看board_nand_init函数内部:
该函数的实现与板子相关,我们使用2440的板子,应该重写该函数。
该函数定义在s3c2410_nand.c文件中
首先,下面这部分代码设置了NFCONF的三个参数
twrph0 = 3;
twrph1 = 0;
tacls = 0;
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg,&nand_reg->NFCONF);
这三个参数是nandflash的相关时序参数,数字表示有几个HCLK,具体意义可以参照韦东山的书135页。
这三个参数要设置到NFCONF寄存器相关的位,注意的是2410和2440具体对应的位有所不同
下面的程序:就是赋值了nand_chip结构体的一些参数,因为这个结构体用于nand软件操作,所以赋值的大多参数是函数指针。
/* initialize nand_chip data structure */
nand->IO_ADDR_R?= nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;

nand->select_chip = NULL;

/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif

/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2410_hwcontrol;

nand->dev_ready = s3c2410_dev_ready;

#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
nand->options = 0;
总之,board_nand_init就是初步设置了nand的时序和nand_chip这个从nand_init传进来的结构体的参数的赋值。
看看其中的函数s3c2410_hwcontrol:
该函数能够更改chip->IO_ADDR_W的值(用于确定写命令还是地址寄存器)以及进行片选还有写命令到nand控制器命令寄存器中,之后select_chip实际上是调用该函数完成的
static void s3c2410_hwcontrol(struct mtd_info *mtd,int cmd,unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2410_nand *nand = s3c2410_get_base_nand();

debugX(1,"hwcontrol(): 0x%02x 0x%02xn",cmd,ctrl);

if (ctrl &?NAND_CTRL_CHANGE) {//确定写命令还是地址寄存器
ulong IO_ADDR_W = (ulong)nand;

if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2410_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2410_ADDR_NALE;

chip->IO_ADDR_W = (void *)IO_ADDR_W;

if (ctrl &?NAND_NCE)//片选
writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,
?? ? ? &nand->NFCONF);
else
writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,
?? ? ? &nand->NFCONF);
}

if (cmd != NAND_CMD_NONE)//将命令写到chip->IO_ADDR_W指向的命令寄存器中
writeb(cmd,chip->IO_ADDR_W);
}

2.另一个是nand_scan函数
CONFIG_SYS_NAND_MAX_CHIPS这个宏传递给了该函数,这个宏指nand芯片的数量,我们应该在板子头文件中将其定义为1。
对于chip的理解,查到了一些资料:


nand_scan函数如下:
{
int ret;
ret =?nand_scan_ident(mtd,maxchips);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
nand_scan_ident函数如下:
int nand_scan_ident(struct mtd_info *mtd,int maxchips)
{
int i,busw,nand_maf_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev?*type;

/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
nand_set_defaults(chip,busw);

/* Read the flash type */
type = nand_get_flash_type(mtd,chip,&nand_maf_id);

if (IS_ERR(type)) {
#ifndef CONFIG_SYS_NAND_QUIET_TEST
printk(KERN_WARNING "No NAND device found!!!n");
#endif
chip->select_chip(mtd,-1);
return PTR_ERR(type);
}

/* Check for a chip array */
for (i = 1; i < maxchips; i++) {
chip->select_chip(mtd,i);
/* See comment in nand_get_flash_type for reset */
chip->cmdfunc(mtd,NAND_CMD_RESET,-1,-1);
/* Send the command for reading device ID */
chip->cmdfunc(mtd,NAND_CMD_READID,0x00,-1);
/* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) ||
?? ?type->id != chip->read_byte(mtd))
break;
}
#ifdef DEBUG
if (i > 1)
printk(KERN_INFO "%d NAND chips detectedn",i);
#endif

/* Store the number of chips and calc total size for mtd */
chip->numchips = i;
mtd->size = i * chip->chipsize;

return 0;
}
struct nand_flash_dev结构体定义了一些nand的硬件信息:
struct nand_flash_dev {
char *name;
int id;
unsigned long pagesize;
unsigned long chipsize;
unsigned long erasesize;
unsigned long options;
};
nand_set_defaults函数:
该函数设置了一些nand_chip结构体中没有赋值的函数指针,这些函数之后将会用到,但是之前没有设置,注意之前在board_nand_init函数就是设置了一部分函数指针,比较重要的是s3c2410_hwcontrol
如函数nand_set_defaults中设置了:
if (!chip->select_chip)
chip->select_chip = nand_select_chip;
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
而nand_select_chip是通过调用chip->cmd_ctrl来实现功能的,而chip->cmd_ctrl在前面board_nand_init函数中已经初始化为s3c2410_hwcontrol,当然,这个hwcontrol是可以并且应当自己实现的,因为他是依赖于板子的。
另外一个函数chip->cmdfunc = nand_command,nand_command也调用了chip->cmd_ctrl函数,也即是s3c2410_hwcontrol,通过该函数的最后一条代码writeb(cmd,chip->IO_ADDR_W);将指定的命令写到nand命令控制寄存器中去。
下面来看看nand_get_flash_type函数:
这个函数通过ReadID命令读取了nandflash类型相关的参数,读取命令如下所示:
chip->cmdfunc(mtd,-1);
因为nandflash容易发生位反转的问题,为了防止读取错误,程序中连续读取了两次,进行了比较:
chip->cmdfunc(mtd,-1);
*maf_id = chip->read_byte(mtd);
dev_id = chip->read_byte(mtd);
chip->cmdfunc(mtd,-1);
tmp_manf = chip->read_byte(mtd);
tmp_id = chip->read_byte(mtd);
if (tmp_manf != *maf_id || tmp_id != dev_id)?{
printk(KERN_INFO "%s: second ID read did not match "
?? ? ? "%02x,%02x against %02x,%02xn",__func__,
?? ? ? *maf_id,dev_id,tmp_manf,tmp_id);
return ERR_PTR(-ENODEV);
}
下面的程序,通过循环比较nand_flash_ids数组中的每个struct nand_flash_dev?类型的变量的设备ID参数与之前读取到的参数,如果两个ID参数相同,说明得到了nand的类型(即得到了该类型nand对应的struct nand_flash_dev结构变量),将该结构变量用type指针指向。
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (dev_id == nand_flash_ids[i].id) {
type = ?&nand_flash_ids[i];
break;
}
}
下面通过type得到chip的大小,因为单位是MB,转换为BYTE:
这个chipsize在后面计算总的nand的容量时就用上了
chip->chipsize?= (uint64_t)type->chipsize << 20;

下面的程序用于设置mtd的其他size相关类的参数,分为两种情况,要么type的pagesize为0,则还要通过读取第三个和第四个ID来确定页的大小,冗余区大小,块的大小以及位宽等,要么type的pagesize不为0,则通过type即可确定各个size参数。
if (!type->pagesize) {
int extid;
/* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = chip->read_byte(mtd);
/* The 4th id byte is the important one */
extid = chip->read_byte(mtd);
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x3);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;

} else {
/*
?* Old devices have chip data hardcoded in the device id table
?*/
mtd->erasesize = type->erasesize;
mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32;
busw = type->options & NAND_BUSWIDTH_16;
}
{"NAND 64MiB 3,3V 8-bit",0x76,512,64,0x4000,0},
{"NAND 256MiB 3,0xDA,256,LP_OPTIONS},
{"NAND 512MiB 3,0xDC,
以上是三个nand_flash_ids数组中的变量,
第一个是K9F1208U0C对应的
第二个是K9F1208U0C对应的
第三个是本人tq2440对应的
本人tq2440的芯片应该是K9F1208U0C,但是因为在实际调试中得到的id是0xdc,这个问题遗留到现在还没有解决
上面程序通过读取第三个和第四个id确定size类参数可以参照下表:


之后的程序本人没有细读,粗略浏览可以看出是在比对nand的生产信息以及对chip->options等变量的赋值……
继续回到nand_scan_ident函数:
该函数先根据nand_get_flash_type函数返回值判断了nand类型是否存在,然后对nand芯片进行了初始化,并再次读取比较了ID,最后将刚才在nand_get_flash_type函数中得到的chip->chipsize?= (uint64_t)type->chipsize << 20;计算到mtd->size中,以便稍后统计总的nand容量。
终于跳出了nand_scan_ident函数,下面是nand_scan_tail函数,本人没有再费心去读,把程序的功能注释摘录如下:
?* nand_scan_tail - [NAND Interface] Scan for the NAND device
?* @mtd: ?? ?MTD device structure
?*
?* 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.
根据意思,应该是赋值一些没有值的mtd的函数指针成员,以后要有需要再读吧……
这样就又跳到了函数nand_init_chip,该函数下面没什么重要代码,最后又返回nand_init函数的代码:
size += nand_info[i].size / 1024;
即将通过nand_init_chip得到的mtd->size统计起来,最后在打印时,转换成单位MB,从而打印出nandflash的总容量:
printf("%u MiBn",size / 1024);
至此,nand_init函数分析完成

三 s3c2440_nand.c文件
为了支持2440的nand,应该重写s3c2410_nand.c文件为s3c2440_nand.c文件,以区分2410。
重写可以参照2410来写:

#include <common.h>
#include <nand.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <asm/io.h>
#include <asm/arch/s3c2410.h>

#define S3C2440_NFCONT_nFCE (1<<1)
#define S3C2440_ADDR_NCLE 0x0c
#define S3C2440_ADDR_NALE 0x08

#if 0
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASEX 0x4e000000 //Nand配置寄存器基地址
#define NFCONFX __REGi(NF_BASEX + 0x0) //偏移后还是得到配置寄存器基地址
#define NFCONTX __REGi(NF_BASEX + 0x4) //偏移后得到Nand控制寄存器基地址
#define NFCMDX __REGb(NF_BASEX + 0x8) //偏移后得到Nand指令寄存器基地址
#define NFADDRX __REGb(NF_BASEX + 0xc) //偏移后得到Nand地址寄存器基地址
#define NFDATAX __REGb(NF_BASEX + 0x10) //偏移后得到Nand数据寄存器基地址
#define NFMECCD0X __REGi(NF_BASEX + 0x14) //偏移后得到Nand主数据区域ECC0寄存器基地址
#define NFMECCD1X __REGi(NF_BASEX + 0x18) //偏移后得到Nand主数据区域ECC1寄存器基地址
#define NFSECCDX __REGi(NF_BASEX + 0x1C) //偏移后得到Nand空闲区域ECC寄存器基地址
#define NFSTATX __REGb(NF_BASEX + 0x20) //偏移后得到Nand状态寄存器基地址
#define NFSTAT0X __REGi(NF_BASEX + 0x24) //偏移后得到Nand ECC0状态寄存器基地址
#define NFSTAT1X __REGi(NF_BASEX + 0x28) //偏移后得到Nand ECC1状态寄存器基地址
#define NFMECC0X __REGi(NF_BASEX + 0x2C) //偏移后得到Nand主数据区域ECC0状态寄存器基地址
#define NFMECC1X __REGi(NF_BASEX + 0x30) //偏移后得到Nand主数据区域ECC1状态寄存器基地址
#define NFSECCX __REGi(NF_BASEX + 0x34) //偏移后得到Nand空闲区域ECC状态寄存器基地址
#define NFSBLKX __REGi(NF_BASEX + 0x38) //偏移后得到Nand块开始地址
#define NFEBLKX __REGi(NF_BASEX + 0x3c) //偏移后得到Nand块结束地址
#endif
//hwcontrol是必须要重写的函数,因为之后的nand_select_chip和nand_command函数都通过调用该函数来完成主要功能
static void?s3c2440_hwcontrol(struct mtd_info *mtd,unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();

debugX(1,ctrl);

if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;

if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2440_ADDR_NALE;

chip->IO_ADDR_W = (void *)IO_ADDR_W;

if (ctrl & NAND_NCE)
writel(readl(&nand->NFCONT) & ~S3C2440_NFCONT_nFCE,?&nand->NFCONT);
?
else
writel(readl(&nand->NFCONT) | S3C2440_NFCONT_nFCE,&nand->NFCONT);
}

if (cmd != NAND_CMD_NONE)
writeb(cmd,chip->IO_ADDR_W);
}
//该 函数返回nand的状态(busy/ready)
static int?s3c2440_dev_ready(struct mtd_info *mtd)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
//return (readl(&nand->NFSTAT)&0x01);
return ((nand->NFSTAT)&0x01);
}
int?board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int32_t cfg1;
u_int8_t tacls,twrph0,twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();//得到时钟控制寄存器访问指针
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();//得到nand控制寄存器访问指针

debugX(1,"board_nand_init()n");

writel(readl(&clk_power->CLKCON) | (1 << 4),&clk_power->CLKCON);//使能nand控制器时钟

/* initialize hardware */
twrph0 = 4;
twrph1 = 2;
tacls = 0;

cfg = ((tacls<<12)|(twrph0<<8)|(twrph1<<4));
nand_reg->NFCONF=cfg;
cfg1 = ((1<<6)|(1<<4)|(0<<1)|(1<<0));//启动nand控制器,初始化ECC等。
nand_reg->NFCONT=cfg1;
/* initialize nand_chip data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;//设置读写命令或者地址的寄存器地址
/*nand->select_chip = NULL;*/


/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2440_hwcontrol;

nand->dev_ready = s3c2440_dev_ready;

nand->ecc.mode = NAND_ECC_SOFT;

nand->options = 0;
debugX(1,"end of nand_initn");

return 0;
}



以上的函数内用到一个新的结构体和一个新的函数:
struct s3c2440_nand *nand = s3c2440_get_base_nand();
仿照s3c2410的相关函数和结构体的定义,我们对这个函数和结构体进行定义:
在include/asm/(注意asm目录是一个链接目录,在uboot编译之前的config时生成)
asm下有目录arch/(注意arch目录也是链接目录,config时生成)
在arch目录下,我们复制s3c2410.h为s3c2440.h,并在其中添加
#define?S3C2440_NAND_BASE0x4E000000
static inline struct s3c2440_nand *s3c2440_get_base_nand(void)
{
return (struct s3c2440_nand *)S3C2440_NAND_BASE;
}
注意,上面的函数也可以不用重写s3c2440.h,可以直接在s3c2410.h文件中添加,本人为了方便区别,就重写了该文件。
如果是复制s3c2410.h而重写了文件s3c2440.h,则应当再修改文件s3c24x0_cpu.h如下:
#ifdef CONFIG_S3C2400
#include <asm/arch/s3c2400.h>
#elif defined CONFIG_S3C2440
#include <asm/arch/s3c2440.h>
#elif defined CONFIG_S3C2410
#include <asm/arch/s3c2410.h>
#else
#error Please define the s3c24x0 cpu type
#endif
这样在需要调用s3c2440_get_base_nand等函数时,直接包含s3c24x0_cpu.h头文件就可以了。
在arch目录下,打开s3c24x0.h文件,在其中添加
struct s3c2440_nand?{
u32? NFCONF;
u32? NFCONT;
u32? NFCMD;
u32? NFADDR;
u32? NFDATA;
u32 NFMECCD0;
u32? NFMECCD1;
u32? NFSECCD;
u32? NFSTAT;
u32? NFESTAT0;
u32? NFESTAT1;
u32? NFMECC0;
u32? NFMECC1;
u32? NFMECC;
u32? NFSBLK;
u32? NFEBLK;
};
注意上面这段结构体定义文档《我的经验之_移植UBOOT_2010TQ2440》中写的顺序有误,需要仔细核对
最后,要在drivers/mtd/nand/Makefile文件中修改来编译出s3c2440_nand.c
COBJS-y += s3c2440_nand.o
COBJS-$(CONFIG_NAND_S3C2410) += s3c2440_nand.o
这样烧进板子去,应该能够支持nand_init()函数了,也就是支持nand读取了。启动时能够看到显示nand的容量。
为了增加nand启动,需要添加文件nand_read.c文件,在板子文件夹下添加即可:

#define NF_BASE 0x4E000000 //Nand Flash 配置寄存器基地址
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NFCONF __REGi(NF_BASE + 0x0 ) //通过偏移量还是得到配置寄存器基地址
#define NFCONT __REGi(NF_BASE + 0x4 ) //通过偏移量得到控制寄存器基地址
#define NFCMD __REGb(NF_BASE + 0x8 ) //通过偏移量得到指令寄存器基地址
#define NFADDR __REGb(NF_BASE + 0xC ) //通过偏移量得到地址寄存器基地址
#define NFDATA __REGb(NF_BASE + 0x10) //通过偏移量得到数据寄存器基地址
#define NFSTAT __REGb(NF_BASE + 0x20) //通过偏移量得到状态寄存器基地址
#define NAND_SECTOR_SIZE_LP 2048
#define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1)
#define TACLS ? 0
#define TWRPH0 ?3
#define TWRPH1 ?0
static void delay(void){
int i;
for(i=0; i<10; i++);
}
void?nand_reset_ll(void){
NFCONT&=~(1<<1);
NFCMD=0xff;
delay();
while(!(NFSTAT&0x01)){
delay();
}
NFCONT|=(1<<1);
}
void?nand_init_ll(void){
NFCONF=((TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4));
NFCONT=((1<<4)|(1<<1)|(1<<0));
nand_reset_ll();
}
void?badblockjump(int* addr){//坏块检测函数是参照tq2440给的uboot的nand相关函数写的
int col,page;
char dat;
col=*addr& NAND_BLOCK_MASK_LP;
page = *addr/ NAND_SECTOR_SIZE_LP;//行地址(页地址)
NFCMD=0; ?//发送读取命令
delay();
NFADDR = 5;
delay();
NFADDR = 8;
delay();
NFADDR = page & 0xff;
delay();
NFADDR = (page >> 8) & 0xff;
delay();
NFADDR = (page >> 16) & 0x03;
delay();
NFCMD=0x30;//因为是大页读取,这里还需要发送30h命令
delay();
while(!(NFSTAT&0x01)){//等待芯片响应
delay();
}
dat=NFDATA;
NFCONT|=(1<<1);//不选中芯片
if(dat!=0xff)
*addr+=131072;
NFCONT&=~(1<<1);//片选选中芯片
}
int?nand_read_ll(unsigned char *buf,unsigned long start_addr,int size){
int i,j,col,page;
nand_init_ll();
if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
? return -1;
? }
NFCONT&=~(1<<1);//片选选中芯片
for(i=start_addr;i<(start_addr+size);){
badblockjump(&i);
NFCMD=0; ?//发送读取命令
delay();
col = i & NAND_BLOCK_MASK_LP;//列地址
page = i / NAND_SECTOR_SIZE_LP;//行地址(页地址)
NFADDR = col & 0xff;
delay();
NFADDR = (col >> 8) & 0x0f;
delay();
NFADDR = page & 0xff;
delay();
NFADDR = (page >> 8) & 0xff;
delay();
NFADDR = (page >> 16) & 0x03;
delay();
NFCMD=0x30;//因为是大页读取,这里还需要发送30h命令
delay();
while(!(NFSTAT&0x01)){//等待芯片响应
delay();
}
for(j=0;j<NAND_SECTOR_SIZE_LP;j++,i++){ //依次读取一页

*buf=NFDATA;
buf++;
}
}
NFCONT|=(1<<1);//不选中芯片
return 0;
}

修改连接文件u-boot.lds如下:
arch/arm/cpu/arm920t/start.o(.text)
board/samsung/Crt2440/lowlevel_init.o (.text)
?? ?? board/samsung/Crt2440/nand_read.o (.text)
修改的原因是一定要使得nand_read.o位于前4KB,nand启动时前4KB的代码会自动拷贝到nand控制器内部的4KBram中,这4KB的ram被映射到整个地址空间的起始,start.S通过调用nand_read.o文件中的函数即可将nand中的uboot代码拷贝到ram中,拷贝到ram中的地址由板子文件夹下的config.mk文件所确定:
TEXT_BASE =0x33D00000
另外,在start.S中支持nand启动的代码本文之前已经分析了,此处不再多说。
最后是对板子头文件的修改:(为了支持saveenv命令)
//#define CONFIG_ENV_IS_IN_FLASH1
#define CONFIG_ENV_IS_IN_NAND? 1
#define CONFIG_ENV_SIZE 0x20000/* Total Size of Environment Sector */
#define CONFIG_ENV_OFFSET 0x40000
不过本人调试这个saveenv命令有点儿问题,后面因为期末考试耽误了时间,现在重新起航,希望能解决这个问题

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读