首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Flash >

uboot之nandflash有关程序解释

2013-01-21 
uboot之nandflash相关程序解释一 在start.S程序中,为了支持nandflash启动,修改&添加了了以下程序#ifndef C

uboot之nandflash相关程序解释
一 在start.S程序中,为了支持nandflash启动,修改&添加了了以下程序#ifndef CONFIG_SKIP_RELOCATE_UBOOTrelocate: /* relocate U-Boot to RAM    */adrr0, _start /* r0 <- current position of code   */ldrr1, _TEXT_BASE /* test if we run from flash or RAM */cmpr0, r1 /* don't reloc during debug         */beqclear_bss
/*上面代码判断是否从ram中执行uboot',如果不是,则执行下面代码,判断是从nor启动还是nand启动*/#define BWSCON 0x48000000ldr r4, =BWSCONldr 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启动ldrr2, _armboot_startldrr3, _bss_startsubr2, r3, r2 /* r2 <- size of armboot            */addr2, r0, r2 /* r2 <- source end address         */copy_loop:ldmiar0!, {r3-r10} /* copy from source address [r0]    */stmiar1!, {r3-r10} /* copy to   target address [r1]    */cmpr0, r2 /* until source end addreee [r2]    */blecopy_loopbclear_bss
#define NAND_SECTOR_SIZE_LP2048#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)*/ldrr2, _armboot_startldrr3, _bss_startsubr2, r3, r2 /* r2 <- size of armboot            */ldrr4, =NAND_BLOCK_MASK_LPaddr2, r2, r4bicr2, r2, r4//以上两句是将r2中保存的地址转换为以页大小为单位的整数bl nand_read_ll //执行nand读程序,将nand中的uboot代码读取到ram中指定位置。cmp r0, #0x0beq ok_nand_read
bad_nand_read:/*若nand_read_nll返回错误,则中间两个led点亮*/loop2: ldr r0,=GPBDATldr r1,[r0]bic r1,r1,#((1<<6)|(1<<7))orr r1,r1,#((1<<5)|(1<<8))str r1,[r0]b loop2    //infinite loop
ok_nand_read:mov r0, #0  ldr r1, =0x33D00000mov r2, #0x400           //比较1KB数据
go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatch subs r2, r2, #4beq clear_bssbne go_next
notmatch:/*若拷贝到ram中的数据有误,则边上两个led点亮*/loop3:ldr r0,=GPBDATldr r1,[r0]bic r1,r1,#((1<<5)|(1<<8))orr r1,r1,#((1<<7)|(1<<6))str r1,[r0]b loop3           //infinite loop#endif /* CONFIG_SKIP_RELOCATE_UBOOT */clear_bss:ldrr0, _bss_start /* find start of bss segment        */ldrr1, _bss_end /* stop here                        */movr2, #0x00000000 /* clear                            */
clbss_l:        str r2, [r0]/* clear loop...                    */addr0, r0, #4cmpr0, r1bleclbss_l
/*如果nand拷贝到ram没有问题,则所有led点亮*/ldr r0,=GPBDATldr r1,[r0]bic r1,r1,#((1<<6)|(1<<7)|(1<<5)|(1<<8))     /* orr r1,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 MiB\n", size / 1024);//把KB转换为MB单位,将nand的总大小打印出来}以上函数中CONFIG_SYS_MAX_NAND_DEVICE宏指nand设备的数量,移植nand支持时,应该在板子头文件中将其设置为1:#define CONFIG_CMD_NAND#define CONFIG_SYS_NAND_BASE0x4e000000#define CONFIG_SYS_MAX_NAND_DEVICE1#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结构的数组。其中mtd_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_WORKSelsemtd->name += gd->reloc_off;#endif} elsemtd->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_SPLnand->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_HWECCnand->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;#elsenand->ecc.mode = NAND_ECC_SOFT;#endifnand->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%02x\n", 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);elsewritel(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, busw, &nand_maf_id);
if (IS_ERR(type)) {#ifndef CONFIG_SYS_NAND_QUIET_TESTprintk(KERN_WARNING "No NAND device found!!!\n");#endifchip->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 DEBUGif (i > 1)printk(KERN_INFO "%d NAND chips detected\n", 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, NAND_CMD_READID, 0x00, -1);因为nandflash容易发生位反转的问题,为了防止读取错误,程序中连续读取了两次,进行了比较:chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);*maf_id = chip->read_byte(mtd);dev_id = chip->read_byte(mtd);chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -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,%02x\n", __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,3V 8-bit",0xDA, 0, 256, 0, LP_OPTIONS},{"NAND 512MiB 3,3V 8-bit",0xDC, 0, 512, 0, LP_OPTIONS},以上是三个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 MiB\n", 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, int cmd, unsigned int ctrl){struct nand_chip *chip = mtd->priv;struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, 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); elsewritel(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_init\n");
return 0;}uboot之nandflash有关程序解释

以上的函数内用到一个新的结构体和一个新的函数: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_BASE0x4E000000static 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;u32NFMECCD0;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.cCOBJS-y += s3c2440_nand.oCOBJS-$(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  0static 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_SIZE0x20000/* Total Size of Environment Sector */#define CONFIG_ENV_OFFSET0x40000不过本人调试这个saveenv命令有点儿问题,后面因为期末考试耽误了时间,现在重新起航,希望能解决这个问题










热点排行