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

u-boot-2009.08在2440上的移植详解(三)

发布时间:2020-12-15 18:30:26 所属栏目:百科 来源:网络整理
导读:5)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上Nand Flash的支持) 。 目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。 首先,在include/configs/my2440.h头文件中定义Nand要用到的宏和寄

5)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上Nand Flash的支持)
目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。

首先,在include/configs/my2440.h头文件中定义Nand要用到的宏和寄存器,如下:

#gedit include/configs/my2440.h??//在文件末尾加入以下Nand Flash相关定义

/*
?*?Nand flash register?and?envionment variables?
?*
/
#define CONFIG_S3C2440_NAND_BOOT? 1

#define NAND_CTL_BASE??0x4E000000??
//Nand Flash配置寄存器基地址,查2440手册可得知

#define STACK_BASE??0x33F00000?????//定义堆栈的地址
#define STACK_SIZE??0x8000?????????//堆栈的长度大小

#define oNFCONF??0x00?//相对Nand配置寄存器基地址的偏移量,还是配置寄存器的基地址
#define oNFCONT??0x04?//相对Nand配置寄存器基地址的偏移量,可得到控制寄存器的基地址(0x4E000004)

#define oNFADDR??0x0c?//相对Nand配置寄存器基地址的偏移量,可得到地址寄存器的基地址(0x4E00000c)
#define oNFDATA??0x10?//相对Nand配置寄存器基地址的偏移量,可得到数据寄存器的基地址(0x4E000010)
#define oNFCMD???0x08?//相对Nand配置寄存器基地址的偏移量,可得到指令寄存器的基地址(0x4E000008)
#define oNFSTAT??0x20?
//相对Nand配置寄存器基地址的偏移量,可得到状态寄存器的基地址(0x4E000020)

#define oNFECC?? 0x2c?//相对Nand配置寄存器基地址的偏移量,可得到ECC寄存器的基地址(0x4E00002c)

其次,修改cpu/arm920t/start.S这个文件,使u-boot从Nand Flash启动,在上一节中提过,u-boot默认是从Nor Flash启动的。修改部分如下:

#gedit cpu/arm920t/start.S

//注意:在上一篇Nor Flash启动中,我们为了把u-boot用supervivi下载到内存中运行而屏蔽掉这段有关CPU初始化的代码。而现在我们要把u-boot下载到Nand Flash中,从Nand Flash启动,所以现在要恢复这段代码。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT?
????bl?cpu_init_crit
#endif

?

#if 0?//屏蔽掉u-boot中的从Nor Flash启动部分
#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?stack_setup

??? 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
#endif?/* CONFIG_SKIP_RELOCATE_UBOOT */
#endif

?

//下面添加2440中u-boot从Nand Flash启动

#ifdef CONFIG_S3C2440_NAND_BOOT
??? mov r1,#NAND_CTL_BASE???//复位Nand Flash
??? ldr r2,=( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
??? str r2,[r1,#oNFCONF]?? //设置配置寄存器的初始值,参考s3c2440手册
??? ldr r2,#oNFCONF]

??? ldr r2,=( (1<<4)|(0<<1)|(1<<0) )
??? str r2,#oNFCONT]?? //设置控制寄存器
??? ldr r2,#oNFCONT]

??? ldr r2,=(0x6)???????????//RnB Clear
??? str r2,#oNFSTAT]
??? ldr r2,#oNFSTAT]
??? mov r2,#0xff????????????//复位command
??? strb r2,#oNFCMD]

??? mov r3,#0??????????????//等待
nand1:
??? add r3,#0x1
??? cmp r3,#0xa
??? blt nand1

nand2:
??? ldr r2,#oNFSTAT]?? //等待就绪
??? tst r2,#0x4
??? beq nand2

??? ldr r2,#oNFCONT]
??? orr r2,r2,#0x2???????? //取消片选
??? str r2,#oNFCONT]

???//get read to call C functions (for nand_read())
??? ldr sp,DW_STACK_START?? //为C代码准备堆栈,DW_STACK_START定义在下面?
??? mov fp,#0???????????????

???//copy U-Boot to RAM
??? ldr r0,=TEXT_BASE//传递给C代码的第一个参数:u-boot在RAM中的起始地址
??? mov r1,#0x0??????//传递给C代码的第二个参数:Nand Flash的起始地址
??? mov r2,#0x30000??//传递给C代码的第三个参数:u-boot的长度大小(128k)
??? bl nand_read_ll???//此处调用C代码中读Nand的函数,现在还没有要自己编写实现
??? tst r0,#0x0
??? beq ok_nand_read

bad_nand_read:
??? loop2: b loop2????//infinite loop

ok_nand_read:
??? //检查搬移后的数据,如果前4k完全相同,表示搬移成功
??? mov r0,#0
??? ldr r1,=TEXT_BASE
??? mov r2,#0x400?????????? //4 bytes * 1024 = 4K-bytes
go_next:
??? ldr r3,[r0],#4
??? ldr r4,[r1],#4
??? teq r3,r4
??? bne notmatch
??? subs r2,#4
??? beq stack_setup
??? bne go_next

notmatch:
??? loop3: b loop3?????????? //infinite loop

#endif//CONFIG_S3C2440_NAND_BOOT

?

_start_armboot:?.word start_armboot//在这一句的下面加上DW_STACK_START的定义

.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

再次,在board/samsung/my2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:

#gedit board/samsung/my2440/nand_read.c??//新建一个nand_read.c文件,记得保存


#include <config.h>


#define NF_BASE ? 0x4E000000 ?


#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_CHIP_ENABLE ?(NFCONT &= ~(1<<1)) ?
#define NAND_CHIP_DISABLE (NFCONT |= (1<<1)) ??
#define NAND_CLEAR_RB ? ? (NFSTAT |= (1<<2))
#define NAND_DETECT_RB ? ?{ while(! (NFSTAT&(1<<2)) );}


#define NAND_SECTOR_SIZE 2048?
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)


/* low level nand read function */
void nand_select(void)
{
NFCONT &= ~(1<<1);
}


void nand_deselect(void)
{
NFCONT |= (1<<1);
}


void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMD = cmd;
for (i = 0; i < 10; i++);
}


