全部博文(92)
分类: 嵌入式
2010-06-24 10:33:08
U-BOOT移植
我的经验就是从u-boot的启动过程和目录结构入手分析,掌握重要的数据结构,完成所需的设备移植。
1.U-BOOT的启动过程
U-Boot第一阶段启动过程
cpu/arm920t/start.s 完成硬件初始化
在flash中执行的引导代码,也就是bootloader中的stage1,负责初始化硬件环境,把u-boot从flash加载到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去执行。
1.1.6版本的start.s流程:
硬件环境初始化:
进入svc模式;关闭watch dog;屏蔽所有IRQ掩码;设置时钟频率FCLK、HCLK、PCLK;清I/D cache;禁止MMU和CACHE;配置memory control为第二阶段准备RAM空间(board/TX2440/lowlevel_init.s);
重定位:
如果当前代码不在连接指定的地址上,则需要把u-boot从当前位置拷贝到RAM指定位置中;
建立堆栈,堆栈是进入C函数前必须初始化的。
清.bss区。
跳到start_armboot函数中执行。(lib_arm/board.c)
U-Boot第二阶段启动过程
第二阶段代码:lib_arm/board.c中的start_armboot函数
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程:
void start_armboot (void)
{
//全局数据变量指针gd占用r8。
DECLARE_GLOBAL_DATA_PTR;
/* 给全局数据变量gd安排空间*/
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
memset ((void*)gd, 0, sizeof (gd_t));
/* 给板子数据变量gd->bd安排空间*/
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;//取u-boot的长度。
/* 顺序执行init_sequence数组中的初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的Flash */
size = flash_init ();
……
/* 初始化堆空间 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/* 重新定位环境变量, */
env_relocate ();
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口MAC 地址 */
……
devices_init (); /* 系统外围设备初始化 */
jumptable_init (); //跳转表初始化
console_init_r (); /* 完整地初始化控制台设备 */
enable_interrupts (); /* 使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;) {
main_loop (); /* 主循环函数处理执行用户命令 -- common/main.c */
}
}
初始化函数序列init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
init_fnc_t *init_sequence[] = {
cpu_init, /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板级相关配置 -- board/TX2440/ TX2440.c */
interrupt_init, /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /* 初始化环境变量 -- common/env_flash.c */
init_baudrate, /* 初始化波特率设置 -- lib_arm/board.c */
serial_init, /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /* 控制台初始化阶段1 -- common/console.c */
display_banner, /* 打印u-boot信息 -- lib_arm/board.c */
dram_init, /* 配置可用的RAM -- board/ TX2440/ TX2440.c */
display_dram_config, /* 显示RAM的配置大小 -- lib_arm/board.c */
NULL,
};
2.U-BOOT的重要数据结构
u-boot的主要功能是用于引导OS的,但是本身也提供许多强大的功能,可以通过输入命令行来完成许多操作。所以它本身也是一个很完备的系统。u-boot的大部分操作都是围绕它自身的数据结构,这些数据结构是通用的,但是不同的板子初始化这些数据就不一样了。所以u-boot的通用代码是依赖于这些重要的数据结构的。这里说的数据结构其实就是一些全局变量。
1)gd 全局数据变量指针,它保存了u-boot运行需要的全局数据,类型定义:
typedef struct global_data {
bd_t *bd; //board data pointor板子数据指针
unsigned long flags; //指示标志,如设备已经初始化标志等。
unsigned long baudrate; //串口波特率
unsigned long have_console; /* 串口初始化标志*/
unsigned long reloc_off; /* 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0 */
unsigned long env_addr; /* 环境参数地址*/
unsigned long env_valid; /* 环境参数CRC检验有效标志 */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
void **jt; /* 跳转表,1.1.6中用来函数调用地址登记 */
} gd_t;
2)bd 板子数据指针。板子很多重要的参数。 类型定义如下:
typedef struct bd_info {
int bi_baudrate; /* 串口波特率 */
unsigned long bi_ip_addr; /* IP 地址 */
unsigned char bi_enetaddr[6]; /* MAC地址*/
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* 启动参数 */
struct /* RAM 配置 */
{
ulong start;
ulong size;
}bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
3)环境变量指针 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义在common/environment.c中。
2.开始移植U-BOOT
2.1 修改Makefile
#Vim Makefile(我们是基于smdk2410开发板,修改配置的)
所以在smdk2410_config 下加入
TX2440_config : unconfig
@$(MKCONFIG) $ (@:_config=) arm arm920t TX2440 NULL s3c24x0
在第128行:
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
2.2 K4S561632 型号SDRAM的配置
修改SDRAM配置,在board/TX2440/lowlevel_init.S中,
#define B6_BWSCON (DW32) 位宽为32 (bank6接的是SDRAM,确定是32位)
Bank4上接了DM9000的网卡驱动
通过REFCNT寄存器的设置,设置SDRAM的刷新参数
由于我所用的开发板的HCLK设置为100Mhz,根据SDRAM芯片(K4S561632数据手册上注:64ms refresh period (8K Cycle))的参数计算REFCNT寄存器的值。
算下来SDRAM的刷新周期为7.8us
计算公式如下:
R_CNT=2^11+1-HCLK(MHz)*SDRAM刷新周期(us)=0x4f4
将 #define REFCNT 0x1113 改为 #define REFCNT 0x4f4
增加对S3C2440的支持,2440的时钟计算公式、NAND操作和2410不太一样。
对于2440开发板,将FCLK设为400MHz,分频比为FCLK:HCLK:PCLK=1:4:8。
2.3 TX2440的时钟设置和管脚设置
增加对S3C2440的支持,2440的时钟计算公式、NAND操作和2410不太一样。
对于2440开发板,将FCLK设为400MHz,分频比为FCLK:HCLK:PCLK=1:4:8。
修改board/TX2440/TX2440.c中的board_init函数
/* S3C2440: Mpll,Upll = (2*m * Fin) / (p * 2^s)
* m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2
*/
#define S3C2440_MPLL_400MHZ ((0x7f<<12)|(0x02<<4)|(0x01))
#define S3C2440_UPLL_48MHZ ((0x38<<12)|(0x02<<4)|(0x02))
#define S3C2440_CLKDIV 0x05 /* FCLK:HCLK:PCLK = 1:4:8 */
int board_init (void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;(地址信号和片选信号)
gpio->GPBCON = 0x00044555;(GPB0~GPB5,GPB7和GPB9为输出,GPB6,GPB8和GPB10为输入)
gpio->GPBUP = 0x000007FF;(GPB0~GPB10不使能)
gpio->GPCCON = 0xAAAAAAAA;( GPC0~GPC15为LCD的配置管脚)
gpio->GPCUP = 0x0000FFFF;(GPC0~GPC15不使能)
gpio->GPDCON = 0xAAAAAAAA; (GPD0~GPD15为LCD的配置管脚)
gpio->GPDUP = 0x0000FFFF;()
gpio->GPECON = 0xAAAAAAAA; (GPE15为IICSDA, GPE14为IICSCL, GPE13为SPICLK0, GPE12为SPIMOSI0, GPE11为SPIMISO0,剩下的为SD芯片配置 )
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA; (GPF0~GPF7为EINT0~EINT7)
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA; (SPI接口四大信号:MOSI, MISO, SCLK, nSS0, 其余为中断)
gpio->GPGUP = 0x0000FFFF;()
gpio->GPHCON = 0x002AFAAA;(串口0和串口1的nCTS, nRTS, TXD,RXD,还有时钟)
gpio->GPHUP = 0x000007FF;
/* FCLK:HCLK:PCLK = 1:4:8 */
clk_power->CLKDIVN = S3C2440_CLKDIV;
/* change to asynchronous bus mod */
__asm__( "mrc p15, 0, r1, c1, c0, 0\n" /* read ctrl register */
"orr r1, r1, #0xc0000000\n" /* Asynchronous */
"mcr p15, 0, r1, c1, c0, 0\n" /* write ctrl register */
:::"r1"
);
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure MPLL =FCLK=400MHZ*/
clk_power->MPLLCON = S3C2440_MPLL_400MHZ;
/* some delay between MPLL and UPLL */
delay (4000);
/* configure UPLL48MHZ FOR USB*/
clk_power->UPLLCON = S3C2440_UPLL_48MHZ;
/* some delay between MPLL and UPLL */
delay (8000);
/* arch number of SMDK2440-Board */
gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
dcache_enable();
return 0;
}
2.3支持K9F2G08大页NAND FLASH
2.3.1 增加CFG_CMD_NAND命令
首先在配置文件include/configs/TX2440.h的宏CONFIG_COMMANDS中增加CFG_CMD_NAND (大概在82行)
/*NAND flash settings*/
#define CFG_NAND_BASE 0 //无实际意义:基地址,在board_nand_init中重新定义
#define CFG_MAX_NAND_DEVICE 1 //NAND Flash设备数目为1
#define NAND_MAX_CHIPS 1 //每个NAND设备由1个NADN芯片组成
修改配置文件后再编译,只有一个错误了“board_nand_init”函数未定义
2.3.1 添加NAND FLASH数据结构和函数
在include/s3c24x0.h中增加S3C2440_NAND数据结构(168行)
/* NAND FLASH (see S3C2440 manual chapter 6) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} /*__attribute__((__packed__))*/ S3C2440_NAND;
在include/s3c2410.h中仿照S3C2410_GetBase_NAND函数(96行)
定义2440的函数:
static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
{
return (S3C2440_NAND * const)S3C2410_NAND_BASE;
}
2.3.2 添加NAND FLASH 驱动
在drivers目录下放置新的nand 和nand_legacy文件夹
并修改Makefile
在cpu\arm920t\s3c24x0目录下放置新的nand_flash.c
在include目录下放置新的nand.h