Mini440之uboot移植之实践NOR启动(六)
一、u-boot流程
1.1 u-boot流程图绘制
我们在前面的几篇博客中,已经对u-boot源码进行了分析。那么我们试着将我们前几节介绍的内容连起来。绘制一个u-boot启动流程图:
二、u-boot移植把步骤
既然我们想要移植u-boot,那么我们首先需要明白u-boot的启动流程。u-boot移植大概可以分为以下几个步骤:
- 内核级配置和初始化;
- SOC级别的配置与初始化;
- board_init_f;
- u-boot重定位;
- board_init_r;
- 启动内核或者处理用户命令;
2.1 内核级配置和初始化
这个主要就是在start.S文件里面那些汇编代码,主要包括:
- 开启SVC模式,关闭fiq,irq中断;
- 关闭看门狗
- 屏蔽所有中断;
- 设置系统时钟;
- 执行cpu_init_crit函数;
- 关闭关闭MMU和cache;
- lowlevel_init函数;
这部分是和内核息息相关的代码,这部分根据内核的不同,配置略有区别。
2.2 SOC级别的配置与初始化
这部分的内容是比较多的,而且可以说贯穿整个u-boot,但基本上必要的都集中在lowlevel_init,其它的就取决于你是否要在u-boot阶段使用某些硬件资源了。
比如网卡是在board_init_r里面,已经是靠后的位置了,有些板子甚至可能都不会在u-boot阶段使用网络,自然也就不需要初始化。
这部分是移植过程中修改较多的地方,而且基本上都是使用汇编编码,只要这部分搞定了,那u-boot基本就可以成功启动了。
2.3 board_init_f
这个阶段主要是对u-boot之后重定位进行内存的规划,虽然执行的子函数多,但都不复杂。移植内容少。
2.4 u-boot重定位
这部分代码基本不需要改。
2.5 board_init_r
这个阶段干的事主要就是准备终端,初始化需要用到的硬件资源,如果使用新的驱动模型的话,还要像kernel一样将硬件进行抽象。这个根据自己的需求,用了什么就要改什么。
2.6 启动内核或者处理用户命令
u-boot最大的使命就是启动内核,所以这部分也是核心的内容。board_init_r最后会调用run_main_loop进入一个循环,要么就是直接去启动内核,要么就是进入终端处理用户命令和用户交互。也可以说不用改,差不多已经是纯软件干的事了。
三、添加单板
由于u-boot-2016.05支持S3C2410开发板,所以我们可以直接将S3C2410的单板相关文件都复制为S3C2440的。
3.1 在board中创建单板目录(board/samsung/smdk2440)
cd board/samsung/
cp smdk2410 -r smdk2440
进入smdk2440路径:
修改文件Kconfig:
if TARGET_SMDK2440 config SYS_BOARD default "smdk2440" config SYS_VENDOR default "samsung" config SYS_SOC default "s3c24x0" config SYS_CONFIG_NAME default "smdk2440" endif
修改MAINTAINERS文件:
SMDK2440 BOARD M: David MüllerS: Maintained F: board/samsung/smdk2440/ F: include/configs/smdk2440.h F: configs/smdk2440_defconfig
从这里可以看到,后面我们还需要创建include/configs/smdk2440.h、configs/smdk2440_defconfig。
修改Makefile文件:
# # (C) Copyright 2000-2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # # SPDX-License-Identifier: GPL-2.0+ # obj-y := smdk2440.o obj-y += lowlevel_init.o
smdk2410.c改名为smdk2440.c
mv smdk2410.c smdk2440.c
3.2 创建单板相关头文件(include/configs/smdk2440.h)
同样,我们将include/configs/目录下的smdk2410.h文件复制为smdk2440.h:
cd include/configs cp smdk2410.h smdk2440.h
这个文件很重要,这里主要用来配置编译u-boot时是否启用某些功能,比如网卡,LCD等,如果定义了,那么将会编译相关模块的代码。
修改smdk2440.h中字符串2410->2440:
#define CONFIG_S3C2440 /* specifically a SAMSUNG S3C2440 SoC */ #define CONFIG_SMDK2440 /* on a SAMSUNG SMDK2440 Board */
/* * NAND configuration */ #ifdef CONFIG_CMD_NAND #define CONFIG_NAND_S3C2440 #define CONFIG_SYS_S3C2440_NAND_HWECC #define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_NAND_BASE 0x4E000000 #endif
3.3 创建defconfig文件
在执行make smdk2440_defconfig,依赖于configs/smdk2440_defconfig文件,所以我们也将smdk2410_defconfig复制过来:
cd configs cp smdk2410_defconfig smdk2440_defconfig
修改smdk2440_defconfig:
CONFIG_ARM=y CONFIG_TARGET_SMDK2440=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="SMDK2440 # " CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_DHCP=y CONFIG_CMD_PING=y CONFIG_CMD_CACHE=y CONFIG_CMD_EXT2=y CONFIG_CMD_FAT=y
3.4 修改arch/arm/Kconfig文件
vim arch/arm/Kconfig
添加以下内容:
config TARGET_SMDK2440 bool "Support smdk2440" select CPU_ARM920T source "board/samsung/smdk2440/Kconfig"
3.5 尝试配置并编译
在做完上述步骤的时候,我们就完成了S3C2440单板的添加,虽然都是复制S3C2410的,但我们只要在S3C2410的配置文件基础上进行修改即可。
make smdk2440_config make ARCH=arm CROSS_COMPILE=arm-linux- V=1
第一步可以编译成功,第二步会编译失败,主要是因为我们在include/configs/smdk2440.h中配置了NAND,但是我们没有移植相关代码。
四、 内核级配置和初始化(arch/arm/cpu/arm920t/start.S)
我们首先来分析arch/arm/cpu/arm920t/start.S文件,看看有没有需要修改的地方。
我们需要修改的第一处就是时钟系统设置。初始化系统时钟 FCLK = 400MHz,HCLK = 100MHz, PCLK = 50MHz, UPLL=48MHz 。
#ifdef CONFIG_S3C24X0 /* turn off the watchdog */ # if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interrupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #else # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C /* 初始化系统时钟 FCLK = 400MHz,HCLK = 100MHz, PCLK = 50MHz, UPLL=48MHz */ # define LOCKTIME 0x4c000000 # define MPLLCON 0x4c000004 # define UPLLCON 0x4c000008 # define CLKDIVN 0x4c000014 # define M_MDIV 92 /* Fin=12M UPLL=400M */ # define M_PDIV 1 # define M_SDIV 1 # define U_MDIV 56 /* Fin=12M UPLL=48M */ # define U_PDIV 2 # define U_SDIV 2 # define DIVN_UPLL 0 /* FCLK:HCLK:PCLK=1:4:8 */ # define HDIVN 2 # define PDIVN 1 # 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 /* 设置Lock Time */ ldr r0, =LOCKTIME ldr r1, =0xffffffff str r1, [r0] /* 设置分频系数 */ ldr r0, =CLKDIVN ldr r1, =((DIVN_UPLL<<3) | HDIVN <<1 | PDIVN) str r1, [r0] /* CPU改为异步总线模式 */ mrc p15,0,r1,c1,c0,0 orr r1, r1,#0xC0000000 mcr p15,0,r1,c1,c0,0 /* 设置UPLL */ ldr r0, =UPLLCON ldr r1, =((U_MDIV<<12) | (U_PDIV<<4) | U_SDIV) str r1, [r0] nop nop nop nop nop nop nop /* 设置MPLL */ ldr r0, =MPLLCON ldr r1, =((M_MDIV << 12) | (M_PDIV << 4) | M_SDIV) str r1, [r0] #endif /* CONFIG_S3C24X0 */
五、SOC级别的配置与初始化(board/samsung/smdk2440/lowlevel_init.S)
在lowlevel_init.S文件中主要包含初始化SDRAM的代码。SDRAM的初始化取决于我们外接的SDRAM芯片。
以Mini2440开发板为例:将两片32MB,16位宽SDRAM内存(型号HY57V561620FTP)焊接在Bank6,并联形成64M,32位内存。因此这里我们主要关注Bandk6相关的配置。
我们将源代码中的配置和Mini2440裸机开发之存储器控制器中的代码做个对比,我们需要修改以下几处:
#define REFCNT 1269 /* period=7.8125us, HCLK=60Mhz, (2048+1-7.8125*100) */
更改BANKSIZE为0xB1:
SMRDATA: .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0xB1 .word 0x30 .word 0x30
六、 board_init_f
既然在start.S中已经进行时钟频率设置了,那在board_init_f中就没必要进行时钟频率设置了。
其中board_early_init_f这个函数定义在board/samsung/smdk2440/smdk2440.c,在里面进行时钟频率设置,所以我们需要修改这个函数中的内容,去掉时钟频率设置。
将board_early_init_f函数修改如下:
int board_early_init_f(void) { struct s3c24x0_clock_power * const clk_power = s3c24x0_get_base_clock_power(); struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio(); /* 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); /* configure UPLL */ // writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV, // &clk_power->upllcon); /* some delay between MPLL and UPLL */ // pll_delay(8000); /* set up the I/O ports */ writel(0x007FFFFF, &gpio->gpacon); writel(0x00044555, &gpio->gpbcon); writel(0x000007FF, &gpio->gpbup); writel(0xAAAAAAAA, &gpio->gpccon); writel(0x0000FFFF, &gpio->gpcup); writel(0xAAAAAAAA, &gpio->gpdcon); writel(0x0000FFFF, &gpio->gpdup); writel(0xAAAAAAAA, &gpio->gpecon); writel(0x0000FFFF, &gpio->gpeup); writel(0x000055AA, &gpio->gpfcon); writel(0x000000FF, &gpio->gpfup); writel(0xFF95FFBA, &gpio->gpgcon); writel(0x0000FFFF, &gpio->gpgup); writel(0x002AFAAA, &gpio->gphcon); writel(0x000007FF, &gpio->gphup); return 0; }
串口初始化函数serial_init在初始化串口0的时候,会利用get_PCLK方法(位于arch/arm/cpu/arm920t/s3c24x0/speed.c)获取PCLK时钟频率,然后计算波特率,根据实际情况判断是否需要修改该方法。
/* return HCLK frequency */ ulong get_HCLK(void) { struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); #ifdef CONFIG_S3C2440 switch (readl(&clk_power->clkdivn) & 0x6) { default: case 0: return get_FCLK(); case 2: return get_FCLK() / 2; case 4: return (readl(&clk_power->camdivn) & (1 << 9)) ? get_FCLK() / 8 : get_FCLK() / 4; case 6: return (readl(&clk_power->camdivn) & (1 << 8)) ? get_FCLK() / 6 : get_FCLK() / 3; } #else return (readl(&clk_power->clkdivn) & 2) ? get_FCLK() / 2 : get_FCLK(); #endif } /* 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(); }
七、u-boot重定位
这一块代码我们不做修改。
八、 board_init_r
因为我们在smdk2440.h中配置了CONFIG_CMD_NAND,因此我们需要修改nand初始化相关的代码。
复制drivers/mtd/nand/s3c2410_nand.c->drivers/mtd/nand/s3c2440_nand.c。
cd drivers/mtd/nand cp s3c2410_nand.c s3c2440_nand.c
修改s3c2440_nand.c,将代码中的2410->2440。
修改drivers/mtd/nand/Makefile,添加如下代码:
obj-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
九、启动内核或者处理用户命令
这一块代码我们不做修改。
十、编译、下载到NOR FALSH
10.1 编译
make distclean make smdk2440_config make ARCH=arm CROSS_COMPILE=arm-linux- V=1
10.2 下载
(1) 打开J-Flash ARM,并进入菜单:Options–>Project settings.
(2) 主要设置CPU选项和Flash选项:
CPU :
Core --> ARM9, Little endian
Use target RAM(faster)–>Addr:0x40000000 4KB(不选很慢;从Nor flash启动时内部SRAM的地址和大小,参考S3C2440A的芯片手册)。
初始化序列(Init sequence)按下面的填,简单做了下注释。
##-------Action-----Value0------Value1 Halt Disable MMU Write 32bit 0x53000000 0x00000000 ; pWTCON , 看门狗定时器控制寄存器 Write 32bit 0x4A000008 0xFFFFFFFF ; INTMSK , 中断屏蔽寄存器 Write 32bit 0x4A00001C 0x000007FF ; INTSUBMSK , 针对INTMAK具体化的一个中断请求屏蔽寄存器 Write 32bit 0x53000000 0x00000000 ; pWTCON , 看门狗定时器控制寄存器 Write 32bit 0x56000050 0x000055AA ; rGPFCON , Port F control Write 32bit 0x4C000014 0x00000007 ; CLKDIVN , CPU时钟分频控制寄存器 Write 32bit 0x4C000000 0x00FFFFFF ; LOCKTIME , 锁时计数寄存器 Write 32bit 0x4C000004 0x00061012 ; MPLLCON , MPLL寄存器 Write 32bit 0x4C000008 0x00040042 ; UPLLCON , UPLL寄存器 Write 32bit 0x48000000 0x22111120 ; Bus width & wait status Write 32bit 0x48000004 0x00002F50 ; Boot ROM control Write 32bit 0x48000008 0x00000700 ; BANK1 control Write 32bit 0x4800000C 0x00000700 ; BANK2 control Write 32bit 0x48000010 0x00000700 ; BANK3 control Write 32bit 0x48000014 0x00000700 ; BANK4 control Write 32bit 0x48000018 0x0007FFFC ; BANK5 control Write 32bit 0x4800001C 0x00018005 ; BANK6 control Write 32bit 0x48000020 0x00018005 ; BANK7 control Write 32bit 0x48000024 0x008E0459 ; DRAM/SDRAM refresh Write 32bit 0x48000028 0x00000032 ; Flexible Bank Size Write 32bit 0x4800002C 0x00000030 ; Mode register set for SDRAM Write 32bit 0x48000030 0x00000030 ; Mode register set for SDRAM
(3) Target->Connect,必须设为norflash启动才能检测到flash。
(4) 击File菜单的Open data File,打开想要下载的bin文件(比如uboot/supervivi/superboot等),在弹出的起始地址中输入0即可。
然后点击Target菜单中的Auto,或者直接按F7键,启动下载流程。大约几秒钟就下载完毕了。
(5) 烧写完成后,关电,取下JLINK,重新启动即可进入u-boot界面:
(6) 保存flash项目,File->Save project方便下次使用。
参考文章
[1]
[2]u-boot2020.04移植(总结)
[3]U-BOOT-2016.07移植 (第二篇) 添加单板
[4]U-BOOT-2016.07移植 (第四篇) 修改代码,从NOR启动
[5]S3C2440移植uboot之支持NORFLASH
[6]S3C2440移植uboot之支持NAND启动
[7]S3C2440移植uboot之新建单板_时钟_SDRAM_串口
[8]uboot支持NORFlash