u-boot移植是系统移植的第一步也是为内核移植做准备工作,板子上电后,芯片内部irom里面的固化代码会判断开发板的启动方式(通过拨码开关可选择是EMMC启动还是SD卡启动),然后将U-BBOOT的第一部分代码加载到IRAM中,这段代码初始化DRAM,之后把 BL2全部加载到DRAM之中,开始初始化芯片内部设备等各种初始化,把u-boot整个代码加载到DRAM中,开始运行。



一 fs4412 (SOC:samsung,exynos4412) 启动



硬件信息
 SOC   : 三星 exynos4412 (4个cortex-A9)  主频 1.4GHZ
 board : fs4412



u-boot版本
  u-boot-2013




1.SOC内部irom代码 (从存储介质拷贝一部分代码到iRAM)
2.SOC内部iRAM代码 (bootloader第一段代码)
  [1]系统时钟初始化
  [2]内存初始化
  [3]将存储介质中的bootloader (u-boot-2013.01) 搬移到DDR内存中



3.内存中bootloader(u-boot-2013.01)运行



  (1)设置异常向量表
     [1]每个异常的处理过程
  [2]告诉ARM核异常向量表的基地址



  (2)设置ARM核为SVC模式



  (3)初始化cp15的协处理
    [1]让cache无效
  [2]关闭cache和mmu



  (4)_main  arch/arm/lib/crt0.S
     [1]设置sp
   [2]板子的第一阶段初始化(board_init_f : arch/arm/lib/board.c)
     (1)获取cpu id
   (2)定时器初始化
   (3)波特率
   (4)串口初始化
   (5)打印u_boot版本信息
   (6)打印cpu信息
   (7)记录内存大小
   (8)预留一部分内存空间,为u_boot重定向做准备




  (5)u_boot重定向 (将u_boot搬移内存的最顶端)



  (6)设置c语言的运行环境
    [1]设置sp
  [2]清bss段



  (7)板子的第二阶段初始化(board_init_r : arch/arm/lib/board.c)
  [1]让串口作为标准输入输出设备
     [2]从存储介质中读取u_boot需要的环境变量
   (注意:由于之前没有保存过,第一次读取会失败,此时使用u_boot自带的默认环境变量)
     [3]初始化MMC卡
  [4]初始化网卡
  [5]main_loop
   (1)判断是否设置bootcmd和bootdelay
     [1]如果已经设置,会在到计时的时间中判断用户是否有输入,
        如果没有输入则执行bootcmd中指定的命令
     如果用户有输入,则进入u_boot交互界面(监视用户是否输入了uboot命令,如果输入则执行命令)
    
     [2]如果没有设置,则进入u_boot交互界面





二 uboot移植核心思想 



1. 厂家直接提供u-boot -> 烧写 或者 修改(增加新功能) 或 u-boot 版本升级
2. 芯片公司,让u-boot支持公司的芯片 ,自己仿照别的厂家,添加自己开发板相关代码
3. 芯片公司根据自己的芯片编写bootloader,仿照u-boot设计思想
-----------------------------------------------------------------------------------
(1)熟悉SOC芯片和u-boot启动流程
(2)系统时钟,内存是否能正常初始化
(3)搬移u-boot到内存过程
(4)对存储介质(emmc)如何进行读写




三  uboot移植



---------------------------------------------------------------------------------------------------
在u-boot源码中找到一块和自己开发板类似板子 ,然后给予这一块开发板做移植
(1)芯片厂家 && 芯片型号
   samsung     exynos4412    ===> 三星母板 : smdk4412



   在u-boot中没有找到母板(芯片厂家做出一款芯片后,设计的第一块开发板) 
   <1>查阅一下芯片厂家给予这个芯片是否做了其他母板
   <2>是不是当前u-boot版本中,芯片厂家还没有将自己的母板代码添加进来 ----> 去新版本的u-boot中寻找 
---------------------------------------------------------------------------------------------------



(1)修改Makefile指定自己的交叉开发工具链前缀