void nand_addr(unsigned int addr)
{
unsigned int col ?= addr % 2048;
unsigned int page = addr / 2048;
volatile int i;


NFADDR = col & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (col >> 8) & 0xff;
for (i = 0; i < 10; i++);

NFADDR ?= page & 0xff;
for (i = 0; i < 10; i++);
NFADDR ?= (page >> 8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR ?= (page >> 16) & 0xff;
for (i = 0; i < 10; i++);
}


void nand_wait_ready_ll(void)
{
while (!(NFSTAT & 1));
}


unsigned char nand_data(void)
{
return NFDATA;
}


int nand_read_ll(unsigned char *buf,unsigned long addr,int len)
{
int col = addr % 2048;
int i = 0;

/* 1. 选脰 */
nand_select();


while (i < len)
{
nand_cmd(0x00);


nand_addr(addr);


nand_cmd(0x30);


nand_wait_ready_ll();


for (; (col < 2048) && (i < len); col++)
{
buf[i] = nand_data();
i++;
addr++;
}

col = 0;
}


nand_deselect();
? ? return 0;
}


0 - 7位:字节在上半部、下半部及OOB内的偏移地址
???? 8位:值为0代表对一页内前256个字节进行寻址,值为1代表对一页内后256个字节进行寻址
?9-13位:对页进行寻址
14-25位:对块进行寻址

注意:上面这段代码中对Nand进行寻址的部分,这跟具体的Nand Flash的寻址方式有关。根据我们开发板上的Nand Flash(K9F1208U0C)数据手册得知,片内寻址是采用26位地址形式。从第0位开始分四次通过I/O0-I/O7进行传送,并进行片内寻址。具体含义和结构图如下(相关概念参考Nand数据手册):

然后,在board/samsung/my2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:

COBJS????:=?my2440.o flash.o?nand_read.o

还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:

.text?:
{
????cpu/arm920t/start.o????(.text)
????
board/samsung/my2440/lowlevel_init.o?(.text)
????board/samsung/my2440/nand_read.o?(.text)

????*)
}

最后编译u-boot,生成u-boot.bin文件。然后先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,打开电源就从Nand Flash启动了,启动结果图如下:

从上面的运行图看,显然现在的Nand还不能做任何事情,而且也没有显示有关Nand的任何信息,所以只能说明上面的这些步骤只是完成了Nand移植的Stage1部分。下面我们来添加我们开发板上的Nand Flash(K9F1208U0C)的Stage2部分的有关操作支持。

6)现在进入u-boot的第二阶段(添加Nand Flash(K9F1208U0C)的有关操作支持)。
在上一节中我们说过,通常在嵌入式bootloader中,有两种方式来引导启动内核:从Nor Flash启动和从Nand Flash启动,但不管是从Nor启动或者从Nand启动,进入第二阶段以后,两者的执行流程是相同的。

当u-boot的start.S运行到“_start_armboot:?.word start_armboot”时,就会调用lib_arm/board.c中的start_armboot函数,至此u-boot正式进入第二阶段。此时注意:以前较早的u-boot版本进入第二阶段后,对Nand Flash的支持有新旧两套代码,新代码在drivers/nand目录下,旧代码在drivers/nand_legacy目录下,CFG_NAND_LEGACY宏决定了使用哪套代码,如果定义了该宏就使用旧代码,否则使用新代码。但是现在的u-boot-2009.08版本对Nand的初始化、读写实现是基于最近的Linux内核的MTD架构,删除了以前传统的执行方法,使移植没有以前那样复杂了,实现Nand的操作和基本命令都直接在drivers/mtd/nand目录下(在doc/README.nand中讲得很清楚)。下面我们结合代码来分析一下u-boot在第二阶段的执行流程:

