TQ2440移植u-boot2016.11-NAND FLASH方式启动U-BOOT


S3C2440 U-BOOT启动方式说明:

TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_TQ2440移植NAND驱动NOR FLASH启动: S3C2440会直接在NOR上运行程序,在NOR上中的代码需要初始化芯片时钟、初始化SDRAM。

TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_TQ2440移植NAND驱动NAND FLASH启动: S3C2440上电后其内部的NAND FLASH控制器会自动把NAND FLASH中的前4K的代码拷贝到S3C2440芯片自带的RAM中,然后开始运行程序,因为片上RAM只有4K的容量,所以这部分的代码就负责初始化芯片时钟、初始化SDRAM、初始化NAND,然后将u-boot的代码拷贝到SDRAM中并跳转运行。

TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_TQ2440移植NAND驱动目前主流的启动方式为NAND FLAHS或者SD卡启动,NOR FLASH用的越来越少了,上二章节已经实现了NOR FLASH的启动,本章节实现NAND FLASH的启动。


TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_TQ2440移植NAND驱动u-boot在编译时候开启了-pie选项,该选项的作用是编译出来的程序是位置无关的,什么意思呢,就像是动态库库一样,代码每次启动可以自由加载到内存的任意合适位置执行。如果我们开启了该功能,那么NAND前4K的代码中有一些变量等代码就可能不在这4K中,而是在一些特殊的段或者位置处,这样的话S3C2440上电自动拷贝NAND前4K的内容并运行就会出错,解决该办法的方式有两种:

  1. 修改u-boot执行的主流程,取消掉uboot重定位,在4K代码中实现复制代码到内存的操作。
  2. 设置为SPL启动,SPL启动简单的说就是编译u-boot的时候会生成u-boot.bin和u-boot-spl.bin文件,通过宏​​CONFIG_SPL_BUILD​​控制是编译u-boot.bin还是u-boot-spl.bin,spl代码只是对时钟的初始化、SDRAM初始化、NAND初始化并将u-boot从NAND中拷贝到内存并跳转运行的操作,所以这段代码肯定是小于4K的。

本例程使用SPL方式进行引导uboot。

(一)修改配置头文件:

​gedit include/configs/tq2440.h​

1、由于SPL的代码已经初始化了内存,所以SPL将uboot拷贝到内存并运行后,不需要再次初始化内存了,将宏CONFIG_SKIP_LOWLEVEL_INIT的编译条件由​CONFIG_SPL_BUILD​控制,改为如下代码:

/* 该宏是在文件arch/arm/cpu/arm920t/start.S中跳过SDRAM初始化代码 */
#ifndef CONFIG_SPL_BUILD
#define CONFIG_SKIP_LOWLEVEL_INIT
#endif

2、修改链接地址也由宏CONFIG_SPL_BUILD控制,如果是SPL那么链接到0地址处,如果是uboot那么链接到内存0x30008000地址处,将宏​CONFIG_SYS_TEXT_BASE​改为:

#ifdef CONFIG_SPL_BUILD
#define CONFIG_SYS_TEXT_BASE 0
#else
#define CONFIG_SYS_TEXT_BASE 0x30008000
#endif

TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_u-boot移植_05

(二)添加SPL编译项

​gedit arch/arm/Kconfig​

找到config TARGET_TQ2440的配置项,在该配置项结尾加入一行:​select SUPPORT_SPL​,效果:

TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_TQ2440_06

(三)添加拷贝u-boot到内存的代码

新建一个init.c文件:

​gedit board/samsung/tq2440/init.c​

粘贴进如下代码:

/*
* init.c
*
* Created on: 2020/03/28
* Author: AnKun
*/

#define NFCONF (*(volatile unsigned int *)0x4E000000)
#define NFCONT (*(volatile unsigned int *)0x4E000004)
#define NFCMD (*(volatile unsigned char *)0x4E000008)
#define NFADDR (*(volatile unsigned char *)0x4E00000C)
#define NFDATA (*(volatile unsigned char *)0x4E000010)
#define NFSTAT (*(volatile unsigned char *)0x4E000020)