ifeq (arm,$(ARCH))
 CROSS_COMPILE ?=arm-none-linux-gnueabi-
endif



【实验步骤】
一、建立自己的平台
1、下载源码
我们可以在下面这个网站上下载最新的和以前任一版本的 uboot
ftp://ftp.denx.de/pub/u-boot/
2、解压 uboot 源码并进入目录
$ tar xvf u-boot-2013.01.tar.bz2
$ cd u-boot-2013.01R
3、指定交叉编译工具链
$ vim Makefile

ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
#endif
下添加
ifeq (arm,$(ARCH))
CROSS_COMPILE ?= arm-none-linux-gnueabi-
#endif4、指定产品 CPU
我们产品用的 CPU 是 exynos 4412
查看 u-boot 源码该 CPU 是否已支持
U-boot 已支持,见 arch/arm/cpu/armv7/exynos/
5、指定产品 BOARD
找一个最类似的 board配置修改, 这里我们参考的是 board/samsung/origen/
$ cp -rf board/samsung/origen/ board/samsung/fs4412
$ mv board/samsung/fs4412/origen.c board/samsung/fs4412/fs4412.c
$ vim board/samsung/fs4412/Makefile
修改 origen.o 为 fs4412.o
$ cp include/configs/origen.h include/configs/fs4412.h
$ vim include/configs/fs4412.h
修改
#define CONFIG_SYS_PROMPT "ORIGEN #"

#define CONFIG_SYS_PROMPT "fs4412 #"
修改
#define CONFIG_IDENT_STRING for ORIGEN

#define CONFIG_IDENT_STRING for fs4412
#vim boards.cfg
参考
origen arm armv7 origen samsung exynos
并在后面新增
fs4412 arm armv7 fs4412 samsung exynos6、编译 u-boot
$ make distclean
$ make fs4412_config
$ make
编译完成后生成的 u-boot.bin 就是可执行的镜像文件。
但是该文件还不能在我们板子上运行,我们需要对 u-boot 源代码进行相应的修改。