1.lib_arm/board.c文件中的start_armboot函数调用了drivers/mtd/nand/nand.c文件中的nand_init函数,如下:
? #if defined(CONFIG_CMD_NAND)?//可以看到CONFIG_CMD_NAND宏决定了Nand的初始化
????? puts ("NAND: ");
????? nand_init();
? #endif

2.nand_init调用了同文件下的nand_init_chip函数;
3.nand_init_chip函数调用drivers/mtd/nand/s3c2410_nand.c文件下的board_nand_init函数,然后再调用drivers/mtd/nand/nand_base.c函数中的nand_scan函数;
4.nand_scan函数调用了同文件下的nand_scan_ident函数等。

因为2440和2410对nand控制器的操作有很大的不同,所以s3c2410_nand.c下对nand操作的函数就是我们做移植需要实现的部分了,他与具体的Nand Flash硬件密切相关。为了区别与2410,这里我们就重新建立一个s3c2440_nand.c文件,在这里面来实现对nand的操作,代码如下:

#gedit drivers/mtd/nand/s3c2440_nand.c???//新建s3c2440_nand.c文件

#include <common.h>

#if 0
#define DEBUGN??? printf
#else
#define DEBUGN(x,args ...) {}
#endif

#include <nand.h>
#include <s3c2410.h>
#include <asm/io.h>

?

#define __REGb(x)??? (*(volatile unsigned char *)(x))
#define __REGi(x)??? (*(volatile unsigned int *)(x))

?

#define NF_BASE? 0x4e000000?????????????//Nand配置寄存器基地址
#define NFCONF?? __REGi(NF_BASE + 0x0)??//偏移后还是得到配置寄存器基地址
#define NFCONT?? __REGi(NF_BASE + 0x4)??//偏移后得到Nand控制寄存器基地址
#define NFCMD??? __REGb(NF_BASE + 0x8)??//偏移后得到Nand指令寄存器基地址
#define NFADDR?? __REGb(NF_BASE + 0xc)??//偏移后得到Nand地址寄存器基地址
#define NFDATA?? __REGb(NF_BASE + 0x10)?//偏移后得到Nand数据寄存器基地址
#define NFMECCD0 __REGi(NF_BASE + 0x14)?//偏移后得到Nand主数据区域ECC0寄存器基地址
#define NFMECCD1 __REGi(NF_BASE + 0x18)?//偏移后得到Nand主数据区域ECC1寄存器基地址
#define NFSECCD? __REGi(NF_BASE + 0x1C)?//偏移后得到Nand空闲区域ECC寄存器基地址
#define NFSTAT?? __REGb(NF_BASE + 0x20)?//偏移后得到Nand状态寄存器基地址
#define NFSTAT0? __REGi(NF_BASE + 0x24)?//偏移后得到Nand ECC0状态寄存器基地址
#define NFSTAT1? __REGi(NF_BASE + 0x28)?//偏移后得到Nand ECC1状态寄存器基地址
#define NFMECC0? __REGi(NF_BASE + 0x2C)?//偏移后得到Nand主数据区域ECC0状态寄存器基地址
#define NFMECC1? __REGi(NF_BASE + 0x30)?//偏移后得到Nand主数据区域ECC1状态寄存器基地址
#define NFSECC?? __REGi(NF_BASE + 0x34)?//偏移后得到Nand空闲区域ECC状态寄存器基地址
#define NFSBLK?? __REGi(NF_BASE + 0x38)?//偏移后得到Nand块开始地址
#define NFEBLK?? __REGi(NF_BASE + 0x3c)?
//偏移后得到Nand块结束地址

?

#define S3C2440_NFCONT_nCE? (1<<1)
#define S3C2440_ADDR_NALE?? 0x0c
#define S3C2440_ADDR_NCLE?? 0x08

?

ulong IO_ADDR_W = NF_BASE;

?

static void s3c2440_hwcontrol(struct mtd_info *mtd,int cmd,unsigned int ctrl)
{
??? struct nand_chip *chip = mtd->priv;

??? DEBUGN("hwcontrol(): 0x%02x 0x%02xn",cmd,ctrl);

??? if (ctrl & NAND_CTRL_CHANGE) {
??????? IO_ADDR_W = NF_BASE;

??????? if (!(ctrl & NAND_CLE))????????????????//要写的是地址
??????????? IO_ADDR_W |= S3C2440_ADDR_NALE;
??????? if (!(ctrl & NAND_ALE))????????????????//要写的是命令
??????????? IO_ADDR_W |= S3C2440_ADDR_NCLE;

??????? if (ctrl & NAND_NCE)?
??????????? NFCONT &= ~S3C2440_NFCONT_nCE;????//使能nand flash
??????? else
??????????? NFCONT |= S3C2440_NFCONT_nCE;?????//禁止nand flash
??? }

??? if (cmd != NAND_CMD_NONE)
??????? writeb(cmd,(void *)IO_ADDR_W);
}

