TQ2440 ARM开发板Uboot移植
移植环境
主 机:VMWare--Fedora 10 Kernel:2.6.34
开发板:TQ2440--256MB Nand
编译器:arm-linux-gcc-4.3.3.tgz
u-boot:u-boot-1.1.6
代码查看工具:Source Insight
移植步骤
本次移植的功能特点包括:
Nand Flash读写
Nor/Nand 自动选择启动
DM9000网卡支持
Yaffs2文件系统支持
Uboot启动菜单
1. 清除已编译的文件,配置编译选项
make distclean
修改u-boot/cpu/arm920t/config.mk文件:
PLATFORM_RELFLAGS += -fno-strict-aliasing -fno-common -ffixed-r8 /
-msoft-float
注释掉 / -msoft-float这一段,不使用软浮点编译。
PLATFORM_CPPFLAGS += -march=armv4修改成
PLATFORM_CPPFLAGS += -march=armv4t
来避免编译时产生target CPU does not support interworking的错误。
2. 建立自己的开发板项目并测试编译。
tar xjfv u-boot-1.1.6.tar.bz2 –C /opt/bootloader
cd /opt/bootloader/u-boot-1.1.6/board
cp –fr smdk2410/ tender/
cd tender
mv smdk2410.c tender.c
cd /opt/bootloader/u-boot-1.1.6/include/configs
cp –f smdk2410.h tender.h
U-boot对我们使用的TQ2440开发板和S3C2440芯片并不支持,但是我们可以以具有相似性的SMDK2410为参考,通过以上几个步骤,建立自己开发板的项目。取名为tender 。现在继续。
vim /opt/bootloader/u-boot-1.1.6/Makefile
对自己的开发板进行配置
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
tender_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
SX1_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm925t sx1
保存退出。
vim /opt/bootloader/u-boot-1.1.6/board/tender/Makefile
将我们的开发板的编译项进行修改。
LIB = $(obj)lib$(BOARD).a
COBJS := tender.o flash.o
SOBJS := lowlevel_init.o
接着:
vim /opt/bootloader/u-boot-1.1.6/include/configs/tender.h
进入配置头文件,添加与我们使用的开发板相关的宏定义。
#define CONFIG_TENDER 1
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C2440 1 /* in a SAMSUNG S3C2440 SoC */
/* #define CONFIG_SMDK2410 1 on a SAMSUNG SMDK2410 Board */
查看board目录下的u-boot.lds文件可以知道,我们整个工程的入口位于cpu/arm920t/start.c。接下去所有跳转指令都是位于u-boot的根目录下进行的操作。
cd cpu/arm920t/start.c
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#elif defined(CONFIG_TENDER)
# define pWTCON 0x53000000
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
S3C2440用户手册中的中断和看门狗寄存器与S3C2410地址相似。进行以上的修改。紧接着进行reset设置,包括关闭看门狗、禁止中断、禁止子中断、配置时钟分频比,同时将总线模式修改为异步模式,原因在于我们使用的分频比为1:4:8,根据用户手册说明,HDIV=1的时候,总线工作模式应该为异步。在相应的位置加入如下代码。
#if defined(CONFIG_TENDER)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
ldr r1, =0x7ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
/* FCLK:HCLK:PCLK = 1:4:8 */
/* default FCLK is 400 MHz ! */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
#endif /* CONFIG_TENDER */
mrc p15,0,r0,c1,c0,0 //根据2440的Datasheet,设置异步总线模式
orr r0,r0,#0xc0000000 /*R1_nF:OR:R1_iA*/
mcr p15,0,r0,c1,c0,0
完成上述任务之后,将会跳到cpu_init_crit函数,
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
在这个函数里面,在完成mmu的关闭的同时,还完成了13个寄存器的初始化,这些寄存器的任务是进行内存管理。
bl lowlevel_init
它位于board/tender/lowlevel_init.S文件中,我们的SDRAM是挂载在Bank6上的,修改同文件下的SDRAM配置,使之和开发板的硬件配置一致。检查在board/smdk2440/lowlevel_init.S:
一并将之后需要涉及的Nor Flash、网卡和串口进入位宽修改。
#define B1_BWSCON (DW16)
#define B4_BWSCON (DW16) //接网卡,位宽为16
#define B5_BWSCON (DW8)
#define B6_BWSCON (DW32) //接内存,位宽为32
根据HCLK设置SDRAM 的刷新参数,主要是REFCNT寄存器,开发板HCLK为100M,查看SDRAM的datasheet进行计算,
将 #define REFCNT 0x1113 改为 #define REFCNT 0x4f4
接着,start.c完成了将程序收入C程序中,入口地址为
ldr pc, _start_armboot
_start_armboot: .word start_armboot
函数start_armboot位于lib_arm/board.c文件中。
#cd lib-arm
#vim board.c
start_armboot首先调用下面这个函数,以完成一系列的初始化。
里面有一个如下的结构体数组,定义了第二阶段运行的具体步骤
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup——cpu/arm920t/cpu.c */
board_init, /* basic board dependent setup——board/tender/tender.c */
interrupt_init, /* set up exceptions ——cpu/arm920t/s3c24x0/interrupt.c*/
env_init, /* initialize environment——common/env_flash.c */
init_baudrate, /* initialze baudrate settings ——lib_arm/board.c*/
serial_init, /* serial communications setup ——cpu/arm920t/s3c24x0/serial.c*/
console_init_f, /* stage 1 init of console ——common/console.c*/
display_banner, /* say that we are here ——lib_arm/board.c*/
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks ——board/tender/tender.c*/
display_dram_config,/*显示RAM的配置大小——lib_arm/board.c*/
NULL,
};
在cpu_init中,主要完成对FIQ和IRQ的栈初始化。无需进行修改。
board_init位于board.c的tender.c的文件中。它的主要工作是对时钟的部分初始化及对I/O的初始化。由于S3C2440与S3C2410的时钟和GPIO都不一样。因而,查看用户手册
S3C24X0_CLOCK_POWER和S3C24X0_GPIO的定义位于include/s3c24x0.h中,
typedef struct {
S3C24X0_REG32 LOCKTIME;
S3C24X0_REG32 MPLLCON;
S3C24X0_REG32 UPLLCON;
S3C24X0_REG32 CLKCON;
S3C24X0_REG32 CLKSLOW;
S3C24X0_REG32 CLKDIVN;
S3C24X0_REG32 CAMDIVN;
} /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER;
/* I/O PORT (see manual chapter 9) */
typedef struct {
#ifdef CONFIG_S3C2400
S3C24X0_REG32 PACON;
S3C24X0_REG32 PADAT;
S3C24X0_REG32 PBCON;
S3C24X0_REG32 PBDAT;
S3C24X0_REG32 PBUP;
S3C24X0_REG32 PCCON;
S3C24X0_REG32 PCDAT;
S3C24X0_REG32 PCUP;
S3C24X0_REG32 PDCON;
S3C24X0_REG32 PDDAT;
S3C24X0_REG32 PDUP;
S3C24X0_REG32 PECON;
S3C24X0_REG32 PEDAT;
S3C24X0_REG32 PEUP;
S3C24X0_REG32 PFCON;
S3C24X0_REG32 PFDAT;
S3C24X0_REG32 PFUP;
S3C24X0_REG32 PGCON;
S3C24X0_REG32 PGDAT;
S3C24X0_REG32 PGUP;
S3C24X0_REG32 OPENCR;
S3C24X0_REG32 MISCCR;
S3C24X0_REG32 EXTINT;
#endif
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
S3C24X0_REG32 GPACON;
S3C24X0_REG32 GPADAT;
S3C24X0_REG32 res1[2];
S3C24X0_REG32 GPBCON;
S3C24X0_REG32 GPBDAT;
S3C24X0_REG32 GPBUP;
S3C24X0_REG32 res2;
S3C24X0_REG32 GPCCON;
S3C24X0_REG32 GPCDAT;
S3C24X0_REG32 GPCUP;
S3C24X0_REG32 res3;
S3C24X0_REG32 GPDCON;
S3C24X0_REG32 GPDDAT;
S3C24X0_REG32 GPDUP;
S3C24X0_REG32 res4;
S3C24X0_REG32 GPECON;
S3C24X0_REG32 GPEDAT;
S3C24X0_REG32 GPEUP;
S3C24X0_REG32 res5;
S3C24X0_REG32 GPFCON;
S3C24X0_REG32 GPFDAT;
S3C24X0_REG32 GPFUP;
S3C24X0_REG32 res6;
S3C24X0_REG32 GPGCON;
S3C24X0_REG32 GPGDAT;
S3C24X0_REG32 GPGUP;
S3C24X0_REG32 res7;
S3C24X0_REG32 GPHCON;
S3C24X0_REG32 GPHDAT;
S3C24X0_REG32 GPHUP;
S3C24X0_REG32 res8;
S3C24X0_REG32 MISCCR;
S3C24X0_REG32 DCLKCON;
S3C24X0_REG32 EXTINT0;
S3C24X0_REG32 EXTINT1;
S3C24X0_REG32 EXTINT2;
S3C24X0_REG32 EINTFLT0;
S3C24X0_REG32 EINTFLT1;
S3C24X0_REG32 EINTFLT2;
S3C24X0_REG32 EINTFLT3;
S3C24X0_REG32 EINTMASK;
S3C24X0_REG32 EINTPEND;
S3C24X0_REG32 GSTATUS0;
S3C24X0_REG32 GSTATUS1;
S3C24X0_REG32 GSTATUS2;
S3C24X0_REG32 GSTATUS3;
S3C24X0_REG32 GSTATUS4;
# if defined(CONFIG_S3C2440)
S3C24X0_REG32 res9;
S3C24X0_REG32 DSC0;
S3C24X0_REG32 DSC1;
S3C24X0_REG32 MSLCON;
S3C24X0_REG32 GPJCON;
S3C24X0_REG32 GPJDAT;
S3C24X0_REG32 GPJUP;
#endif
#endif
} /*__attribute__((__packed__))*/ S3C24X0_GPIO;
由于之前我们的对S3C2440芯片的支持也是直接拷贝s3c2410.h的,因此也需要进入修改。在此先进行简单的修改。也可以直接参考用户手册,进行全部校正。进入s3c2440.h文件中。将所有的2410全都替换成2440即可。回到tender.c文件中。
修改宏定义如下使之满足我们使用的频率:
#define FCLK_SPEED 2
#if FCLK_SPEED==0 /* Fout = 203MHz, Fin = 12MHz for Audio */
#define M_MDIV 0xC3
#define M_PDIV 0x4
#define M_SDIV 0x1
#elif FCLK_SPEED==1 /* Fout = 202.8MHz */
#define M_MDIV 0xA1
#define M_PDIV 0x3
#define M_SDIV 0x1
#elif FCLK_SPEED==2 /* Fout = 400MHz */
#define M_MDIV 0x5C
#define M_PDIV 0x1
#define M_SDIV 0x1
#endif
#define USB_CLOCK 2
#if USB_CLOCK==0
#define U_M_MDIV 0xA1
#define U_M_PDIV 0x3
#define U_M_SDIV 0x1
#elif USB_CLOCK==1
#define U_M_MDIV 0x48
#define U_M_PDIV 0x3
#define U_M_SDIV 0x2
#elif USB_CLOCK==2
#define U_M_MDIV 0x38
#define U_M_PDIV 0x2
#define U_M_SDIV 0x2
#endif
再将我们之前添加的GPJ进入初始化:
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
gpio->GPJCON = 0x02AAAAAA;
gpio->GPJUP = 0x00001FFF;
gd->bd->bi_arch_number = MACH_TYPE_TENDER;
这里还添加了我们自己设置的机器码MACH_TYPE_TENDER位于include/asm-arm/mach-types.h文件中。相应位置加入
MACH_TYPE_TENDER 1111
到此,board的初始化告一段落。
对于:
interrupt_init, /* set up exceptions ——cpu/arm920t/s3c24x0/interrupt.c*/
(由于我有编译的时候有出错,这里选在这个文件中,模仿CONFIG_SMDK2410加入CONFIG_TENDER的配置。)
在此文件中定义
timer_load_val = get_PCLK()/(2 * 16 * 100);
用到了get_PCLK(),该函数位于cpu/arm920t/s3c24x0/speed.c中,
原型为
ulong get_PCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());
}
再调用函数get_HCLK()
ulong get_HCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
}
又调用了get_FCLK()函数
ulong get_FCLK(void)
{
return(get_PLLCLK(MPLL));
}
层层调用之后的底层函数也是同一文件下的get_PLLCLK(int pllreg),原型为
static ulong get_PLLCLK(int pllreg)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
ulong r, m, p, s;
if (pllreg == MPLL)
r = clk_power->MPLLCON;
else if (pllreg == UPLL)
r = clk_power->UPLLCON;
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
}
我们查看了用户手册,知道时钟最大的区别在用户手册中的如下一段介绍:
MPLL Control Register
Mpll = (2 * m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
UPLL Control Register
Upll = (m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
PLL Value Selection Guide (MPLLCON)
1. Fout = 2 * m * Fin / (p*2s), Fvco = 2 * m * Fin / p where : m=MDIV+8, p=PDIV+2, s=SDIV
2. 600MHz ≤ FVCO ≤ 1.2GHz
3. 200MHz ≤ FCLKOUT ≤ 600MHz
4. Don't set the P or M value as zero, that is, setting the P=000000, M=00000000 can cause malfunction of
the PLL.
5. The proper range of P and M: 1 ≤ P ≤ 62, 1 ≤ M ≤ 248
由此我们对以下两个函数进行修改。
static ulong get_PLLCLK(int pllreg)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
ulong r, m, p, s;
if (pllreg == MPLL)
r = clk_power->MPLLCON;
else if (pllreg == UPLL)
r = clk_power->UPLLCON;
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
#ifdef CONFIG_TENDER
return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));
#else
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
#endif
}
及其
ulong get_HCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
#ifdef CONFIG_TENDER
return(get_FCLK()/4);
#else
return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
#endif
}
做完这么多事,我们还需要进入关键的一步
在uboot的根目录下,使用下述指令,
find . –name /* -type f –print | xargs grep “<s3c2410.h>”
找出所有包含s3c2410.h的头文件,将它们替换成s3c2440.h。
完成上述步骤,我们已经完成了对S3C2440的基本支持。
对NorFlash的支持
本开发板使用的NorFlash的型号为EON的EN29LV160A,大小2M,扇区为35个。可以参考PDF文档。
在tender.h配置文件中的NorFlash部分进入如下修改。
#if 0
#define CONFIG_AMD_LV400 1 /* uncomment this if you have a LV400 flash */
#define CONFIG_AMD_LV800 1 /* uncomment this if you have a LV800 flash */
#endif
#define CONFIG_EON_LV160A 1
#define CFG_MAX_FLASH_BANKS 1 /* max number of memory banks */
#ifdef CONFIG_EON_LV160A
#define PHYS_FLASH_SIZE 0x00200000 /* 2MB */
#define CFG_MAX_FLASH_SECT (35) /* max number of sectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE 0x00100000 /* 1MB */
#define CFG_MAX_FLASH_SECT (19) /* max number of sectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE 0x00080000 /* 512KB */
#define CFG_MAX_FLASH_SECT (11) /* max number of sectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x070000) /* addr of environment */
#endif
这里配置的NorFlash并不是Uboot所支持的。需要我们进行添加。
进入include/flash.h文件中,在Device IDs位置加入
#define EON_MANUFACT 0X00110011 /* 这里的号码是我自己定义的 */
#define EON_ID_LV160B 0x11491149 /* 同样,此处也是自己定义的 */
通过阅读board.c文件,我们知道,在完成init_fnc_t *init_sequence[]这个数组的初始化之后,uboot将会进行flash_init函数调用,即进行Flash的初始化。这个函数位于board/tender/flash.c文件中。
flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV800B & FLASH_TYPEMASK);
#elif defined(CONFIG_EON_LV160A)
(EON_MANUFACT & FLASH_VENDMASK) |
(EON_ID_LV160A & FLASH_TYPEMASK);
在flash_init函数下面,flash_print_info函数也需要进行修改。
void flash_print_info (flash_info_t * info)
{
int i;
switch (info->flash_id & FLASH_VENDMASK) {
case (AMD_MANUFACT & FLASH_VENDMASK):
printf ("AMD: ");
break;
case (EON_MANUFACT & FLASH_VENDMASK):
printf ("EON: ");
default:
printf ("Unknown Vendor ");
break;
}
switch (info->flash_id & FLASH_TYPEMASK) {
case (AMD_ID_LV400B & FLASH_TYPEMASK):
printf ("1x Amd29LV400BB (4Mbit)/n");
break;
case (AMD_ID_LV800B & FLASH_TYPEMASK):
printf ("1x Amd29LV800BB (8Mbit)/n");
break;
case (EON_ID_LV160A & FLASH_TYPEMASK):
printf ("1x EN29LV160A (16Mbit)/n");
break;
default:
printf ("Unknown Chip Type/n");
goto Done;
break;
}
编译u-boot,下载到开发板运行,启动时正常显示Nor Flash的信息。
对NandFlash的支持
本开发板使用的NandFlash芯片是K9F2G08U0A,大小为256MHz
继续根据start_armboot的走向进行修改。完成了前面的NorFlash的初始化后,有如下一段代码:
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
这里指引我们进入NandFlash的初始化。于是为了对NandFlash进入初始化,我们进入配置文件tender.h中将CONFIG_COMMANDS & CFG_CMD_NAND的宏定义加上
#define CONFIG_COMMANDS /
(CONFIG_CMD_DFL | /
CFG_CMD_CACHE | /
CFG_CMD_NAND | /
/*CFG_CMD_EEPROM |*/ /
/*CFG_CMD_I2C |*/ /
/*CFG_CMD_USB |*/ /
CFG_CMD_REGINFO | /
CFG_CMD_DATE | /
CFG_CMD_ELF)
这样,start_armboot在运行的时候将会调用位于drivers/nand/nand.c文件中的nand_init函数。下述就是这个函数原型
void nand_init(void)
{
int i;
unsigned int size = 0;
for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
size += nand_info[i].size;
if (nand_curr_device == -1)
nand_curr_device = i;
}
printf("%lu MiB/n", size / (1024 * 1024));
在查看这个函数的过程中,我们发现上述两个红色标出的宏都是未定义的。于是,继续在配置文件tender.h最后面加入如下:
#define CFG_MAX_NAND_DEVICE 1
因为我们只能用一片NandFlash所以都设置为1。
我们还发现在nand_init函数中调用了同文件下面的nand_init_chip函数。上面的蓝色部分。
引入了nand_chip函数,我们查看的过程中发现,这个函数引用的结构体nand_bbt_descr中有一个宏NAND_MAX_CHIPS并未在文件中进行声明。依然像上面所说的,
在配置文件tender.h最后面加入如下:
#define NAND_MAX_CHIPS 1
引入的另一个参数是base_address,它的定义位于同文件中如下:
static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST;
这里的CFG_NAND_BASE_LIST也在nand.c文件开头进行了宏定义。
#ifndef CFG_NAND_BASE_LIST
#define CFG_NAND_BASE_LIST { CFG_NAND_BASE }
#endif
经过层层调用,我们会发现,其实源头在CFG_NAND_BASE这个宏,依旧需要我们自己进行定义。于是在tender.h文件中继续添加:
#define CFG_NAND_BASE 1
完成上述之后,我们接着看nand_init_chip这个函数。
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
ulong base_addr)
{
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
board_nand_init(nand);
if (nand_scan(mtd, 1) == 0) {
if (!mtd->name)
mtd->name = (char *)default_nand_name;
} else
mtd->name = NULL;
}
因为uboot不知道用户使用的是nandflash是什么型号的,于是留下一个接口函数,board_nand_init(nand); 让用户自己来添加。这个函数需要自己来写。在cpu/arm920t/s3c24x0下建一个文件nand_flash.c,,同时在这个文件下的Makefile文件中将这个文件加入编译否则会出错。每加入一个文件都应该相应的在Makefile中进行修改。
#include <common.h>
#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include <nand.h>
#include <s3c2440.h>
#define S3C2440_NFSTAT_READY (1<<0)
#define S3C2440_NFCONT_nFCE (1<<1)
/* select chip, for s3c2440 */
static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
if (chip == -1) {
s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
} else {
s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
}
}
/* command and control functions */
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd,int cmd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
struct nand_chip *chip = mtd->priv;
switch (cmd){
case NAND_CTL_SETNCE:
s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
case NAND_CTL_CLRNCE:
s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
printf("%s: called for NCE/n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR;
break;
/* NAND_CTL_CLRCLE: */
/* NAND_CTL_CLRALE: */
default:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
break;
}
}
/* s3c2440_nand_devready()
*
*returns 0 if the nand is busy, 1 if it is ready
*/
static int s3c2440_nand_devready(struct mtd_info *mtd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
return(s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
}
/*
*Called by driver/nand/nand.c,initialize the interface of nand flash
*/
int board_nand_init(struct nand_chip *chip)
{
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);
chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
chip->hwcontrol = s3c2440_nand_hwcontrol;
chip->dev_ready = s3c2440_nand_devready;
chip->select_chip = s3c2440_nand_select_chip;
chip->options = 0;
chip->eccmode = NAND_ECC_SOFT;
return 0;
}
#endif
我们来看看board_nand_init这个函数,里面的TACLS/TWRPH0/TWRPH1分别为0/4/2代表0个HCLK时钟、5个HCLK时钟、3个HCLK时钟。当HCLK=100MHZ时,(0+5+3)/100us=80ns>46ns=(CLE Hold Time=5+CLE Setup Time=12+ALE Hold Time=5+ALE Setup Time=12+WE# Pulse Width=12)ns,也满足K9F2G08U0A的时序要求。
S3C2440_NAND我们在s3c2440.h头文件中进行修改时,由于没有相应在s3c24x0.h文件加入我们的s3c2440的NAND寄存器的地址结构。这里会提示未定义。
于是,我们在s3c24x0.h中,加入如下(参考S3C2440用户手册第六):
/* 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;
board_nand_init()这个函数的主要功能是对struct nand_chip结构体的函数指针赋值,让它们指向自己为nand驱动编写的一些函数,对未赋值的指针,uboot会在后面为其赋上通用nand驱动函数指针。
nand_init_chip()接着调用nand_scan().
nand_scan()定义在drivers/nand/nand_base.c文件中。它首先对struct nand_chip结构体中在board_nand_init()函数中未赋值的指针赋上通用nand驱动函数指针。
通用nand驱动函数nand_select_chip()赋值给struct nand_chip结构体的函数指针用于打开或关闭nand芯片,0为打开,1为关闭。在这个函数中会调用nand_chip结构体中的hwcontrol函数指针,这个指针指向的函数是需要自己编写的。这个函数指针在board_nand_init()函数中被赋值。主要作用是向nand flash发送一些nand flash开启与关闭命令。
nand_scan()剩余部分初始化nand_chip和mtd_info结构体。
nand_scan()最后在返回时调用drivers/nand/nand_bbt.c文件中的nand_default_bbt()。
nand_default_bby()选择一个坏块描述表,返回时调用本文件中的nand_scan_bbt()。
nand_scan_bbt()寻找建立一个坏块描述表。
最后返回到nand_init(),这样nand驱动的初始化完成了。
以上部分是对我们NANDFlash的操作进行了配置,我们回过来看,在start.S中,我们知道它的引导是直接从NorFlash中启动的。为了让它能从NandFlash中启动。我们进行了如下修改。主要是将栈的初始化放到前面,用于后面调用C函数的需要;判断u-boot是不是从内存启动后,调用CopyCode2Ram函数,实现把启动代码拷贝到内存。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss /* stack_setup */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
在boad/tender文件夹下面创建文件boot_init.c文件,用于实现从NandFlsh读取字节;判断启动方式,并拷贝启动代码到内存。
#include <common.h>
#include <s3c2440.h>
#define BUSY 1
#ifdef NAND_LARGEPAGE
#define NAND_SECTOR_SIZE 2048
#else
#define NAND_SECTOR_SIZE 512
#endif
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE-1)
void nand_init_ll(void);
void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_data(void);
static void nand_reset(void)
{
nand_select_chip();
write_cmd(0xff);
wait_idle();
nand_deselect_chip();
}
static void wait_idle(void)
{
int i;
S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
while(!(*p & BUSY))
for(i=0;i<10;i++);
}
static void nand_select_chip(void)
{
int i;
S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;
s3c2440nand->NFCONT &= ~(1<<1);
for(i=0;i<10;i++);
}
static void nand_deselect_chip(void)
{
S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;
s3c2440nand->NFCONT |= (1<<1);
}
static void write_cmd(int cmd)
{
S3C2440_NAND * s3c2440nand =(S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
*p = cmd;
}
static void write_addr(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
#ifndef NAND_LARGEPAGE
*p = addr & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 9) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 17) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 25) & 0xff;
for(i=0; i<10; i++);
#else
int col, page;
col = addr & NAND_BLOCK_MASK;
page = addr / NAND_SECTOR_SIZE;
*p = col & 0xff; /* Column Address A0~A7 */
for(i=0; i<10; i++);
*p = (col >> 8) & 0x0f; /* Column Address A8~A11 */
for(i=0; i<10; i++);
*p = page & 0xff; /* Row Address A12~A19 */
for(i=0; i<10; i++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(i=0; i<10; i++);
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
#endif
}
/* 读取数据 */
static unsigned char read_data(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
return *p;
}
void nand_init_ll(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
nand_reset();
}
void nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return ;
}
nand_select_chip();
for(i=start_addr; i < (start_addr + size);) {
write_cmd(0);
/* Write Address */
write_addr(i);
#ifdef NAND_LARGEPAGE
write_cmd(0x30);
#endif
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
*buf = read_data();
buf++;
}
}
nand_deselect_chip();
return ;
}
int bBootFrmNORFlash(void)
{
volatile unsigned int *bwsCON = (volatile unsigned int *)0x48000000;
unsigned int bwsVal;
bwsVal = *bwsCON;
bwsVal &= 0x06;
if (bwsVal==0){
return 0;
}
else {
return 1;
}
}
int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
unsigned int *pdwDest;
unsigned int *pdwSrc;
int i;
if (bBootFrmNORFlash())
{
pdwDest = (unsigned int *)buf;
pdwSrc = (unsigned int *)start_addr;
for (i = 0; i < size / 4; i++)
{
pdwDest[i] = pdwSrc[i];
}
}
else
{
nand_init_ll();
nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
}
return 0;
}
在 board/smdk2440/Makefile 中添加 boot_Init.c 的编译选项,使他编译到u-boot中:
COBJS := my2440.o flash.o boot_Init.o
还有一个重要的地方要修改,在 cpu/arm920t/u-boot.lds 中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码,主要目的是防止编译器把CopyCode2Ram的子函数放到4K之后,否则是无法启动的。如下:
.text :
{
cpu/arm920t/start.o (.text)
board/smdk2440/boot_Init.o (.text)
board/smdk2440/lowlevel_init.o (.text)
*(.text)
}
将环境变量存储到Nand Flash中
从上面的启动信息看,有一个警告信息“*** Warning - bad CRC or NAND, using default environment”,这是因为没有将u-boot的环境变量保存nand中的缘故。u-boot在默认的情况下把环境变量都是保存到Nor Flash中的,所以要修改代码,让他保存到Nand中。修改 include/configs/smdk2440.h 文件:
/* 配置环境变量存放设置 */ |
4、最后编译u-boot,生成u-boot.bin文件。将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,保存环境变量后重启开发板,那条警告信息现在没有了。
对DM9000的支持
首先修改 include/configs/tender.h ,在文件中加入这些宏的定义:
/* * Hardware drivers */ 屏蔽u-boot默认对CS8900网卡的支持
//#define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */
//#define CS8900_BASE 0x19000300
//#define CS8900_BUS16 1 /* the Linux driver does accesses as shorts */
//添加u-boot对DM9000X网卡的支持
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_DM9000_BASE 0x20000300 //网卡片选地址
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4) //网卡数据地址
#define CONFIG_DM9000_USE_16BIT 1
注意:
u-boot-2009.08 可以自动检测DM9000网卡的位数,根据开发板原理图可知网卡的数据位为16位,并且网卡位于CPU的BANK4上,所以只需在board/samsung/my2440/lowlevel_init.S中设置 #define B4_BWSCON (DW16) 即可,不需要此处的 #define CONFIG_DM9000_USE_16BIT 1
//给u-boot加上ping命令,用来测试网络通不通
#define CONFIG_COMMANDS /
(CONFIG_CMD_DFL | /
CFG_CMD_CACHE | /
CFG_CMD_NAND | /
CFG_CMD_PING | /
//恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b //开发板MAC地址
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.2.100 //开发板IP地址
#define CONFIG_SERVERIP 192.168.2.101 //TFTP主机IP地址
修改MD9000网卡驱动代码 drivers/net/dm9000x.c:
//注释掉eth_init函数中的这一部分,不然使用网卡的时候会报“could not establish link”的错误
i = 0;
while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link ");
return 0;
}
}
在303行左右把下面这段也注释掉
#if 0
for (i = 0; i < 6; i++)
((u16 *) bd->bi_enetaddr)[i] = read_srom_word(i);
#endif
在323行左右把下面这段也注释掉
#if 0
i = 0;
while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link/n");
return 0;
}
}
#endif