二、实现能看到串口终端信息
1、确认第一条指令有运行到 (点灯法)

 在 arch/arm/cpu/armv7/start.S 134 行后添加点灯程序 
  
 #if 1 
  
 ldr r0, =0x11000c40 @GPK2_7 led2 
  
 ldr r1, [r0] 
  
 bic r1, r1, #0xf0000000 
  
 orr r1, r1, #0x10000000 
  
 str r1, [r0] 
  
 ldr r0, =0x11000c44 
  
 mov r1,#0xff 
  
 str r1, [r0] 
  
 #endif 
  
  添加三星加密方式 
  
 exynos 需要三星提供的初始引导加密后,我们的 u-boot,才能被引导运行 
  
 $cp sdfuse_q u-boot-2013.01 -rf 
  
 注:sdfuse_q 三星提供的加密处理 
  
 $cp CodeSign4SecureBoot u-boot-2013.01 -rf 
  
 注:CodeSign4SecureBoot 三星提供的安全启动方式 修改 Makefile 
  
 $vim Makefile 
  
 修改实现 sdfuse_q 的编译 
  
 在 
  
 $(obj)u-boot.bin: $(obj)u-boot 
  
 $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ 
  
 $(BOARD_SIZE_CHECK) 
  
 下添加 
  
 @#./mkuboot 
  
 @split -b 14336 u-boot.bin bl2 
  
 @+make -C sdfuse_q/ 
  
 @#cp u-boot.bin u-boot-4212.bin 
  
 @#cp u-boot.bin u-boot-4412.bin 
  
 @#./sdfuse_q/add_sign 
  
 @./sdfuse_q/chksum 
  
 @./sdfuse_q/add_padding 
  
 @rm bl2a* 
  
 @echo 
  
 注意是 tab 键缩进的,否则 makefile 编译报错 
  
 注意如果执行了 make distclean 需重新拷贝 CodeSign4SecureBoot 
  
  拷贝编译脚本 
  
 $ cp build.sh u-boot-2013.01 
  
 $ chmod 777 u-boot-2013.01/ build.sh 
  
 $ ./buildsh 
  
 注:build.sh 脚本方式完成自动添加加密方式,编译生成所需文件 u-boot_fs4412.bin 
  
 烧写新的 u-boot_fs4412.bin 
  
 复位,发现灯有点亮,说明 我们的 u-boot 有运行到 
  
 

   2、实现串口输出 
  
 修改 lowlevel_init.S 文件 
  
 $vim board/samsung/fs4412/lowlevel_init.S 
  
  添加临时栈 
  
 在 
  
 lowlevel_init: 
  
 后添加 
  
 ldr sp,=0x02060000 @use iRom stack in bl2 
  
  添加关闭看门狗代码 
  
 在 
  
 beq wakeup_reset 
  
 后添加 
  
 #if 1 /*for close watchdog */ 
  
 /* PS-Hold high */ 
  
 ldr r0, =0x1002330c 
  
 ldr r1, [r0] 
  
 orr r1, r1, #0x300 
  
 str r1, [r0] 
  
 ldr r0, =0x11000c08 
  
 ldr r1, =0x0 
  
 str r1, [r0] 
  
 /* Clear MASK_WDT_RESET_REQUEST */ldr r0, =0x1002040c 
  
 ldr r1, =0x00 
  
 str r1, [r0] 
  
 #endif 
  
  添加串口初始化代码 
  
 在 uart_asm_init: 的 
  
 str r1, [r0, #EXYNOS4_GPIO_A1_CON_OFFSET] 
  
 后添加 
  
 ldr r0, =0x10030000 
  
 ldr r1, =0x666666 
  
 ldr r2, =CLK_SRC_PERIL0_OFFSET 
  
 str r1, [r0, r2] 
  
 ldr r1, =0x777777 
  
 ldr r2, =CLK_DIV_PERIL0_OFFSET 
  
 str r1, [r0, r2] 
  
 注释掉 trustzone 初始化 
  
 注释掉 
  
 bl uart_asm_init 
  
 下的 
  
 bl tzpc_init 
  
 重新编译 u-boot 
  
 $ ./build.sh 
  
 烧写新的 u-boot_fs4412.bin 
  
 复位会看到串口信息




三、网卡移植



1、添加网络初始化代码

$ vim board/samsung/fs4412/fs4412.c 
  
 在 struct exynos4_gpio_part2 *gpio2; 后添加 
  
 #ifdef CONFIG_DRIVER_DM9000 
  
 #define EXYNOS4412_SROMC_BASE 0X12570000 
  
 #define DM9000_Tacs (0x1) 
  
 #define DM9000_Tcos (0x1) 
  
 #define DM9000_Tacc (0x5) 
  
 #define DM9000_Tcoh (0x1) 
  
 #define DM9000_Tah (0xC)#define DM9000_Tacp (0x9) 
  
 #define DM9000_PMC (0x1) 
  
 struct exynos_sromc { 
  
 unsigned int bw; 
  
 unsigned int bc[6]; 
  
 }; 
  
 /* 
  
 * s5p_config_sromc() - select the proper SROMC Bank and configure the 
  
 * band width control and bank control registers 
  
 * srom_bank - SROM 
  
 * srom_bw_conf - SMC Band witdh reg configuration value 
  
 * srom_bc_conf - SMC Bank Control reg configuration value 
  
 */ 
  
 void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf) 
  
 { 
  
 unsigned int tmp; 
  
 struct exynos_sromc *srom = (struct exynos_sromc 
  
 *)(EXYNOS4412_SROMC_BASE); 
  
 /* Configure SMC_BW register to handle proper SROMC bank */ 
  
 tmp = srom->bw; 
  
 tmp &= ~(0xF << (srom_bank * 4)); 
  
 tmp |= srom_bw_conf; 
  
 srom->bw = tmp; 
  
 /* Configure SMC_BC register */srom->bc[srom_bank] = srom_bc_conf; 
  
 } 
  
 static void dm9000aep_pre_init(void) 
  
 { 
  
 unsigned int tmp; 
  
 unsigned char smc_bank_num = 1; 
  
 unsigned int smc_bw_conf=0; 
  
 unsigned int smc_bc_conf=0; 
  
 /* gpio configuration */ 
  
 writel(0x00220020, 0x11000000 + 0x120); 
  
 writel(0x00002222, 0x11000000 + 0x140); 
  
 /* 16 Bit bus width */ 
  
 writel(0x22222222, 0x11000000 + 0x180); 
  
 writel(0x0000FFFF, 0x11000000 + 0x188); 
  
 writel(0x22222222, 0x11000000 + 0x1C0); 
  
 writel(0x0000FFFF, 0x11000000 + 0x1C8); 
  
 writel(0x22222222, 0x11000000 + 0x1E0); 
  
 writel(0x0000FFFF, 0x11000000 + 0x1E8); 
  
 smc_bw_conf &= ~(0xf<<4); 
  
 smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4); 
  
 smc_bc_conf = ((DM9000_Tacs << 28) 
  
 | (DM9000_Tcos << 24) 
  
 | (DM9000_Tacc << 16) 
  
 | (DM9000_Tcoh << 12) 
  
 | (DM9000_Tah << 8) 
  
 | (DM9000_Tacp << 4)| (DM9000_PMC)); 
  
 exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf); 
  
 } 
  
 #endif 
  
 在 gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL); 后添加 
  
 #ifdef CONFIG_DRIVER_DM9000 
  
 dm9000aep_pre_init(); 
  
 #endif 
  
 在文件末尾添加 
  
 #ifdef CONFIG_CMD_NET 
  
 int board_eth_init(bd_t *bis) 
  
 { 
  
 int rc = 0; 
  
 #ifdef CONFIG_DRIVER_DM9000 
  
 rc = dm9000_initialize(bis); 
  
 #endif 
  
 return rc; 
  
 } 
  
 #endif 
  
 2、 修改配置文件添加网络相关配置 
  
 $ vim include/configs/fs4412.h 
  
 修改 
  
 #undef CONFIG_CMD_PING 
  
 为#def ine CONFIG_CMD_PING 
  
 修改 
  
 #undef CONFIG_CMD_NET 
  
 为 
  
 #def ine CONFIG_CMD_NET 
  
 在文件末尾 
  
 #endif /* __CONFIG_H */ 
  
 前面添加 
  
 #ifdef CONFIG_CMD_NET 
  
 #define CONFIG_NET_MULTI 
  
 #define CONFIG_DRIVER_DM9000 1 
  
 #define CONFIG_DM9000_BASE 0x05000000 
  
 #define DM9000_IO CONFIG_DM9000_BASE 
  
 #define DM9000_DATA (CONFIG_DM9000_BASE + 4) 
  
 #define CONFIG_DM9000_USE_16BIT 
  
 #define CONFIG_DM9000_NO_SROM 1 
  
 #define CONFIG_ETHADDR 11:22:33:44:55:66 
  
 #define CONFIG_IPADDR 192.168.9.200 
  
 #define CONFIG_SERVERIP 192.168.9.120 
  
 #define CONFIG_GATEWAYIP 192.168.9.1 
  
 #define CONFIG_NETMASK 255.255.255.0 
  
 #endif


