TQ2440移植u-boot2016.11全過程記錄-【7】NAND FLASH方式啓動U-BOOT

TQ2440移植u-boot2016.11-NAND FLASH方式啓動U-BOOT


S3C2440 U-BOOT啓動方式說明:

        ~~~~~~~~NOR FLASH啓動: S3C2440會直接在NOR上運行程序,在NOR上中的代碼需要初始化芯片時鐘、初始化SDRAM。

        ~~~~~~~~NAND FLASH啓動: S3C2440上電後其內部的NAND FLASH控制器會自動把NAND FLASH中的前4K的代碼拷貝到S3C2440芯片自帶的RAM中,然後開始運行程序,因爲片上RAM只有4K的容量,所以這部分的代碼就負責初始化芯片時鐘、初始化SDRAM、初始化NAND,然後將u-boot的代碼拷貝到SDRAM中並跳轉運行。

        ~~~~~~~~目前主流的啓動方式爲NAND FLAHS或者SD卡啓動,NOR FLASH用的越來越少了,上二章節已經實現了NOR FLASH的啓動,本章節實現NAND FLASH的啓動。


        ~~~~~~~~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

在這裏插入圖片描述

(二)添加SPL編譯項

gedit arch/arm/Kconfig

找到config TARGET_TQ2440的配置項,在該配置項結尾加入一行:select SUPPORT_SPL,效果:
在這裏插入圖片描述

(三)添加拷貝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 ---> 選中
    在這裏插入圖片描述 在這裏插入圖片描述
    保存退出後,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…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章