到u-boot官网http://www.denx.de/wiki/U-Boot 上 下载了最新版u-boot-2009.06。移植到过程主要是参照了韦东山的《嵌入式linux应用开发完全手册》一书。首先阅读了一下readme,全 E文,有点晕(又开始后悔英文学的不好了)。大体知道了编译u-boot官方推荐的开发环境ELDK,以及一些串口调试工具的设置 (minicom,ckermit..)。由于原来机器上本来就装了比较新的交叉编译工具(arm-linux-gcc4.3.2),就没有装eldk。 但后来在编译过程中发现很多东西的不兼容,没办法,还是把eldk给装上了(又得浪费了1G的空间了)。
环境搭建好后就开始了移植过程。简单看了一下书上讲的bootloader的原理,它的主要功能是初始化硬件,引导操作系统。当然,对于开发人员来说,它还有一个功能是下载操作系统和文件系统到开发板的存储设备上去。下面是移植到具体过程:
(1) 首先选定开发板型号,若u-boot中没有提供,则选择一款最相近的,在其基础上修改。我的这款开发板(友善micro2440)是友善根据三星的公板自己做的,u-boot中与其最相近的就是smdk2410了。
(2) 在顶层的makefile中添加开发板的选项。找到顶层makefile的smdk2410选项:
smdk2410_config : unconfig
(TAB键)@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
仿照其格式,在下面添加我自己的开发板选项(取名为micro2440)。
micro2440_config : unconfig
(TAB键) @$(MKCONFIG) $(@:_config=) arm arm920t micro2440 NULL s3c24x0
(3) 在board/samsung目录下添加micro2440文件夹(与makefile添加的项对应)。并复制该目录下smdk2410文件夹中的文件到新建立的文件夹下。
(4) 在inclue/config目录下添加micro2440.h文件,内容就复制smdk2410.h的。这个文件是u-boot的配置文件,后面对u-boot进行配置时基本上就是通过在这个文件中增加或减少宏定义实现的。
至此,第一阶段工作完成。在u-boot下执行make distclean ;make micro2440_congfig ;make all验证一下编译是否能通过。没出错的话会编译生成u-boot.bin文件。我download到开发板下试了一下,如我所料,一点反应也没有。因为 代码还没有做任何修改,实际上这个u-boot是基于2410的,而2440和2410在时钟频率设置上有很大差别,所以cpu初始化时不能通过的。
下面进行代码修改:
u-boot属于两阶段的bootloader,第一阶段文件为cpu/arm920t/start.s和board/smdk2410 /lowlevel_init.s。前者是平台相关,后者是开发板相关。移植到新的平台时,一般这两个文件都要根据具体情况修改。
(1)修改sdram设置
sdram的初始化是在u-boot引导的第一阶段的lowlevel_init.s中完成的。检查bank6设置:
#define B1_BWSCON (DW32) #define B2_BWSCON (DW16) #define B3_BWSCON (DW16 + WAIT + UBLB) #define B4_BWSCON (DW16) #define B5_BWSCON (DW16) #define B6_BWSCON (DW32) #define B7_BWSCON (D
B6_BWSCON (DW32) 位宽为32,无需修改。
然后是根据hclk设置sdram刷新参数,主要是对REFCNT寄存器进行设置。我的开发板的hclk是100MHz,sdram芯片为HY57V561620,查阅其datasheet,刷新周期为7.8125us。所以将
#define REFCNT 1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
修改为
#define REFCNT 0x4f4 /* period=7.8125us, HCLK=100Mhz, (2048+1-7.8125*100) */
(2)修改与时钟相关的部分。
首先是board/samsung/micro2440/micro2440.c中的board_init函数。将2410的时钟设置改成2440的。代码如下:
/* S3C2440: Mpll = (2*m * Fin) / (p * 2^s), UPLL = (m * Fin) / (p * 2^s) * m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2 */ #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01)) #define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02)) #define S3C2440_MPLL_100MHZ ((0x5c<<12)|(0x01<<4)|(0x03)) #define S3C2440_UPLL_96MHZ ((0x38<<12)|(0x02<<4)|(0x01)) #define S3C2440_UPLL_48MHZ ((0x38<<12)|(0x02<<4)|(0x02)) #define S3C2440_CLKDIV (0x05) // | (1<<3)) /* FCLK:HCLK:PCLK = 1:4:8, UCLK = UPLL/2 */ #define S3C2440_CLKDIV188 0x04 /* FCLK:HCLK:PCLK = 1:8:8 */ #define S3C2440_CAMDIVN188 ((0<<8)|(1<<9)) /* FCLK:HCLK:PCLK = 1:8:8 */ int board_init (void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); /* set up the I/O ports */ gpio->GPACON = 0x007FFFFF; gpio->GPBCON = 0x00044555; gpio->GPBUP = 0x000007FF; gpio->GPCCON = 0xAAAAAAAA; gpio->GPCUP = 0x0000FFFF; gpio->GPDCON = 0xAAAAAAAA; gpio->GPDUP = 0x0000FFFF; gpio->GPECON = 0xAAAAAAAA; gpio->GPEUP = 0x0000FFFF; gpio->GPFCON = 0x000055AA; gpio->GPFUP = 0x000000FF; gpio->GPGCON = 0xFF95FFBA; gpio->GPGUP = 0x0000FFFF; gpio->GPHCON = 0x002AFAAA; gpio->GPHUP = 0x000007FF; /* arch number of SMDK2440-Board */ gd->bd->bi_arch_number = MACH_TYPE_S3C2440; /* FCLK:HCLK:PCLK = 1:4:8 */ clk_power->CLKDIVN = S3C2440_CLKDIV; /* change to asynchronous bus mod */ __asm__( "mrc p15, 0, r1, c1, c0, 0/n" /* read ctrl register */ "orr r1, r1, #0xc0000000/n" /* Asynchronous */ "mcr p15, 0, r1, c1, c0, 0/n" /* write ctrl register */ :::"r1" ); /* to reduce PLL lock time, adjust the LOCKTIME register */ clk_power->LOCKTIME = 0xFFFFFFFF; /* configure UPLL */ clk_power->UPLLCON = S3C2440_UPLL_48MHZ; /* some delay between MPLL and UPLL */ delay (4000); /* configure MPLL */ clk_power->MPLLCON = S3C2440_MPLL_400MHZ; /* some delay between MPLL and UPLL */ delay (8000); /* adress of boot parameters */ gd->bd->bi_boot_params = 0x30000100; icache_enable(); #if 0 dcache_enable(); #endif return 0; }
然后修改cpu/arm920t/s3c24x0/speed.c中的get_HCLK,get_PLLCLK,get_PCLK函数。get_PLLCLK函数中,将 return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
改为 return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /* S3C2440 */。
/* for s3c2440 */ #define S3C2440_CLKDIVN_PDIVN (1<<0) #define S3C2440_CLKDIVN_HDIVN_MASK (3<<1) #define S3C2440_CLKDIVN_HDIVN_1 (0<<1) #define S3C2440_CLKDIVN_HDIVN_2 (1<<1) #define S3C2440_CLKDIVN_HDIVN_4_8 (2<<1) #define S3C2440_CLKDIVN_HDIVN_3_6 (3<<1) #define S3C2440_CLKDIVN_UCLK (1<<3) #define S3C2440_CAMDIVN_CAMCLK_MASK (0xf<<0) #define S3C2440_CAMDIVN_CAMCLK_SEL (1<<4) #define S3C2440_CAMDIVN_HCLK3_HALF (1<<8) #define S3C2440_CAMDIVN_HCLK4_HALF (1<<9) #define S3C2440_CAMDIVN_DVSEN (1<<12) /* return HCLK frequency */ ulong get_HCLK(void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); unsigned long clkdiv; unsigned long camdiv; int hdiv = 1; clkdiv = clk_power->CLKDIVN; camdiv = clk_power->CAMDIVN; /* work out clock scalings */ switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { case S3C2440_CLKDIVN_HDIVN_1: hdiv = 1; break; case S3C2440_CLKDIVN_HDIVN_2: hdiv = 2; break; case S3C2440_CLKDIVN_HDIVN_4_8: hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; break; case S3C2440_CLKDIVN_HDIVN_3_6: hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3; break; return get_FCLK() / hdiv; }
get_PCLK函数: /* return PCLK frequency */ ulong get_PCLK(void) { S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER(); unsigned long clkdiv; unsigned long camdiv; int hdiv = 1; clkdiv = clk_power->CLKDIVN; camdiv = clk_power->CAMDIVN; /* work out clock scalings */ switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { case S3C2440_CLKDIVN_HDIVN_1: hdiv = 1; break; case S3C2440_CLKDIVN_HDIVN_2: hdiv = 2; break; case S3C2440_CLKDIVN_HDIVN_4_8: hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; break; case S3C2440_CLKDIVN_HDIVN_3_6: hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3; break; return get_FCLK() / hdiv / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1); }
至此,第二阶段工作完成。重新执行 make micro2440_config 和 make all 后生成u-boot.bin。下载到开发板上,串口有输出了。