3、重新编译 u-boot
$ ./build.sh烧写新的 u-boot_fs4412.bin
复位后
# ping 192.168.9.120







四、FLASH 移植 (EMMC)
1、 初始化 EMMC

$cp movi.c arch/arm/cpu/armv7/exynos/ 
  
 $vim arch/arm/cpu/armv7/exynos/Makefile 
  
 在 pinmux.o 后添加 movi.o 
  
 修改板级文件 
  
 $vim board/samsung/fs4412/fs4412.c 
  
 在 
  
 #include <asm/arch/mmc.h> 
  
 后面添加 
  
 #include <asm/arch/clk.h> 
  
 #include "origen_setup.h" 
  
 在 
  
 #ifdef CONFIG_GENERIC_MMC 
  
 后面添加 
  
 u32 sclk_mmc4; /*clock source for emmc controller*/ 
  
 #define __REGMY(x) (*((volatile u32 *)(x)))#define CLK_SRC_FSYS __REGMY(EXYNOS4_CLOCK_BASE + 
  
 CLK_SRC_FSYS_OFFSET) 
  
 #define CLK_DIV_FSYS3 __REGMY(EXYNOS4_CLOCK_BASE + 
  
 CLK_DIV_FSYS3_OFFSET) 
  
 int emmc_init() 
  
 { 
  
 u32 tmp; 
  
 u32 clock; 
  
 u32 i; 
  
 /* setup_hsmmc_clock */ 
  
 /* MMC4 clock src = SCLKMPLL */ 
  
 tmp = CLK_SRC_FSYS & ~(0x000f0000); 
  
 CLK_SRC_FSYS = tmp | 0x00060000; 
  
 /* MMC4 clock div */ 
  
 tmp = CLK_DIV_FSYS3 & ~(0x0000ff0f); 
  
 clock = get_pll_clk(MPLL)/1000000; 
  
 for(i=0 ; i<=0xf; i++) { 
  
 sclk_mmc4=(clock/(i+1)); 
  
 if(sclk_mmc4 <= 160) //200 
  
 { 
  
 CLK_DIV_FSYS3 = tmp | (i<<0); 
  
 break; 
  
 } 
  
 } 
  
 emmcdbg("[mjdbg] sclk_mmc4:%d MHZ; mmc_ratio: %d\n",sclk_mmc4,i);sclk_mmc4 *= 1000000; 
  
 /* 
  
 * MMC4 EMMC GPIO CONFIG 
  
 * 
  
 * GPK0[0] SD_4_CLK 
  
 * GPK0[1] SD_4_CMD 
  
 * GPK0[2] SD_4_CDn 
  
 * GPK0[3:6] SD_4_DATA[0:3] 
  
 */ 
  
 writel(readl(0x11000048)&~(0xf),0x11000048); //SD_4_CLK/SD_4_CMD pull-down 
  
 enable 
  
 writel(readl(0x11000040)&~(0xff),0x11000040);//cdn set to be output 
  
 writel(readl(0x11000048)&~(3<<4),0x11000048); //cdn pull-down disable 
  
 writel(readl(0x11000044)&~(1<<2),0x11000044); //cdn output 0 to shutdown the emmc 
  
 power 
  
 writel(readl(0x11000040)&~(0xf<<8)|(1<<8),0x11000040);//cdn set to be output 
  
 udelay(100*1000); 
  
 writel(readl(0x11000044)|(1<<2),0x11000044); //cdn output 1 
  
 writel(0x03333133, 0x11000040); 
  
 writel(0x00003FF0, 0x11000048); 
  
 writel(0x00002AAA, 0x1100004C); 
  
 #ifdef CONFIG_EMMC_8Bitwritel(0x04444000, 0x11000060); 
  
 writel(0x00003FC0, 0x11000068); 
  
 writel(0x00002AAA, 0x1100006C); 
  
 #endif 
  
 #ifdef USE_MMC4 
  
 smdk_s5p_mshc_init(); 
  
 #endif 
  
 } 
  
 将 int board_mmc_init(bd_t *bis)函数内容改写为 
  
 int board_mmc_init(bd_t *bis) 
  
 { 
  
 int i, err; 
  
 #ifdef CONFIG_EMMC 
  
 err = emmc_init(); 
  
 #endif 
  
 return err; 
  
 } 
  
 在末尾添加 
  
 #ifdef CONFIG_BOARD_LATE_INIT 
  
 #include <movi.h> 
  
 int chk_bootdev(void)//mj for boot device check 
  
 { 
  
 char run_cmd[100]; 
  
 struct mmc *mmc;int boot_dev = 0; 
  
 int cmp_off = 0x10; 
  
 ulong start_blk, blkcnt; 
  
 mmc = find_mmc_device(0); 
  
 if (mmc == NULL) 
  
 { 
  
 printf("There is no eMMC card, Booting device is SD card\n"); 
  
 boot_dev = 1; 
  
 return boot_dev; 
  
 } 
  
 start_blk = (24*1024/MOVI_BLKSIZE); 
  
 blkcnt = 0x10; 
  
 sprintf(run_cmd,"emmc open 0"); 
  
 run_command(run_cmd, 0); 
  
 sprintf(run_cmd,"mmc read 
  
 0 %lx %lx %lx",CFG_PHY_KERNEL_BASE,start_blk,blkcnt); 
  
 run_command(run_cmd, 0); 
  
 /* switch mmc to normal paritition */ 
  
 sprintf(run_cmd,"emmc close 0"); 
  
 run_command(run_cmd, 0); 
  
 return 0; 
  
 }int board_late_init (void) 
  
 { 
  
 int boot_dev =0 ; 
  
 char boot_cmd[100]; 
  
 boot_dev = chk_bootdev(); 
  
 if(!boot_dev) 
  
 { 
  
 printf("\n\nChecking Boot Mode ... EMMC4.41\n"); 
  
 } 
  
 return 0; 
  
 } 
  
 #endif 
  
 2、 添加相关命令 
  
 $ cp cmd_movi.c common/ 
  
 $ cp cmd_mmc.c common/ 
  
 $ cp cmd_mmc_fdisk.c common/ 
  
 修改 Makefile 
  
 $ vim common/Makefile 
  
 在 
  
 COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o 
  
 后添加 
  
 COBJS-$(CONFIG_CMD_MMC) += cmd_mmc_fdisk.o 
  
 COBJS-$(CONFIG_CMD_MOVINAND) += cmd_movi.o添加驱动 
  
 $ cp mmc.c drivers/mmc/ 
  
 $ cp s5p_mshc.c drivers/mmc/ 
  
 $ cp mmc.h include/ 
  
 $ cp movi.h include/ 
  
 $ cp s5p_mshc.h include/ 
  
 修改 Makefile 
  
 $vim drivers/mmc/Makefile 
  
 添加 
  
 COBJS-$(CONFIG_S5P_MSHC) += s5p_mshc.o 
  
 3、添加 EMMC 相关配置 
  
 $vim include/configs/fs4412.h 
  
 添加 
  
 #define CONFIG_EVT1 1 /* EVT1 */ 
  
 #ifdef CONFIG_EVT1 
  
 #define CONFIG_EMMC44_CH4 //eMMC44_CH4 (OMPIN[5:1] = 4) 
  
 #ifdef CONFIG_SDMMC_CH2 
  
 #define CONFIG_S3C_HSMMC 
  
 #undef DEBUG_S3C_HSMMC 
  
 #define USE_MMC2 
  
 #endif 
  
 #ifdef CONFIG_EMMC44_CH4 
  
 #define CONFIG_S5P_MSHC#define CONFIG_EMMC 1 
  
 #define USE_MMC4 
  
 /* #define CONFIG_EMMC_8Bit */ 
  
 #define CONFIG_EMMC_EMERGENCY 
  
 /*#define emmcdbg(fmt,args...) printf(fmt ,##args) *///for emmc debug 
  
 #define emmcdbg(fmt,args...) 
  
 #endif 
  
 #endif /*end CONFIG_EVT1*/ 
  
 #define CONFIG_CMD_MOVINAND 
  
 #define CONFIG_CLK_1000_400_200 
  
 #define CFG_PHY_UBOOT_BASE CONFIG_SYS_SDRAM_BASE + 0x3e00000 
  
 #define CFG_PHY_KERNEL_BASE CONFIG_SYS_SDRAM_BASE + 0x8000 
  
 #define BOOT_MMCSD 0x3 
  
 #define BOOT_EMMC43 0x6 
  
 #define BOOT_EMMC441 0x7 
  
 #define CONFIG_BOARD_LATE_INIT 
  
 4、 重新编译 u-boot 
  
 $ ./build.sh 
  
 烧写新的 u-boot_fs4412.bin 
  
 复位后 
  
 # mmcinfo