本篇文章阐述移植 u-boot v2018.01 至 S5PV210 开发板上的主要流程和细节。市场上的S5PV210开发板,均是基于三星smdkv210公版平台山寨出来的。我使用的GEC210开发板也与公版只数个元器件的差异。所以,若你也用S5PV210类开发板,参考本篇文章,或者直接使用我发布的补丁打到源码上,能帮你解决许多困惑及运行最新的u-boot。
$ git clone git://git.denx.de/u-boot.git
$ git checkout v2018.01
$ git apply u-boot-v2018.01_gec210.patch
,如下所示,有警告无影响;由于补丁中含有二进制数据,使用patch
命令打补丁将出错。u-boot/Makefile
中CROSS_COMPILE
变量为前面解压好的工具链可执行文件的前缀,如:$ make gec210_defconfig
$ make
,最终产生u-boot.bin
文件以下阐述移植的过程,由于篇幅有限,重点难点的修改才会作详细的解释,参考上述补丁文件更详尽。开始移植时还需要分析u-boot的流程,可以参考我另一篇博文《u-boot v2018.01 启动流程分析》。
以下章节中出现的脚本名字有如下关系,每一个章节对应一次git提交,一次对u-boot源码打上所有阶段补丁等效于一次性打上补丁u-boot-v2018.01_gec210.patch
:
1.设置交叉编译工具链,参考上面;
CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-7.2.1-2017.11-i686_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
2.板级相关驱动移植smdkc100的代码:$ cp board/samsung/smdkc100 board/samsung/gec210 -r
① 修改配置菜单board/samsung/gec210/Kconfig
:
@@ -1,7 +1,7 @@
-if TARGET_SMDKC100
+if TARGET_GEC210
config SYS_BOARD
- default "smdkc100"
+ default "gec210"
config SYS_VENDOR
default "samsung"
@@ -10,6 +10,6 @@
default "s5pc1xx"
config SYS_CONFIG_NAME
- default "smdkc100"
+ default "gec210"
endif
② 修改板级初始化文件名:$ mv board/samsung/gec210/smdkc100.c board/samsung/gec210/gec210.c
,相应修改
board/samsung/gec210/Makefile
:
@@ -8,6 +8,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-y := smdkc100.o
+obj-y := gec210.o
③ 建立了新板代码,但还没能被编译和链接,所以修改arch/arm/mach-s5pc1xx/Kconfig
,才能在配置菜单中选中
gec210单板进行编译:
@@ -12,6 +12,10 @@
bool "Support smdkc100 board"
select OF_CONTROL
+config TARGET_GEC210
+ bool "Support gec210 board"
+ select OF_CONTROL
+
endchoice
config SYS_SOC
@@ -19,5 +23,6 @@ config SYS_SOC
source "board/samsung/goni/Kconfig"
source "board/samsung/smdkc100/Kconfig"
+source "board/samsung/gec210/Kconfig"
3.移植smdkc100的菜单配置文件:$ cp configs/smdkc100_defconfig configs/gec210_defconfig
,配置源码$ make gec210_defconfig
,$ make menuconfig
,在菜单-> ARM architecture -> S5PC1XX board select
中选择Support gec210 board
,如下图。完后需及时保存好配置:$ cp .config configs/gec210_defconfig
;
4.移植非菜单的配置:$ cp include/configs/smdkc100.h include/configs/gec210.h
:
@@ -19,7 +19,9 @@
#define CONFIG_SAMSUNG 1 /* in a SAMSUNG core */
#define CONFIG_S5P 1 /* which is in a S5P Family */
-#define CONFIG_S5PC100 1 /* which is in a S5PC100 */
-#define CONFIG_SMDKC100 1 /* working with SMDKC100 */
+//#define CONFIG_SMDKC100 1 /* working with SMDKC100 */
5.芯片S5PV210上电启动时,首先运行位于irom内BL0,它根据BL1开始的16Byte加载BL1;所以需要定义BL1的头部信息,创建文件:$ touch arch/arm/mach-s5pc1xx/include/mach/boot0.h
,文件内容如下;u-boot称这个头部信息为"hook"(钩子),开启该功能还需在菜单里选中:-> ARM architecture -> [*] prepare BOOT0 header
。
/*
* Copyright 2018 Lucifer Zhu, [email protected].
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* BOOT0 header information */
.word 0x2000 @ indicate BL1 size for irom
.word 0x0
.word 0x0
.word 0x0
_start:
ARM_VECTORS
1.移植S5PV210的特殊寄存器定义。采用三星发布的u-boot-1.3.4中s5pc110.h
文件(s5pc110与s5pv210的寄存器地址相同):$ cp ../u-boot-samsung-dev/include/s5pc110.h arch/arm/mach-s5pc1xx/include/mach/s5pc110.h
。s5pc110.h
文件中定义及引用了当前源码未定义的内容,所以还需做裁剪,具体参考u-boot-v2018.01_gec210.patch
:
2.修改lowlevel_init.S以实现SOC级别硬件初始化,包括WATCHDOG,clock,DDR2,uart,nand。第一章已经从smdkc100单板中移植了lowlevel_init.S
,其代码流程与S5PV210一致,但各硬件的初始化需重写,需修改函数包括:lowlevel_init
,system_clock_init
,uart_asm_init
,tzpc_asm_init
,还需添加nand_asm_init
及mem_ctrl_asm_init
(定义于mem_init.S
文件),另外引用的宏需在include/configs/gec210.h
里定义。这些基础的soc级别初始化均可移植于u-boot-samsung-dev
,详细参考补丁:
3.保证lowlevel_init.S
和mem_init.S
的text位于u-boot.bin前8KB内。启动流程中运行于iRAM的BL1大小为8KB,所以soc级别的lowlevel_init
也应被包含在内,所以修改链接脚本arch/arm/cpu/u-boot.lds
如下。
@@ -44,6 +44,8 @@
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
+ board/samsung/gec210/lowlevel_init.o (.text*)
+ board/samsung/gec210/mem_init.o (.text*)
*(.text*)
}
另外,这两文件的目标文件不应追加到变量obj-y
,否则编译时被认为重复定义,所以修改board/samsung/gec210/Makefile
:
@@ -10,4 +10,5 @@
obj-y := gec210.o
obj-$(CONFIG_SAMSUNG_ONENAND) += onenand.o
-obj-y += lowlevel_init.o
+extra-y += lowlevel_init.o
+extra-y += mem_init.o
4.移植smdkc100的设备树:$ cp arch/arm/dts/s5pc1xx-smdkc100.dts arch/arm/dts/s5pc1xx-gec210.dts
,修改如下:
@@ -9,26 +9,26 @@
/dts-v1/;
#include "skeleton.dtsi"
-#include "s5pc100-pinctrl.dtsi"
+#include "s5pc110-pinctrl.dtsi"
/ {
- model = "Samsung SMDKC100 based on S5PC100";
- compatible = "samsung,smdkc100", "samsung,s5pc100";
+ model = "Samsung GEC210 based on S5PV210";
+ compatible = "samsung,gec210", "samsung,s5pc110";
aliases {
- serial0 = "/serial@ec000000";
- console = "/serial@ec000000";
+ serial0 = "/serial@e2900000";
+ console = "/serial@e2900000";
pinctrl0 = &pinctrl0;
};
- pinctrl0: pinctrl@e0300000 {
- compatible = "samsung,s5pc100-pinctrl";
+ pinctrl0: pinctrl@e0200000 {
+ compatible = "samsung,s5pc110-pinctrl";
reg = <0xe0200000 0x1000>;
};
- serial@ec000000 {
+ serial@e2900000 {
compatible = "samsung,exynos4210-uart";
- reg = <0xec000000 0x100>;
+ reg = <0xe2900000 0x400>;
另外还需修改arch/arm/dts/Makefile
使该设备树被编译和附加到u-boot.bin尾部:
@@ -9,6 +9,7 @@
dtb-$(CONFIG_S5PC100) += s5pc1xx-smdkc100.dtb
dtb-$(CONFIG_S5PC110) += s5pc1xx-goni.dtb
+dtb-$(CONFIG_S5PV210) += s5pc1xx-gec210.dtb
dtb-$(CONFIG_EXYNOS4) += exynos4210-origen.dtb \
exynos4210-smdkv310.dtb \
exynos4210-universal_c210.dtb \
到目前为止,代码只是在soc的iRAM内执行,在不使用u-boot新功能SPL(secondary program loader)的话,我们需要在arch/arm/cpu/armv7/start.S
中实现进行从Nand和mmc/sd加载BL2到DDR2并跳转之的操作。
1.移植读nand的底层裸机驱动。它的唯一使命是读取Nand flash中BL2代码到DDR2,从u-boot 1.3.4中移植cpu/s5pc11x/nand_cp.c
到u-boot 2018的board/samsung/gec210/nand_cp.c
路径。参考03_boot_from_nand_sd.patch
:
需要注意的是,我们需指定加载BL2到的目标内存地址CONFIG_SYS_TEXT_BASE
,以及BL2大小COPY_BL2_SIZE
,将其定义于include/configs/gec210.h
:
由于nand_cp.c的代码依然是在iram中被调用,意味着需将nand_cp.c链接进u-boot.bin前8KB内,否则程序跑飞:
diff --git a/arch/arm/cpu/u-boot.lds b/arch/arm/cpu/u-boot.lds
index a6bfd3b..6c3feac 100755
--- a/arch/arm/cpu/u-boot.lds
+++ b/arch/arm/cpu/u-boot.lds
@@ -46,6 +46,7 @@
CPUDIR/start.o (.text*)
board/samsung/gec210/lowlevel_init.o (.text*)
board/samsung/gec210/mem_init.o (.text*)
+ board/samsung/gec210/nand_cp.o (.text*)
*(.text*)
}
diff --git a/board/samsung/gec210/Makefile b/board/samsung/gec210/Makefile
index fe65af4..ddeb488 100755
--- a/board/samsung/gec210/Makefile
+++ b/board/samsung/gec210/Makefile
@@ -12,3 +12,4 @@
obj-$(CONFIG_SAMSUNG_ONENAND) += onenand.o
extra-y += lowlevel_init.o
extra-y += mem_init.o
+extra-y += nand_cp.o
2.实现mmc/sd的读取操作。S5PV210的iROM中保存着实现诸如mmc/sd,各类型nand flash,eSSD的读取操作的代码。所以直接调用irom内的读取mmc/sd函数,来加载位于mmc/sd内第49扇区开始的BL2代码,参考03_boot_from_nand_sd.patch
中第172行,代码如下:
/*************************************************************************
*
* copy U-Boot to SDRAM and jump to ram (from SD/MMC)
* size align with a block (512Byte)
*
*************************************************************************/
ENTRY(copy_from_mmcsd)
push {lr} /* save return address */
/* @param_0 channel */
ldr r0, =0
/* @param_1 u32 StartBlkAddress */
ldr r1, =49 @ BL1 at block1,BL2 at block49
/* @param_2 u16 blockSize */
ldr r2, =512<<10 @ 512kB, BL2 should include dtb file
lsr r2, #9 @ r2 >>= 9, as r2/=512
add r2, #1 @ r2 += 1
/* @param_3 u32* memoryPtr */
ldr r3, _TEXT_PHY_BASE
/* @param_4 bool with_init */
ldr r4, =0 @ no init
push {r4} @ fourth arg at stack
ldr r4, copy_sd_mmc_to_mem
mov lr, pc
ldr pc, [r4]
pop {r4, pc}
ENDPROC(copy_from_mmcsd)
3.调用以上函数从指定存储设备加载BL2到ddr2。在arch/arm/cpu/armv7/start.S
实现调用过程,具体修改需参考03_boot_from_nand_sd.patch
第25行:
4.加载完BL2后跳转到DDR2中:
after_copy:
ldr pc, =_main @ jump into ddr
5.到此已经跳转到ddr2中_main
函数的位置,该函数定义于arch/arm/lib/crt0.S
。该函数一开始就将sp
定义到了0x2f000000
上,而该地址并不在DDR2上,导致后面代码运行出错,如下所示:
修改关联的宏以让sp=0x34700000
,修改如下:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index 61c38ef..adbb872 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -209,7 +207,7 @@
/* memtest works on */
#define CONFIG_SYS_MEMTEST_START CONFIG_SYS_SDRAM_BASE
#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_SDRAM_BASE + 0x5e00000)
-#define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_SDRAM_BASE
+#define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_TEXT_BASE
参考
03_boot_from_nand_sd.patch
作其他方面的细致修改。至此,u-boot可以运行起来,并可进入命令行模式如下图所示。从中可看到net
驱动初始化失败,接下来作dm9000和nand flash读写的驱动移植。
u-boot v2018.01源码已经实现了dm9000系列驱动drivers/net/dm9000x.c
,我们只需定义寄存器级别的内容就行。
1.设置dm9000的内存地址。由上图知DM9000的片选线CS#接到了S5PV210的CSn1,也就是SROMC_BANK1,而观下图可知访问DM9000的基址是0x88000000
,DM9000的CMD接到了地址线ADDR2,所以访问DM9000数据的地址为0x88000000+2x4Byte
。
在include/configs/gec210.h
中定义被drivers/net/dm9000x.c
驱动引用到的宏:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index 843eba2..f66ecb5 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -24,6 +24,7 @@
#endif
#include /* get chip and board defs */
+#include
#define CONFIG_ARCH_CPU_INIT
@@ -256,8 +262,13 @@
/*
* Ethernet Contoller driver
*/
+#define CONFIG_DRIVER_DM9000 1
+#define CONFIG_DM9000_BASE (0x88000000)
+#define DM9000_IO (CONFIG_DM9000_BASE)
+#define DM9000_DATA (CONFIG_DM9000_BASE+8)
+
#ifdef CONFIG_CMD_NET
-#define CONFIG_ENV_SROM_BANK 3 /* Select SROM Bank-3 for Ethernet*/
+#define CONFIG_ENV_SROM_BANK 1 /* Select SROM Bank-1 for Ethernet*/
#endif /* CONFIG_CMD_NET */
#endif /* __CONFIG_H */
2.添加SROM控制器和dm9000设备驱动初始化的代码。板级设备驱动初始化代码board/samsung/gec210/gec210.c
中只存在网卡smc9115的驱动初始化代码,需要将其注释掉且添加进dm9000驱动初始化相关的代码:
diff --git a/board/samsung/gec210/gec210.c b/board/samsung/gec210/gec210.c
index 8011259..f5a6249 100755
--- a/board/samsung/gec210/gec210.c
+++ b/board/samsung/gec210/gec210.c
@@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR;
/*
* Miscellaneous platform dependent initialisations
*/
+#ifdef CONFIG_SMC911X
static void smc9115_pre_init(void)
{
u32 smc_bw_conf, smc_bc_conf;
@@ -34,11 +35,34 @@ static void smc9115_pre_init(void)
/* Select and configure the SROMC bank */
s5p_config_sromc(CONFIG_ENV_SROM_BANK, smc_bw_conf, smc_bc_conf);
}
+#endif
+
+#ifdef CONFIG_DRIVER_DM9000
+static void dm9000_pre_init(void)
+{
+ unsigned int tmp;
+
+ /* DM9000 on SROM BANK1, 16 bit */
+ SROM_BW_REG &= ~(0xf << 4);
+ SROM_BW_REG |= (0x1 << 4);
+ SROM_BC1_REG = ((0<<28)|(0<<24)|(5<<16)|(0<<12)|(0<<8)|(0<<4)|(0<<0));
+ /* Set MP01_1 as SROM_CSn[1] */
+ tmp = MP01CON_REG;
+ tmp &=~(0xf<<4);
+ tmp |=(2<<4);
+ MP01CON_REG = tmp;
+}
+#endif
int board_init(void)
{
+#ifdef CONFIG_SMC911X
smc9115_pre_init();
+#endif
+#ifdef CONFIG_DRIVER_DM9000
+ dm9000_pre_init();
+#endif
gd->bd->bi_arch_number = MACH_TYPE_SMDKC110;
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;
@@ -75,8 +99,13 @@
int board_eth_init(bd_t *bis)
{
int rc = 0;
+
#ifdef CONFIG_SMC911X
rc = smc911x_initialize(0, CONFIG_SMC911X_BASE);
#endif
+
+#ifdef CONFIG_DRIVER_DM9000
+ rc = dm9000_initialize(bis);
+#endif
return rc;
}
3.配置关闭smc9115网卡相关的代码。GEC210板不像SMDKC100那样搭载了smc9115网卡,所以通过$ make menuconfig
关闭该网卡的配置,菜单-> Device Drivers -> Network device support -> [ ] SMSC LAN911x and LAN921x controller driver
取消选择:
4.定义网络相关的环境变量。在默认环境变量集(include/env_default.h
)中定义了数个与网络参数相关的变量如下图:
我们需要在include/configs/gec210.h
中定义其值:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index 843eba2..f66ecb5 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -118,6 +119,11 @@
"ubiblock=4\0" \
"ubi=enabled"
+#define CONFIG_NETMASK 255.255.255.0
+#define CONFIG_IPADDR 192.168.1.48
+#define CONFIG_SERVERIP 192.168.1.46
+#define CONFIG_GATEWAYIP 192.168.1.1
另外我们没有定义网卡的MAC地址,但u-boot中提供生成随机MAC地址的功能,需要配置菜单开启:-*- Networking support ---> [*] Random ethaddr if unset
。
现在我们可以通过TFTP命令
tftp 0x40000000 uImage.bin
来下载网络服务器中的文件。
移植nand的驱动目的是使u-boot支持向nand烧写uboot.bin,uImage以及文件系统等。u-boot v2018.01对于存储设备驱动的实现是移植了kernel的mtd层次结构,所以对于nand flash的驱动实现,我们只需实现并填充struct nand_chip
的成员就行。
1.移植u-boot-samsung-dev/cpu/s5pc11x/nand.c
驱动。执行$ cp xxx/u-boot-samsung-dev/cpu/s5pc11x/nand.c drivers/mtd/nand/s5p_nand.c
;u-boot v2018.01与1.3.4版本的MTD相比,部分结构体增减了结构体的成员,所以针对该差异修改nand驱动代码,参考s5p_nand.patch
了解全部细节。其中由于两版本结构体nand_flash_dev
参考如下图,老版本的成员id
为nand的设备id,而新版本的中表示设备id的成员是dev_id
,没注意到这个差异的话,将导致在初始化中识别到错误的nand型号,进而读取操作nand时出错。
所以,移植时需要做对应的修改:
--- ../u-boot-samsung-dev/cpu/s5pc11x/nand.c 2011-03-21 15:15:23.000000000 +0800
+++ drivers/mtd/nand/s5p_nand.c 2018-06-06 22:05:44.121795642 +0800
@@ -1012,20 +994,23 @@
tmp = readb(nand->IO_ADDR_R); /* Device ID */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
- if (tmp == nand_flash_ids[i].id) {
+ if (tmp == nand_flash_ids[i].dev_id) {
type = &nand_flash_ids[i];
break;
}
}
2.移植lowlevel_init.S中nand控制器初始化的代码。由于我们要启用nand控制器的硬件ecc功能,所以nand控制器初始化时也需要做相应的设置,作如下所示修改,具体参考04_05_nand_dm9000_drv.patch
第101行。
3.屏蔽掉nand id表中dev_id
与GEC210上所搭载nand(K9K8G08U1A
)相同但并不兼容的设备。由于nand初始化过程中,会根据从nand读回来的dev_id
从nand_flash_ids[]
中遍历找到第一个对应设备,所以"TC58NVG3S0F 8G 3.3V 8-bit"
会首先被匹配到,但它各项属性并不兼容当前板的flash,使用其中的属性将会运行出错,所以需要将其注释掉:
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 4009d64..af83901 100755
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
struct nand_flash_dev nand_flash_ids[] = {
@@ -49,9 +49,11 @@
{"TC58NVG2S0H 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
+#if 0 /* ignore this incompatible NAND chips */
{"TC58NVG3S0F 8G 3.3V 8-bit",
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
+#endif
{"TC58NVG5D2 32G 3.3V 8-bit",
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
4.修改Makefile
和Kconfig
以添加编译s5p_nand.c
文件的菜单配置选项。
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 78a39ab..077fc5f 100755
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -160,6 +160,24 @@
This flag prevent U-boot reconfigure NAND flash controller and reuse
the NAND timing from 1st stage bootloader.
+config NAND_S5P
+ bool "Support for Samsung S5PV210 Nand controller"
+ imply CMD_NAND
+ help
+ This enables Nand driver support for Nand flash controller
+ on Samsung S5P1XX SoC.
+
+config S5P_NAND_HWECC
+ bool "Enable use of S5P1XX nand flash controller's hardware ecc"
+ depends on NAND_S5P
+
+config NAND_BL1_8BIT_ECC
+ bool "Enable write bootloader stage 1 in nand flash with 8-bit ecc"
+ depends on NAND_S5P
+ help
+ Soc s5pc11x's BL0 require BL1 which located in block 0 on nand to carry
+ with 8-bit ecc, otherwise it will be failed when boot the BL1 in nand.
+
comment "Generic NAND options"
# Enhance depends when converting drivers to Kconfig which use this config
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 9f7d9d6..43efb21 100755
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -66,6 +66,7 @@
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
+obj-$(CONFIG_NAND_S5P) += s5p_nand.o
else # minimal SPL drivers
5.配置菜单开启nand驱动功能。选择菜单Device Drivers ---> [*] NAND Device Support --->
中的如下选项:
另外开启nand相关的命令功能:Command line interface ---> Device access commands ---> [*] nand
6.支持烧写u-boot.bin到nand中。CPU S5PV210要求BL1头部附带16Byte的信息,用以表示BL1的大小及其校验和如下图所示;我们在第一章中已经设定了u-boot镜像偏移地址0x0处为0x2000即8KB,即表示BL1 size为8KB;而偏移地址0x8处填了0x0,通过当前移植状态的nand驱动,将u-boot.bin
烧写进nand的block0处(使用命令nand write 0x40000000 0 0x80000
),u-boot是启动不起来的。由下图BL0启动流程可知,u-boot.bin偏移地址0x8处应填充u-boot.bin前8KB内容的校验和checksum
,使得iROM内的BL0在对BL1进行校验和时检测成功,才可以执行u-boot;否则将尝试其他启动方式最终启动失败。
烧写u-boot.bin前计算校验和并填充到0x8偏移地址处。校验和的填充不像BL1 size那样在代码编写时就确定,需要在生成u-boot.bin后才可以确定。所以,在命令nand write addr 0 0x80000
执行流程上添加计算校验和及修改u-boot.bin之0x8偏移地址处内容:
diff --git a/cmd/nand.c b/cmd/nand.c
index a22945d..234a7b7 100755
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -1,4 +1,4 @@
-/*
+/*
* Driver for NAND support, Rick Bronson
* borrowed heavily from:
* (c) 1999 Machine Vision Holdings, Inc.
@@ -387,6 +387,11 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
int dev = nand_curr_device;
int repeat = flag & CMD_FLAG_REPEAT;
+#if defined(CONFIG_S5PC110) && !defined(CONFIG_FUSED) && !defined(CONFIG_SECURE)
+ ulong checksum;
+ uint8_t *ptr;
+#endif
+
/* at least two arguments please */
if (argc < 2)
goto usage;
@@ -611,11 +616,23 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
ret = nand_read_skip_bad(mtd, off, &rwsize,
NULL, maxsize,
(u_char *)addr);
- else
+ else {
+#if defined(CONFIG_S5PC110) && !defined(CONFIG_FUSED) && !defined(CONFIG_SECURE)
+ if (off == 0) {
+ ptr = (u_char *)(addr + 16);
+ for(i = 16, checksum = 0; i < COPY_BL1_SIZE; i++) {
+ checksum += *ptr;
+ ptr++;
+ }
+ *((volatile u32 *)(addr + 0x8)) = checksum;
+ pr_info("BL1's checksum is calculated.\n");
+ }
+#endif
ret = nand_write_skip_bad(mtd, off, &rwsize,
NULL, maxsize,
(u_char *)addr,
WITH_WR_VERIFY);
+ }
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
烧写和读取nand上block0的数据需作8bit ecc。GEC210单板搭载的nand flash的oob大小为64Byte,由文件《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》可知,烧写进nand中的BL1需作8bit ecc如下图所示,而其他区域没有要求则默认为1bit ecc。在烧写和读nand数据的流程中添加条件判断是否操作block0的内容,是则直接调用s5p_nand.c硬件驱动中的s3c_nand_read_page_8bit()
或者s3c_nand_write_page_8bit()
:
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index eb9f121..5383cd5 100755
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1,4 +1,4 @@
-/*
+/*
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
@@ -353,6 +353,11 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
ofs += mtd->erasesize - mtd->writesize;
page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+#if defined(CONFIG_NAND_BL1_8BIT_ECC) && defined(CONFIG_S5PC110)
+ if (page < CFG_NAND_PAGES_IN_BLOCK)
+ return 0;
+#endif
do {
if (chip->options & NAND_BUSWIDTH_16) {
@@ -1677,6 +1682,9 @@ static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
return chip->setup_read_retry(mtd, retry_mode);
}
+extern int s3c_nand_read_page_8bit(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf);
+
/**
* nand_do_read_ops - [INTERN] Read data with ECC
* @mtd: MTD device structure
@@ -1744,18 +1752,26 @@ read_retry:
* Now read the page into the buffer. Absent an error,
* the read methods return max bitflips per ecc step.
*/
- if (unlikely(ops->mode == MTD_OPS_RAW))
- ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
- oob_required,
- page);
- else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
- !oob)
- ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi,
- page);
- else
- ret = chip->ecc.read_page(mtd, chip, bufpoi,
- oob_required, page);
+#if defined(CONFIG_NAND_BL1_8BIT_ECC) && defined(CONFIG_S5PC110)
+ if (page < CFG_NAND_PAGES_IN_BLOCK) {
+ ret = s3c_nand_read_page_8bit(mtd, chip, bufpoi);
+ } else
+#endif
+ {
+ if (unlikely(ops->mode == MTD_OPS_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
+ oob_required,
+ page);
+ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
+ !oob)
+ ret = chip->ecc.read_subpage(mtd, chip,
+ col, bytes, bufpoi,
+ page);
+ else
+ ret = chip->ecc.read_page(mtd, chip, bufpoi,
+ oob_required, page);
+ }
+
if (ret < 0) {
if (use_bufpoi)
/* Invalidate page cache */
@@ -2404,6 +2420,9 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
return 0;
}
+
+extern int s3c_nand_write_page_8bit(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf);
/**
* nand_write_page - [REPLACEABLE] write one page
@@ -2431,15 +2450,23 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (nand_standard_page_accessors(&chip->ecc))
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
- if (unlikely(raw))
- status = chip->ecc.write_page_raw(mtd, chip, buf,
- oob_required, page);
- else if (subpage)
- status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
- buf, oob_required, page);
- else
- status = chip->ecc.write_page(mtd, chip, buf, oob_required,
- page);
+#if defined(CONFIG_NAND_BL1_8BIT_ECC) && defined(CONFIG_S5PC110)
+ if (page < CFG_NAND_PAGES_IN_BLOCK) {
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ status = s3c_nand_write_page_8bit(mtd, chip, buf);
+ } else
+#endif
+ {
+ if (unlikely(raw))
+ status = chip->ecc.write_page_raw(mtd, chip, buf,
+ oob_required, page);
+ else if (subpage)
+ status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
+ buf, oob_required, page);
+ else
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required,
+ page);
+ }
if (status < 0)
return status;
至此,我们已经可以通过命令
nand write/read
来有效烧写u-boot.bin和uImage.bin。
到当前为止,引导kernel启动之前必须的驱动及移植已经完成,现需要引导启动kernel,主要是修改环境变量bootcmd
相关定义。另外还需通过配置菜单裁剪u-boot,减小尺寸。本章节所有修改细节参考补丁06_bootkernel.patch
。
1.设置环境变量bootcmd
。如下图,从default_environment[]
得知,通过定义CONFIG_BOOTCOMMAND
来设置环境变量bootcmd
的值。
在include/configs/gec210.h
中定义和修改CONFIG_BOOTCOMMAND
相关的值如下中的run bootk
。
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index b95a833..62daaff 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -1,4 +1,4 @@
-/*
+/*
@@ -63,30 +65,30 @@
#define CONFIG_MTD_DEVICE
#define CONFIG_MTD_PARTITIONS
-#define CONFIG_BOOTCOMMAND "run ubifsboot"
+#define CONFIG_BOOTCOMMAND "run bootk"
#define CONFIG_RAMDISK_BOOT "root=/dev/ram0 rw rootfstype=ext2" \
" console=ttySAC0,115200n8" \
- " mem=128M"
+ " mem=512M"
#define CONFIG_COMMON_BOOT "console=ttySAC0,115200n8" \
- " mem=128M " \
+ " mem=512M " \
" " CONFIG_MTDPARTS_DEFAULT
-#define CONFIG_UPDATEB "updateb=onenand erase 0x0 0x40000;" \
- " onenand write 0x32008000 0x0 0x40000\0"
+#define CONFIG_UPDATEB "updateb=nand erase.part boot;" \
+ "nand write 0x40000000 boot\0"
#define CONFIG_ENV_OVERWRITE
#define CONFIG_EXTRA_ENV_SETTINGS \
CONFIG_UPDATEB \
"updatek=" \
- "onenand erase 0x60000 0x300000;" \
- "onenand write 0x31008000 0x60000 0x300000\0" \
+ "nand erase.part kernel;" \
+ "nand write 0x40000000 kernel\0" \
"updateu=" \
"onenand erase block 147-4095;" \
"onenand write 0x32000000 0x1260000 0x8C0000\0" \
"bootk=" \
- "onenand read 0x30007FC0 0x60000 0x300000;" \
+ "nand read 0x30007FC0 kernel;" \
"bootm 0x30007FC0\0" \
"flashboot=" \
"set bootargs root=/dev/mtdblock${bootblock} " \
而环境变量bootk
改为"nand read 0x30007FC0 kernel;bootm 0x30007FC0"
。指示将uImage加载到地址0x30007FC0
并启动之,该地址的确定需结合编译出来的uImage和u-boot流程来分析。u-boot加载启动kernel流程中的bootm_load_os()
,进行了解压os数据或移动到images->os.load指定的地址,如下图所示。从流程中得知os数据用image->os.image_start
表示(uboot 1.3.4中的image_start表示整个uImage的起始地址),有关系:image->os.image_start
=image->os.start
+header大小(64B);而images->os.load
从uImage的头部信息中得到,即编译uImage输出log中的Load Address: xxxxxxxx
。然后u-boot要求images->os.load
等于image->os.image_start
,意味kernel数据需加载到images->os.load
地址,进而要求uImage的Load Address
等于Entry Point
。
综上所述有0x30008000
=Load Address
=Entry Point
=images->os.load
=image->os.image_start
=image->os.start
+ 64B
;即整个uImage需加载到image->os.start
=0x30008000
-64=0x30007FC0
。所以与u-boot 1.3.4不同的是,编译完uImage后不需要更改Load Address
或Entry Point
即可直接烧写进nand。
2.设置启动参数和机器码。启动参数和机器码在uboot跳转到kernel时,需保存到寄存器R2和R1上。在配置菜单中选中[*] Enable boot arguments
并编辑启动参数为:root=/dev/mtdblock4 rootfstype=yaffs console=ttySAC0,115200n8 mem=512M mtdparts=s5p-nand:1m(boot),5m@0x100000(recovery),5m@0x600000(kernel),3m@0xb00000(ramdisk),-(rootfs)
如下图;告知kernel:根文件系统位于分区四,类型是yaffs
,串口0作为控制台,DDR2空间为512MB,nand的分区表。
在文件include/configs/gec210.h
中定义机器码,宏CONFIG_MACH_TYPE
在函数setup_machine()
中用于设置gd->bd->bi_arch_number
,并最终在启动kernel前保存到R1,另外往后的流程中函数board_init()
又设置了gd->bd->bi_arch_number
,需将对应操作注释掉:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index b95a833..62daaff 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -22,6 +22,8 @@
#if 0
#define CONFIG_GEC210 1 /* working with GEC210 */
#endif
+#define CONFIG_MACH_TYPE MACH_TYPE_SMDKV210
+
#include /* get chip and board defs */
#include
diff --git a/board/samsung/gec210/gec210.c b/board/samsung/gec210/gec210.c
index f5a6249..57e38c9 100755
--- a/board/samsung/gec210/gec210.c
+++ b/board/samsung/gec210/gec210.c
@@ -63,7 +63,8 @@ int board_init(void)
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init();
#endif
- gd->bd->bi_arch_number = MACH_TYPE_SMDKC110;
+ /* bi_arch_number has set in setup_machine(). */
+ /* gd->bd->bi_arch_number = MACH_TYPE_SMDKC110; */
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100;
return 0;
3.规划和设置nand分区。nand分区表最好与kernel中mtd设备驱动设置的分区一致。nand分区图如下:
菜单配置Command line interface ---> Filesystem commands ---> [*] MTD partition support
分区表设为"mtdparts=s5p-nand:1m(boot),5m@0x100000(recovery),5m@0x600000(kernel),3m@0xb00000(ramdisk),-(rootfs)"
:
修改保存到nand中环境变量env数据的起始地址及大小:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index b95a833..62daaff 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -246,8 +248,8 @@
#define BOOT_SEC_DEV 0x5
#define CONFIG_ENV_SIZE (128 << 10) /* 128KiB, 0x20000 */
-#define CONFIG_ENV_ADDR (256 << 10) /* 256KiB, 0x40000 */
-#define CONFIG_ENV_OFFSET (256 << 10) /* 256KiB, 0x40000 */
+#define CONFIG_ENV_ADDR (512 << 10) /* 512KiB, 0x80000 */
+#define CONFIG_ENV_OFFSET (512 << 10) /* 512KiB, 0x80000 */
/* nand copy size from nand to DRAM.*/
#define COPY_BL1_SIZE (8 << 10) /* for irom's BL0 copy */
由于没有ramdisk相关的内容,还需要关闭引导启动kernel流程中与ramdisk有关的代码,否则会导致启动kernel失败:
diff --git a/arch/arm/include/asm/config.h b/arch/arm/include/asm/config.h
index 9f17829..c5d7658 100755
--- a/arch/arm/include/asm/config.h
+++ b/arch/arm/include/asm/config.h
@@ -8,7 +8,7 @@
#define _ASM_CONFIG_H_
#define CONFIG_LMB
-#define CONFIG_SYS_BOOT_RAMDISK_HIGH
+/* #define CONFIG_SYS_BOOT_RAMDISK_HIGH */
#if defined(CONFIG_ARCH_LS1021A) || \
defined(CONFIG_CPU_PXA27X) || \
4.在第二章的底层初始化中uart_asm_init
将作为控制台的uart初始化为fifo模式,linux 2.6启动流程中会调用putc
函数作打印动作如下图所示,若uart为fifo模式则会执行图中if
流程。圈内的fifo_max
为全局变量,在该版本的内核中其未被初始化(即值被默认设为0),导致level < fifo_max
条件永远不能成立,则导致kernel在初始化启动流程中调用putc
函数时陷入死循环。所以需添加代码初始化这样的全局变量:
diff --git a/arch/arm/mach-s5pv210/include/mach/uncompress.h b/arch/arm/mach-s5pv210/include/mach/uncompress.h
index 08ff2fd..76db538 100755
--- a/arch/arm/mach-s5pv210/include/mach/uncompress.h
+++ b/arch/arm/mach-s5pv210/include/mach/uncompress.h
@@ -19,6 +19,8 @@
static void arch_detect_cpu(void)
{
/* we do not need to do any cpu detection here at the moment. */
+ fifo_mask = S5PV210_UFSTAT_TXMASK;
+ fifo_max = 255 << S5PV210_UFSTAT_TXSHIFT;
}
#endif /* __ASM_ARCH_UNCOMPRESS_H */
目前为止,u-boot已经实现了其主要的工作——引导启动kernel,kernel启动流程中会加载根文件系统,若nand中rootfs分区中未有根文件系统则启动失败;所以我们还需要令u-boot支持根文件系统的烧写,u-boot v2018默认支持烧写jffs2格式的根文件系统)(# nand write.jffs2 addr off size
),尚未支持烧写yaffs2格式根文件系统。本章节实现烧写yaffs2根文件系统,参考07_yaffs_fuse.patch
了解全部细节;大致思路是,参考nand write.jffs2
流程,在流程中添加yaffs
实例化相关的代码,最终实现命令# nand write.yaffs2 addr off size
:
1.定义宏CONFIG_CMD_NAND_YAFFS
。由于我们是在已有的流程中添加针对yaffs2
相关的代码,那为了方便功能的开关,使用宏CONFIG_CMD_NAND_YAFFS
将相关的代码框起来。由于宏定义CONFIG_CMD_NAND_YAFFS
并不存在于配置宏白名单scripts/config_whitelist.txt
中,如直接在include/configs/gec210.h
重定义在编译时将报错。所以我们在Kconfig
在添加该宏,且可以在配置菜单Command line interface -> Device access commands -> [*] nand -> [*] nand write.yaffs2
中开关:
diff --git a/cmd/Kconfig b/cmd/Kconfig
index c033223..f2883cc 100755
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -774,6 +774,12 @@ config CMD_NAND_TRIMFFS
help
Allows one to skip empty pages when flashing something on a NAND.
+config CMD_NAND_YAFFS
+ bool "nand write.yaffs2"
+ default y
+ help
+ Allows one to misalign write pages when flashing something on a NAND.
+
config CMD_NAND_LOCK_UNLOCK
bool "nand lock/unlock"
help
2.添加识别命令nand write.yaffs2
的代码和命令帮助信息。同.jffs2
命令同样需调用nand_write_skip_bad
函数作底层的nand烧写动作,带入WITH_YAFFS_OOB
参数用于区别不同的格式,因此还要定义WITH_YAFFS_OOB
:
diff --git a/cmd/nand.c b/cmd/nand.c
index 234a7b7..65d3da4 100755
--- a/cmd/nand.c
+++ b/cmd/nand.c
@@ -643,6 +643,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
maxsize, (u_char *)addr,
WITH_DROP_FFS | WITH_WR_VERIFY);
#endif
+#ifdef CONFIG_CMD_NAND_YAFFS
+ } else if (!strcmp(s, ".yaffs2")) {
+ if (read) {
+ printf("Unknown nand command suffix '%s'\n", s);
+ return 1;
+ }
+ ret = nand_write_skip_bad(mtd, off, &rwsize, NULL,
+ maxsize, (u_char *)addr,
+ WITH_YAFFS_OOB);
+#endif
} else if (!strcmp(s, ".oob")) {
/* out-of-band data */
mtd_oob_ops_t ops = {
@@ -820,6 +830,11 @@ static char nand_help_text[] =
" 'addr', skipping bad blocks and dropping any pages at the end\n"
" of eraseblocks that contain only 0xFF\n"
#endif
+#ifdef CONFIG_CMD_NAND_YAFFS
+ "nand write.yaffs2 - addr off|partition size\n"
+ " write ‘size‘ bytes starting at offset ‘off‘ with yaffs format\n"
+ " from memory address ‘addr‘, skipping bad blocks.\n"
+#endif
"nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'\n"
" With '.spread', erase enough for given file size, otherwise,\n"
diff --git a/include/nand.h b/include/nand.h
index cead563..40ad5bf 100755
--- a/include/nand.h
+++ b/include/nand.h
@@ -1,1 +1,1 @@
-/*
+/*
@@ -103,6 +103,7 @@ int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
#define WITH_DROP_FFS (1 << 0) /* drop trailing all-0xff pages */
#define WITH_WR_VERIFY (1 << 1) /* verify data was written correctly */
+#define WITH_YAFFS_OOB (1 << 2)
int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
size_t *actual, loff_t lim, u_char *buffer, int flags);
3.写nand流程中添加yaffs2
相关的代码:
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 9c8a373..1e94f23 100755
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -581,7 +581,22 @@ int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
if (actual)
*actual = 0;
- blocksize = mtd->erasesize;
+#ifdef CONFIG_CMD_NAND_YAFFS
+ if (flags & WITH_YAFFS_OOB) {
+ if (flags & ~WITH_YAFFS_OOB)
+ return -EINVAL;
+
+ int pages;
+ pages = mtd->erasesize / mtd->writesize;
+ blocksize = (pages * mtd->oobsize) + mtd->erasesize;
+ if (*length % (mtd->writesize + mtd->oobsize)) {
+ printf ("Attempt to write incomplete page"
+ " in yaffs mode\n");
+ return -EINVAL;
+ }
+ } else
+#endif
+ blocksize = mtd->erasesize;
/*
* nand_write() handles unaligned, partial page writes.
@@ -617,7 +632,7 @@ int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
return -EFBIG;
}
- if (!need_skip && !(flags & WITH_DROP_FFS)) {
+ if (!need_skip && !(flags & WITH_DROP_FFS) && !(flags & WITH_YAFFS_OOB)) {
rval = nand_write(mtd, offset, length, buffer);
if ((flags & WITH_WR_VERIFY) && !rval)
@@ -650,22 +665,52 @@ int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
else
write_size = blocksize - block_offset;
- truncated_write_size = write_size;
+#ifdef CONFIG_CMD_NAND_YAFFS
+ if (flags & WITH_YAFFS_OOB) {
+ int page, pages;
+ size_t pagesize = mtd->writesize;
+ size_t pagesize_oob = pagesize + mtd->oobsize;
+ struct mtd_oob_ops ops;
+
+ ops.len = pagesize;
+ ops.ooblen = mtd->oobsize;
+ ops.mode = MTD_OPS_AUTO_OOB;
+ ops.ooboffs = 0;
+
+ pages = write_size / pagesize_oob;
+ for (page = 0; page < pages; page++) {
+ WATCHDOG_RESET();
+
+ ops.datbuf = p_buffer;
+ ops.oobbuf = ops.datbuf + pagesize;
+
+ rval = mtd->_write_oob(mtd, offset, &ops);
+ if (rval != 0)
+ break;
+
+ offset += pagesize;
+ p_buffer += pagesize_oob;
+ }
+ } else
+#endif
+ {
+ truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
- if (flags & WITH_DROP_FFS)
- truncated_write_size = drop_ffs(mtd, p_buffer,
- &write_size);
+ if (flags & WITH_DROP_FFS)
+ truncated_write_size = drop_ffs(mtd, p_buffer,
+ &write_size);
#endif
- rval = nand_write(mtd, offset, &truncated_write_size,
- p_buffer);
+ rval = nand_write(mtd, offset, &truncated_write_size,
+ p_buffer);
- if ((flags & WITH_WR_VERIFY) && !rval)
- rval = nand_verify(mtd, offset,
- truncated_write_size, p_buffer);
+ if ((flags & WITH_WR_VERIFY) && !rval)
+ rval = nand_verify(mtd, offset,
+ truncated_write_size, p_buffer);
- offset += write_size;
- p_buffer += write_size;
+ offset += write_size;
+ p_buffer += write_size;
+ }
if (rval != 0) {
printf("NAND write to offset %llx failed %d\n",
经过本章节的操作,可直接通过命令
# nand write.yaffs2 addr off size
来烧写yaffs2
格式的根文件系统,从而成功启动kernel。
前几张章节已经完成了u-boot的基本移植,本章操作额外增强了u-boot功能,最终目的是在LCD上定制显示LOGO,另外将LCD作为第二个输出控制台以显示启动过程中的log。本章移植的全部细节参考08_support_LCD.patch
。
1.LCD控制器初始化。在初始化启动流程中的stdio_add_devices()
调用了drv_lcd_init()
(依赖宏CONFIG_LCD
);因此,通过实现drv_lcd_init()
来初始化LCD相关资源,且需定义CONFIG_LCD
。创建文件drivers/video/s5p_fb.c
实现LCD控制器初始化函数LCD_Initialize_AT070TN90()
;并在drv_lcd_init()
中调用它如下代码;另外需特别注意的是在drv_lcd_init()
中需调用lcd_set_flush_dcache(1);
使得每次操作完显存对应的内存地址,就调用flush_dcache_range()
来回写dcache的内容,从而保持DMA一致性;否则将出现显示混乱、花屏等现象。
而结构体实例panel_info
在lcd驱动通用层(common/lcd.c
)中被引用来唯一确定LCD显示屏的分辨率和像素位数:
/*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* S5PC110 - LCD Driver for U-Boot
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include
#include
#include
#include
#include
#define LCD_BGCOLOR 0x0//0x1428A0
#define Inp32(_addr) readl(_addr)
#define Outp32(addr, data) (*(volatile u32 *)(addr) = (data))
#define Delay(_a) udelay(_a*1000)
#if defined(CONFIG_LCD_AT070TN90)
static void *fb_base; /* Start of framebuffer memory */
vidinfo_t panel_info = {
.vl_col = 800, /* Number of columns (i.e. 160) */
.vl_row = 480, /* Number of rows (i.e. 100) */
.vl_bpix = LCD_BPP, /* Bits per pixel, 0 = 1, 1 = 2, 2 = 4, 3 = 8 */
} ;
static void LCD_Initialize_AT070TN90(void *lcdbase)
{
/* 1.配置引脚
* LCD_HSYNC LCD_VSYNC LCD_VDEN LCD_VCLK
* LCD_VD[0] LCD_VD[1] LCD_VD[2] LCD_VD[3]
*/
Outp32(GPF0CON, 0x22222222);
/* 2.配置引脚
* LCD_VD[4] LCD_VD[5] LCD_VD[6] LCD_VD[7]
* LCD_VD[8] LCD_VD[9] LCD_VD[10] LCD_VD[11]
*/
Outp32(GPF1CON, 0x22222222);
/* 3.配置引脚
LCD_VD[12] LCD_VD[13] LCD_VD[14] LCD_VD[15]
LCD_VD[16] LCD_VD[17] LCD_VD[18] LCD_VD[19]
*/
Outp32(GPF2CON, 0x22222222);
/* 4.配置引脚
LCD_VD[20] LCD_VD[21] LCD_VD[22] LCD_VD[23]
*/
Outp32(GPF3CON, Inp32(GPF3CON) & (~((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0))));
Outp32(GPF3CON, Inp32(GPF3CON) | ((0x2<<12)|(0x2<<8)|(0x2<<4)|(0x2<<0)));
/* 5.VIDCON0
Note:ENVID and ENVID_F are set to “1”
[0]:1 Display On必须置1
[1]:1 Display On必须置1
[4]:1 因为显示的时钟需要用到33.3MHz的频率,那么要进行分频
[6]:4 显示主区域的HCLK时钟为166MHz,为了得到33.3MHz左右的频率,当前的值要设置为4
[18]:0 因为我们当前是RGB并行接口,意思就是说一次发送3个字节,什么是串行接口,3个字节分3次来发送
[26]:0 配置为RGB接口
*/
Outp32(ELFIN_LCD_BASE, (0<<26)|(0<<18)|(4<<6)|(1<<4)|(1<<1)|(1<<0));
/* 6.VIDCON1,通过观看S70-AT070TN92(GEC210).pdf中的13页发现VSYNC和HSYNC信号发现与210手册的1207页极性相反,需要进行配置
[5]:1 VSYNC极性取反
[6]:1 HSYNC极性取反
*/
Outp32( ELFIN_LCD_BASE+0x4, (1<<6)|(1<<5));
/* 7.配置rVIDTCON0,这部分要查看S70-AT070TN92(GEC210).pdf中的14页中3.3.3Timing
[7:0]:9+1 VSPW垂直同步信号宽度,这是VSYNC同步信号的高电平持续时间
[8:15]:21+1 VFPD
[16:23]:12+1 VBPD
*/
Outp32(ELFIN_LCD_BASE+0x10, (12<<16)|(21<<8)|(9<<0));
/* 8.配置rVIDTCON1,这部分要查看S70-AT070TN92(GEC210).pdf中的14页中3.3.3 Timing
[7:0]:19+1 HSPW水平同步信号宽度,这是VSYNC同步信号的高电平持续时间
[8:15]:209+1 HFPD
[16:23]:25+1 HBPD
*/
Outp32(ELFIN_LCD_BASE+0x14, (25<<16)|(209<<8)|(19<<0));
/* 9.配置rVIDTCON2,这个要看LCD的分辨率,800x480
[10:0]:799+1 一行有多少个像素点
[11:21]:479+1 有多少行
*/
Outp32(ELFIN_LCD_BASE+0x18, (479<<11)|(799<<0));
/* 10.配置rWINCON0
[0]:1 使能视频输出
[5:2]:1011 显示格式是24位色,R:8 G:8 B:8
[15]:1 发现EN_LOCAL=0,因为我们待会使用显存即DMA,使能字交换
即每次DMA传输数据最小单元为字传输
[22]:0 使用DMA(直接内存访问:直接向某一地址写数据的时候会自动操作硬件,不需要CPU进行干预)
*/
Outp32(ELFIN_LCD_BASE+0x20, (1<<15)|(0xB<<2)|(1<<0));
/* 11.配置rVIDW00ADD0B0,用于设置显示缓存的起始地址
*/
Outp32(ELFIN_LCD_BASE+0xA0, (u32)lcdbase);
/* 12.rVIDW00ADD1B0,用于配置显示缓存的结束地址,告诉CPU我要占用多大的空间
公式如下:VBASEL = VBASEU +(PAGEWIDTH+OFFSIZE) x (LINEVAL+1)
= 0x48000000+(800+0)x(479+1)x4
800x480*4[A:R:G:B]
*/
Outp32(ELFIN_LCD_BASE+0xD0, ((u32)lcdbase)+800*4*480);
/* 13.rVIDW00ADD2:用于配置窗口的偏移值还有一行占用多少字节
[12:0] : 800*4 因为Linux显示是32位色,为了兼容以后的Linux驱动部分,增加它的空间
字对齐
[13:25] : 因为当前显示不需要偏移,设置为0就可以了。
*/
Outp32(ELFIN_LCD_BASE+0x100, (0<<13)|((800*4)<<0));
/* 14.rSHADOWCON 使能Channel 0
[0]:1
*/
Outp32(ELFIN_LCD_BASE+0x34, Inp32(ELFIN_LCD_BASE+0x34) | (1<<0));
/* 15.rVIDOSD0A 配置显示区域的左上角x/y坐标值
[10:0] :0 左上角的Y坐标值
[21:11]:0 左上角的X坐标值
*/
Outp32(ELFIN_LCD_BASE+0x40, (0<<11)|(0<<0));
/* 16.rVIDOSD0B 配置显示区域的右下角x/y坐标值
[10:0] :479 右下角的Y坐标值
[21:11]:799 右下角的X坐标值
*/
Outp32(ELFIN_LCD_BASE+0x44, (799<<11)|(479<<0));
/* 17.rVIDOSD0C 配置窗口的大小也就是窗口的分辨率
[23:0] :800*480 For example, Height * Width
*/
Outp32(ELFIN_LCD_BASE+0x48, 800*480);
/* 18.配置FIMD一定要为RGB接口
*/
Outp32(0xe0107008, 2<<0);
}
void lcd_ctrl_init(void *lcdbase)
{
fb_base = lcdbase;
LCD_Initialize_AT070TN90(lcdbase);
/* Enable flushing if we enabled dcache for staying dma coherent */
lcd_set_flush_dcache(1);
}
void lcd_enable (void)
{
return;
}
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
{
return;
}
#endif
添加文件进编译工程,以及在Kconfig中定义所依赖的宏,并在配置菜单中开启:
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index dfafe08..242f5a7 100755
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -53,6 +53,8 @@
obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
+obj-$(CONFIG_S5P_FB) += s5p_fb.o
+
obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
obj-${CONFIG_EXYNOS_FB} += exynos/
obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 45a105d..8b12213 100755
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -658,4 +658,16 @@ config VIDEO_DT_SIMPLEFB
The video output is initialized by U-Boot, and kept by the
kernel.
+config S5P_FB
+ bool "Enable s5pc11x's fimd lcd driver support"
+ help
+ Enable soc s5pc11x lcd driver without driver model.
+
+config LCD_AT070TN90
+ bool "support lcd model named AT070TN90"
+ default y
+ depends on S5P_FB
+ help
+ support the lcd model AT070TN90
+
endmenu
2.开启logo显示功能和定义像素位数。在配置文件include/configs/gec210.h
中定义:
diff --git a/include/configs/gec210.h b/include/configs/gec210.h
index 62daaff..c05bcb2 100755
--- a/include/configs/gec210.h
+++ b/include/configs/gec210.h
@@ -269,4 +271,8 @@
#define CONFIG_ENV_SROM_BANK 1 /* Select SROM Bank-1 for Ethernet*/
#endif /* CONFIG_CMD_NET */
+#define CONFIG_LCD_LOGO 1
+#define LCD_BPP LCD_COLOR32
+
+
#endif /* __CONFIG_H */
增加lcd驱动通用层对24位像素LCD的支持。修改common/lcd.c
:
diff --git a/common/lcd.c b/common/lcd.c
index 4b3d7dc..15c2893 100755
--- a/common/lcd.c
+++ b/common/lcd.c
@@ -1,4 +1,4 @@
-/*
+/*
@@ -27,7 +27,8 @@
#include
#include
#if (CONSOLE_COLOR_WHITE >= BMP_LOGO_OFFSET) && (LCD_BPP != LCD_COLOR16)
-#error Default Color Map overlaps with Logo Color Map
+/* #error Default Color Map overlaps with Logo Color Map */
+#warning Default Color Map overlaps with Logo Color Map
#endif
#endif
@@ -349,8 +350,11 @@ void lcd_logo_plot(int x, int y)
uchar *bmap = &bmp_logo_bitmap[0];
unsigned bpix = NBITS(panel_info.vl_bpix);
uchar *fb = (uchar *)(lcd_base + y * lcd_line_length + x * bpix / 8);
+#if 0
ushort *fb16;
-
+#else
+ uint *fb32;
+#endif
debug("Logo: width %d height %d colors %d\n",
BMP_LOGO_WIDTH, BMP_LOGO_HEIGHT, BMP_LOGO_COLORS);
@@ -366,6 +370,7 @@ void lcd_logo_plot(int x, int y)
}
}
else { /* true color mode */
+ #if 0
u16 col16;
fb16 = (ushort *)fb;
for (i = 0; i < BMP_LOGO_HEIGHT; ++i) {
@@ -379,6 +384,21 @@ void lcd_logo_plot(int x, int y)
bmap += BMP_LOGO_WIDTH;
fb16 += panel_info.vl_col;
}
+ #else /* for 24bit color */
+ fb32 = (uint *)fb;
+ for (i=0; i < BMP_LOGO_HEIGHT; ++i) {
+ for (j=0; j < BMP_LOGO_WIDTH; j++) {
+ u32 color = bmp_logo_palette[(bmap[j]-BMP_LOGO_OFFSET)];
+ /* RGB-888 */
+ fb32[j] = ((color & 0x000F) << 4) |
+ ((color & 0x00F0) << 8) |
+ ((color & 0x0F00) << 12);
+ }
+ bmap += BMP_LOGO_WIDTH;
+ fb32 += panel_info.vl_col;
+ }
+ #endif
+
}
WATCHDOG_RESET();
3.定制自己的logo。u-boot已有的logo保存于路径tools/logos/
,并通过tools/Makefile
来引用并被可执行文件bmp_logo
转换成二进制文件,然后再u-boot代码中被引用显示到显示屏。准备好8位的bmp格式图片文件:
修改tools/Makefile
以引用自己的logo;修改bmp_logo
工具的源文件使得转换logo图输出的二进制数据为8位色图,解决Logo显示模糊的问题:
diff --git a/tools/Makefile b/tools/Makefile
index 4d32fe5..0a27b04 100755
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -219,6 +219,8 @@ LOGO-$(CONFIG_VIDEO_LOGO) += $(LOGO_H)
LOGO-$(CONFIG_VIDEO_LOGO) += $(LOGO_DATA_H)
# Generic logo
+LOGO_BMP= $(srctree)/$(src)/logos/zhoumou.bmp
+
ifeq ($(LOGO_BMP),)
LOGO_BMP= $(srctree)/$(src)/logos/denx.bmp
diff --git a/tools/bmp_logo.c b/tools/bmp_logo.c
index 55f833f..47d9ecc 100755
--- a/tools/bmp_logo.c
+++ b/tools/bmp_logo.c
@@ -1,4 +1,4 @@
-#include "compiler.h"
+#include "compiler.h"
enum {
MODE_GEN_INFO,
@@ -12,7 +12,7 @@ typedef struct bitmap_s { /* bitmap description */
uint8_t *data;
} bitmap_t;
-#define DEFAULT_CMAP_SIZE 16 /* size of default color map */
+#define DEFAULT_CMAP_SIZE 8 /* size of default color map */
void usage(const char *prog)
{
4.设置环境参数使得LCD作为输出控制台。通过配置菜单Console ---> [*] Enable console multiplexing
使能多控制台功能,启动u-boot后输入命令setenv stdout serial,lcd; saveenv
。