目标板:FL2440
硬件资源:64M SDRAM/256M nandflash(k9f2g08u0a)/4M nor flash(JS28F320)
开发环境:fedora 14
源码:u-boot-2009.11
交叉编译工具链:cross-3.3.2.tar.bz2
参考资料:http://linchunai1212.blog.163.com/blog/static/3511214320106169413757/?fromdm&fromSearch&isFromSearchEngine=yes
天嵌TQ2440 uboot-1.16源码(主要参考256M nand flash 代码重定位部分程序)
关于uboot的源码结构这里就不多说了,网上很这方面的资料。注意,此文章不支持nor flash启动。
一、建立开发板项目,并尝试编译
1、下面的操作都是基于目录[root@fzliu u-boot-2009.11]#
2、指定交叉编译器
[root@fzliu u-boot-2009.11]# vim Makefile,
修改163行为CROSS_COMPILE ?=arm-linux-
3、在board/samsung/下建立自己的开发板项目
[root@fzliu u-boot-2009.11]# mkdir board/samsung/rocko2440
(取名rocko是因为喜欢Sylvester Stallone主演的励志电影《洛奇》,)
4、注意,uboot的移植是基于源码包的smdk2410开发板,因为2410和2440资源并不多。将smdk2410下所有文件复制到rocko2440.
[root@fzliu u-boot-2009.11]# cp -rf board/samsung/smdk2410/* board/samsung/rocko2440/
[root@fzliu u-boot-2009.11]# mv board/samsung/smdk2410/smdk2410.c board/samsung/rocko2440/rocko2440.c
[root@fzliu u-boot-2009.11]# cp include/configs/smdk2410.h include/configs/rocko2440.h( 建立2440头文件)
[root@fzliu u-boot-2009.11]# vim board/samsung/rocko2440/Makefile
28行改为:
COBJS := rocko2440.o flash.o //因在 rocko2440 下我们将 smdk2410.c 改名为rocko2440.c
5、参考smdk2410_config建立rocko2440_configs选项
[root@fzliu u-boot-2009.11]# vim Makefile
在3050行添加:
rocko2440_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t rocko2440 samsung s3c24x0
*说明:arm :CPU 的架构(ARCH)
arm920t:CPU 的类型
rocko2440:对应在 board 目录下建立新的开发板项目的目录
samsung:新开发板项目目录的上级目录,如直接在 board 下建立新的开发板项
目的目录,则这里就为 NULL
s3c24x0:CPU 型号
*注意:编译选项格式的第二行要用 Tab 键开始,否则编译会出错
6、测试编译新建的 rocko2440 开发板项目
[root@fzliu u-boot-2009.11]# make rocko2440_config
Configuring for rocko2440 board...(此信息表明设置正确)
7、尝试编译
[root@fzliu u-boot-2009.11]# make
此时有可能会出现编译错误,
linux/3.3.2/libgcc.a(_modsi3.oS) uses hardware FP, whereas u-boot uses software FP
解决方法:
[root@fzliu u-boot-2009.11]# vim cpu/arm920t/config.mk
24行改为:
PLATFORM_RELFLAGS += -fno-common -ffixed-r8
#-msoft-float
[root@fzliu u-boot-2009.11]# make
编译后在目录下生成u-boot.bin文件,移植的第一步完成。别急哦,真正的工作还没开始呢
二、分析uboot启动流程之stage1,并修改源码使之能在FL2440开发板上运行。
一般在嵌入式系统软件开发中,在所有源码文件编译完成之后,链接器要读取一个链接分配文件,在该文件中定义了程序的入口点,代码段、数据段等分配情况等。对于rocko2440开发板来说,这个链接文件就是: cpu/arm920t/u-boot.lds,从该文件知程序入口点为_start(在cpu/arm920t/start.S文件中)
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start)//指定程序入口点为_start SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { cpu/arm920t/start.o (.text) *(.text) } . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); .got : { *(.got) } . = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .; . = ALIGN(4); __bss_start = .; .bss (NOLOAD) : { *(.bss) . = ALIGN(4); } _end = .; }
1、知道了程序的入口点是_start,那么我们就从这个文件入手,一步步剖析uboot stage1之启动流程
[root@fzliu u-boot-2009.11]# cpu/arm920t/start.S
这里只分析start.S的启动流程
(1)首先,跳转到start_code执行,设置cpu工作模式为SVC模式,关闭看门狗,屏蔽中断,设置时钟分频比
(2)b1 cpu_init_crit
这部分代码将进行清flush 清caches,关闭MMU功能,调用lowlevel_init函数配置SDRAM(在 board/samsung/rocko2440/目录下),下面修改SDRAM的刷新频率:
[root@fzliu u-boot-2009.11]#vim board/samsung/rocko2440/lowlevel_init.c
将REFCNT改为:
#define REFCNT 0x4f4 (refresh_count)
S3C2440手册知,计算公式:Refresh period = (2^11-refresh_count+1)/HCLK
SDRAM手册知:Refresh period =8192/64ms
下面start.S设置FCLK为405MHZ,分频系数 FCLK:HCLK:PCLK = 1:4:8 ,HCLK=100MHZ
(3)设置堆栈,因为下面的代码重定位程序将使用C函数。注意一定要在调用C前设置堆栈。
(4)代码重定位,这里会有两种情况。如果系统是从norflash启动或者直接下到SDRAM中运行,则不需要进行代码搬移,直接跳到清bss段代码,最后进入uboot的stage2阶段入口_start_armboot;如果从nandflash启动,则调用C函数进行代码搬移,然后跳到清bss段代码,最后进入uboot的stage2阶段入口_start_armboot。
start_code: /* * set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0 //bl coloured_LED_init#if defined(CONFIG_S3C2440) //区别与其他开发板 #define GPBCON 0x56000010 //fl2440开发板的GPB5 6 8 10 控制四个LED灯 #define GPBDAT 0x56000014 #define GPBUP 0x56000018 ldr r0,=GPBUP ldr r1,=0x00 str r1,[r0] ldr r0,=GPBCON ldr r1,=0xddd7fc str r1,[r0] ldr r0,=GPBDAT ldr r1,=0xffc0 //GPB5亮,其它灭 str r1,[r0] #endif
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) /* * relocate exception table */ ldr r0, =_start ldr r1, =0x0 mov r2, #16 copyex: subs r2, r2, #1 ldr r3, [r0], #4 str r3, [r1], #4 bne copyex #endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)|| defined(CONFIG_S3C2440) /* turn off the watchdog */ # if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */#else //下面 2410 和 2440 的寄存器地址是一致的 # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ # endif 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] # endif
# if defined(CONFIG_S3C2440)//添加 s3c2440 的中断禁止部分 ldr r1, =0x7fff //根据 2440 芯片手册,INTSUBMSK 寄存器有 15位可用 ldr r0, =INTSUBMSK str r1, [r0] # endif # if defined(CONFIG_S3C2440) //添加 s3c2440 的时钟部分 #define MPLLCON 0x4C000004 //系统主频配置寄存器基地址 #define UPLLCON 0x4C000008 //USB 时钟频率配置寄存器基地址 ldr r0, =CLKDIVN //设置分频系数 FCLK:HCLK:PCLK = 1:4:8 mov r1, #5 str r1, [r0] ldr r0, =MPLLCON //设置系统主频为 405MHz ldr r1, =0x7F021 //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分 str r1, [r0] ldr r0, =UPLLCON //设置 USB 时钟频率为 48MHz ldr r1, =0x38022 //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分 str r1, [r0] # else //其他开发板的时钟部分,这里就不用管了,我们现在是做 2440 的 /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] #endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410|| CONFIG_S3C2440 */ /* * we do sys-critical inits only at reboot, * not when booting from ram! */#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif /* Set up the stack */stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ #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 clear_bss ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */ #if 1 bl CopyCode2Ram //此处调用 C 代码中读 Nand 的函数,现在还没有要自己编写实现 #else 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
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */ clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l /*进入stage2 入口函数_start_armboot*/ ldr pc, _start_armboot _start_armboot: .word start_armboot/* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit: /* * flush v4 I/D caches */ mov r0, #0 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ /* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) orr r0, r0, #0x00000002 @ set bit 2 (A) Align orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache mcr p15, 0, r0, c1, c0, 0 /* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */ mov ip, lr bl lowlevel_init mov lr, ip mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
2、完成了start.S修改后,在include/configs/rocko2440.h下添加CONFIG_S3C2440 配置选项,
[root@fzliu u-boot-2009.11]# vim include/configs/rocko2440.h
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK2410 1 /* on a SAMSUNG SMDK2410 Board */
#define CONFIG_S3C2440 1 /* in a SAMSUNG S3C2440 SoC */
3、S3C2440 的时钟部分除了在 start.S 中添加外,还要分别在 board/samsung/rocko2440/rocko2440.c 和
cpu/arm920t/s3c24x0/speed.c 中修改或添加部分代码,如下:
[root@fzliu u-boot-2009.11]# vim board/samsung/rocko2440/rocko2440.c
黑体部分有效
#define FCLK_SPEED 2
#if FCLK_SPEED==0 /* Fout = 203MHz, Fin = 12MHz for Audio */ #define M_MDIV 0xC3 #define M_PDIV 0x4 #define M_SDIV 0x1 #elif FCLK_SPEED==1 /* Fout = 202.8MHz */ #define M_MDIV 0xA1 #define M_PDIV 0x3 #define M_SDIV 0x1
#elif FCLK_SPEED==2 /* Fout = 405MHz */ #define M_MDIV 0x7F //这三个值根据 S3C2440 芯片手册“PLL VALUE SELECTION TABLE”部分进行设置 #define M_PDIV 0x2 #define M_SDIV 0x1
#endif
#define USB_CLOCK 2 #if USB_CLOCK==0 #define U_M_MDIV 0xA1 #define U_M_PDIV 0x3 #define U_M_SDIV 0x1 #elif USB_CLOCK==1 #define U_M_MDIV 0x48 #define U_M_PDIV 0x3 #define U_M_SDIV 0x2
#elif USB_CLOCK==2 /* Fout = 48MHz */ #define U_M_MDIV 0x38 //这三个值根据 S3C2440 芯片手册“PLL VALUESELECTIONTABLE”部分进行设置 #define U_M_PDIV 0x2 #define U_M_SDIV 0x2
#endif
[root@fzliu u-boot-2009.11]# vim cpu/arm920t/s3c24x0/speed.c
static ulong get_PLLCLK(int pllreg) { struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); ulong r, m, p, s; if (pllreg == MPLL) r = readl(&clk_power->MPLLCON); else if (pllreg == UPLL) r = readl(&clk_power->UPLLCON); else hang(); m = ((r & 0xFF000) >> 12) + 8; p = ((r & 0x003F0) >> 4) + 2; s = r & 0x3;
#if defined(CONFIG_S3C2440) if(pllreg == MPLL) { //参考S3C2440 芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2 s ) return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); } #endif
return (CONFIG_SYS_CLK_FREQ * m) / (p << s); } /* return FCLK frequency */ ulong get_FCLK(void) { return get_PLLCLK(MPLL); } /* return HCLK frequency */ ulong get_HCLK(void) { struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
#if defined(CONFIG_S3C2440) return(get_FCLK()/4); #endif
return (readl(&clk_power->CLKDIVN) & 2) ? get_FCLK() / 2 : get_FCLK(); } /* return PCLK frequency */ 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(); } /* return UCLK frequency */ ulong get_UCLK(void) { return get_PLLCLK(UPLL); } #endif
4、到此,uboot是否就支持了nandflash 启动了呢,还记得我们在start.S文件中添加的用于将uboot从nandflash搬到SDRAM中的函数CopyCode2Ram 吗?现在我们就去实现这个函数,在board/samsung/rocko2440/下新建一个nand_read.c
[root@fzliu u-boot-2009.11]# vim board/samsung/rocko2440/nand_read.c(这部分参考TQ2440 uboot1.1.6)
#include <config.h> #define NF_BASE 0x4E000000 //Nand Flash 配置寄存器基地址 #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_SECTOR_SIZE 512 #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1) #define NAND_SECTOR_SIZE_LP 2048 #define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1) #define BUSY 1 #if defined(CONFIG_S3C2440) #define GPBCON (*(volatile unsigned long *)0x56000010) #define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPBUP (*(volatile unsigned long *)0x56000018) #endif /* low level nand read function */ ldr r0, =TEXT_BASE//传递给 C 代码的第一个参数:u-boot 在 RAM 中的起始地址 mov r1, #0x0 //传递给 C 代码的第二个参数:Nand Flash 的起始地址 ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 //r2 <- size of armboot 传递给 C 代码的第三个参数:u-boot 的长度大小(128k) static void s3c2440_wait_idle(void) { int i; while(!(NFSTAT & BUSY)) for(i=0; i<10; i++); } static void s3c2440_nand_select_chip(void) { int i; NFCONT &= ~(1<<1); for(i=0; i<10; i++); } static void s3c2440_nand_deselect_chip(void) { NFCONT |= (1<<1); } static void s3c2440_write_cmd(int cmd) { NFCMD = cmd; } static void s3c2440_nand_reset(void) { s3c2440_nand_select_chip(); s3c2440_write_cmd(0xff); //reset s3c2440_wait_idle(); s3c2440_nand_deselect_chip();//disable chip select } static void s3c2440_write_addr_lp(unsigned int addr) { int i; int col, page; col = addr & NAND_BLOCK_MASK_LP;//取NAND_BLOCK_MASK_LP的整数倍得到col page = addr / NAND_SECTOR_SIZE_LP;//取NAND_BLOCK_MASK_LP的余数得到row NFADDR = col & 0xff; /* Column Address A0~A7 */ for(i=0; i<10; i++); NFADDR = (col >> 8) & 0x0f; /* Column Address A8~A11 */ for(i=0; i<10; i++); NFADDR = page & 0xff; /* Row Address A12~A19 */ for(i=0; i<10; i++); NFADDR = (page >> 8) & 0xff; /* Row Address A20~A27 */ for(i=0; i<10; i++); //if (b128MB == 0) NFADDR = (page >> 16) & 0x03; /* Row Address A28~A29 or 0x01? */ for(i=0; i<10; i++); } static unsigned char s3c2440_read_data(void) { return NFDATA; } int nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size) { int i, j; char dat; if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) { return -1; } s3c2440_nand_select_chip(); for(i=start_addr; i < (start_addr + size);) { /* Check Bad Block */ if(0){ int col, page; col = i & NAND_BLOCK_MASK_LP; page = i / NAND_SECTOR_SIZE_LP; s3c2440_write_cmd(0x00); NFADDR = 5; for(j=0; j<10; j++); NFADDR = 8; for(j=0; j<10; j++); NFADDR = page & 0xff; /* Row Address A12~A19 */ for(j=0; j<10; j++); NFADDR = (page >> 8) & 0xff; /* Row Address A20~A27 */ for(j=0; j<10; j++); //if (b128MB == 0)//big page NFADDR = (page >> 16) & 0x03; /* Row Address A28~A29 */ for(j=0; j<10; j++); s3c2440_write_cmd(0x30); s3c2440_wait_idle(); dat = s3c2440_read_data(); /*slect chip */ s3c2440_nand_deselect_chip(); if(dat != 0xff) i += 131072; // 1 Block = 2048*64= 131072 /* Read Page */ /* slect chip */ s3c2440_nand_select_chip(); }///*end of Check Bad Block */ /* send read0 cmd */ s3c2440_write_cmd(0); /* Write Address */ s3c2440_write_addr_lp(i); s3c2440_write_cmd(0x30); s3c2440_wait_idle(); //NAND_SECTOR_SIZE_LP=2048bytes for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) { *buf = s3c2440_read_data(); buf++; } } /* disable chip select */ s3c2440_nand_deselect_chip(); return 0; } /* nor boot return 1*/ int bBootFrmNORFlash(void) { volatile unsigned int *pdw = (volatile unsigned int *)0; unsigned int dwVal; dwVal = *pdw; *pdw = 0x12345678; if (*pdw != 0x12345678) { return 1; } else { *pdw = dwVal; return 0; } } void nand_init_ll(void) { #define TACLS 0 #define TWRPH0 3 #define TWRPH1 0 NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); NFCONT = (1<<4)|(1<<1)|(1<<0); s3c2440_nand_reset(); } int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size) { unsigned int *pdwDest; unsigned int *pdwSrc; int i; if (bBootFrmNORFlash()) { pdwDest = (unsigned int *)buf; pdwSrc = (unsigned int *)start_addr; /* boot form NOR Flash*/ for (i = 0; i < size / 4; i++) { pdwDest[i] = pdwSrc[i]; } return 0; } else { /* init NAND Flash */ nand_init_ll(); nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP)); } return 0; }
5、修改链接文件,,在 cpu/arm920t/u-boot.lds 中,这个 u-boot 启动连接脚本文件决定了 u-boot 运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于 nandboot 的子函数放到 4K 之后,否则是无法启动的。如下:
.text :
{
cpu/arm920t/start.o (.text)
board/samsung/rocko2440/lowlevel_init.o (.text)
board/samsung/rocko2440/nand_read.o (.text)
*(.text)
6、到此完成了对从nandflash启动的支持,下面修改uboot启动时命令行前的名字。
[root@fzliu u-boot-2009.11]# vim include/configs/rocko2440.h //修改命令行前的名字
#define CONFIG_SYS_PROMPT "[rocko2440]#" //将命令行前的名字改成[rocko2440]
7、[root@fzliu u-boot-2009.11]# make 将生成的u-boot.bin 下载到nandflash中,从nandflash启动,将会看到如下信息:
U-Boot 2009.11 (Oct 24 2011 - 18:12:47)
DRAM: 64 MB
## Unknown FLASH on Bank 0: ID 0xffff, Size = 0x00000000 = 0 MB
Flash: 0 kB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
[rocko2440]#
8、现在的 Nand 还不能做任何事情,而且也没有显示有关 Nand 的任何信息,所以只能说明上面的这些步骤只是完成了 Nand 移植的 Stage1 部分。关于stage2的nandflash驱动移植将在后面补上。