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.2

2) 下载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进行修改。

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();

评论

此博客中的热门博文

在OpenWRT上配置Shadowsocks,并通过Dnsmasq+ipset按域名翻墙

通过串口root kindle voyage,并安装koreader