移植uboot2012.04.01到JZ2440(长文,详细记录)
1.下载uboot2012.04.01,并尝试编译smdk2410
a.官网下载
b.tar解压
c.配置:make smdk2410_config
d.编译:make
遇到问题:
make: *** [u-boot] Error 139
编译器版本低
故:换arm-linux-gcc-4.3.2
e.再次编译:
遇到问题:
error trying to exec 'cc1': execvp: No such file or directory
编译器位置的问题
故:将编译器解压到 /
d.再次编译:编译成功,达到uboot.bin可执行文件
2.基于smdk2410创建单板目录和配置文件
a.创建jz2440 board目录
在u-boot-2012.04.01\board\samsung\ 目录下创建jz2440目录,并将同目录下smdk2410中的文件全部拷贝到jz2440
同时将smdk2410.c改成jz2440.c ,并在同目录下将Makefile 中的 COBJS := smdk2410.o 改为COBJS := jz2440.
b.创建jz2440.h配置文件
在u-boot-2012.04.01\include\configs中copy smdk2410.h文件并重命名为jz2440.h粘贴到该目录下。
c.在/board.cfg中添加:
jz2440 arm arm920t - samsung s3c2440
d./Makefile中:
添加:arm-linux-
目标:成功编译新board
make jz2440_config
make
(成功)
3.创建SI-project,分析启动流程,了解内存布局,及各关键部位
a.创建SI-project
b.分析启动流程
从arch/arm/cpu/arm920t/start.S开始:
start_code:
set the cpu to SVC32 mode -> .....详情见移植过程......
c.内存布局
d.各关键部位
4.修改时钟和存储控制器,与board适配,并debugSDRAM是否可用
step1:修改时钟和存储控制器
set the cpu to SVC32 mode
->#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) //delete 这部分
->turn off the watchdog //2410 2440看门狗继存器位置相同,不改动
->mask all IRQs by setting all bits in the INTMR - default //屏蔽IRQs INTSUBMSK 2440也有,将CONFIG_S3C2410换成 CONFIG_S3C2440 并将CONFIG_S3C2440在jz2440中定义
->第一个重要post1:时钟配置(MPLL){
MPLL和UPLL在board/sansung/jz2440.c中int board_early_init_f(void)有:
将以上MPLL注释,UPLL保留;在post1处手动添加MPLL的配置,如下:
a.set FCLK 和 Fin的关系
b.set FCLK、HCLK、PCLK三者关系
c.改变总线模式,使cpu核时钟使用FCLK(默认使用HCLK)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
->CONFIG_SKIP_LOWLEVEL_INIT jz2440不定义,bl cpu_init_crit
flush v4 I/D caches 、 disable MMU stuff and caches、bl lowlevel_init(执行存储控制器的配置)
->bl lowlevel_init:在jz2440/lowlevel.S中:
首先检查BWSCON 存储控制器寄存器组的基址:0x48000000 没错
分析配置过程,没错
更改SMRDATA:的值(与jz2440适配,注意时钟的变化,修改刷新reg)
step2:验证SDRAM是否可用:
使用JTAG物理调试(类似于gdb),采用openOCD作为上位机应用程序:
a.烧写一个移植好的uboot,采用dnw(win7) + usb + uboot 下载自己的uboot到Nor / Nand
b.在uboot启动过程中,q进入命名模式:
usb 1 30000000 //采用usb下载 1-一直等待至下载完成 30000000 下载目的地址
对Nor Flash:
protect off all //解除写保护
erase 0 7FFFF //擦除Nor 512K的大小(从0x0地址到0x7FFFF)
cp.b 30000000 0 80000 //从0x30000000处复制 0-0x80000内容到Nor
对Nand Flash:
nand erase 0 80000 //擦除nand 从0x0 到 0x80000
nand write 30000000 0 80000 //从0x30000000复制0-0x80000的内容写入nand
c.win7安装OpenOCD(包括驱动) + OpenJTAG + telent进行debug
d.打开telent功能(控制面板 - 程序与功能 - ... )
e.打开openocd,OPJTAG连接开发板和PC,启动开发板,connect,识别成功后telnet到cmd中:
reset halt
halt
step 0x0 //从0x0开始运行
step //单步运行 pc += 4
bp 0xb0 4 hw //在0xb0处设置断点,使其完成存储 控制器的配置
resume //继续运行到断点0xb0
mdw 0x30000000 //查看0x30000000处原来的值
mww 0x30000000 0x11112222 //向0x30000000写入0x11112222
mdw 0x30000000 //查看0x30000000处现在的值,若= 0x11112222则读写入成功(SDRAM可用)
小结:在start.S中配置MPLL,在lowlevel.S中配置存储控制器,并在jz2440.c中注释MPLL的配置,以及在s3c24x0.h中注释s3c2410的GPIO结构体,(下一步:在jz2440.h中添加CONFIG_S3C2440宏)
目标:MPLL配置成功,SDRAM可用,串口有输出(不配置CONFIG_S3C2440则为乱码)
(成功)
5.修改时钟获取函数(如get_PCLK()等),使baudrate正常,使串口能输出
->Set stackpointer in internal RAM to call board_init_f(在SDRAM中设置栈指针,并调用board_init_f 第一阶段的初始化工作)
->board_init_f:主要做一些第一阶段的初始化工作:
->serial_init源码追溯:
int serial_init(void)
{
return serial_init_dev(UART_NR);
}
static int serial_init_dev(const int dev_index)
{
//获得UART控制器基址
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
#ifdef CONFIG_HWFLOW //不管
hwflow = 0; /* turned off by default */
#endif
/* FIFO enable, Tx/Rx FIFO clear */
writel(0x07, &uart->ufcon);
writel(0x0, &uart->umcon);
/* Normal,No parity,1 stop,8 bit */ //8N1
writel(0x3, &uart->ulcon);
/*
* tx=level,rx=edge,disable timeout int.,enable rx error int.,
* normal,interrupt or polling
*/
writel(0x245, &uart->ucon); //interrupt or polling
#ifdef CONFIG_HWFLOW
writel(0x1, &uart->umcon); /* rts up */
#endif
/* FIXME: This is sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
/* we need auto hw flow control on the gsm and gps port */
if (dev_index == 0 || dev_index == 1)
writel(0x10, &uart->umcon);
#endif
_serial_setbrg(dev_index);
return (0);
}
void _serial_setbrg(const int dev_index)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
unsigned int reg = 0;
int i;
/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
reg = get_PCLK() / (16 * gd->baudrate) - 1;
writel(reg, &uart->ubrdiv);
for (i = 0; i < 100; i++)
/* Delay */ ;
}
/*划重点了*/
ulong get_PCLK(void)
{
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
return (readl(&clk_power->clkdivn) & 1) ? get_HCLK() / 2 : get_HCLK();
}
ulong get_HCLK(void)
{
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
#ifdef CONFIG_S3C2440 //需要定义这个宏,在include/configs/jz2440.h中
switch (readl(&clk_power->clkdivn) & 0x6) {
default:
case 0:
return get_FCLK();
case 2:
return get_FCLK() / 2;
case 4:
return (readl(&clk_power->camdivn) & (1 << 9)) ?
get_FCLK() / 8 : get_FCLK() / 4;
case 6:
return (readl(&clk_power->camdivn) & (1 << 8)) ?
get_FCLK() / 6 : get_FCLK() / 3;
}
#else
return (readl(&clk_power->clkdivn) & 2) ? get_FCLK() / 2 : get_FCLK();
#endif
}
串口的支持其实就是追溯到/arch/arm/cpu/arm920t/s3c24x0/speed.c中 影响get_PLLCLK函数和get_HCLK函数,并在自己的配置文件中添加宏定义CONFIG_S3C2440,
去掉CONFIG_S3C2410
->编译:
q1.在s3c2410_nand.c 中出错:
对照源码:
可见是因为把CONFIG_S3C2410这个宏去掉了,导致条件编译时没有生成struct s3c2410_nand结构体,后续的所有配置也就是错误的。
故:
暂时添加CONFIG_S3C2410这个宏,因为在speed.c中只要配置了CONFIG_S3C2440这个宏即可,其余问题后续再解决。
(解决)
q2:
大概是个结构体吧,对照源码:从cpu_info.c追溯到:s3c24x0_cpu.h
可以看出CONFIG_S3C2410 又在作怪了。。。
故:
那就将以上源码的CONFIG_S3C2440与CONFIG_S3C2410位置对换,且同时修改.h文件
(未解决,到s3c24x0.h中看:)
可见二者不能同时存在
故:
直接把2410的部分结构体成员注释把。
(解决)
q3:
以上未定义的函数,在s3c2410.h中定义。。。。。
故:
在/driver/mtd/nand/Makefile中修改如上,s3c2440.o备用
(解决)
q4:
故:暂时注释吧
终于:(编译成功)
小结:其实可以直接在jz2440.h中注释CONFIG_NAND_S3C2410 使得在/drivers/mtd/nand/Makefile中不编译s3c2410.o;然后注释掉2410的nand结构体。
也可以不注释CONFIG_S3C2410,直接添加CONFIG_S3C2440,并在s3c24x0.h中注释2410的GPIO结构体。因为串口获取时钟只要定义CONFIG_S3C2440即可。
->烧写、启动、观察串口输出:
这个结果其实很好得到,就是要注意MPLLCON的配置顺序!!!!
小结:
MPLL的配置;ICache开启;添加CONFIG_S3C2440(使得获取总线时钟函数获取到2440的正确时钟)才能使波特率正常。
6.修改启动方式之 支持Nand Flash启动
原本,uboot是默认Nor Flash启动,不管是*.lds中的链接地址还是重定位代码,都是针对Nor Flash;
现在,要支持Nand Flash启动,必然涉及修改*.lds中的链接地址和修改重定位方式,并去掉针对Nor 启动的-pie选项(-pie会导致在*.lds中生成一些关于已初始化全局变量和静态变量的段,可在arch/arm/config.mk中修改)
此外,要注意将重定位之前的代码和相关变量定位于前4K,/jz2440/Makefile中修改
1.将原来的nand_drv.c复制到jz2440/下,并在jz2440/Makefile中做对应修改(主要是nand_init 和nand_read 用于nand初始化和重定位uboot)
编译,烧写,测试:没问题
在board.c中定义_DEBUG,查看输出:
2.在进入第一阶段的初始前(调用board_init_f之前),进行nand_init 和 重定位代码
如下:
其中CONFIG_SYS_TEXT_BASE是原本在jz2440.h中定义为0x0,表示自动生成的uboot.lds中指定链接地址为0;
现在,修改为:(更改链接地址了)
这样不好!!!因为还没有重定位,在jz2440.h中定义的值可能编译器输出在4K之外,所以将上面的r1修改:
关于_TEXT_BASE和_bss_start_ofs在start.S中:
4.去掉针对Nor 启动的-pie选项;去掉针对Nor 启动的重定位相关代码
在/arch/arm/config.mk中:
在arch/arm/lib/board.c中:
在arch/arm/cpu/arm920t/start.S:
以上直接去掉,直到清bss段。
3.清bss段
篇外:
一个可执行程序有:text、data等段,bss段不在可执行文件里面。
bss段里面存的是为初始化的全局变量和静态变量,是一个个值。当从定位代码的时候,只重定位了text开始。。。bss_start,另外的bss段可以不重定位,直接在重定位后clear(初始化为0)。
所以,我们应该给程序留的最终大小应该是从text开始。。。bss_end。
uboot原本的:
是结合Nor 重定位的,直接自己写吧:
4.进入第二阶段的初始化工作(进入到board_init_r函数)
篇外:接clear_bss后的启动流程分析
经过以上,启动流程分析,走过了board_init_f,再经过一些Nor Flash的重定位和clear bss,进入到board_init_r第二阶段的初始化工作:
->board_init_r:这里面就超级重要了,主要是对外设的支持,比如Nand Flash、Nor Flash 、网卡、LCD等。
以及开启cache、设置parm参数地址 和 2410机器ID(需要修改)、环境变量重定位、设置栈等。
board_init_r有两个参数需要确定:
void board_init_r(gd_t *id, ulong dest_addr)
gd_t *id 在board_init_f中就确定了,直接使函数返回(返回值保存在r0中) //在函数定义和声明处(/include/common.h)更改返回值类型
ulong dest_addr就是链接地址:_TEXT_BASE
所以:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit //完成储存控制器的配置
#endif
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
bl nand_init_new //Nand Flash 控制器初始化
mov r0, #0x0
ldr r1, _TEXT_BASE
ldr r2, _bss_start_ofs
bl copy_bootcode_to_sdram //重定位nand中的uboot到SDRAM 0x33f00000
clear_bss:
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
//bl coloured_LED_init
//bl red_led_on
ldr pc, = call_board_init_f //到SDRAM去执行
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr r0,=0x00000000
bl board_init_f //第一阶段的初始化,修改返回id参数,保存在r0
ldr r1, =_TEXT_BASE
bl board_init_r
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#if 0 //都是针对Nor启动的代码
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start
#endif
_rel_dyn_start_ofs:
.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
.word __rel_dyn_end - _start
_dynsym_start_ofs:
.word __dynsym_start - _start
#endif
5.修改jz2440/ 下的Makefile ,添加nand_drv.o
6.在arch/arm/cpu/在的uboot.lds中修改(将start.o、nand_drev.o、lowlevel.o等放到前4K的位置)
经编译后,在jz2440/下:没有对应的start.o、nand_drev.o、lowlevel.o,却有一个libjz2440.o,可见uboot将他们做成了一个库。
所以将arch/arm/cpu/下的uboot.lds:
修改为:
7.总结修改了哪些文件:
start.S jz2440/ jz2440.h /arch/arm/config.mk arch/arm/lib/board.c /include/common.h arch/arm/cpu/下的uboot.lds
8.编译:
q1:
(解决)
q2:
注意英文逗号。。。。。
(解决)
q3:
board前面没有/
(解决)
(编译成功)
9.烧写结果:
!!!!!不断重启或者Nor Flash哪里没有输出,不能正常终止或hang!!!!
如下:
纠错:
问题:做好移植后测试,根据串口信息,SDRAM,时钟,Flash,串口等基本判定能正常工作,但是在某个位置会重启或有单句调试信息不能正常输出。
排查:
出错点的上下文逻辑;
整体配置;
从SDRAM,时钟,串口等设置断点等方式逐一排查;
检查关键点参数配置;
内存分布,最终检查出在board_init_f中有个addr参数的设置没修改为Nand 启动的链接地址,导致了堆区上移,在board_init_r中执行堆的初始化时将已经重定位的前300K左右部分一并初始化了,当后面的代码执行时,程序自然就跑飞了。
再次编译测试:
(解决,已正常支持Nand 启动)
7.支持Nor Flash
添加宏 DEBUG (在linclude/common.h中),这样可以使所有的debug信息输出:
可见用的是CFI接口(在Nor Flash中也能找到支持CFI字样)
继续在board.c board_init_r中分析:
进入flash_init()(cfi_flash.c中):
unsigned long flash_init (void)
{
unsigned long size = 0;
int i;
//没定义
#ifdef CONFIG_SYS_FLASH_PROTECTION
/* read environment from EEPROM */
char s[64];
getenv_f("unlock", s, sizeof(s));
#endif
/* Init: no FLASHes known */
//CONFIG_SYS_MAX_FLASH_BANKS jz2440.h中定义 = 1(只有一片Nor Flash)
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
/* include/flash.c中 = 0xFFFF 先初始化Nor Flash id */
flash_info[i].flash_id = FLASH_UNKNOWN;
/* Optionally write flash configuration register 写操作reg config*/
cfi_flash_set_config_reg(cfi_flash_bank_addr(i), //0x0
cfi_flash_config_reg(i));//0xFFFF
//以上无作用
//JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,
//如果芯片不支持CFI,就需使用JEDEC了
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))// 0 0 USE CFI FLASH
flash_get_size(cfi_flash_bank_addr(i), i); //USE SMI FLASH
size += flash_info[i].size;
if (flash_info[i].flash_id == FLASH_UNKNOWN) {
//没定义,执行
#ifndef CONFIG_SYS_FLASH_QUIET_TEST
printf ("## Unknown flash on Bank %d "
"- Size = 0x%08lx = %ld MB\n",
i+1, flash_info[i].size,
flash_info[i].size >> 20);
#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
}
#ifdef CONFIG_SYS_FLASH_PROTECTION
else if ((s != NULL) && (strcmp(s, "yes") == 0)) {
/*
* Only the U-Boot image and it's environment
* is protected, all other sectors are
* unprotected (unlocked) if flash hardware
* protection is used (CONFIG_SYS_FLASH_PROTECTION)
* and the environment variable "unlock" is
* set to "yes".
*/
if (flash_info[i].legacy_unlock) {
int k;
/*
* Disable legacy_unlock temporarily,
* since flash_real_protect would
* relock all other sectors again
* otherwise.
*/
flash_info[i].legacy_unlock = 0;
/*
* Legacy unlocking (e.g. Intel J3) ->
* unlock only one sector. This will
* unlock all sectors.
*/
flash_real_protect (&flash_info[i], 0, 0);
flash_info[i].legacy_unlock = 1;
/*
* Manually mark other sectors as
* unlocked (unprotected)
*/
for (k = 1; k < flash_info[i].sector_count; k++)
flash_info[i].protect[k] = 0;
} else {
/*
* No legancy unlocking -> unlock all sectors
*/
flash_protect (FLAG_PROTECT_CLEAR,
flash_info[i].start[0],
flash_info[i].start[0]
+ flash_info[i].size - 1,
&flash_info[i]);
}
}
#endif /* CONFIG_SYS_FLASH_PROTECTION */
}
flash_protect_default();
#ifdef CONFIG_FLASH_CFI_MTD
cfi_mtd_init();
#endif
return (size);
}
进入flash_detect_legacy函数(本文件中):
static int flash_detect_legacy(phys_addr_t base, int banknum)
{
flash_info_t *info = &flash_info[banknum];
if (board_flash_get_legacy(base, banknum, info)) {
debug("detect_legacy......\n");
/* board code may have filled info completely. If not, we
use JEDEC ID probing. */
debug("detect_legacy......info->vendor = %d\n",(unsigned short)(info->vendor));
if (!info->vendor) {
//厂家ID
int modes[] = {
CFI_CMDSET_AMD_STANDARD, //在下面的for中两次循环用到
CFI_CMDSET_INTEL_STANDARD //AMD 和 INTEL是JEDEC查询采用的两个命令集
};
int i;
debug("sizeof(modes) = %d\n",(int)sizeof(modes));
for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {
info->vendor = modes[i]; //更改info->vendor = 2
//Flash 0x0 addr
info->start[0] =
(ulong)map_physmem(base,
info->portwidth,
MAP_NOCACHE);
//info->portwidth = 2
debug("detect_legacy......info->portwidth = %d\n",(uchar)(info->portwidth));
if (info->portwidth == FLASH_CFI_8BIT
&& info->interface == FLASH_CFI_X8X16) {
info->addr_unlock1 = 0x2AAA;
info->addr_unlock2 = 0x5555;
} else {//here 2 FLASH_CFI_16BIT = 2
info->addr_unlock1 = 0x5555;
info->addr_unlock2 = 0x2AAA;
}
flash_read_jedec_ids(info);
debug("JEDEC PROBE: ID %x %x %x\n",
info->manufacturer_id,
info->device_id,
info->device_id2);
if (jedec_flash_match(info, info->start[0])) //重点查找对应的Nor Flash
break;
else
unmap_physmem((void *)info->start[0],
MAP_NOCACHE);
}
}
switch(info->vendor) {
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
info->cmd_reset = FLASH_CMD_RESET;
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_LEGACY:
info->cmd_reset = AMD_CMD_RESET;
break;
}
info->flash_id = FLASH_MAN_CFI;
debug("return 1 not use CFI\n"); //here
return 1;
}
debug("return 0 use CFI\n");
return 0; /* use CFI */
}
进入(jedec_flash_match函数(jedec_flash.c中):
/*-----------------------------------------------------------------------
* match jedec ids against table. If a match is found, fill flash_info entry
*/
int jedec_flash_match(flash_info_t *info, ulong base)
{
int ret = 0;
int i;
ulong mask = 0xFFFF;
if (info->chipwidth == 1)
mask = 0xFF;
//jedec_table数组存储着要匹配的Nor Flash参数,没有则自己对应datasheet添加
for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&
(jedec_table[i].dev_id & mask) == (info->device_id & mask)) {
fill_info(info, &jedec_table[i], base);
ret = 1;
break;
}
}
return ret;
}
进入jedec_table数组:
这下应该可以了,在include/common.h中取消DEBUG的定义,编译测试:
OK!
上述有个ERROR: too many flash sectors(扇区太多了)
在ulong flash_get_size (phys_addr_t base, int banknum)函数中找到:
以上问题就是说检测到的扇区个数 sect_cnt > 程序中定义的扇区个数(在jz2440.h中)
再次编译测试:
ERROR没有了!OK!
现在,开发板已经支持Nor Flash的各种操作了,意味着在uboot里能用一些操作Nor Flash的命令了
可以看到shell交互成功(flinfo 查看Nor flash)!
其中的RO是只读的意思,是由于前一个uboot在进行烧写Nor Flash的时候,将烧写区进行保护,要写该区域就要执行protect命令。
执行erase 命令时有如下不必要的输出:
在cfi_flash.c flash_is_busy函数中找到:
本文件的DEBUG没有取消,那就取消吧!
最后一个问题:
因为此时我们的栈还停留在SDRAM的下部:
应该设置重新设置在上部(board_init_f 中已经定义的addr_sp ,只不过我们由于修改为Nand 启动没有调用原来的relocate_code(addr_sp, id, addr); 故还没有使用)
会造成什么问题呢?
最直观的就是在uboot命令行交互时:(情景再现)
上电后由于SDRAM不保存数据,会将内存数据初始化为0xff;
当在uboot 中使用命令cp.b 30000000 80000 10000拷贝一部分数据到Nor flash时,uboot的命令时C函数实现,则必然要改变栈区数据,又同时在复制数据,当复制的区域与栈区重叠时,数据就会出现差异,导致程序奔溃。
情景2:
uboot向内核传递的参数在0x30000100,也有可能栈溢出改变参数,那样内核肯定启动不了。
所以我们要修改栈区:
a.在start.S中定义一个全局变量,好在board.c中使用
b.在board.c board_init_f 中:
c.在start.S中修改栈:
再次编译测试:
不过此时已经可以用自己的uboot下载程序了,虽然还不能使用usb,但可以用loady命令:
用help命令查看找到:
其中loadb 可以使用kermit协议下载二进制文件;loady可以使用ymodem协议下载二进制文件,那就找一个支持这两个协议的串口工具吧:
(CRT是真的强,支持ymodem、xmodem、ASCII)
因为现在使用的是sp = 0x30000F80 所以下载到0x32000000,避免命令造成栈数据和下载数据的差异;(loady address,然后CRT选项:传输-发送ymodem)
现在直接使用reset命令重启开发板:
OK!再测试栈有没有改变:
就这样吧。。。
8.支持Nand Flash
在board_init_r中:
进入nand_init函数(drivers/mtd/nand/nand.c):
void nand_init(void)
{
//没定义
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
int i;
//重点,CONFIG_SYS_MAX_NAND_DEVICE = 1(只有一块NAND Flash)
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i); //0
#endif
printf("%lu MiB\n", total_nand_size / 1024);
//没定义
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*
* Select the chip in the board/cpu specific driver
*/
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
进入nand_init_chip(0)本文件:
这里有两个重点函数:
board_nand_init: 这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数。
nand_scan:
先进入board_nand_init(在上s3c2410_nand.c中):
int board_nand_init(struct nand_chip *nand)
{/*这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数*/
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
//获得时钟和nand控制器的基址0x4E000000
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
debug("board_nand_init()\n");
//使能nand flash
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
/* initialize hardware 时序、ECC、禁止选中等*/
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = 4;
twrph0 = 8;
twrph1 = 8;
#endif
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);
/* initialize nand_chip data structure 初始化 读写 指向NFDATA */
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
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; //软件设置ECC
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0; //
#endif
debug("end of nand_init\n");
return 0;
}
再进入nand_scan(nand_base.c)函数:
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
*
* This fills out all the uninitialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values.
* The mtd->owner field must be set to the module of the caller
*
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
ret = nand_scan_ident(mtd, maxchips, NULL);//正常情况返回0
if (!ret)
ret = nand_scan_tail(mtd); //ECC。。。
return ret;
}
进入nand_scan_ident函数(本文件):
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
* @table: Alternative NAND ID table
*
* This is the first phase of the normal nand_scan() function. It
* reads the flash ID and sets up MTD fields accordingly.
*
* The mtd->owner field must be set to the module of the caller.
*/
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
const struct nand_flash_dev *table)
{
int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv; //
const struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16; //0
/* Set the default functions */
nand_set_defaults(chip, busw);//各种默认的函数指针,不执行其他函数
/* Read the flash type 检测NAND芯片 类型并填充info*/
type = nand_get_flash_type(mtd, chip, busw,
&nand_maf_id, &nand_dev_id, table);
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);//向NAND 写复位命令
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//读设备ID
/* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) || //找到就退出for
nand_dev_id != chip->read_byte(mtd))
break;
}
#ifdef DEBUG
if (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; //i是NAND芯片个数,下面计算NAND大小会用到
// chip->chipsize应该在上面nand_get_flash_type里面读到了大小
mtd->size = i * chip->chipsize;
return 0;
}
以上就是主要的分析流程,可见主要涉及的文件:board.c ->nand.c -> s3c2410_nand.c -> nand_base.c
nand.c之uboot自己的nand 协议层,实际的操作在s3c2410_nand.c -> nand_base.c中;
s3c2410_nand.c 是针对2410的nand文件,我们cp并命名为s3c2440_nand.c,并在对应Makefile中做修改:
Makefile:
#COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
在jz2440.h中用宏开关控制这个Makefile的选择:
/*
* NAND configuration
*/
#ifdef CONFIG_CMD_NAND
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
#endif
然后根据流程分析,修改s3c2440_nand.c:
board_nand_init:
static void
s3c2440_nand_select(struct mtd_info *mtd, int chipnr)//自己写的
{
//struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
switch (chipnr) {
case -1://-1 diselect
//chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
nand_reg->nfcont |= (1 << 1);
break;
case 0:
nand_reg->nfcont &= ~(1 << 1);
break;
default:
BUG();
}
}
int board_nand_init(struct nand_chip *nand)
{/*这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数*/
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
//获得时钟和nand控制器的基址0x4E000000
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
debug("board_nand_init()\n");
//使能nand flash 时钟 2440 2410同
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
/* initialize hardware 时序、ECC、禁止选中等*/
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = 0;
twrph0 = 1;
twrph1 = 0;
#endif
#if 0
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
#endif
cfg = (tacls << 12) | (twrph0 << 8) | (twrph1 << 4);
writel(cfg, &nand_reg->nfconf);
cfg = (1 << 4) | (1 << 1) | (1 << 0);
writel(cfg, &nand_reg->nfcont);
/* initialize nand_chip data structure 初始化 读写 指向NFDATA */
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
//初始化选中芯片的函数,可以自己写
nand->select_chip = s3c2440_nand_select;
/* 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 = s3c2440_hwcontrol;//重要函数,要修改
nand->dev_ready = s3c2440_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; //软件设置ECC
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0; //
#endif
debug("end of nand_init\n");
return 0;
}
修改s3c2410_hwcontrol和s3c2410_dev_ready:
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();
if( ctrl & NAND_CLE ){
writeb(cmd, &nand->nfcmd);
}else{
if( ctrl & NAND_ALE )
writeb(cmd, &nand->nfaddr);
}
#if 0
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
//以下就是先区分地址还是命令,确定NFCMMD 和 NFDATA 基址
//再发送cmd (可能是命令或数据 ctrl 决定)
ulong IO_ADDR_W = (ulong)nand; //2440nand 结构体的首地址4E000000
if ((ctrl & NAND_CLE))
//S3C2410_ADDR_NCLE = 8(则IO_ADDR_W = 4E000008 就是NFCMMD地址)
//IO_ADDR_W |= S3C2410_ADDR_NCLE;
IO_ADDR_W |= 8;
if ((ctrl & NAND_ALE))
//IO_ADDR_W |= S3C2410_ADDR_NALE;
IO_ADDR_W |= 0xC;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE) //选中芯片
writel(readl(&nand->nfcont) & ~(1 << 1),
&nand->nfcont);
else //取消选中
writel(readl(&nand->nfcont) | (1 << 1),
&nand->nfcont);
if (cmd != NAND_CMD_NONE)
//因为chip->IO_ADDR_W已经确定了是NFDATA / NFCMMD ,所以既可以写命令也可以写地址
writeb(cmd, chip->IO_ADDR_W);
#endif
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("dev_ready\n");
return readl(&nand->nfstat) & 0x01;
}
再看nand_scan(nand_base.c)本不修改函数:
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
*
* This fills out all the uninitialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values.
* The mtd->owner field must be set to the module of the caller
*
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
ret = nand_scan_ident(mtd, maxchips, NULL);//正常情况返回0
if (!ret)
ret = nand_scan_tail(mtd); //ECC。。。
return ret;
}
修改nand_scan_ident函数:
/**
* nand_scan_ident - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
* @table: Alternative NAND ID table
*
* This is the first phase of the normal nand_scan() function. It
* reads the flash ID and sets up MTD fields accordingly.
*
* The mtd->owner field must be set to the module of the caller.
*/
int nand_scan_ident(struct mtd_info *mtd, int maxchips,
const struct nand_flash_dev *table)
{
int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv; //mtd->priv前面已给出
const struct nand_flash_dev *type;
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16; //0
/* Set the default functions */
nand_set_defaults(chip, busw);//各种默认的函数指针,不执行其他函数,不修改
/* Read the flash type 检测NAND芯片 类型并填充info,可以不修改,不过最好查看检测时使用的命令是否与Nand Flash一致*/
type = nand_get_flash_type(mtd, chip, busw,
&nand_maf_id, &nand_dev_id, table);
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);//向NAND 写复位命令
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//读设备ID
/* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) || //找到就退出for
nand_dev_id != chip->read_byte(mtd))
break;
}
#ifdef DEBUG
if (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; //i是NAND芯片个数,下面计算NAND大小会用到
// chip->chipsize应该在上面nand_get_flash_type里面读到了大小
mtd->size = i * chip->chipsize;
return 0;
}
在nand_set_defaults里面指定默认的chip->cmdfunc = nand_command,但是这是used for small page devices (256/512 Bytes per page),
不过在第一次调用nand_command时,函数里面会发命令检测Nand Flash的各种参数,并做适当的修改,比如将大页 换成
chip->cmdfunc = nand_command_lp,以下是nand_command_lp发命令和地址,注意其中有将NAND_CTRL_CHANGE取消,所以在更底层的s3c2440_hwcontrol中要注意一下。
编译测试:
以上是在Nor 中运行,可以识别出Nor 和 Nand。(Nor 启动时可以访问Nor 和 Nand)
再次测试:
在先将Nor中的uboot写入Nand。然后nand read 到SDRAM,最后间接比较Nor 和 Nand中的数据是否一致,可见是一致的,nand write /read等命令成功。
最后使用Nand 启动:
因为Nand启动无法访问Nor ,所以我自己给出的0MiB,但是成功识别了Nand Flash,并且到了命令行,一切正常。
9.支持DM9000网卡
在board_init_r中,在NAND FLASH后面紧接着可以有ONENAND、DATAFLASH、MMC、MISC、PCI等用于存储类的接口设备初始化。。。都不必用就没定义
来到下一个重点:
前言:dm9000和cs8900等网卡协议层和底层驱动uboot都已经写好了,我们只需要做适当的改动。
在drivers/net/Makefile中可以看到:
uboot把Makefile都写好了,只需要我们用宏开关(CONFIG_CS8900、CONFIG_DRIVER_DM9000)来控制Makefile即可:
在jz2440.h中配置CONFIG_DRIVER_DM9000:
仅仅是这样肯定不行的,DM9000是内存类接口的设备,要访问他,至少也要确定它的时序,位宽和基地址吧;
如此我们知道要设置存储控制器:
在原理图:
可以知道:
jz2440选择的片选信号是nGCS4,也就是BANK4,基地址 = 0x20000000;
只有一个LADDR2接到DM9000C的CMD接口,这是IO接口吧(在DM9000中看到这是区分发COMMAND和INDEX的)
有LDATA0 - LDATA15共16根数据线 ,则位宽是16位。
设置存储控制寄存器BWSCON
ST4是设置SRAM的不管,WS4是设置等待信号,在原理图上没接WAIT信号(芯片手册上好像也没有wait引脚),不管,WD4是位宽 = 01
BANKCON4:
再配合DM9000C的datasheet中的时序图和S3C2440中的时序图在DM9000datasheet中找到对应的时序参数设置存储控制寄存器BANKCON4,以下是S3C2440和DM9000中对时序的要求:
对比两个图可以看出:
tacs就是0ns (nGCS4 = CS#, A[24:0] = CMD) = 0ns 则在BANKCON4中设置tacs = 0 (0clock);
tcos就是T1 (A[24:0] = CMD nOE = IOR# ) = 0ns 则在BANKCON4中设置tcos = 0 (0clock);
tacc就是T2(write) (nOW低电平时间) = 10ns(max) 则在BANKCON4中设置tacc = 0 (1clock = 10ns(HCLK=100M));
tcoh = T5 (nOE上升,nGCS变化) = 0ns 则在BANKCON4中设置tacp = 0 (0 lock )
tcah 类似tacs = 0ns 则在BANKCON4中设置tcah = 0 (0clock);
tacp = 是page的周期 = CMD的周期(read) = T1 + T2 + T5 >= 10ns 则在BANKCON4中设置tacp = 0 (2 lock = 20ns )
最后一个PMC有些特殊。。。在DM9000中查找到:
DM9000 1个周期只能处理1个数据,所以PMC应该为normal(1data)
则BANKCON4 = 0x0,不过可以采用默认的0x00000700
以上是硬件访问相关,下面进入eth_initialize函数(net/eth.c):
eth_initialize函数是uboot中实现的net协议层,其中的board_eth_init才是我们配置自己单板的函数(在jz2440.c中):
可见,这个函数此时的CONFIG_CS8900在开始被注释了,那么就是空函数,下面仿照格式添加修改:
小结:修改了jz2440.h 添加配置宏;修改了lowlevel.S 配置了相关的存储控制器reg;修改了jz2440.c 更改dm9000初始化函数。
编译测试:
Q1:缺少定义DM9000_DATA
看看其他board怎么用的吧:
可见我们要在jz2440.h中定义DM9000_DATA 和 CONFIG_DM9000_BASE:
再次编译:
看来来需要定义DM9000_IO:
再次编译:OK!
测试:
能识别DM9000了,再次测试uboot能不能ping通PC(PC的IP: 192.168.1.101)
先设置ipaddr 和ethaddr 环境变量,再ping,同时可以用tftp功能(要在PC上打开tftp软件)。
10.裁剪和修改默认参数
环境变量相关:
a.有哪些环境变量?
例如:
还有ethaddr(MAC)、bootcmd、bootags等
bootags是要传递给内核的参数 bootm是在bootdelay变为0后自动执行的一连串命令(如读内核,bootcmd启动内核等)
b.环境变量代码在哪些文件?
c.环境变量存储在flash的什么位置?
代码分析:
在board_init_f的初始化中:
这个函数在各个存储器相关的文件里都有,那么该用哪个呢=====因为各个存储器的文件都在common/下,查看Makefile:
在jz2440.h中又有:
那么用的就是common/env_flash.c:
主要就是CRC效验(去flash读环境变量,判断格式,是否可用,返回一个值),用这个值确定gd->env_valid以备重定位时使用。
在board_init_r的初始化中:
void env_relocate(void) //common/env_common.c
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
env_reloc();
#endif
if (gd->env_valid == 0) {//在board_init_f的env_init初始化中将gd->env_valid = 0
#if defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
set_default_env(NULL);
#else
//前面判断在flash上的环境变量不可用,返回bad CRC ,
bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); //bootstage.h中枚举 = 60
set_default_env("!bad CRC");
#endif
} else {
env_relocate_spec();
}
}
void set_default_env(const char *s)
{
if (sizeof(default_environment) > ENV_SIZE) {
puts("*** Error - default environment is too large\n\n");
return;
}
if (s) {
if (*s == '!') {
printf("*** Warning - %s, "
"using default environment\n\n",
s + 1);
} else {
puts(s);
}
} else {
puts("Using default environment\n\n");
}
if (himport_r(&env_htab, (char *)default_environment, //环境变量数组
sizeof(default_environment), '\0', 0) == 0)
error("Environment import failed: errno = %d\n", errno);
gd->flags |= GD_FLG_ENV_READY;
}
这个数组的取值是宏定义,可以在jz2440.h这个配置文件中设置:
此时环境变量使用的还是默认的,数据保存在uboot的data中,而根据jz2440.h中定义的ENV_ADDR = 0x70000(大约是450K左右)
如果在uboot交互时使用save命令就把数据保存到flash上了,但是必然会破坏Flash中的UBOOT,那程序不就蹦了嘛
所在一般在NAND FLASH中采用分区的形式:
那么根据在common/Makefile:需要注释掉env_flash的相关配置,添加env_nand相关的配置:
其他是否还需要配置,看env_nand.c
int env_init(void)
{
#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1;
#ifdef CONFIG_ENV_OFFSET_REDUND
env_t *tmp_env2;
tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
#endif
tmp_env1 = env_ptr;
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
if (!crc1_ok && !crc2_ok) {
gd->env_addr = 0;
gd->env_valid = 0;
return 0;
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = 1;
}
#ifdef CONFIG_ENV_OFFSET_REDUND
else if (!crc1_ok && crc2_ok) {
gd->env_valid = 2;
} else {
/* both ok - check serial */
if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = 2;
else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = 1;
else if (tmp_env1->flags > tmp_env2->flags)
gd->env_valid = 1;
else if (tmp_env2->flags > tmp_env1->flags)
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
}
if (gd->env_valid == 2)
env_ptr = tmp_env2;
else
#endif
if (gd->env_valid == 1)
env_ptr = tmp_env1;
gd->env_addr = (ulong)env_ptr->data;
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
return 0;
}
/* ! CONFIG_ENV_OFFSET_REDUND */
int saveenv(void)
{
int ret = 0;
env_t env_new;
ssize_t len;
char *res;
nand_erase_options_t nand_erase_options;
memset(&nand_erase_options, 0, sizeof(nand_erase_options));
nand_erase_options.length = CONFIG_ENV_RANGE;
nand_erase_options.offset = CONFIG_ENV_OFFSET;
if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
return 1;
res = (char *)&env_new.data;
len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
if (len < 0) {
error("Cannot export environment: errno = %d\n", errno);
return 1;
}
env_new.crc = crc32(0, env_new.data, ENV_SIZE);
puts("Erasing Nand...\n");
if (nand_erase_opts(&nand_info[0], &nand_erase_options))
return 1;
puts("Writing to Nand... ");
if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {
puts("FAILED!\n");
return 1;
}
puts("done\n");
return ret;
}
在saveenv中又有4个宏CONFIG_ENV_RANGE、CONFIG_ENV_OFFSET、CONFIG_ENV_SIZE和ENV_SIZE
其中
在environment.h中有了定义,余下的三个在jz2440.h中自己配置:
相关地址可以确定mtd分区后确定。
现在,我们已经将环境变量的一些默认值确定了,也选择了保存在难度 flash中,现在编译测试:(目前只修改了jz2440.h文件)
编译成功,烧写测试:
可见环境变量已经起的作用,能倒计时,也能自动执行bootm命令启动内核,只是bootcmd环境变量的相关参数还没设置正确。
bad CRC还在,是因为环境变量还没有保存到NAND FLASH,执行save命令即可:
果然save成功。 说明我们前面的配置使环境变量七座了,且保存在NAND分区上了。
那么目前最激动人心的便是:正确配置和 mtd分区:
bootargs:内核打印信息输出采用串口0,挂载根文件系统在mtd的第三块区域
将uImage下载到0x30000000 再执行bootm 30000000启动内核,查看内核中的分区:
现在希望自动执行bootargs中的命令开自动启动内核:
先将uImage下载到NAND FLASH的 kernel分区0x60000
再改变环境变量
重启后:
MTD分区,虽然知道了Lunux中的分区,但UBOOT还没分区呢:
首先确定一点,mtd在drivers/mtd/Makefile中添加:
在jz2440.h:
可见目前是添加了mtd的,在uboot中查看mtd输出:
根据提示找到:(common/cmd_mtdparts.c)
int mtdparts_init(void)中:
那么看看Makefile中有没有添加这个文件:
可见是添加了的,那么是哪里的问题呢?根据代码推测是mtdids_default的问题:
由于MTDIDS_DEFAULT没有设置,也就输出我们看到的错误了,那么就设置默认的mtdids呗:
先看看别人怎么配置:
然后根据我们的分区信息在jz2440.h中配置如下:
更改了哪些文件:jz2440.h
编译测试:
mtd命令生效,也有了分区信息,不过还是有错误信息,搜索:
在int mtdparts_init(void):
默认的变量虽然设置了,但是没有执行mtdparts default 做默认相关的工作,在uboot中看看:
果然是这样,那么就在错误之前添加:
run_command("mtdparts default", 0);
再次编译测试:
经验:在mtdparts_init函数调用之前设置run_command("mtdparts default", 0);,那就在board_init_r中吧:
再次编译测试:
解决最后一个问题:
现在UBOOT太大了,可以剪裁一下,其实主要就是在jz2440.h中注释宏:
先在uboot中看uboot有哪些可以不要功能:
功能太多,比如USB、操作文件系统:
编译测试:
只有249K了,也能放到NAND 的第一个分区了,太棒了。
11.yaffs映像和制作补丁
既然已经能启动内核了,那么就在fs分区下载一个文件系统,看能不能正确启动fs,并进入Linux shell:
先烧写jffs2文件系统:
a.下载jffs2到SDRAM,然后写到NAND FLASH的fs分区:(注意这里的mtd分区已经做好)
整个系统 = uboot2012 + Linux2.6 + jffs2
下面测试烧写yaffs2文件系统:
uboot执行nand write.yaffs2 命令时出错,在uboot中找到:
在common/cmd_nand.c中有好几处Unknown nand command suffix ,但是都在一个do_nand函数里面,并且可以看出,是do_nand对下载不同fs的打印信息:
分析程序,大概就是先对比nand read/write 确定是read 还是write ,再对比后缀,如write.jffs2 write.yaffs 等,其中关于对比yaffs的代码由宏开关决定,没有打开,所以肯定下载不能成功。
#ifdef CONFIG_CMD_NAND_YAFFS //这个宏要打开,宏打开后用nand_write_skip_bad烧写!
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, WITH_YAFFS_OOB);
#endif
先在jz2440.h中定义一个吧:
再来查找一下Unknown nand command suffix在哪里输出,肯定不是上面,根据源码追到:
现在编译uboot再测试一下:
VFS成功挂载yaffs2文件系统,但是串口打不开,init进程也找不到!!!
根据韦老师找到的uboot bug:
在uboot中根据nand dump查看烧写在nand上fs分区的内容,一页一页的比较与yaffs二进制文件的差别,发现nand write只烧写了1页!这是uboot自身的bug!!!下面改正:
#ifdef CONFIG_CMD_NAND_YAFFS //宏打开后用nand_write_skip_bad烧写!
进入nand_write_skip_bad(nand, off, &rwsize,(u_char *)addr, WITH_YAFFS_OOB); //drivers/mtd/nand/nand_util.c
/**
* nand_write_skip_bad:
*
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
* skipping the bad blocks.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
int need_skip;
#ifdef CONFIG_CMD_NAND_YAFFS
//WITH_YAFFS_OOB是作为参数传进来的
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)//肯定是不行的。。。
return -EINVAL;
int pages; //erasesize writesize 在nand_get_flash_type中设定
//128K / 2K = 64页
pages = nand->erasesize / nand->writesize;
//blocksize 就是一块总的大小 = 128K + (oobsize*64页)
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
#endif
{
blocksize = nand->erasesize;//不执行
}
/*
* nand_write() handles unaligned, partial page writes.
*
* We allow length to be unaligned, for convenience in
* using the $filesize variable.
*
* However, starting at an unaligned offset makes the
* semantics of bad block skipping ambiguous (really,
* you should only start a block skipping access at a
* partition boundary). So don't try to handle that.
*/
if ((offset & (nand->writesize - 1)) != 0) {//对齐检查
printf ("Attempt to write non page aligned data\n");
*length = 0;
return -EINVAL;
}
/*check_skip_len返回值:
当offset在nand flash中无效时,返回-1(程序员应该不会范这种低级错误。。。)
当offset在nand flash中有效时,若在offset和length之间有坏块则返回1,否则返回0
*/
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
/*当need_skip = 0(无坏块) 且 flag中不存在WITH_DROP_FFS
目前flag之没有WITH_DROP_FFS的,所以当没有坏块时就会在这里面执行nand_write,
都不会到下面的while中取写了,所以下面的修改也就没意义了!!!
重点:下面的while是针对有坏块的情况,涉及OOB的操作,所以可以在可以添加一个条件:
!(WITH_YAFFS_OOB)即当没有定义WITH_YAFFS_OOB这个宏时。
*/
//if (!need_skip && !(flags & WITH_DROP_FFS)) {
if (!need_skip && !(flags & WITH_DROP_FFS) && !(WITH_YAFFS_OOB)) {
rval = nand_write (nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
while (left_to_write > 0) {//循环写
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size, truncated_write_size;
WATCHDOG_RESET ();
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
if (left_to_write < (blocksize - block_offset))
write_size = left_to_write;
else
write_size = blocksize - block_offset;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;//原来是MTD_OOB_AUTO;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
WATCHDOG_RESET();
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
rval = nand->write_oob(nand, offset, &ops);
//write_oob写第一页时,返回0,成功,但是下面的!会break写
if (rval) //原本为if(!rval)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
}
else
#endif
{
truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
if (flags & WITH_DROP_FFS)
truncated_write_size = drop_ffs(nand, p_buffer,
&write_size);
#endif
rval = nand_write(nand, offset, &truncated_write_size,
p_buffer);
offset += write_size;
p_buffer += write_size;
}
if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
}
left_to_write -= write_size;
}
return 0;
以上修改了3个bug!
再次编译测试:
注意:对jffs文件系统:烧写时用nand write.jffs2;对yaffs文件系统:烧写时用nand write.yaffs //必须是固定了,在代码中定死了。。。不然可能会写错误。
tftp 30000000 uImage_4.3;nand erase.part kernel;nand write 30000000 kernel
tftp 30000000 uImage_new;nand erase.part kernel;nand write 30000000 kernel
tftp 30000000 fs_mini_mdev_new.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize
tftp 30000000 fs_mini.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize
tftp 30000000 fs_mini_mdev_new.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize
tftp 30000000 fs_mini.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize
tftp 30000000 u-boot_new;nand erase.part u-boot;nand write 30000000 u-boot
1.uboot2012 + uImage_4.3 2.6 + jffs2
使用:fs_mini_mdev_new.jffs2
能挂载,但是显示s3c2440-sdi s3c2440-sdi: powered down.
注意要内核正确启动,这个的machid = 16a
不能正常启动fs的原因可能是内核与fs的编译工具链不同
使用fs_mini.jffs2:
也有,s3c2440-sdi s3c2440-sdi: powered down.
但是能正常启动fs,显示shell
2.uboot2012 + uImage_4.3 2.6 + yaffs2
使用:fs_mini_mdev_new.yaffs2
能挂载
Warning: unable to open an initial console.
Kernel panic - not syncing: No init found. Try passing init= option to kernel.
使用fs_mini.yaffs2:
相同错误
3.uboot2012 + uImage_new 3.4 + jffs2
使用machid = 7cf 内核分区才正确
使用fs_mini.jffs2:
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000200
使用:fs_mini_mdev_new.jffs2
正常挂载,正常显示shell
ALSA 是音频驱动,显示没有声卡,这个没关系
4.uboot2012 + uImage_new 3.4 + yaffs2
使用machid = 7cf 内核分区才正确
使用fs_mini.yaffs2:
使用:fs_mini_mdev_new.yaffs2
制作补丁:
diff -urN 老的目录 新的目录 > name.patch
u是按照某种格式输出
r递归比较
N是把没有的文件当空文件
12.总结