1,移植环境:
u-boot版本:u-boot-2012.04.01(包含了S3C2410 , 但是未包含S3C2440)
硬件环境: S3C2440平台
2,过程:
1),首先解压缩 tar -xvf u-boot-2012.04.01.tar.bz2;
然后编译make smdk2410_config && make ,之后得到u-boot.bin,通过openjtag烧入Norflash,从Norflash启动开发板,通过minicom串口查看没有任何反应;
2),u-boot-2012.04.01,第一阶段启动过程分析:
->设置CPU到管理模式
->关闭Watchdog
->屏蔽所有中断
->禁止MMU和Caches及初始化SDRAM
->设置栈,在board_init_f函数中初始化:划分内存,设置时钟,初始化串口,初始化控制台等重要的初始化;
->在relocate_code代码中:重新设置栈,重定位代码;
->处理.rel.dyn(通过编译选项PIE生成的代码段)代码段相关的链接地址到新地址,即就是处理位置无关代码;
->清bss段;
->对S3C24X0不支持从Nandflash启动,所以进入board_init_r函数:初始化gd_t结构体实例,设置malloc内存空间等,获取flash大小,进入main_loop函数;
->在main_loop函数中通过bootcmd命令进入u-boot启动第二阶段启动内核;
3),对于S3C2440,在上面的步骤中有两个地方需要说明:
在设置分频系数的代码如下:
对于S3C2440,通过查询硬件手册可知 default FCLK 是12MHz;
分频系数设置在SDRAM初始化之前,
初始化SDRAM的时候对SDRAM控制器的时钟相关寄存器使用下面的值:
即使用HCLK=60Mhz,但是在SDRAM之前并未设置MPLL,而是在board_init_f->board_early_init_f函数设置时钟的时候才设置时钟控制器的MPLL;
通过上述时钟频率的分析,可知,S3C2440初始化SDRAM的时候 HCLK并不是60MHz,所以此处需要调整,所以这样初始化的SDRAM在使用的时候会有问题;
4),添加S3C2440单板目录:
cp -rd board/samsung/smdk2410/ board/samsung/smdk2440/
修改其中的smdk2410.c 为smdk2440.c
修改board/samsung/smdk2440/Makefile中的2410为2440
cp include/configs/smdk2410.h include/configs/smdk2440.h
并修改如下内容:
为了能够编译通过,将CONFIG_CMD_NAND 和 CONFIG_YAFFS2宏注释掉,暂时先关闭Nandflash;
修改源码目录下面的boards.cfg文件,添加内容如下:
修改完毕之后执行make smdk2440_config && make 开始编译,编译通过得到u-boot.bin暂时还是无法运行;
5),修改上述3)中问题;
修改策略:将时钟控制器的初始化全部放在SDRAM初始化之前(根据硬件手册设置MPLL,设置分频系数,设置总线异步模式);然后初始化SDRAM(根据硬件手册从新设置SDRAM控制器寄存器的值)
start.S 和 smdk2440.c 中修改时钟控制器部分,lowlevel_init.S修改SDRAM控制器部分;
arch/arm/cpu/arm920t/start.S
/* modify by gh begin */
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #0x5
str r1, [r0]
/* set BUS mode */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
mcr p15,0,r0,c1,c0,0
/* set MPLL FCLK=400MHz */
ldr r0,=0x4C000004
ldr r1,=0x5c011
str r1,[r0]
/* modify by gh end */
board/samsung/smdk2440/smdk2440.c : serial_init_dev方法
/* modify by gh begin; */
/* to reduce PLL lock time, adjust the LOCKTIME register */
//writel(0xFFFFFF, &clk_power->locktime);
/* configure MPLL */
//writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
// &clk_power->mpllcon);
/* some delay between MPLL and UPLL */
//pll_delay(4000);
/* modify by gh end; */
board/samsung/smdk2440/lowlevel_init.S中的内存控制器初始化值:
/* modify by gh begin */
SMRDATA:
.long 0x32333330
.long 0x000007a0
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00002a60
.long 0x00018001
.long 0x00018001
.long 0x008404f5
.long 0x000000b1
.long 0x00000020
.long 0x00000020
/* modify by gh end */
经过以上修改之后的执行结果如下图:
已经有串口信息输出了,只不过是乱码,还需要进一步修改;
6),修改串口输出:
串口初始化方法的调用路径:
board_init_f->serial_init->serial_init_dev->_serial_setbrg
serial_init_dev方法的修改在上述3)问题的修改中已经修改完毕,查看_serial_setbrg这个方法,在这个方法中用PCLK时钟和波特率计算UBRDIV寄存器的[10:0]为的值,在获取PCLK时钟的get_PCLK方法中最终会调用到get_HCLK方法,在这个方法中已经支持S3C2440只不过未定义CONFIG_S3C2440的宏,代码如下图:
在 include/configs/smdk2440.h中添加CONFIG_S3C2440宏定义,注释CONFIG_S3C2410宏定义;
为了编译通过还需要修改如下两个宏定义:
修改完毕之后,执行make distclean 然后重新配置编译,烧写到Norflash之后,效果如下图:
串口已经能够正常运行;
7),修改Norflash信息获取:
1> Norflash支持XIP技术,所以从Norflash启动时,Norflash不需要初始化;
2> Norflash支持两种规范JEDEC,CFI(Common Flash Interface);
JEDEC规范是从Norflash中查寻到厂商ID和设备ID,然后用这两个ID在u-boot中记录的芯片信息数组中查询Norflash的其他硬件信息,例如:大小,sector的种类的数量,位宽等;
CFI规范将所有的硬件信息存储在Norflash芯片内,u-boot可以从Norflash读取所有的硬件信息;
从上述串口输出截图中的错误信息可以定位到,如下代码问题:
arch/arm/lib/board.c中的方法 board_init_r(gd_t *id, ulong dest_addr);
void board_init_r(gd_t *id, ulong dest_addr)
{
flash_size = flash_init();
if (flash_size > 0) {
. . . . .
} else {//由于flash_size=0,打印错误信息;
puts(failed);
hang();
}
进一步跟踪代码; flash_init(cfi_flash.c)->flash_detect_legacy(cfi_flash.c);就会看到
#ifdef CONFIG_FLASH_CFI_LEGACY
u-boot默认使用JEDEC规范,而u-boot的硬件信息数组:
static const struct amd_flash_info jedec_table[]; drivers/mtd/jedec_flash.c
中并没有我用的Norflash硬件信息:MX29LV160DBTI-70G;所以,有两种该法支持Norflash;
1> 在jedec_table数组中添加MX29LV160DBTI-70G的硬件信息(硬件信息查询芯片数据手册);
2> 在include/configs/smdk2440.h中去掉CONFIG_FLASH_CFI_LEGACY宏定义,让u-boot使用CFI规范,然后在根据错误信息修改最大sector的数量,修改如下:
/* modify by gh begin */
//#define CONFIG_SYS_MAX_FLASH_SECT (19)
#define CONFIG_SYS_MAX_FLASH_SECT (35)
/* modify by gh end */
/* modify by gh begin */
//#define CONFIG_FLASH_CFI_LEGACY
/* modify by gh end */
我按照第二种修改方法修改完毕之后,烧写重启开发板,结果如下图:
u-boot已经启动成功,可以输入u-boot的各种命令,可以读写Norflash;
9),用u-boot的loadb命令和minicom的kermit模式发送文件,将uImage装载到SDRAM中,然后启动内核,在u-boot命令行输入loadb 30000000,然后用minicom发送文件:
发送过程如下图:
传送完毕之后,执行bootm 30000000命令,可以看到内核启动成功,如下图:
内核启动成功!
10)支持Nandflash;
Nandflash初始化序列如下:
start.S -> board_init_r(arch/arm/lib/board.c) ->
nand_init(drivers/mtd/nand/nand.c) -> nand_init_chip(drivers/mtd/nand/nand.c) ->
board_nand_init(drivers/mtd/nand/s3c2440_nand.c) 和 nand_scan(drivers/mtd/nand/nand_base.c)
对上面调用序列分析,通过和S3C2440 芯片手册 和 Nandflash芯片手册中的操作过程比对,可以发现需要修改一下几点:
修改include/configs/smdk2440.h
/* modify by gh begin; */
#define CONFIG_CMD_NAND //去掉之前的注释;
#define CONFIG_S3C24XX_CUSTOM_NAND_TIMING
#define CONFIG_S3C24XX_TACLS 0
#define CONFIG_S3C24XX_TWRPH0 1
#define CONFIG_S3C24XX_TWRPH1 0
/* modify by gh end; */
// modify by gh begin
#ifndef CONFIG_S3C2440
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440 //如果是S3C2440则添加这个宏;
#endif
// modify by gh end
创建drivers/mtd/nand/s3c2440_nand.c
cp drivers/mtd/nand/s3c2410_nand.c drivers/mtd/nand/s3c2440_nand.c
修改drivers/mtd/nand/Makefile
添加如下代码:
# modify by gh begin
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
# modify by gh end
修改drivers/mtd/nand/s3c2440_nand.c
首先把S3C2410对于的宏定义修改为S3C2440,然后根据芯片手册(S3C2440芯片手册,Nandflash芯片手册)调整宏定义的值
/* modify by gh begin */
#define S3C2440_NFCONF_EN (1<<0)
/* modify by gh end */
/* modify by gh begin */
#define S3C2440_NFCONF_nFCE (1<<1)
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_ADDR_NALE 0xC
#define S3C2440_ADDR_NCLE 8
/* modify by gh end */
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
... ...
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
/* modify by gh begin */
if (ctrl & NAND_CLE)
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (ctrl & NAND_ALE)
IO_ADDR_W |= S3C2440_ADDR_NALE;
/* modify by gh end; */
chip->IO_ADDR_W = (void *)IO_ADDR_W;
/* modify by gh begin */
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~S3C2440_NFCONF_nFCE,
&nand->nfcont);
else
writel(readl(&nand->nfcont) | S3C2440_NFCONF_nFCE,
&nand->nfcont);
/* modify by gh end; */
}
... ...
}
int board_nand_init(struct nand_chip *nand)
{
... ...
#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
/* modify by gh begin */
writel((1<<4|1<<1|1),&nand_reg->nfcont);
cfg = S3C2440_NFCONF_TACLS(tacls);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1);
writel(cfg, &nand_reg->nfconf);
/* modify by gh end; */
... ...
}
修改drivers/mtd/nand/nand_base.c
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct nand_chip *chip = mtd->priv;
switch (chipnr) {
case -1:
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
break;
case 0:
/* add by gh begin */
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/* add by gh end; */
break;
default:
BUG();
}
}
修改完毕编译,烧写到Norflash上,运行结果如下图:
从Nandflash的kernel分区(0x60000,2M)加载uImage到SDRAM,然后bootm 30000000运行:
nand read 0x30000000 0x60000 0x200000
bootm 30000000
也可以在u-boot命令行执行:
set bootcmd "nand read 0x30000000 0x60000 0x200000;bootm 30000000"
save //将bootcmd参数保存到u-boot的环境变量中;
reset //重启之后直接进入Linux内核;
运行结果如下图:
从Nandflash加载Linux内核,启动成功!
由于Norflash空间不够大,放不下内核,所以,通常将Linux内核和u-boot都放在Nandflash的分区上,然后直接从Nandflash启动,这个有两种方案:
1> u-boot本身的SPL;
2> 去掉直接u-boot的编译时的PIE选项和重定位代码时对链接地址的动态处理,这样可以减少u-boot体积,使得重定位代码的位置尽量位于SRAM的4k空间内,手工安排u-boot链接地址,将u-boot装载到SDRAM确定的地址上;
上述两种方案,这篇博客就不详细说明了,后续博客再详细说明;