移植u-boot v2018

本篇文章阐述移植 u-boot v2018.01S5PV210 开发板上的主要流程和细节。市场上的S5PV210开发板,均是基于三星smdkv210公版平台山寨出来的。我使用的GEC210开发板也与公版只数个元器件的差异。所以,若你也用S5PV210类开发板,参考本篇文章,或者直接使用我发布的补丁打到源码上,能帮你解决许多困惑及运行最新的u-boot。

最新更新

  1. [2018-12-2 15:17:41] 可直接下载我发布到github已经移植好的源码:u-boot-v2018.01_for_gec210

结果

  1. 下载u-boot-2018.01.tar.bz2,或通过git下载:
    $ git clone git://git.denx.de/u-boot.git
    $ git checkout v2018.01
  2. 该源码要求使用>=6.0版本GCC编译,下载最新编译好的linaro arm交叉编译工具链,选择对应Linux环境的工具链下载并解压,如:我的为32位 ubuntu则选如下标注的文件:
    移植u-boot v2018_第1张图片
  3. 下载我已发布到github的补丁到源码目录:u-boot-v2018.01_gec210.patch
  4. 打补丁到源码:$ git apply u-boot-v2018.01_gec210.patch,如下所示,有警告无影响;由于补丁中含有二进制数据,使用patch命令打补丁将出错。
    移植u-boot v2018_第2张图片
  5. 设置交叉编译工具链前缀:设置u-boot/MakefileCROSS_COMPILE变量为前面解压好的工具链可执行文件的前缀,如:
    移植u-boot v2018_第3张图片
  6. 配置源码为目标板:$ make gec210_defconfig
  7. 编译:$ make,最终产生u-boot.bin文件

以下阐述移植的过程,由于篇幅有限,重点难点的修改才会作详细的解释,参考上述补丁文件更详尽。开始移植时还需要分析u-boot的流程,可以参考我另一篇博文《u-boot v2018.01 启动流程分析》。
以下章节中出现的脚本名字有如下关系,每一个章节对应一次git提交,一次对u-boot源码打上所有阶段补丁等效于一次性打上补丁u-boot-v2018.01_gec210.patch
移植u-boot v2018_第4张图片

一、建立新板

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
移植u-boot v2018_第5张图片
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

二、底层初始化(clock,ddr2,uart,nand)

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.hs5pc110.h文件中定义及引用了当前源码未定义的内容,所以还需做裁剪,具体参考u-boot-v2018.01_gec210.patch
移植u-boot v2018_第6张图片
2.修改lowlevel_init.S以实现SOC级别硬件初始化,包括WATCHDOG,clock,DDR2,uart,nand。第一章已经从smdkc100单板中移植了lowlevel_init.S,其代码流程与S5PV210一致,但各硬件的初始化需重写,需修改函数包括:lowlevel_initsystem_clock_inituart_asm_inittzpc_asm_init,还需添加nand_asm_initmem_ctrl_asm_init(定义于mem_init.S文件),另外引用的宏需在include/configs/gec210.h里定义。这些基础的soc级别初始化均可移植于u-boot-samsung-dev,详细参考补丁:
移植u-boot v2018_第7张图片
3.保证lowlevel_init.Smem_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 \

至此,底层基础的初始化代码移植完成,运行之会打印“OK”:
移植u-boot v2018_第8张图片

三、从Nand和mmc/sd加载启动u-boot

  到目前为止,代码只是在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
移植u-boot v2018_第9张图片
需要注意的是,我们需指定加载BL2到的目标内存地址CONFIG_SYS_TEXT_BASE,以及BL2大小COPY_BL2_SIZE,将其定义于include/configs/gec210.h
移植u-boot v2018_第10张图片
由于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行:
移植u-boot v2018_第11张图片
4.加载完BL2后跳转到DDR2中:

after_copy:
	ldr	pc, =_main		@ jump into ddr

5.到此已经跳转到ddr2中_main函数的位置,该函数定义于arch/arm/lib/crt0.S。该函数一开始就将sp定义到了0x2f000000上,而该地址并不在DDR2上,导致后面代码运行出错,如下所示:
移植u-boot v2018_第12张图片
修改关联的宏以让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_第13张图片

四、修改dm9000驱动

  u-boot v2018.01源码已经实现了dm9000系列驱动drivers/net/dm9000x.c,我们只需定义寄存器级别的内容就行。
移植u-boot v2018_第14张图片
1.设置dm9000的内存地址。由上图知DM9000的片选线CS#接到了S5PV210的CSn1,也就是SROMC_BANK1,而观下图可知访问DM9000的基址是0x88000000,DM9000的CMD接到了地址线ADDR2,所以访问DM9000数据的地址为0x88000000+2x4Byte
移植u-boot v2018_第15张图片
  在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取消选择:
移植u-boot v2018_第16张图片

4.定义网络相关的环境变量。在默认环境变量集(include/env_default.h)中定义了数个与网络参数相关的变量如下图:
移植u-boot v2018_第17张图片
  我们需要在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 flash驱动

  移植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 v2018_第18张图片
  所以,移植时需要做对应的修改:

--- ../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行。
移植u-boot v2018_第19张图片

3.屏蔽掉nand id表中dev_id与GEC210上所搭载nand(K9K8G08U1A)相同但并不兼容的设备。由于nand初始化过程中,会根据从nand读回来的dev_idnand_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.修改MakefileKconfig以添加编译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 --->中的如下选项:
移植u-boot v2018_第20张图片
  另外开启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 v2018_第21张图片
  烧写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()
移植u-boot v2018_第22张图片

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启动之前必须的驱动及移植已经完成,现需要引导启动kernel,主要是修改环境变量bootcmd相关定义。另外还需通过配置菜单裁剪u-boot,减小尺寸。本章节所有修改细节参考补丁06_bootkernel.patch

1.设置环境变量bootcmd。如下图,从default_environment[]得知,通过定义CONFIG_BOOTCOMMAND来设置环境变量bootcmd的值。
移植u-boot v2018_第23张图片
  在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 AddressEntry Point即可直接烧写进nand。
移植u-boot v2018_第24张图片

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的分区表。
移植u-boot v2018_第25张图片
  在文件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分区图如下:
移植u-boot v2018_第26张图片
  菜单配置Command line interface ---> Filesystem commands ---> [*] MTD partition support分区表设为"mtdparts=s5p-nand:1m(boot),5m@0x100000(recovery),5m@0x600000(kernel),3m@0xb00000(ramdisk),-(rootfs)"
移植u-boot v2018_第27张图片
  修改保存到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函数时陷入死循环。所以需添加代码初始化这样的全局变量:
移植u-boot v2018_第28张图片

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 */

到此为止,可以成功启动kernel输出log:
移植u-boot v2018_第29张图片

七、支持yaffs2烧写nand

  目前为止,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

移植u-boot v2018_第30张图片

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。

八、移植LCD驱动并作控制台

  前几张章节已经完成了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格式图片文件
移植u-boot v2018_第31张图片
  修改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

到此阶段,上电开机准备引导kernel前LCD显示内容如下图:
移植u-boot v2018_第32张图片

你可能感兴趣的:(嵌入式arm,linux)