前面分析过 u-boot1.1.6 三个阶段的启动过程,也尝试自己写了一个简单的 bootloader ,是时候尝试移植一个相对较新一点的 u-boot 了,看过韦东山老师移植 u-boot2012 的教学视频,我决定按照自己的思路来移植它。
u-boot:u-boot2012.04.1
开发板:MINI2440 ,天下 2440 都一样
编译器:4.3.2
2440 平台在移植时,均参考 SMDK2410 ,以它作为母版进行修改,所以,我们需要对 SMDK2410 在 u-boot 中的代码非常熟悉,才能做到移植起来得心应手,没有别的好办法,移植之前先进行分析,或者边移植边分析。u-boot2012相对于 u-boot1.1.6 有了哪些变化,对于 u-boot2012 的大框架,我们也是需要提前分析和了解的。移植之前的分析工作,请参考我之前的博客:http://blog.csdn.net/lizuobin2/article/details/52089474 ,最大的变化就是代码的组织规则发生了变化,这里简单提一下:在老的 uboot 中,链接地址是固定的,是 uboot 跑在 sdram 的实际地址,而在 uboot2012 等较新的版本中,链接地址为 0 ,代码重定位到 sdram 之后,更正位置有关码,从而让代码正确执行。这样一来,uboot中多了两个段,体积增大,逻辑复杂。
由于 smdk2410 代码默认只支持 Nor 启动,那我们我们第一个目标,就是移植一个 支持 nor 启动的u-boot ,采用默认的链接地址为 0 的方式(因为不用修改)。
一、创建单板
仿照 SMDK2410 创建我们自己的单板,SMDK2410 位于 board/samsung 目录
cp -r board/samsung/smdk2410 board/samsung/smdk2440
cd board/samsung/smdk2440
将 smdk2410.c 重命名为 smdk2440.c
修改该目录下的 Makefile ,将 COBJS := smdk2410.o 改为 COBJS := smdk2440.o
每一个单板都有一个配置相关的头文件位于 include/configs 目录
cp include/configs/smdk2410.h include/configs/smdk2440.h
修改 smdk2440.h 中 #define CONFIG_S3C2410 为 #define CONFIG_S3C2440 ,代码中的宏开关
修改 smdk2440.h 中 #define CONFIG_SMDK2410 为 #define CONFIG_SMDK2440 ,编译时才会编译我们所创建的smdk2440.c 文件
编译之前要先配置,配置命令是 make $(boardname)_config 以 smdk2410 为例就是 make smdk2410_config
执行报错!回想 uboot1.1.6 中,是在顶层的 makefile 中添加了一个配置选项如下:
smdk2410_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
那么,我们也要做类似的修改,看了下 2012 的顶层 makefile ,没有发现具体某个单板的配置选项,但是有这么一行:
%_config:: unconfig
@$(MKCONFIG) -A $(@:_config=)
sinclude $(obj).boards.depend
$(obj).boards.depend: boards.cfg
大概的意思,配置选项依赖于 boards.cfg 这个文件,OK 打开看一看。
照葫芦画瓢,我们新增一行
smdk2410 arm arm920t - samsung s3c24x0
再来 make smdk2410_config 一下,提示 Configuring for smdk2440 board...
然后 make ,悲剧发生编译不过
它说 s3c2410_get_base_nand 这个函数没有声明,打开drivers\mtd\nand\s3c2410_nand.c 查找错误,在 s3c2410_hwcontrol 等4个函数中,都引用了 struct s3c2410_nand *nand = s3c2410_get_base_nand();
s3c2410_get_base_nand 函数声明在arch\arm\include\asm\arch-s3c24x0\s3c2410.h 中,而我们的单板是smdk2440.c ,那么它引用的头文件应该是 arch\arm\include\asm\arch-s3c24x0\s3c2440.h
在 s3c2440.h 中,s3c2410_get_base_nand 已经更新为 s3c2440_get_base_nand ,而且返回值类型为struct s3c2440_nand ,这个结构体在 s3c2440.h 中声明。
那么,我们要做的就是在 s3c2410_nand.c
将所有的 struct s3c2410_nand *nand = s3c2410_get_base_nand()
改成 struct s3c2440_nand *nand = s3c2440_get_base_nand() ,共计 4 处
修改完 make 一下,你会发现还有一个类似的错误 ,依旧是在 s3c2410_nand.c 中
将 board_nand_init 函数中 struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
修改为 struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
再来 make 一下,不出意外,编译通过生成 u-boot.bin
检查一下我们新建的单板目录文件是否被编译进去 board/samsung/smdk2410 board/samsung/smdk2440
.o 文件已经生成,真是极好的,第一部分新建单板完成。
经过这一阶段,我们也应该留意到 struct s3c2410_nand 和 struct s3c2440_nand 寄存器数目不同,我们在后边对 nandflash 相关部分进行移植的时候还需要修改,这里留意一下。
二、第一阶段代码移植
arch\arm\cpu\arm920t\start.S 是我们需要分析修改的第一个文件,做过裸机实验的同学对这部分应该相当熟悉,要求改的内容不过是因为 2410 和 2440 的一些寄存器差异,我这里是针对 mini2440 的修改,请结合自己的开发板进行移植(都差不多)。
1、中断屏蔽
# if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interrupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
# elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# elif defined(CONFIG_S3C2440) //新增
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
# define MPLLCON 0x4C000004
# endif
/* turn off the watchdog */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#elif defined(CONFIG_S3C2440)//新增
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
2、修改时钟,这个是重点,时钟不对的话后边的串口 nandflash sdram 时序都会受到影响,这里我们直接将FCLK 设置为 400Mhz FCLK:HCLK:PCLK=1:4:8 ,HCLK 100Mhz PCLK 50Mhz
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) //修改
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#elif defined(CONFIG_S3C2440) //新增
ldr r0, =CLKDIVN
mov r1, #0x05 // FCLK:HCLK:PCLK=1:4:8, HDIVN=2,PDIVN=1
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器*/
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode?*/
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器*/
ldr r0, =MPLLCON
ldr r1, =0x5c011 //MPLL_400MHZ (0x5c<<12)|(0x01<<4)|(0x01))
str r1, [r0]
3、内存控制器初始化部分修改 board\samsung\smdk2440\lowlevel_init.S ,修改13个值就可以了
SMRDATA:
.long 0x22011110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018005 @ BANKCON6
.long 0x00018005 @ BANKCON7
.long 0x008C07A3 @ rEFrESH
.long 0x000000B1 @ BANKSIZE
.long 0x00000030 @ MrSrB6
.long 0x00000030 @ MrSrB7
然后,代码就跳转到 arch\arm\lib\board.c -> board_init_f ,这个函数的作用是调用函数指针数组 init_sequence 中的每一个初始化函数,还有进行内存划分,先来看一下 init_sequence 中的初始化函数。
4、单板相关初始化修改 board\samsung\smdk2440\smdk2440.c -> board_early_init_f
这里边有一次初始化了时钟,而且与我们在start.S中初始化的不同,果断删掉
/* 注释掉以下代码 */
/*writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
*/
简单修改至此,make 将u-boot.bin 烧写到 norflash 启动开发板