U-boot移植 (v2012.04.1 S3C2440平台)
一 概述
项目概述:
移植u-boot v2012.4.1到micro2440开发板(S3C2440平台)项目github地址:https://github.com/novawl/u-boot-v2010.4.1
预期目标:
1) 实现Nand flash Nor flash 烧写;2) 实现Nand flash 启动U-boot;
3) USB驱动移植,实现USB下载镜像;
4) 网络驱动支持;
5) 设置环境变量,实现Nor flash 和 Nand flash引导Linux kernel.
二 移植过程
1 准备工作
1) 安装ARM交叉编译工具:arm-linux-gcc 4.3.22) 下载u-boot代码:git clone git://git.denx.de/u-boot.git
2 配置micro2440编译选项,创建micro2440平台相关文件
1) 和之前的版本不同,平台编译选项不再是在顶层Makefile里添加,而是修改u-boot/boards.cfg文件。查看boards.cfg文件可以看到每一个平台由以下选项组成:
Target ARCH CPU Board name Vendor SoC Options
mx31ads arm arm1136 - freescale mx31
mx31pdk arm arm1136 - freescale mx31 mx31pdk:NAND_U_BOOT
tt01 arm arm1136 - hale mx31
smdk2410 arm arm920t - samsung s3c24x0
micro2440 arm arm920t - samsung s3c24x0
我们只需要按照smdk2410的配置,添加micro2440的配置即可。
2) 在u-boot/board/samsung目录创建micro2440目录,复制smdk2410下的全部文件到此目录。复制u-boot/include/configs/smdk2410.h到u-boot/include/configs/micro2440.h。
3) 修改arch/arm/config.mk,设置CROSS_COMPILE ?= arm-linux- (此处依据交叉工具链具体位置设定)
4) 执行make micro2440_config && make all,此时会在u-boot目录下生成u-boot.bin等文件,不过这是针对smdk2410平台的,想生成micro2440平台的,还需要对board/samsung/micro2440下的文件和include/configs/micro2440.h进行修改。
Target ARCH CPU Board name Vendor SoC Options
mx31ads arm arm1136 - freescale mx31
mx31pdk arm arm1136 - freescale mx31 mx31pdk:NAND_U_BOOT
tt01 arm arm1136 - hale mx31
smdk2410 arm arm920t - samsung s3c24x0
micro2440 arm arm920t - samsung s3c24x0
我们只需要按照smdk2410的配置,添加micro2440的配置即可。
2) 在u-boot/board/samsung目录创建micro2440目录,复制smdk2410下的全部文件到此目录。复制u-boot/include/configs/smdk2410.h到u-boot/include/configs/micro2440.h。
3) 修改arch/arm/config.mk,设置CROSS_COMPILE ?= arm-linux- (此处依据交叉工具链具体位置设定)
4) 执行make micro2440_config && make all,此时会在u-boot目录下生成u-boot.bin等文件,不过这是针对smdk2410平台的,想生成micro2440平台的,还需要对board/samsung/micro2440下的文件和include/configs/micro2440.h进行修改。
3 u-boot 调试
在对u-boot进行移植的前期,由于系统经常会在启动阶段挂掉,此时串口还未初始化,没有信息输出,调试起来非常不方便。此时我们最好使用调试工具进行调试,我采用的是OpenJTAG。关于OpenJTAG,openocd, arm-linux-gdb的使用方法,可以参考相关手册。这里只说调试期间需要注意的几个问题:
1) 在micro2440.h中添加 #define CONFIG_SKIP_LOWLEVEL_INIT: 由于openocd会加载u-boot镜像到开发板中运行,之前会对SDRAM进行初始化,所以u-boot在内存里执行时,不再需要执行lowlevel_init.S中的代码来对储存器进行初始化了。
2) 修改micro2440.h中CONFIG_SYS_TEXT_BASE:#define CONFIG_SYS_TEXT_BASE 0x33D80000
arm-linux-gdb会根据TEXT_BASE的值将u-boot镜像加载到SDRAM中对应地方。对于该版本的u-boot,在初始化过程中,会把u-boot代码拷贝到SDRAM末端,所以我们在定义TEXT_BASE值时,不能定义得太靠后,否则代码会overlap。(之前我把CONFIG_SYS_TEXT_BASE定义为0x33F80000就出现过这种情况)。
3) 注意arm-linux-gdb的版本:由于我的交叉编译工具为4.3.2版本的,刚开始用老版本的arm-linux-gdb工具调试时,指令总是跑飞,根本执行不到内存中的u-boot代码。后来下载最新的gdb代码,重新编译最新的arm-linux-gdb才解决此问题。
4 修改micro2440平台相关文件
1) 修改u-boot/include/configs/micro2440.h:
#define CONFIG_S3C2410 改为 #define CONFIG_S3C2440
#define CONFIG_SMDK2410 改为 #define CONFIG_MICRO2440。
2) 修改u-boot/board/samsung/micro2440/lowlevel_init.S。该文件的作用是配置储存控制器,依据开发板对应的储存映射来修改相关寄存器。对于micro2440开发板,该文件如下:
#include <config.h>
#include <version.h>
/* some parameters for the board */
/*
*
* Taken from linux/arch/arm/boot/compressed/head-s3c2410.S
*
* Copyright (C) 2002 Samsung Electronics SW.LEE <hitchcar@sec.samsung.com>
*
*/
#define BWSCON 0x48000000
/* BWSCON */
#define DW8 (0x0)
#define DW16 (0x1)
#define DW32 (0x2)
#define WAIT (0x1<<2)
#define UBLB (0x1<<3)
#define B1_BWSCON (DW16)
#define B2_BWSCON (DW16)
#define B3_BWSCON (DW16)
#define B4_BWSCON (DW16)
#define B5_BWSCON (DW8)
#define B6_BWSCON (DW32)
#define B7_BWSCON (DW32)
/* BANK0CON */
#define B0_Tacs 0x0 /* 0clk */
#define B0_Tcos 0x0 /* 0clk */
#define B0_Tacc 0x7 /* 14clk */
#define B0_Tcoh 0x0 /* 0clk */
#define B0_Tah 0x0 /* 0clk */
#define B0_Tacp 0x0
#define B0_PMC 0x0 /* normal */
/* BANK1CON */
#define B1_Tacs 0x0 /* 0clk */
#define B1_Tcos 0x0 /* 0clk */
#define B1_Tacc 0x7 /* 14clk */
#define B1_Tcoh 0x0 /* 0clk */
#define B1_Tah 0x0 /* 0clk */
#define B1_Tacp 0x0
#define B1_PMC 0x0
#define B2_Tacs 0x0
#define B2_Tcos 0x0
#define B2_Tacc 0x7
#define B2_Tcoh 0x0
#define B2_Tah 0x0
#define B2_Tacp 0x0
#define B2_PMC 0x0
#define B3_Tacs 0x0 /* 0clk */
#define B3_Tcos 0x0 /* 0clk */
#define B3_Tacc 0x7 /* 14clk */
#define B3_Tcoh 0x0 /* 0clk */
#define B3_Tah 0x0 /* 0clk */
#define B3_Tacp 0x0
#define B3_PMC 0x0
#define B4_Tacs 0x0 /* 0clk */
#define B4_Tcos 0x0 /* 0clk */
#define B4_Tacc 0x7 /* 14clk */
#define B4_Tcoh 0x0 /* 0clk */
#define B4_Tah 0x0 /* 0clk */
#define B4_Tacp 0x0
#define B4_PMC 0x0 /* normal */
#define B5_Tacs 0x0 /* 0clk */
#define B5_Tcos 0x0 /* 0clk */
#define B5_Tacc 0x7 /* 14clk */
#define B5_Tcoh 0x0 /* 0clk */
#define B5_Tah 0x0 /* 0clk */
#define B5_Tacp 0x0
#define B5_PMC 0x0 /* normal */
#define B6_MT 0x3 /* SDRAM */
#define B6_Trcd 0x1
#define B6_SCAN 0x1 /* 9bit */
#define B7_MT 0x3 /* SDRAM */
#define B7_Trcd 0x1 /* 3clk */
#define B7_SCAN 0x1 /* 9bit */
/* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x1 /* 3clk */
#define Tsrc 0x3 /* 7clk */
#define REFCNT 0x4f4 /* period=7.8125, HCLK=100Mhz, (2048+1-7.8125*100) */
/**************************************/
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+REFCNT)
.word 0x31
.word 0x30
.word 0x30
3) 修改u-boot/boards/samsung/micro2440/micro2440.c: 该文件是与开发析配置相关的,对于micro2440来说,主要需要修改的是board_early_init_f函数,该函数主是要设置系统时钟频率和GPIO。由于S3C2410和S3C2440时钟设置方法不一样,我们需要对该函数针对S3C2440进行修改:
#define M_MDIV 0x5C
#define M_PDIV 0x1
#define M_SDIV 0x1
#define U_M_MDIV 0x38
#define U_M_PDIV 0x2
#define U_M_SDIV 0x2
/*
* Miscellaneous platform dependent initialisations
*/
int board_early_init_f(void)
{
struct s3c24x0_clock_power * const clk_power =
s3c24x0_get_base_clock_power();
struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
writel(0x5, &clk_power->clkdivn);
/* to reduce PLL lock time, adjust the LOCKTIME register */
writel(0xFFFFFFFF, &clk_power->locktime);
/* configure MPLL */
writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
/* some delay between MPLL and UPLL */
pll_delay(4000);
/* configure UPLL */
writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV,
&clk_power->upllcon);
/* some delay between MPLL and UPLL */
pll_delay(8000);
/* set up the I/O ports */
writel(0x007FFFFF, &gpio->gpacon);
writel(0x00044555, &gpio->gpbcon);
writel(0x000007FF, &gpio->gpbup);
writel(0xAAAAAAAA, &gpio->gpccon);
writel(0x0000FFFF, &gpio->gpcup);
writel(0xAAAAAAAA, &gpio->gpdcon);
writel(0x0000FFFF, &gpio->gpdup);
writel(0xAAAAAAAA, &gpio->gpecon);
writel(0x0000FFFF, &gpio->gpeup);
writel(0x000055AA, &gpio->gpfcon);
writel(0x000000FF, &gpio->gpfup);
writel(0xFF95FFBA, &gpio->gpgcon);
writel(0x0000FFFF, &gpio->gpgup);
writel(0x002AFAAA, &gpio->gphcon);
writel(0x000007FF, &gpio->gphup);
return 0;
}
这里需要注意的是对分频比clk_power->clkdivn的设置:因为这个值在arch/arm/cpu/arm920t/start.S里有过一次设置(那里设置的是3),默认情况下,这里可以不用设置的。但我针对S3C2440修改了分频设置,CLKDIV要设置成5,所以这里必须要设置下。(一开始我就由于CLKDIV没有设置正确,造成系统挂掉。)
4) 此时再执行make micro2440_config && make all,将生成的u-boot.bin烧入micro2440开发Nor flash中,应该就能看到串口有信息输出了,不过因为还没有配置Nor flash,会看到flash error,系统挂掉。
5 NAND Flash 启动支持
对于老版的u-boot, 由于Nor flash支持读取代码执行,所以u-boot 默认是烧写进Nor flash启动的。想要在Nand flash中启动u-boot,需要在启动阶段将u-boot拷贝到内存中执行才行。
而最新版本的u-boot在启动第一阶段时,会将u-boot代码重定向到sdram里运行,具体过程如下:
① 在start.S中设置完CPU后,接着调用arch/arm/lib/board.c中的board_init_f做第一阶段平台的初始化工作(timer_init, env_init, ram_init... 计算堆栈addr_sp,设置gd结构体,计算出重定向u-boot的位置addr)。
② 执行完board_init_f后,会调用relocate_code(addr_sp, id, addr)返回start.S, 执行relocate_code的代码,拷贝u-boot代码到内存addr位置。
对于这个版本的u-boot,我实现同时支持Nor flash和Nand flash启动的方法是:在启动的开始阶段,初始化SDRAM后,通过CopyCode2Ram函数(CopyCode2Ram能够分辨代码是在Nor flash中还是在Nand flash中,从而采用不同的拷贝方法,具体实现在board/samsung/micro2440/nand.c中),将u-boot代码拷贝到TEXT_BASE指定的内存区域。然后再执行board_init_f和relocate_code。此时relocate_code不再是从flash中拷贝u-boot代码到内存了,而是从内存中TEXT_BASE处将代码拷贝到board_init_f中计算的addr位置。
这样,不管是从Nor flash还是Nand flash启动,一开始时就通过Copycode2Ram将u-boot代码复制到内存中,然后就可以顺利的执行后面的board_init_f和relocate_code了,简单方便的实现了同时支持Nor flash和Nand flash启动的功能。
u-boot第一阶段启动时,重定向代码示意图
下面是具体实现过程:
1) 修改include/configs/micro2440.h,设置TEXT_BASE
#define CONFIG_SYS_TEXT_BASE 0x33D80000
这里要注意,board_init_f计算出来的relocate addr一般是内存的末尾,所以此处的TEXT_BASE定义得不能太靠后,不然会出现代码拷贝过程中的overlap。(以前老版本的u-boot在内存中运行时,习惯将u-boot拷贝到0x33F80000处,这次我刚开始也设定的是这个值,结果第二次relocate_code拷贝代码时,就覆盖了先前的代码,造成系统错误。)
2) 修改arch/arm/cpu/u-boot.lds,将Copycode2Ram的代码放在.text段的最前面:
由于micro2440在通过Nand flash启动时,只会将flash中前4k的代码复制到内部ram中执行,所以我们在第一阶段拷贝代码时,必须保证执行拷贝操作的代码在.text段的前4k。这个可以通过修改u-boot.lds文件实现。
.text :
{
__image_copy_start = .;
CPUDIR/start.o (.text)
board/samsung/micro2440/libmicro2440.o (.text)
*(.text)
}
3) 修改arch/arm/cpu/arm920t/start.S:
这里,我们可以把设置系统时钟,初始化MPLL的操作提到前面来做,设置完时钟后再执行代码复制的操作,这样可以加快代码复制的速度。
在start.S的call_board_init_f前添加下面一段代码:
#ifdef CONFIG_MICRO2440
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
bl board_early_init_f /* configure MPLL UPLL */
ldr r0, =0x0
ldr r1, =(CONFIG_SYS_TEXT_BASE)
ldr r2, _end_ofs
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
ldr pc, =call_board_init_f /* jump to SDRAM */
#endif
6 NAND Flash 启动支持 ⇒ 改进
按照上节描述的方法实现同时支持从Nor flash 和Nand flash启动后,在后面做usbslave download时,发现这种实现方法有缺陷(这个缺陷应该是u-boot官方code自带的?)。问题出现在中断处理过程里,我们先来看一下start.S中的中断向量和中断处理例程(假设链接地址_TEXT_BASE=0x0):
.globl _start
_start: b start_code
ldr pc, _undefined_instruction /* 当产生中断或异常时,Arm内核会跳转到0x0地址
ldr pc, _software_interrupt * 开始处按照中断(或异常)的类型偏移一定地址执行
ldr pc, _prefetch_abort * 跳转指令。
ldr pc, _data_abort */
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq /*此处,编译时确定_irq的值:irq = _TEXT_BASE + irq_ofs */
_fiq: .word fiq
需要注意的是,此处_irq的值是编译时确定的,为_TEXT_BASE加上irq函数相对于_TEXT_BASE的偏移。当u-boot执行relocate函数,把u-boot代码重定向到内存中后,若有中断发生,会执行0x0开始的中断跳转指令。由于重定向时,并未更改0x0开始处的_irq, _fiq等label的值,此处的跳转仍然会跳转到重定向前的地址执行。(执行relocate时,在relocate .rel.dyn段时,会重新计算.rel.dyn段内汇编代码中Label的值。参见http://blog.csdn.net/caiyuqing2001/article/details/7377925)
所以在这种情况下执行中断处理例程时,执行的是_TXET_BASE为0x0,重定向前的代码,引用的汇编代码内的Label和C语言的全局变更也是重定向前的。而我们在初始化中断时,初始化的Label和全局变量是重写向后的变量(中断初始化时,代码已经重定向了),所以这种情况下执行的中断处理例程用到的变量全是未初始化的,会导致系统出错。
解决这个问题有两个方法:
1) 运行时根据重定向后中断处理例程的实际地址,更改0x0开始处的中断跳转地址。
若u-boot从Nand flash启动,0x0开始的4K的地址是在内部ram里的,此处的中断向量地址可以直接修改;但若是从Nor flash启动时,0x0开始处的地址是在Nor flash中的,此处的中断向量不能直接修改,得按照Nor flash的写时序来操作。所以想要同时支持Nor flash和Nand flash 启动,这种方法不可行。
2) 在运行过程中指定中断向量跳转地址。
中断向量跳转可以写成这种形式:ldr pc, =CONFIG_VECTOR_TABLE+0x08*irq_no。其中CONFIG_VECTORY_TABLE是设定的内存中的中断向量基地址,这样,relocate代码后在初始化中断过程中,可以根据中断处理函数的最终地址来设置中断向量表:
#define CONFIG_VECTOR_TABLE 0x33FFFFC0
#define INSTRUCTION_LDR_PC 0xE51FF004 /*ldr pc, [pc, #4]的指令代码*/
unsigned long int p_vect_table;
p_vect_table = (unsigned long int *) (CONFIG_VECTOR_TABLE + irq_no<<3);
p_vect_table++ = INSTRUCTION_LDR_PC;
p_vect_table = irq_handle;
3) 移除掉u-boot的重定向代码的功能,初始化SDRAM后,直接把u-boot代码拷贝至_TEXT_BASE处(假设_TEXT_BASE=0x33F40000),不再进行代码的重定向。这样发生中断时,跳转到的就是代码在内存中的最终地址,便没有上述问题了。
我采用的是第3种方法,这样在启动开始时,只需要复制一次代码就行了,实现也较简单。
① 修改arch/arm/config.mk,去除掉-pie链接选项,这样就不会生成.rel.dyn段了
# needed for relocation
#ifndef CONFIG_NAND_SPL
#LDFLAGS_u-boot += -pie
#endif
② 移除掉arch/arm/cpu/arm920t/start.S中的relocate代码和clear_bss代码:
#ifndef CONFIG_MICRO2440
adr r0, _start
cmp r0, r6
beq clear_bss /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
…
fixnext:
str r1, [r0]
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
#endif
#endif /* !CONFIG_MICRO2440 */
…
clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r4, =_TEXT_BASE /* reloc addr */
add r0, r0, r4
add r1, r1, r4
…
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
#ifndef CONFIG_MICRO2440
add lr, lr, r9
#endif
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
③ 修改arch/arm/lib/board.c的board_init_f函数:
…
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
#ifdef CONFIG_MICRO2440
addr -= CONFIG_SYS_U_BOOT_SIZE;
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
addr);
#endif
#endif
...
#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR;
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */
#ifndef CONFIG_MICRO2440
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~(4096 - 1);
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
#else
addr &= ~(4096 - 1);
#endif
…
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
#ifdef CONFIG_MICRO2440
gd->relocaddr = CONFIG_SYS_TEXT_BASE;
gd->start_addr_sp = addr_sp;
gd->reloc_off = 0;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof(gd_t));
relocate_code(addr_sp, id, addr);
#else
gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof(gd_t));
relocate_code(addr_sp, id, addr);
#endif
7 NOR Flash(SSTVF1601) 支持
在u-boot中添加对Nor flash的支持比较简单,大多数Nor flash都支持CFI接口,而u-boot有对cfi flash的驱动支持。对于SSTVF1601,并不支持标准的CFI接口,所以得使用JEDEC接口。但jedec_flash.c中并没有SSTVF1601的配置信息,所以得手动添加上:
drivers/mtd/jedec_flash.c:
static const struct amd_flash_info jedec_table[] = {
…
#ifdef CONFIG_SYS_FLASH_LEGACY_1024Kx16
{
.mfr_id = (u16)SST_MANUFACT,
.dev_id = SST39VF1601,
.name = "SST 39VF1601",
.uaddr = {
[1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x1000,96),
ERASEINFO(0x1000,160),
ERASEINFO(0x1000,240),
ERASEINFO(0x1000,16),
}
},
#endif
};
修改micro2440.h:
/*-----------------------------------------------------------------------
* FLASH and environment organization
*/
#define CONFIG_SYS_FLASH_CFI
#define CONFIG_FLASH_CFI_DRIVER
#define CONFIG_FLASH_CFI_LEGACY
#define CONFIG_SYS_FLASH_LEGACY_1024Kx16
#define CONFIG_FLASH_SHOW_PROGRESS 45
#define CONFIG_SYS_MAX_FLASH_BANKS 1
#define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE }
#define CONFIG_SYS_MAX_FLASH_SECT (512)
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x1f0000)
#define CONFIG_ENV_IS_IN_FLASH
#define CONFIG_ENV_SIZE 0x10000
7 NAND Flash支持
u-boot的driver/mtd/nand下有s3c2410 nand控制器的驱动,并没有s3c2440的。但2410和2440的nand控制器差别并不大,我们可以在2410的基础上进行移植。具体代码可以在我的github(https://github.com/novawl/u-boot-v2010.4.1)下的drivers/mtd/nand/s3c2440_nand.c中查看。需要注意的是,hwcontrol函数中,当cmd不为命令时,得将IO_ADDR_W赋值为nfdata地址,不然数据将无法写入到nand flash中。
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2440_ADDR_NALE;
if (cmd == NAND_CMD_NONE)
IO_ADDR_W = &nand->nfdata;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~S3C2440_NFCONT_nFCE,
&nand->nfcont);
else
writel(readl(&nand->nfcont) | S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
修改arch/arm/lib/board.c,当从Nor flash启动时才初始化nor flash,否则只初始化nand flash:
#if !defined(CONFIG_SYS_NO_FLASH)
static char *failed = "*** failed ***\n";
#ifdef CONFIG_MICRO2440
extern int BootFrmNORFlash(); /*在board/samsung/micro2440/nand.c中实现*/
#endif
#endif
void board_init_r(gd_t *id, ulong dest_addr)
…
#if !defined(CONFIG_SYS_NO_FLASH)
#ifdef CONFIG_MICRO2440
if (BootFrmNORFlash()) {
#endif
puts("Flash: ");
flash_size = flash_init();
if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
char *s = getenv("flashchecksum");
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
if (s && (*s == 'y')) {
printf(" CRC: %08X", crc32(0,
(const unsigned char *) CONFIG_SYS_FLASH_BASE,
flash_size));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#ifdef CONFIG_MICRO2440
}
#endif
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif
ECC校验:
s3c2410和s3c2440 Nand flash控制器ECC校验模块有很大差别,所以基于s3c2440_nand.c的ecc校验函数和s3c2410_nand.c有较大差别。下面是与ECC校验相关的三个函数的实现:
#ifdef CONFIG_S3C2440_NAND_HWECC
void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
writel(readl(&nand->nfcont) | S3C2440_NFCONT_INITECC, &nand->nfcont);
}
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
unsigned long ecc = readl(&nand->nfecc0);
ecc_code[0] = ecc;
ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16;
debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
printf("s3c2440_nand_correct_data: not implemented\n");
return -1;
}
#endif
micro2440.h中与ECC相关的配置:
#define CONFIG_S3C2440_NAND_HWECC
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 3
还有一个需要注意的地方是新版的u-boot中s3c24x0.h中定义的struct s3c2440_nand结构体定义并不全面,并没有nfmecc0 和nfmecc1等寄存器的实现,这里需要添加上:
#ifdef CONFIG_S3C2440
/* NAND FLASH (see S3C2440 manual chapter 6) */
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfeccd0;
u32 nfeccd1;
u32 nfeccd;
u32 nfstat;
u32 nfstat0;
u32 nfstat1;
u32 nfecc0;
u32 nfecc1;
u32 nfecc;
};
#endif
8 usbslave 下载功能实现
网上有很多关于在u-boot中添加usbslave功能的教程,我主要是参照了一位叫tekkamanninja的网友的实现,在u-boot-v2012.04上实现了usbslave下载功能。下面是我移植的步骤:
1) 添加driver/usb/slave目录:
这个目录里的代码是usbslave的驱动具体实现,我是从tekkamanninja的github上下载的代码,将driver/usb/slave里的代码拷贝过来的。由于新u-boot里2440寄存器结构体的变量名全部改成小写了,所以需要将slave目录下关于寄存器的变量全改成小写的。具体代码可以查看我的github目录。
然后再修改顶层Makefile,加入对usbslave驱动的编译:
LIBS += drivers/usb/phy/libusb_phy.o
LIBS += drivers/usb/slave/libusb_slave.o
LIBS += drivers/usb/ulpi/libusb_ulpi.o
LIBS += drivers/video/libvideo.o
2) 修改arch/arm/cpu/arm920t/s3c24x0/interrupts.c,加入arch_interrupt_init
的实现:
void do_irq (struct pt_regs *pt_regs)
{
struct s3c24x0_interrupt *irq = s3c24x0_get_base_interrupt();
readl(&irq->intpnd);
}
int arch_interrupt_init (void)
{
return 0;
}
3) 修改arch/arm/cpu/arm920t/start.S:
#ifdef CONFIG_USE_IRQ
.align 5
irq:
/*
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
*/
sub lr, lr, #4
ldr sp, IRQ_STACK_START
stmdb sp!, {r0-r12, lr}
ldr lr, =int_return
ldr pc, =IRQ_Handle
int_return:
ldmia sp!, {r0-r12, pc}^
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
4) 修改arch/arm/lib/board.c,在init_board_r函数中加入usbslave的初始化操作:
/* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts();
#ifdef CONFIG_USB_DEVICE
usb_init_slave();
#endif
5) 修改board/samsung/micro2440/micro2440.c中的gpio初始化部分,设置gpc5引脚为usb_en功能:
/* set up the I/O ports */
writel(0x007FFFFF, &gpio->gpacon);
writel(0x00044555, &gpio->gpbcon);
writel(0x000007FF, &gpio->gpbup);
writel(0xAAAAA6AA, &gpio->gpccon);
writel(0x0000FFFF, &gpio->gpcup);
writel(readl(&gpio->gpcdat)&~(1<<5), &gpio->gpcdat);
writel(0xAAAAAAAA, &gpio->gpdcon);
writel(0x0000FFFF, &gpio->gpdup);
writel(0xAAAAAAAA, &gpio->gpecon);
writel(0x0000FFFF, &gpio->gpeup);
writel(0x000055AA, &gpio->gpfcon);
writel(0x000000FF, &gpio->gpfup);
writel(0xFF95FFBA, &gpio->gpgcon);
writel(0x0000FFFF, &gpio->gpgup);
writel(0x002AFAAA, &gpio->gphcon);
writel(0x000007FF, &gpio->gphup);
6) 加入cmd_usbslave支持:
修改common/Makefile,添加COBJS-$(CONFIG_USB_DEVICE) += cmd_usbslave.o,cmd_usbslave.c的具体实现可以查看我的github.
7) 修改micro2440.h,加入IRQ和usb device的配置:
/* input clock of PLL (the MICRO2440 has 12MHz input clock) */
#define CONFIG_SYS_CLK_FREQ 12000000
//#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */
#define CONFIG_CMDLINE_TAG /* enable passing of ATAGs */
…
/************************************************************
* USB support (currently only works with D-cache off)
************************************************************/
#define CONFIG_USB_OHCI
#define CONFIG_USB_KEYBOARD
#define CONFIG_USB_STORAGE
#define CONFIG_DOS_PARTITION
#define CONFIG_USB_DEVICE 1
#ifdef CONFIG_USB_DEVICE
#define CONFIG_USE_IRQ 1
#endif
8) 最后是对头文件s3c24x0.h的修改:
这里需要注意的是新版的u-boot s3c2440的struct s3c24x0_dma的定义是有问题的,我们需要对该结构体的定义针对2440加以修改。我最开始时参照网上的教程移植好usbslave后,发现usb传输的dma中断怎么也收不到,调试了好久才发现的这个问题。
/* DMAS (see manual chapter 8) */
struct s3c24x0_dma {
u32 disrc;
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
u32 disrcc;
#endif
u32 didst;
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
u32 didstc;
#endif
u32 dcon;
u32 dstat;
u32 dcsrc;
u32 dcdst;
u32 dmasktrig;
#if defined(CONFIG_S3C2400)
u32 res[1];
#endif
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
u32 res[7];
#endif
};
…
struct s3c24x0_usb_device {
…
#else /* little endian */
u8 func_addr_reg;
u8 res1[3];
u8 pwr_reg;
u8 res2[3];
u8 ep_int_reg;
u8 res3[15];
u8 usb_int_reg;
u8 res4[3];
u8 ep_int_en_reg;
u8 res5[15];
u8 usb_int_en_reg;
u8 res6[3];
u8 frame_num1_reg;
u8 res7[3];
u8 frame_num2_reg;
u8 res8[3];
u8 index_reg;
u8 res9[7];
u8 maxp_reg;
u8 res10[3];
u8 ep0_csr_in_csr1_reg;
u8 res11[3];
u8 in_csr2_reg;
u8 res12[7];
u8 out_csr1_reg;
u8 res13[3];
u8 out_csr2_reg;
u8 res14[3];
u8 out_fifo_cnt1_reg;
u8 res15[3];
u8 out_fifo_cnt2_reg;
u8 res16[3];
#endif /* __BIG_ENDIAN */
u32 res17[8];
struct s3c24x0_usb_dev_fifos fifo[5];
u32 res18[11];
//struct s3c24x0_usb_dev_dmas dma[5];
struct s3c24x0_usb_dev_dmas ep1;
struct s3c24x0_usb_dev_dmas ep2;
u8 res19[16];
struct s3c24x0_usb_dev_dmas ep3;
struct s3c24x0_usb_dev_dmas ep4;
};
9 DM9000 网卡驱动支持
u-boot-v2012.04对DM9000网上的支持已经比较完善的,只需要在配置里加上DM9000的编译选项,并在启动时加上DM9000的初始化就行了。
include/configs/micro2440.h:
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT 1
board/samsung/micro2440/micro2440.c:
#ifdef CONFIG_CMD_NET
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
#endif
10 YAFFS 文件系统烧写支持
新版的u-boot对yaffs文件系统的支持已经很完善了,不过要支持烧写yaffs文件系统,还是要进行一些修改。
修改include/configs/micro2440.h:
/*
* File system
*/
#define CONFIG_CMD_FAT
#define CONFIG_CMD_EXT2
#define CONFIG_CMD_UBI
#define CONFIG_CMD_UBIFS
#define CONFIG_CMD_MTDPARTS
#define CONFIG_CMD_NAND_YAFFS
#define CONFIG_MTD_DEVICE
修改common/cmd_nand.c:
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_YAFFS_OOB);
#endif
修改driver/mtd/nand/nand_util.c:
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
#ifndef CONFIG_CMD_NAND_YAFFS
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write (nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
#endif
…
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
WATCHDOG_RESET();
1) 在micro2440.h中添加 #define CONFIG_SKIP_LOWLEVEL_INIT: 由于openocd会加载u-boot镜像到开发板中运行,之前会对SDRAM进行初始化,所以u-boot在内存里执行时,不再需要执行lowlevel_init.S中的代码来对储存器进行初始化了。
2) 修改micro2440.h中CONFIG_SYS_TEXT_BASE:#define CONFIG_SYS_TEXT_BASE 0x33D80000
arm-linux-gdb会根据TEXT_BASE的值将u-boot镜像加载到SDRAM中对应地方。对于该版本的u-boot,在初始化过程中,会把u-boot代码拷贝到SDRAM末端,所以我们在定义TEXT_BASE值时,不能定义得太靠后,否则代码会overlap。(之前我把CONFIG_SYS_TEXT_BASE定义为0x33F80000就出现过这种情况)。
3) 注意arm-linux-gdb的版本:由于我的交叉编译工具为4.3.2版本的,刚开始用老版本的arm-linux-gdb工具调试时,指令总是跑飞,根本执行不到内存中的u-boot代码。后来下载最新的gdb代码,重新编译最新的arm-linux-gdb才解决此问题。
4 修改micro2440平台相关文件
1) 修改u-boot/include/configs/micro2440.h:
#define CONFIG_S3C2410 改为 #define CONFIG_S3C2440
#define CONFIG_SMDK2410 改为 #define CONFIG_MICRO2440。
2) 修改u-boot/board/samsung/micro2440/lowlevel_init.S。该文件的作用是配置储存控制器,依据开发板对应的储存映射来修改相关寄存器。对于micro2440开发板,该文件如下:
#include <config.h>
#include <version.h>
/* some parameters for the board */
/*
*
* Taken from linux/arch/arm/boot/compressed/head-s3c2410.S
*
* Copyright (C) 2002 Samsung Electronics SW.LEE <hitchcar@sec.samsung.com>
*
*/
#define BWSCON 0x48000000
/* BWSCON */
#define DW8 (0x0)
#define DW16 (0x1)
#define DW32 (0x2)
#define WAIT (0x1<<2)
#define UBLB (0x1<<3)
#define B1_BWSCON (DW16)
#define B2_BWSCON (DW16)
#define B3_BWSCON (DW16)
#define B4_BWSCON (DW16)
#define B5_BWSCON (DW8)
#define B6_BWSCON (DW32)
#define B7_BWSCON (DW32)
/* BANK0CON */
#define B0_Tacs 0x0 /* 0clk */
#define B0_Tcos 0x0 /* 0clk */
#define B0_Tacc 0x7 /* 14clk */
#define B0_Tcoh 0x0 /* 0clk */
#define B0_Tah 0x0 /* 0clk */
#define B0_Tacp 0x0
#define B0_PMC 0x0 /* normal */
/* BANK1CON */
#define B1_Tacs 0x0 /* 0clk */
#define B1_Tcos 0x0 /* 0clk */
#define B1_Tacc 0x7 /* 14clk */
#define B1_Tcoh 0x0 /* 0clk */
#define B1_Tah 0x0 /* 0clk */
#define B1_Tacp 0x0
#define B1_PMC 0x0
#define B2_Tacs 0x0
#define B2_Tcos 0x0
#define B2_Tacc 0x7
#define B2_Tcoh 0x0
#define B2_Tah 0x0
#define B2_Tacp 0x0
#define B2_PMC 0x0
#define B3_Tacs 0x0 /* 0clk */
#define B3_Tcos 0x0 /* 0clk */
#define B3_Tacc 0x7 /* 14clk */
#define B3_Tcoh 0x0 /* 0clk */
#define B3_Tah 0x0 /* 0clk */
#define B3_Tacp 0x0
#define B3_PMC 0x0
#define B4_Tacs 0x0 /* 0clk */
#define B4_Tcos 0x0 /* 0clk */
#define B4_Tacc 0x7 /* 14clk */
#define B4_Tcoh 0x0 /* 0clk */
#define B4_Tah 0x0 /* 0clk */
#define B4_Tacp 0x0
#define B4_PMC 0x0 /* normal */
#define B5_Tacs 0x0 /* 0clk */
#define B5_Tcos 0x0 /* 0clk */
#define B5_Tacc 0x7 /* 14clk */
#define B5_Tcoh 0x0 /* 0clk */
#define B5_Tah 0x0 /* 0clk */
#define B5_Tacp 0x0
#define B5_PMC 0x0 /* normal */
#define B6_MT 0x3 /* SDRAM */
#define B6_Trcd 0x1
#define B6_SCAN 0x1 /* 9bit */
#define B7_MT 0x3 /* SDRAM */
#define B7_Trcd 0x1 /* 3clk */
#define B7_SCAN 0x1 /* 9bit */
/* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x1 /* 3clk */
#define Tsrc 0x3 /* 7clk */
#define REFCNT 0x4f4 /* period=7.8125, HCLK=100Mhz, (2048+1-7.8125*100) */
/**************************************/
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+REFCNT)
.word 0x31
.word 0x30
.word 0x30
3) 修改u-boot/boards/samsung/micro2440/micro2440.c: 该文件是与开发析配置相关的,对于micro2440来说,主要需要修改的是board_early_init_f函数,该函数主是要设置系统时钟频率和GPIO。由于S3C2410和S3C2440时钟设置方法不一样,我们需要对该函数针对S3C2440进行修改:
#define M_MDIV 0x5C
#define M_PDIV 0x1
#define M_SDIV 0x1
#define U_M_MDIV 0x38
#define U_M_PDIV 0x2
#define U_M_SDIV 0x2
/*
* Miscellaneous platform dependent initialisations
*/
int board_early_init_f(void)
{
struct s3c24x0_clock_power * const clk_power =
s3c24x0_get_base_clock_power();
struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
writel(0x5, &clk_power->clkdivn);
/* to reduce PLL lock time, adjust the LOCKTIME register */
writel(0xFFFFFFFF, &clk_power->locktime);
/* configure MPLL */
writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
/* some delay between MPLL and UPLL */
pll_delay(4000);
/* configure UPLL */
writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV,
&clk_power->upllcon);
/* some delay between MPLL and UPLL */
pll_delay(8000);
/* set up the I/O ports */
writel(0x007FFFFF, &gpio->gpacon);
writel(0x00044555, &gpio->gpbcon);
writel(0x000007FF, &gpio->gpbup);
writel(0xAAAAAAAA, &gpio->gpccon);
writel(0x0000FFFF, &gpio->gpcup);
writel(0xAAAAAAAA, &gpio->gpdcon);
writel(0x0000FFFF, &gpio->gpdup);
writel(0xAAAAAAAA, &gpio->gpecon);
writel(0x0000FFFF, &gpio->gpeup);
writel(0x000055AA, &gpio->gpfcon);
writel(0x000000FF, &gpio->gpfup);
writel(0xFF95FFBA, &gpio->gpgcon);
writel(0x0000FFFF, &gpio->gpgup);
writel(0x002AFAAA, &gpio->gphcon);
writel(0x000007FF, &gpio->gphup);
return 0;
}
这里需要注意的是对分频比clk_power->clkdivn的设置:因为这个值在arch/arm/cpu/arm920t/start.S里有过一次设置(那里设置的是3),默认情况下,这里可以不用设置的。但我针对S3C2440修改了分频设置,CLKDIV要设置成5,所以这里必须要设置下。(一开始我就由于CLKDIV没有设置正确,造成系统挂掉。)
4) 此时再执行make micro2440_config && make all,将生成的u-boot.bin烧入micro2440开发Nor flash中,应该就能看到串口有信息输出了,不过因为还没有配置Nor flash,会看到flash error,系统挂掉。
5 NAND Flash 启动支持
对于老版的u-boot, 由于Nor flash支持读取代码执行,所以u-boot 默认是烧写进Nor flash启动的。想要在Nand flash中启动u-boot,需要在启动阶段将u-boot拷贝到内存中执行才行。
而最新版本的u-boot在启动第一阶段时,会将u-boot代码重定向到sdram里运行,具体过程如下:
① 在start.S中设置完CPU后,接着调用arch/arm/lib/board.c中的board_init_f做第一阶段平台的初始化工作(timer_init, env_init, ram_init... 计算堆栈addr_sp,设置gd结构体,计算出重定向u-boot的位置addr)。
② 执行完board_init_f后,会调用relocate_code(addr_sp, id, addr)返回start.S, 执行relocate_code的代码,拷贝u-boot代码到内存addr位置。
对于这个版本的u-boot,我实现同时支持Nor flash和Nand flash启动的方法是:在启动的开始阶段,初始化SDRAM后,通过CopyCode2Ram函数(CopyCode2Ram能够分辨代码是在Nor flash中还是在Nand flash中,从而采用不同的拷贝方法,具体实现在board/samsung/micro2440/nand.c中),将u-boot代码拷贝到TEXT_BASE指定的内存区域。然后再执行board_init_f和relocate_code。此时relocate_code不再是从flash中拷贝u-boot代码到内存了,而是从内存中TEXT_BASE处将代码拷贝到board_init_f中计算的addr位置。
这样,不管是从Nor flash还是Nand flash启动,一开始时就通过Copycode2Ram将u-boot代码复制到内存中,然后就可以顺利的执行后面的board_init_f和relocate_code了,简单方便的实现了同时支持Nor flash和Nand flash启动的功能。
u-boot第一阶段启动时,重定向代码示意图
下面是具体实现过程:
1) 修改include/configs/micro2440.h,设置TEXT_BASE
#define CONFIG_SYS_TEXT_BASE 0x33D80000
这里要注意,board_init_f计算出来的relocate addr一般是内存的末尾,所以此处的TEXT_BASE定义得不能太靠后,不然会出现代码拷贝过程中的overlap。(以前老版本的u-boot在内存中运行时,习惯将u-boot拷贝到0x33F80000处,这次我刚开始也设定的是这个值,结果第二次relocate_code拷贝代码时,就覆盖了先前的代码,造成系统错误。)
2) 修改arch/arm/cpu/u-boot.lds,将Copycode2Ram的代码放在.text段的最前面:
由于micro2440在通过Nand flash启动时,只会将flash中前4k的代码复制到内部ram中执行,所以我们在第一阶段拷贝代码时,必须保证执行拷贝操作的代码在.text段的前4k。这个可以通过修改u-boot.lds文件实现。
.text :
{
__image_copy_start = .;
CPUDIR/start.o (.text)
board/samsung/micro2440/libmicro2440.o (.text)
*(.text)
}
3) 修改arch/arm/cpu/arm920t/start.S:
这里,我们可以把设置系统时钟,初始化MPLL的操作提到前面来做,设置完时钟后再执行代码复制的操作,这样可以加快代码复制的速度。
在start.S的call_board_init_f前添加下面一段代码:
#ifdef CONFIG_MICRO2440
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
bl board_early_init_f /* configure MPLL UPLL */
ldr r0, =0x0
ldr r1, =(CONFIG_SYS_TEXT_BASE)
ldr r2, _end_ofs
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
ldr pc, =call_board_init_f /* jump to SDRAM */
#endif
6 NAND Flash 启动支持 ⇒ 改进
按照上节描述的方法实现同时支持从Nor flash 和Nand flash启动后,在后面做usbslave download时,发现这种实现方法有缺陷(这个缺陷应该是u-boot官方code自带的?)。问题出现在中断处理过程里,我们先来看一下start.S中的中断向量和中断处理例程(假设链接地址_TEXT_BASE=0x0):
.globl _start
_start: b start_code
ldr pc, _undefined_instruction /* 当产生中断或异常时,Arm内核会跳转到0x0地址
ldr pc, _software_interrupt * 开始处按照中断(或异常)的类型偏移一定地址执行
ldr pc, _prefetch_abort * 跳转指令。
ldr pc, _data_abort */
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq /*此处,编译时确定_irq的值:irq = _TEXT_BASE + irq_ofs */
_fiq: .word fiq
需要注意的是,此处_irq的值是编译时确定的,为_TEXT_BASE加上irq函数相对于_TEXT_BASE的偏移。当u-boot执行relocate函数,把u-boot代码重定向到内存中后,若有中断发生,会执行0x0开始的中断跳转指令。由于重定向时,并未更改0x0开始处的_irq, _fiq等label的值,此处的跳转仍然会跳转到重定向前的地址执行。(执行relocate时,在relocate .rel.dyn段时,会重新计算.rel.dyn段内汇编代码中Label的值。参见http://blog.csdn.net/caiyuqing2001/article/details/7377925)
所以在这种情况下执行中断处理例程时,执行的是_TXET_BASE为0x0,重定向前的代码,引用的汇编代码内的Label和C语言的全局变更也是重定向前的。而我们在初始化中断时,初始化的Label和全局变量是重写向后的变量(中断初始化时,代码已经重定向了),所以这种情况下执行的中断处理例程用到的变量全是未初始化的,会导致系统出错。
解决这个问题有两个方法:
1) 运行时根据重定向后中断处理例程的实际地址,更改0x0开始处的中断跳转地址。
若u-boot从Nand flash启动,0x0开始的4K的地址是在内部ram里的,此处的中断向量地址可以直接修改;但若是从Nor flash启动时,0x0开始处的地址是在Nor flash中的,此处的中断向量不能直接修改,得按照Nor flash的写时序来操作。所以想要同时支持Nor flash和Nand flash 启动,这种方法不可行。
2) 在运行过程中指定中断向量跳转地址。
中断向量跳转可以写成这种形式:ldr pc, =CONFIG_VECTOR_TABLE+0x08*irq_no。其中CONFIG_VECTORY_TABLE是设定的内存中的中断向量基地址,这样,relocate代码后在初始化中断过程中,可以根据中断处理函数的最终地址来设置中断向量表:
#define CONFIG_VECTOR_TABLE 0x33FFFFC0
#define INSTRUCTION_LDR_PC 0xE51FF004 /*ldr pc, [pc, #4]的指令代码*/
unsigned long int p_vect_table;
p_vect_table = (unsigned long int *) (CONFIG_VECTOR_TABLE + irq_no<<3);
p_vect_table++ = INSTRUCTION_LDR_PC;
p_vect_table = irq_handle;
3) 移除掉u-boot的重定向代码的功能,初始化SDRAM后,直接把u-boot代码拷贝至_TEXT_BASE处(假设_TEXT_BASE=0x33F40000),不再进行代码的重定向。这样发生中断时,跳转到的就是代码在内存中的最终地址,便没有上述问题了。
我采用的是第3种方法,这样在启动开始时,只需要复制一次代码就行了,实现也较简单。
① 修改arch/arm/config.mk,去除掉-pie链接选项,这样就不会生成.rel.dyn段了
# needed for relocation
#ifndef CONFIG_NAND_SPL
#LDFLAGS_u-boot += -pie
#endif
② 移除掉arch/arm/cpu/arm920t/start.S中的relocate代码和clear_bss代码:
#ifndef CONFIG_MICRO2440
adr r0, _start
cmp r0, r6
beq clear_bss /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
…
fixnext:
str r1, [r0]
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
#endif
#endif /* !CONFIG_MICRO2440 */
…
clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r4, =_TEXT_BASE /* reloc addr */
add r0, r0, r4
add r1, r1, r4
…
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
#ifndef CONFIG_MICRO2440
add lr, lr, r9
#endif
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
③ 修改arch/arm/lib/board.c的board_init_f函数:
…
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
#ifdef CONFIG_MICRO2440
addr -= CONFIG_SYS_U_BOOT_SIZE;
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
addr);
#endif
#endif
...
#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR;
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */
#ifndef CONFIG_MICRO2440
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~(4096 - 1);
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
#else
addr &= ~(4096 - 1);
#endif
…
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
#ifdef CONFIG_MICRO2440
gd->relocaddr = CONFIG_SYS_TEXT_BASE;
gd->start_addr_sp = addr_sp;
gd->reloc_off = 0;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof(gd_t));
relocate_code(addr_sp, id, addr);
#else
gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof(gd_t));
relocate_code(addr_sp, id, addr);
#endif
7 NOR Flash(SSTVF1601) 支持
在u-boot中添加对Nor flash的支持比较简单,大多数Nor flash都支持CFI接口,而u-boot有对cfi flash的驱动支持。对于SSTVF1601,并不支持标准的CFI接口,所以得使用JEDEC接口。但jedec_flash.c中并没有SSTVF1601的配置信息,所以得手动添加上:
drivers/mtd/jedec_flash.c:
static const struct amd_flash_info jedec_table[] = {
…
#ifdef CONFIG_SYS_FLASH_LEGACY_1024Kx16
{
.mfr_id = (u16)SST_MANUFACT,
.dev_id = SST39VF1601,
.name = "SST 39VF1601",
.uaddr = {
[1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x1000,96),
ERASEINFO(0x1000,160),
ERASEINFO(0x1000,240),
ERASEINFO(0x1000,16),
}
},
#endif
};
修改micro2440.h:
/*-----------------------------------------------------------------------
* FLASH and environment organization
*/
#define CONFIG_SYS_FLASH_CFI
#define CONFIG_FLASH_CFI_DRIVER
#define CONFIG_FLASH_CFI_LEGACY
#define CONFIG_SYS_FLASH_LEGACY_1024Kx16
#define CONFIG_FLASH_SHOW_PROGRESS 45
#define CONFIG_SYS_MAX_FLASH_BANKS 1
#define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE }
#define CONFIG_SYS_MAX_FLASH_SECT (512)
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x1f0000)
#define CONFIG_ENV_IS_IN_FLASH
#define CONFIG_ENV_SIZE 0x10000
7 NAND Flash支持
u-boot的driver/mtd/nand下有s3c2410 nand控制器的驱动,并没有s3c2440的。但2410和2440的nand控制器差别并不大,我们可以在2410的基础上进行移植。具体代码可以在我的github(https://github.com/novawl/u-boot-v2010.4.1)下的drivers/mtd/nand/s3c2440_nand.c中查看。需要注意的是,hwcontrol函数中,当cmd不为命令时,得将IO_ADDR_W赋值为nfdata地址,不然数据将无法写入到nand flash中。
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2440_ADDR_NALE;
if (cmd == NAND_CMD_NONE)
IO_ADDR_W = &nand->nfdata;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~S3C2440_NFCONT_nFCE,
&nand->nfcont);
else
writel(readl(&nand->nfcont) | S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
修改arch/arm/lib/board.c,当从Nor flash启动时才初始化nor flash,否则只初始化nand flash:
#if !defined(CONFIG_SYS_NO_FLASH)
static char *failed = "*** failed ***\n";
#ifdef CONFIG_MICRO2440
extern int BootFrmNORFlash(); /*在board/samsung/micro2440/nand.c中实现*/
#endif
#endif
void board_init_r(gd_t *id, ulong dest_addr)
…
#if !defined(CONFIG_SYS_NO_FLASH)
#ifdef CONFIG_MICRO2440
if (BootFrmNORFlash()) {
#endif
puts("Flash: ");
flash_size = flash_init();
if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
char *s = getenv("flashchecksum");
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
if (s && (*s == 'y')) {
printf(" CRC: %08X", crc32(0,
(const unsigned char *) CONFIG_SYS_FLASH_BASE,
flash_size));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang();
}
#ifdef CONFIG_MICRO2440
}
#endif
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif
ECC校验:
s3c2410和s3c2440 Nand flash控制器ECC校验模块有很大差别,所以基于s3c2440_nand.c的ecc校验函数和s3c2410_nand.c有较大差别。下面是与ECC校验相关的三个函数的实现:
#ifdef CONFIG_S3C2440_NAND_HWECC
void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debug("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
writel(readl(&nand->nfcont) | S3C2440_NFCONT_INITECC, &nand->nfcont);
}
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
unsigned long ecc = readl(&nand->nfecc0);
ecc_code[0] = ecc;
ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16;
debug("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
printf("s3c2440_nand_correct_data: not implemented\n");
return -1;
}
#endif
micro2440.h中与ECC相关的配置:
#define CONFIG_S3C2440_NAND_HWECC
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 3
还有一个需要注意的地方是新版的u-boot中s3c24x0.h中定义的struct s3c2440_nand结构体定义并不全面,并没有nfmecc0 和nfmecc1等寄存器的实现,这里需要添加上:
#ifdef CONFIG_S3C2440
/* NAND FLASH (see S3C2440 manual chapter 6) */
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfeccd0;
u32 nfeccd1;
u32 nfeccd;
u32 nfstat;
u32 nfstat0;
u32 nfstat1;
u32 nfecc0;
u32 nfecc1;
u32 nfecc;
};
#endif
8 usbslave 下载功能实现
网上有很多关于在u-boot中添加usbslave功能的教程,我主要是参照了一位叫tekkamanninja的网友的实现,在u-boot-v2012.04上实现了usbslave下载功能。下面是我移植的步骤:
1) 添加driver/usb/slave目录:
这个目录里的代码是usbslave的驱动具体实现,我是从tekkamanninja的github上下载的代码,将driver/usb/slave里的代码拷贝过来的。由于新u-boot里2440寄存器结构体的变量名全部改成小写了,所以需要将slave目录下关于寄存器的变量全改成小写的。具体代码可以查看我的github目录。
然后再修改顶层Makefile,加入对usbslave驱动的编译:
LIBS += drivers/usb/phy/libusb_phy.o
LIBS += drivers/usb/slave/libusb_slave.o
LIBS += drivers/usb/ulpi/libusb_ulpi.o
LIBS += drivers/video/libvideo.o
2) 修改arch/arm/cpu/arm920t/s3c24x0/interrupts.c,加入arch_interrupt_init
的实现:
void do_irq (struct pt_regs *pt_regs)
{
struct s3c24x0_interrupt *irq = s3c24x0_get_base_interrupt();
readl(&irq->intpnd);
}
int arch_interrupt_init (void)
{
return 0;
}
3) 修改arch/arm/cpu/arm920t/start.S:
#ifdef CONFIG_USE_IRQ
.align 5
irq:
/*
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
*/
sub lr, lr, #4
ldr sp, IRQ_STACK_START
stmdb sp!, {r0-r12, lr}
ldr lr, =int_return
ldr pc, =IRQ_Handle
int_return:
ldmia sp!, {r0-r12, pc}^
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
4) 修改arch/arm/lib/board.c,在init_board_r函数中加入usbslave的初始化操作:
/* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts();
#ifdef CONFIG_USB_DEVICE
usb_init_slave();
#endif
5) 修改board/samsung/micro2440/micro2440.c中的gpio初始化部分,设置gpc5引脚为usb_en功能:
/* set up the I/O ports */
writel(0x007FFFFF, &gpio->gpacon);
writel(0x00044555, &gpio->gpbcon);
writel(0x000007FF, &gpio->gpbup);
writel(0xAAAAA6AA, &gpio->gpccon);
writel(0x0000FFFF, &gpio->gpcup);
writel(readl(&gpio->gpcdat)&~(1<<5), &gpio->gpcdat);
writel(0xAAAAAAAA, &gpio->gpdcon);
writel(0x0000FFFF, &gpio->gpdup);
writel(0xAAAAAAAA, &gpio->gpecon);
writel(0x0000FFFF, &gpio->gpeup);
writel(0x000055AA, &gpio->gpfcon);
writel(0x000000FF, &gpio->gpfup);
writel(0xFF95FFBA, &gpio->gpgcon);
writel(0x0000FFFF, &gpio->gpgup);
writel(0x002AFAAA, &gpio->gphcon);
writel(0x000007FF, &gpio->gphup);
6) 加入cmd_usbslave支持:
修改common/Makefile,添加COBJS-$(CONFIG_USB_DEVICE) += cmd_usbslave.o,cmd_usbslave.c的具体实现可以查看我的github.
7) 修改micro2440.h,加入IRQ和usb device的配置:
/* input clock of PLL (the MICRO2440 has 12MHz input clock) */
#define CONFIG_SYS_CLK_FREQ 12000000
//#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */
#define CONFIG_CMDLINE_TAG /* enable passing of ATAGs */
…
/************************************************************
* USB support (currently only works with D-cache off)
************************************************************/
#define CONFIG_USB_OHCI
#define CONFIG_USB_KEYBOARD
#define CONFIG_USB_STORAGE
#define CONFIG_DOS_PARTITION
#define CONFIG_USB_DEVICE 1
#ifdef CONFIG_USB_DEVICE
#define CONFIG_USE_IRQ 1
#endif
8) 最后是对头文件s3c24x0.h的修改:
这里需要注意的是新版的u-boot s3c2440的struct s3c24x0_dma的定义是有问题的,我们需要对该结构体的定义针对2440加以修改。我最开始时参照网上的教程移植好usbslave后,发现usb传输的dma中断怎么也收不到,调试了好久才发现的这个问题。
/* DMAS (see manual chapter 8) */
struct s3c24x0_dma {
u32 disrc;
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
u32 disrcc;
#endif
u32 didst;
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
u32 didstc;
#endif
u32 dcon;
u32 dstat;
u32 dcsrc;
u32 dcdst;
u32 dmasktrig;
#if defined(CONFIG_S3C2400)
u32 res[1];
#endif
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
u32 res[7];
#endif
};
…
struct s3c24x0_usb_device {
…
#else /* little endian */
u8 func_addr_reg;
u8 res1[3];
u8 pwr_reg;
u8 res2[3];
u8 ep_int_reg;
u8 res3[15];
u8 usb_int_reg;
u8 res4[3];
u8 ep_int_en_reg;
u8 res5[15];
u8 usb_int_en_reg;
u8 res6[3];
u8 frame_num1_reg;
u8 res7[3];
u8 frame_num2_reg;
u8 res8[3];
u8 index_reg;
u8 res9[7];
u8 maxp_reg;
u8 res10[3];
u8 ep0_csr_in_csr1_reg;
u8 res11[3];
u8 in_csr2_reg;
u8 res12[7];
u8 out_csr1_reg;
u8 res13[3];
u8 out_csr2_reg;
u8 res14[3];
u8 out_fifo_cnt1_reg;
u8 res15[3];
u8 out_fifo_cnt2_reg;
u8 res16[3];
#endif /* __BIG_ENDIAN */
u32 res17[8];
struct s3c24x0_usb_dev_fifos fifo[5];
u32 res18[11];
//struct s3c24x0_usb_dev_dmas dma[5];
struct s3c24x0_usb_dev_dmas ep1;
struct s3c24x0_usb_dev_dmas ep2;
u8 res19[16];
struct s3c24x0_usb_dev_dmas ep3;
struct s3c24x0_usb_dev_dmas ep4;
};
9 DM9000 网卡驱动支持
u-boot-v2012.04对DM9000网上的支持已经比较完善的,只需要在配置里加上DM9000的编译选项,并在启动时加上DM9000的初始化就行了。
include/configs/micro2440.h:
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT 1
board/samsung/micro2440/micro2440.c:
#ifdef CONFIG_CMD_NET
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
#endif
10 YAFFS 文件系统烧写支持
新版的u-boot对yaffs文件系统的支持已经很完善了,不过要支持烧写yaffs文件系统,还是要进行一些修改。
修改include/configs/micro2440.h:
/*
* File system
*/
#define CONFIG_CMD_FAT
#define CONFIG_CMD_EXT2
#define CONFIG_CMD_UBI
#define CONFIG_CMD_UBIFS
#define CONFIG_CMD_MTDPARTS
#define CONFIG_CMD_NAND_YAFFS
#define CONFIG_MTD_DEVICE
修改common/cmd_nand.c:
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix '%s'\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_DROP_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_YAFFS_OOB);
#endif
修改driver/mtd/nand/nand_util.c:
need_skip = check_skip_len(nand, offset, *length);
if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
#ifndef CONFIG_CMD_NAND_YAFFS
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write (nand, offset, length, buffer);
if (rval == 0)
return 0;
*length = 0;
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
#endif
…
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
WATCHDOG_RESET();
评论
发表评论