?

static int s3c2440_dev_ready(struct mtd_info *mtd)
{
??? DEBUGN("dev_readyn");
??? return (NFSTAT & 0x01);
}

?

int board_nand_init(struct nand_chip *nand)
{
??? u_int32_t cfg;
??? u_int8_t tacls,twrph0,twrph1;
??? S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

?

??? DEBUGN("board_nand_init()n");

?

??? clk_power->CLKCON |= (1 << 4);

?

??? twrph0 = 4; twrph1 = 2; tacls = 0;

?

??? cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4);
??? NFCONF = cfg;

?

??? cfg = (1<<6)|(1<<4)|(0<<1)|(1<<0);
??? NFCONT = cfg;

?

????/* initialize nand_chip data structure */
??? nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;

?

????/* read_buf and write_buf are default */
??? /* read_byte and write_byte are default */

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

?

??? nand->dev_ready = s3c2440_dev_ready;

?

??? return 0;
}



其次,在开发板配置文件include/configs/my2440.h文件中定义支持Nand操作的相关宏,如下:

#gedit include/configs/my2440.h

/* Command line configuration. */
#define CONFIG_CMD_NAND
#define CONFIG_CMDLINE_EDITING

#ifdef CONFIG_CMDLINE_EDITING
#undef CONFIG_AUTO_COMPLETE
#else
#define CONFIG_AUTO_COMPLETE
#endif

?

/* NAND flash settings */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_SYS_NAND_BASE????????????0x4E000000?//Nand配置寄存器基地址
#define CONFIG_SYS_MAX_NAND_DEVICE??????1?
#define CONFIG_MTD_NAND_VERIFY_WRITE??? 1?
//#define NAND_SAMSUNG_LP_OPTIONS???????1??//注意:我们这里是64M的Nand Flash,所以不用,如果是128M的大块Nand Flash,则需加上
#endif

?

然后,在drivers/mtd/nand/Makefile文件中添加s3c2440_nand.c的编译项,如下:

# gedit drivers/mtd/nand/Makefile


COBJS-$(CONFIG_NAND_S3C2440)?+=?s3c2440_nand.o



最后,重新编译u-boot并使用supervivi的a命令下载到Nand Flash中,把开发板调到Nand档从Nand启动,启动结果图如下:

从上图可以看出,现在u-boot已经对我们开发板上64M的Nand Flash完全支持了。Nand相关的基本命令也都可以正常使用了。

补充内容:

从以上的启动信息看,有一个警告信息“*** Warning - bad CRC or NAND,using default environment”,我们知道,这是因为我们还没有将u-boot的环境变量保存nand中的缘故,那现在我们就用u-boot的saveenv命令来保存环境变量,如下:

从上图可以看到保存环境变量并没有成功,而且从信息看他将把环境变量保存到Flash中,显然这不正确,我们是要保存到Nand中。原来,u-boot在默认的情况下把环境变量都是保存到Nor Flash中的,所以我们要修改代码,让他保存到Nand中,如下:

#gedit include/configs/my2440.h

//注释掉环境变量保存到Flash的宏(注意:如果你要使用上一篇中的从Nor启动的saveenv命令,则要恢复这些Flash宏定义)

//#define CONFIG_ENV_IS_IN_FLASH 1
//#define CONFIG_ENV_SIZE
????? 0x10000?/* Total Size of Environment Sector */

?

//添加环境变量保存到Nand的宏(注意:如果你要使用上一篇中的从Nor启动的saveenv命令,则不要这些Nand宏定义)

#define CONFIG_ENV_IS_IN_NAND? 1
#define CONFIG_ENV_OFFSET????? 0x40000?
?//将环境变量保存到nand中的0x40000位置必,须在 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//块的起始位置
#define CONFIG_ENV_SIZE??????? 0x20000?//必须为块大小的整数倍 , 否则会提示下面的信息

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 将擦除整个块


Warning: Erase size 0x00010000 smaller than one erase block 0x00020000
???????? Erasing 0x00020000 instead
NAND 128MiB 3,3V 8-bit: MTD Erase failure: -22
Writing to Nand... FAILED!


重新编译u-boot,下载到nand中,启动开发板再来保存环境变量,如下:

可以看到,现在成功保存到Nand中了,为了验证,我们重新启动开发板,那条警告信息现在没有了,如下:

(编辑:李大同)

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

    推荐文章
      热点阅读