#define GPBCON (*(volatile unsigned int *)0x56000010)
#define GPBDAT (*(volatile unsigned int *)0x56000014)
#define GPBUP (*(volatile unsigned int *)0x56000018)

#define tq2440_nand_select() do{NFCONT &= ~(1 << 1);}while(0);
#define tq2440_nand_deselect() do{NFCONT |= 1 << 1;}while(0);

static int is_boot_from_nor_flash(void)
{
int tmp = 0;
volatile int* p = (volatile int *)0;

tmp = *p;
*p = 0xAAAAAAAA;

if (*p == 0xAAAAAAAA) /* boot from nand */
{
*p = tmp;
return 0;
}
else /* boot from nor */
{
return 1;
}
}

void tq2440_nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0

NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);
NFCONT = (1 << 4) | (1 << 1) | (1 << 0);
}

void tq2440_nand_delay(void)
{
int delay = 0;
for (delay = 0; delay < 10; delay++);
}

void tq2440_nand_write_cmd(unsigned char cmd)
{
NFCMD = cmd;
tq2440_nand_delay();
}

void tq2440_nand_write_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;

NFADDR = col & 0xFF;
tq2440_nand_delay();

NFADDR = (col >> 8) & 0xFF;
tq2440_nand_delay();

NFADDR = page & 0xFF;
tq2440_nand_delay();

NFADDR = (page >> 8) & 0xFF;
tq2440_nand_delay();

NFADDR = (page >> 16) & 0xFF;
tq2440_nand_delay();
}

void tq2440_nand_wait_ready(void)
{
while (!(NFSTAT & 0x1)) {};
}

void tq2440_nand_read(unsigned int addr, void* buf, unsigned int len)
{
int i = 0;
int col = addr % 2048;
unsigned char* p = (unsigned char *)buf;
tq2440_nand_select();

while (i < len)
{
tq2440_nand_write_cmd(0x00);
tq2440_nand_write_addr(addr);
tq2440_nand_write_cmd(0x30);
tq2440_nand_wait_ready();
for (; (col < 2048) && (i < len); col++)
{
*p++ = (NFDATA & 0xFF);
i++;
addr++;
}
col = 0;
}
tq2440_nand_deselect();
}

int mymemcmp(const void* addr1, const void* addr2, unsigned int len)
{
char* p1 = (char *)addr1;
char* p2 = (char *)addr2;

while (len--)
{
if (*p1 != *p2)
return 1;
p1++;
p2++;
}
return 0;
}

#define TQ2440_UBOOT_COPYTO 0x30008000 /* 拷贝到内存中的地址,此值与CONFIG_SYS_TEXT_BASE一致 */
#define TQ2440_UBOOT_OFFSET 0x20000 /* u-boot在NAND中的偏移地址(NAND中的前128K存放的是u-boot-spl.bin) */
#define TQ2440_UBOOT_SIZE 0xE0000 /* u-boot的大小896K,多拷贝点没关系 */

void copy_uboot_to_sdram(void)
{
void (*jump)(void) = (void *)TQ2440_UBOOT_COPYTO;

GPBDAT &= ~(1 << 5);

if (is_boot_from_nor_flash())
{
int len = TQ2440_UBOOT_SIZE;
char* s = (char *)TQ2440_UBOOT_OFFSET;
char* d = (char *)TQ2440_UBOOT_COPYTO;
GPBDAT &= ~(1 << 6);
while (len--) *d++ = *s++;
GPBDAT &= ~(1 << 7);
GPBDAT &= ~(1 << 8);
}
else
{
tq2440_nand_init();
GPBDAT &= ~(1 << 6);
tq2440_nand_read(TQ2440_UBOOT_OFFSET, (void *)TQ2440_UBOOT_COPYTO, TQ2440_UBOOT_SIZE);
GPBDAT &= ~(1 << 7);
GPBDAT &= ~(1 << 8);
}

jump();
}

void tq2440_led_init(void)
{
GPBCON = 0x002956A9;
GPBUP = 0x000007FF;
GPBDAT = 0x000001E0; /* led off */
}

void clsbss(void)
{
extern int __bss_start;
extern int __bss_end;
int* p = &__bss_start;
while (p < &__bss_end) *p++ = 0;
}

void tq2440_board_init_f(void)
{
clsbss();
tq2440_led_init();
copy_uboot_to_sdram();
}

(四)修改汇编启动文件crt0.S:

​gedit arch/arm/lib/crt0.S​

找到:

mov r0, #0
bl board_init_f

替换为:

#ifndef CONFIG_SPL_BUILD
mov r0, #0
bl board_init_f
#else
bl tq2440_board_init_f
#endif

(五)将init.c添加进编译项:

​gedit board/samsung/tq2440/Makefile​

添加一行:

obj-$(CONFIG_SPL_BUILD) += init.o

(六)编译
1、首先make一下编译出u-boot.bin
2、然后打开SPL的编译项:

  • ​make menuconfig ---> SPL / TPL ---> 选中​
  • TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_#define_07

    TQ2440移植u-boot2016.11全过程记录-【7】NAND FLASH方式启动U-BOOT_初始化_08

  • 保存退出后,make一下,在spl目录下就编译出了u-boot-spl.bin文件。

将u-boot-spl.bin烧写到NAND中的0地址处,将u-boot.bin烧写到NAND的128K地址处,TQ2440开发板拨到NAND启动,重新上电即可成功启动。

注意:NAND启动无法识别NOR FLASH,此处问题先保留,回头解决。


【附加】:分别烧写u-boot-spl.bin和u-boot.bin很麻烦,所以我写了个小程序将这两个文件合并为一个文件。新建文件ubootmeger.c文件,粘贴进如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

uint8_t buffer[0x200000];

#define UBOOT_SPL_SIZE (128*1024)

int main(int argc, char* argv[])
{
int nread = 0;
int nwrite = 0;
FILE* fp1;
FILE* fp2;
FILE* fp3;

if(argc < 3)
{
printf("\r\nUsage:\r\n\tubootmerge [u-boot-spl.bin] [u-boot.bin] [output filename]\r\n");
printf("e.g.\r\n\tubootmerge u-boot-spl.bin u-boot.bin tq2440-uboot-2016.11.bin\r\n");
return 0;
}

fp1 = fopen(argv[1], "rb");
fp2 = fopen(argv[2], "rb");
fp3 = fopen(argv[3], "wb+");
if(!fp1 || !fp2 || !fp1)
{
fprintf(stderr, "Can not open file, please again!\r\n");
goto __exit;
}

memset(buffer, 0xFF, sizeof(buffer));

nread = fread(buffer, 1, UBOOT_SPL_SIZE, fp1);
if(nread < 0)
{
fprintf(stderr, "Can not read file %s, please again!\r\n", argv[1]);
goto __exit;
}
nwrite += UBOOT_SPL_SIZE;

nread = fread(buffer + nwrite, 1, 0x100000, fp2);
if(nread < 0)
{
fprintf(stderr, "Can not read file %s, please again!\r\n", argv[2]);
goto __exit;
}

nwrite += nread;
if(fwrite(buffer, 1, nwrite, fp3) != nwrite)
{
fprintf(stderr, "Can not write file %s, please again!\r\n", argv[3]);
goto __exit;
}

printf("---==> done !\r\n");
goto __exit;

__exit:
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}

使用gcc编译一下即可:

gcc -o ubootmeger ubootmeger.c

使用方法:ubootmeger接收三个参数,第一个参数为spl文件名,第二个参数为uboot的文件名,第三个参数为合并新uboot的文件名,例:
​​​./ubootmeger u-boot-spl.bin u-boot.bin tq2440-u-boot-v201611.bin​

文件tq2440-u-boot-v201611.bin即为最终版的u-boot,将该u-boot烧写进nor或者nand中0地址处即可。


ends…