arm-linux移植手记(二)bootloader移植(中)

    这里是u-boot的移植,包括了网卡DM9000驱动,在使用时是通过nfs将内核下载到sdram中,再写到nand flash中的,然后可以实现直接从nand flash启动,引导内核,加载yaffs2文件系统。声明下,所有都是是亲自实现的记录。
    步骤是严格按照《u-boot-2010-06在mini2440上的移植》来的,后面的DM9000驱动则是按照《u-boot-2009.08在mini2440上的移植(四)》修改。
    一、测试编译环境
    (1)移植环境介绍
U-boot版本:u-boot 2010-6
Linux平台:虚拟机下Fedora 11  自己原来编译的gcc在debian6下,因此在debian6下也顺利
交叉编译工具:fedora 11 :gcc-4.3.2   debian 6 :gcc 4.4.5  
arm开发板:mini2440(CPU:S3C2440 ,SDRAM:64M,Nor Flash:2M,Nand Flash:256M,网卡:DM9000EP)
    (2)参考文章要删除部分文件,我没有,反正编译的时候不影响,我也就没有做。
    (3)修改顶层Makefile文件

     设置交叉编译器

CROSS_COMPILE ?= arm-linux-gcc 
#仿照smdk2410,配置自己的开发板
     mini2440_config   :      unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mini2440 samsung s3c24x0

开发板配置选项中各项的含义如下:
arm CPU 的类型(CPU)
arm920t 其对应于cpu/arm920t 子目录
samsung 开发者/或经销商(vender),对应于board/samsung目录
mini2440 开发板的型号(BOARD),对应于board/ samsung /mini2440 目录
s3c24x0 片上系统(SOC)定义
    进行这些配置,我简单说下原因吧(仅供参考),这里mini2440_config是添加make后的参数命令,而后面的arm,arm920t等是确定了你在arch目录下的启动代码的位置,相当于告诉系统去哪里寻找,同时也就说明通常区别于其他板,你在这些地方修改就可以了。对应的就要在这些地方建立你的板子的文件。我这里还是改回samsung,因为参考的文章后面弄混了。
    (4)在/board 中建立mini2440 目录和文件,主要就是拷贝smdk2410的,因为2440的和2410的差别不太大,稍加修改就能用。

	#cd board
	#mkdir -p mini2440
	#cp -arf  samsung/smdk2410/* samsung/mini2440/
	#cd mini2440/
	#mv smdk2410.c mini2440.c
    (5)修改mini2440 目录下的Makefile文件

LIB = $(obj)lib$(BOARD).a
#COBJS := sbc2410x.o flash.o
COBJS := mini2440.o flash.o
SOBJS := lowlevel_init.o

    (6)在include/configs/中建立开发板配置文件

#cp include/configs/smdk2410.h include/configs/mini2440.h
    (7)测试编译环境(此问题在以前移植u-boot时出现)
至此,最基本的配置已经完成。如果对自己环境不放心的,可以尝试的先make smdk2410_config 再make下,看默认提供的能用不。

	[root@localhost u-boot-2010.06]# make mini2440_config
	Configuring for mini2440 board...
二、基本功能实现,后面全部都是参考文章做的,只是在其中有部分的复制错误的修改
    (1)mini2440开发板u-boot的stage1阶段的硬件设备初始化
    由于在u-boot启动代码处有两行是AT91RM9200DK的LED初始代码,但我们mini2440上的LED资源与该开发板的不一致,所以我们要删除或屏蔽该处代码,再加上mini2440的LED驱动代码(注:添加my2440 LED功能只是用于表示u-boot运行的状态,给调试带来方便,可将该段代码放到任何你想调试的地方),代码如下:

#gedit cpu/arm920t/start.S
/*bl coloured_LED_init  //这两行是AT91RM9200DK开发板的LED初始化,注释掉
    bl red_LED_on*/
#if defined(CONFIG_S3C2440) //区别与其他开发板
//根据mini2440原理图可知LED分别由S3C2440的PB5、6、7、8口来控制,以下是PB端口寄存器基地址(查2440的DataSheet得知)
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
#define GPBUP  0x56000018     
//以下对寄存器的操作参照S3C2440的DataSheet进行操作
    ldr r0, =GPBUP
    ldr r1, =0x7FF    //即:二进制11111111111,关闭PB口上拉
    str r1, [r0]
    ldr r0, =GPBCON  //配置PB5、6、7、8为输出口,对应PBCON寄存器的第10-17位
    ldr r1, =0x154FD  //即:二进制010101010011111101
    str r1, [r0]
 
    ldr r0, =GPBDAT
    ldr r1, =0x1C0    //即:二进制111000000,PB5设为低电平,6、7、8为高电平
    str r1, [r0]
#endif
 
//此段代码使u-boot启动后,点亮开发板上的LED1,LED2、LED3、LED4不亮
在include/configs/mini2440.h头文件中添加CONFIG_S3C2440宏
#gedit include/configs/mini2440.h
#define CONFIG_ARM920T        1    /* This is an ARM920T Core     */
#define CONFIG_S3C2410         1    /* in a SAMSUNG S3C2410 SoC    */
#define CONFIG_SMDK2410       1    /* on a SAMSUNG SMDK2410 Board */
#define CONFIG_S3C2440         1    /* in a SAMSUNG S3C2440 SoC    */
现在编译u-boot,在根目录下会生成一个u-boot.bin文件。然后我们利用mini2440原有的supervivi把u-boot.bin下载到RAM中运行测试(注意:我们使用supervivi进行下载时已经对CPU、RAM进行了初始化,所以我们在u-boot中要屏蔽掉对CPU、RAM的初始化),这里使用命令选项d如下:
/*#ifndef CONFIG_SKIP_LOWLEVEL_INIT //在start.S文件中屏蔽u-boot对CPU、RAM的初始
   bl cpu_init_crit                        //化
#endif*/
#make mini2440_config
#make
    (2)在u-boot中添加对S3C2440一些寄存器的支持、添加中断禁止部分和时钟设置部分。
    由于2410和2440的寄存器及地址大部分是一致的,所以这里就直接在2410的基础上再加上对2440的支持即可,代码如下:

#gedit cpu/arm920t/start.S
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
    /* turn off the watchdog */
 
# if defined(CONFIG_S3C2400)
# define pWTCON     0x15300000
# define INTMSK     0x14400008    /* Interupt-Controller base addresses */
# define CLKDIVN    0x14800014    /* clock divisor register */
#else     //下面2410和2440的寄存器地址是一致的
# define pWTCON     0x53000000
# define INTMSK     0x4A000008    /* Interupt-Controller base addresses */
# define INTSUBMSK  0x4A00001C
# define CLKDIVN    0x4C000014    /* clock divisor register */
# endif
    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]
# if defined(CONFIG_S3C2410)
    ldr  r1, =0x3ff
    ldr  r0, =INTSUBMSK
    str  r1, [r0]
# endif
# if defined(CONFIG_S3C2440)    //添加s3c2440的中断禁止部分
    ldr  r1, =0x7fff              //根据2440芯片手册,INTSUBMSK寄存器有15位可用  
    ldr  r0, =INTSUBMSK
    str  r1, [r0]
# endif
 
# if defined(CONFIG_S3C2440)      //添加s3c2440的时钟部分
#define MPLLCON   0x4C000004   //系统主频配置寄存器基地址
#define UPLLCON   0x4C000008   //USB时钟频率配置寄存器基地址
    ldr  r0, =CLKDIVN           //设置分频系数FCLK:HCLK:PCLK = 1:4:8
    mov  r1, #5
str  r1, [r0]
 
    ldr  r0, =MPLLCON  //设置系统主频为405MHz 
    ldr  r1, =0x7F021    //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
str  r1, [r0]
 
    ldr  r0, =UPLLCON  //设置USB时钟频率为48MHz  
    ldr  r1, =0x38022    //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
    str  r1, [r0]
 
# else //其他开发板的时钟部分,这里就不用管了,我们现在是做2440的
    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */ 
    ldr  r0, =CLKDIVN
    mov  r1, #3
    str  r1, [r0]
# endif
#endif    /* CONFIG_S3C2400 || CONFIG_S3C2410 || CONFIG_S3C2440 */
S3C2440的时钟部分除了在start.S中添加外,还要分别在board/samsung/mini2440/mini2440.c和arch/arm/cpu/arm920t/s3c24x0/speed.c中修改或添加部分代码,如下:

#gedit board/samsung/mini2440/mini2440.c //设置主频和USB时钟频率参数与start.S中的一致
#define FCLK_SPEED 2       //设置默认等于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 = 405MHz */
#define M_MDIV    0x7F     //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION //TABLE”部分进行设置
#define M_PDIV    0x2
#define M_SDIV    0x1
#endif
 
#define USB_CLOCK 2        //设置默认等于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         /* Fout = 48MHz */
#define U_M_MDIV    0x38   //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION //TABLE”部分进行设置
#define U_M_PDIV    0x2
#define U_M_SDIV    0x2
#endif
 
#gedit cpu/arm920t/s3c24x0/speed.c //根据设置的分频系数FCLK:HCLK:PCLK = 1:4:8修改获取时//频率的函数
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;
 
#if defined(CONFIG_S3C2440)
    if(pllreg == MPLL)
    {   //参考S3C2440芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2s)
        return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));
    }
#endif
    return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
}
/* return HCLK frequency */
ulong get_HCLK(void)
{
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
#if defined(CONFIG_S3C2440)
    return(get_FCLK()/4);
#endif
    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
}
    修改完毕后,重新编译u-boot,通过mini2440自带的supervivi中的d命令,将生成的u-boot.bin文件下载到ram中运行一下,结果如下,当时运行的截图保留:

    三、增加对nand flash的支持
    我是在nor flash中继续使用原厂的supervivi ,而且还是由supervivi来下载u-boot(使用a命令),省去了直接用JTag的麻烦,在后续的内核下载通过u-boot的nfs,而且为了方便原有的yaffs2文件系统没有改变,也可以直接通过supervivi下载。内核会有变化,后面会讲到。下面继续照抄参考文章,不过有些define的复制有问题,这里改正。
    (1)支持u-boot从Nand flash启动
    目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。
    首先,在include/configs/mini2440.h头文件中定义Nand要用到的宏和寄存器,如下:

#gedit include/configs/my2440.h  //在文件末尾加入以下Nand Flash相关定义
/*
 * Nand flash register and envionment variables
 */
#define CONFIG_S3C2440_NAND_BOOT  1
#define NAND_CTL_BASE  0x4E000000  //Nand Flash配置寄存器基地址,查2440手册可得知
#define bINT_CTL(Nb)   __REG(INT_CTL_BASE+(Nb))
#define UBOOT_RAM_BASE  0x33f80000
#define STACK_BASE  0x33F00000     //定义堆栈的地址
#define STACK_SIZE  0x8000         //堆栈的长度大小
    其次,修改cpu/arm920t/start.S这个文件,使u-boot从Nand Flash启动,在上一节中提过,u-boot默认是从Nor Flash启动的。修改部分如下:
#gedit cpu/arm920t/start.S
/*注意:在上一篇Nor Flash启动中,我们为了把u-boot用supervivi下载到内存中运行而屏蔽掉这段有关CPU初始化的代码。而现在我们要把u-boot下载到Nand Flash中,从Nand Flash启动,所以现在要恢复这段代码。*/
 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl cpu_init_crit
#endif
 
#if 0 //屏蔽掉u-boot中的从Nor Flash启动部分
#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 stack_setup
 
    ldr r2, _armboot_start
    ldr r3, _bss_start
    sub r2, r3, r2      /* r2 <- size of armboot */
    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 /* CONFIG_SKIP_RELOCATE_UBOOT */
#endif
 
 
//下面添加2440中u-boot从Nand Flash启动
 
#ifdef CONFIG_S3C2440_NAND_BOOT
 
#define  oNFCONF  0x00
#define  oNFCONT  0x04
#define  oNFCMD   0x08
#define  oNFSTAT   0x20
#define  LENGTH_UBOOT  0x60000
 
    mov r1, #NAND_CTL_BASE   //复位Nand Flash
    ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
    str r2, [r1, #oNFCONF]   //设置配置寄存器的初始值,参考s3c2440手册
    ldr r2, [r1, #oNFCONF]
 
    ldr r2, =( (1<<4)|(0<<1)|(1<<0) )
    str r2, [r1, #oNFCONT]   //设置控制寄存器
    ldr r2, [r1, #oNFCONT]
 
    ldr r2, =(0x6)           //RnB Clear
    str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
 
    mov r2, #0xff            //复位command
    strb r2, [r1, #oNFCMD]
    mov r3, #0               //等待
nand1:
    add r3, r3, #0x1
    cmp r3, #0xa
    blt nand1
 
nand2:
    ldr r2, [r1, #oNFSTAT]   //等待就绪
    tst r2, #0x4
    beq nand2
 
    ldr r2, [r1, #oNFCONT]
    orr r2, r2, #0x2         //取消片选
    str r2, [r1, #oNFCONT]
 
    //get read to call C functions (for nand_read())
    ldr sp, DW_STACK_START   //为C代码准备堆栈,DW_STACK_START定义在下面
    mov fp, #0              
 
    //copy U-Boot to RAM
    ldr r0, =TEXT_BASE//传递给C代码的第一个参数:u-boot在RAM中的起始地址
    mov r1, #0x0      //传递给C代码的第二个参数:Nand Flash的起始地址
    mov r2, # LENGTH_UBOOT //传递给C代码的第三个参数:u-boot的长度大小(128k)
    bl nand_read_ll   //此处调用C代码中读Nand的函数,现在还没有要自己编写实现
    tst r0, #0x0
    beq ok_nand_read
 
bad_nand_read:
    loop2: b loop2    //infinite loop
 
ok_nand_read: //检查搬移后的数据,如果前4k完全相同,表示搬移成功
    mov r0, #0
    ldr r1, =TEXT_BASE
    mov r2, #0x400           //4 bytes * 1024 = 4K-bytes
go_next:
    ldr r3, [r0], #4
    ldr r4, [r1], #4
    teq r3, r4
    bne notmatch
    subs r2, r2, #4
    beq stack_setup
    bne go_next
 
notmatch:
    loop3: b loop3           //infinite loop
#endif                      //CONFIG_S3C2440_NAND_BOOT
 
_start_armboot: .word start_armboot //在这一句的下面加上DW_STACK_START的定义
 
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
再次,在board/samsung/mini2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:
#gedit board/samsung/mini2440/nand_read.c  //新建一个nand_read.c文件,记得保存
#include <common.h>
#include <linux/mtd/nand.h>
 
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGw(x) (*(volatile unsigned short *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
 
#define NF_BASE 0x4e000000
#if defined(CONFIG_S3C2410) && !define (CONFIG_S3C2440)
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCMD  __REGb(NF_BASE + 0x4)
#define NFADDR __REGb(NF_BASE + 0x8)
#define NFDATA __REGb(NF_BASE + 0xc)
#define NFSTAT __REGb(NF_BASE + 0x10)
#define NFSTAT_BUSY 1
#define nand_select() (NFCONF &= ~0x800)
#define nand_deselect() (NFCONF |= 0x800)
#define nand_clear_RnB() do {} while (0)
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
#define NFCONF   __REGi(NF_BASE + 0x0)
#define NFCONT   __REGi(NF_BASE + 0x4)
#define NFCMD    __REGb(NF_BASE + 0x8)
#define NFADDR   __REGb(NF_BASE + 0xc)
#define NFDATA   __REGb(NF_BASE + 0x10)
#define NFDATA16 __REGw(NF_BASE + 0x10)
#define NFSTAT   __REGb(NF_BASE + 0x20)
#define NFSTAT_BUSY 1
#define nand_select()    (NFCONT &= ~(1 << 1))
#define nand_deselect()  (NFCONT |= (1 << 1))
#define nand_clear_RnB() (NFSTAT |= (1 << 2))
#endif
 
static inline void nand_wait(void)
{
       int i;
       while (!(NFSTAT & NFSTAT_BUSY))
            for (i=0; i<10; i++);
}
 
struct boot_nand_t {
       int page_size;
       int block_size;
       int bad_block_offset;
       // unsigned long size;
};
 
static int is_bad_block(struct boot_nand_t * nand, unsigned long i)
{
       unsigned char data;
       unsigned long page_num;
       nand_clear_RnB();
       if (nand->page_size == 512) {
              NFCMD = NAND_CMD_READOOB; /* 0x50 */
              NFADDR = nand->bad_block_offset & 0xf;
              NFADDR = (i >> 9) & 0xff;
              NFADDR = (i >> 17) & 0xff;
              NFADDR = (i >> 25) & 0xff;
       } else if (nand->page_size == 2048) {
              page_num = i >> 11; /* addr / 2048 */
              NFCMD = NAND_CMD_READ0;
              NFADDR = nand->bad_block_offset & 0xff;
              NFADDR = (nand->bad_block_offset >> 8) & 0xff;
              NFADDR = page_num & 0xff;
              NFADDR = (page_num >> 8) & 0xff;
              NFADDR = (page_num >> 16) & 0xff;
              NFCMD = NAND_CMD_READSTART;
       } else {
              return -1;
       }
       nand_wait();
       data = (NFDATA & 0xff);
       if (data != 0xff)
               return 1;
       return 0;
}
 
static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
{
       unsigned short *ptr16 = (unsigned short *)buf;
       unsigned int i, page_num;
       nand_clear_RnB();
       NFCMD = NAND_CMD_READ0;
       if (nand->page_size == 512) {
              /* Write Address */
              NFADDR = addr & 0xff;
              NFADDR = (addr >> 9) & 0xff;
              NFADDR = (addr >> 17) & 0xff;
              NFADDR = (addr >> 25) & 0xff;
              } else if (nand->page_size == 2048) {
              page_num = addr >> 11; /* addr / 2048 */
              /* Write Address */
              NFADDR = 0;
              NFADDR = 0;
              NFADDR = page_num & 0xff;
              NFADDR = (page_num >> 8) & 0xff;
              NFADDR = (page_num >> 16) & 0xff;
              NFCMD = NAND_CMD_READSTART;
              } else {
                     return -1;
              }
              nand_wait();
#if defined(CONFIG_S3C2410)&& !define (CONFIG_S3C2440)
              for (i = 0; i < nand->page_size; i++) {
              *buf = (NFDATA & 0xff);
              buf++;
                }
 
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
 
       for (i = 0; i < (nand->page_size>>1); i++) {
              *ptr16 = NFDATA16;
              ptr16++;
       }
#endif
       return nand->page_size;
}
 
static unsigned short nand_read_id()
{
       unsigned short res = 0;
       NFCMD = NAND_CMD_READID;
       NFADDR = 0;
       res = NFDATA;
       res = (res << 8) | NFDATA;
       return res;
}
 
extern unsigned int dynpart_size[];
 
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
       int i, j;
       unsigned short nand_id;
       struct boot_nand_t nand;
       /* chip Enable */
       nand_select();
       nand_clear_RnB();
       for (i = 0; i < 10; i++)
       ;
       nand_id = nand_read_id();
       if (0) {                          /* dirty little hack to detect if nand id is misread */
       unsigned short * nid = (unsigned short *)0x31fffff0;
       *nid = nand_id;
       }
       if (nand_id == 0xec76 ||            /* Samsung K91208 */
           nand_id == 0xad76 ) {         /*Hynix HY27US08121A*/
              nand.page_size = 512;
              nand.block_size = 16 * 1024;
              nand.bad_block_offset = 5;
       // nand.size = 0x4000000;
       } else if (nand_id == 0xecf1 ||       /* Samsung K9F1G08U0B */
              nand_id == 0xecda ||          /* Samsung K9F2G08U0B */
              nand_id == 0xecd3 ) {         /* Samsung K9K8G08 */
              nand.page_size = 2048;
              nand.block_size = 128 * 1024;
              nand.bad_block_offset = nand.page_size;
       // nand.size = 0x8000000;
       } else {
              return -1; // hang
       }
       if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))
              return -1; /* invalid alignment */
       for (i=start_addr; i < (start_addr + size);) {
#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
              if (i & (nand.block_size-1)== 0) {
              if (is_bad_block(&nand, i) ||
                 is_bad_block(&nand, i + nand.page_size)) {
                     /* Bad block */
                     i += nand.block_size;
                     size += nand.block_size;
                     continue;
              }
              }
#endif
              j = nand_read_page_ll(&nand, buf, i);
              i += j;
              buf += j;
       }
       /* chip Disable */
       nand_deselect();
       return 0;
}
然后,在board/samsung/mini2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:
COBJS    := mini2440.o flash.o nand_read.o
还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:
text :
{
    cpu/arm920t/start.o    (.text)
    board/samsung/mini2440/lowlevel_init.o (.text)
    board/samsung/mini440/nand_read.o (.text)
    *(.text)
}
最后编译u-boot,生成u-boot.bin文件。然后先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,打开电源就从Nand Flash启动了。

    (2)添加Nand Flash(K9F2g08U0C)的有关操作支持
    在上一节中我们说过,通常在嵌入式bootloader中,有两种方式来引导启动内核:从Nor Flash启动和从Nand Flash启动,但不管是从Nor启动或者从Nand启动,进入第二阶段以后,两者的执行流程是相同的。
现在的u-boot-2010-06版本对Nand的初始化、读写实现是基于最近的Linux内核的MTD架构,删除了以前传统的执行方法,使移植没有以前那样复杂了,实现Nand的操作和基本命令都直接在drivers/mtd/nand目录下(在doc/README.nand中讲得很清楚)。下面我们结合代码来分析一下u-boot在第二阶段的执行流程:
1.lib_arm/board.c文件中的start_armboot函数调用了drivers/mtd/nand/nand.c文件中的nand_init函数,如下:
  #if defined(CONFIG_CMD_NAND) //可以看到CONFIG_CMD_NAND宏决定了Nand的初始化
      puts ("NAND: ");
      nand_init();
  #endif
2.nand_init调用了同文件下的nand_init_chip函数;
3.nand_init_chip函数调用drivers/mtd/nand/s3c2410_nand.c文件下的board_nand_init函数,然后再调用drivers/mtd/nand/nand_base.c函数中的nand_scan函数;
4.nand_scan函数调用了同文件下的nand_scan_ident函数等。
我们在u-boot提供的关于S3C2410的nand_flash驱动文件的基础上添加相关代码以支持S3C2440.

#gedit driver/mtd/nand/s3c2410_nand.c
#include <common.h>
#include <nand.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <asm/io.h>
#define   NF_BASE             0x4e000000
 
#if defined(CONFIG_S3C2410)&&!defined(CONFIG_S3C2440)
#define S3C2410_NFCONF_EN          (1<<15)
#define S3C2410_NFCONF_512BYTE     (1<<14)
#define S3C2410_NFCONF_4STEP       (1<<13)
#define S3C2410_NFCONF_INITECC     (1<<12)
#define S3C2410_NFCONF_nFCE        (1<<11)
#define S3C2410_NFCONF_TACLS(x)    ((x)<<8)
#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<4)
#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<0)
 
#define S3C2410_ADDR_NALE 4
#define S3C2410_ADDR_NCLE 8
#endif
 
#if defined(CONFIG_S3C2440)
#define S3C2410_NFCONT_EN          (1<<0)
#define S3C2410_NFCONT_INITECC     (1<<4)
#define S3C2410_NFCONT_nFCE        (1<<1)
#define S3C2410_NFCONT_MAINECCLOCK (1<<5)
#define S3C2410_NFCONF_TACLS(x)    ((x)<<12)
#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<8)
#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<4)
 
#define S3C2410_ADDR_NALE 0x08
#define S3C2410_ADDR_NCLE 0x0c
#endif
 
ulong IO_ADDR_W = NF_BASE;
#ifdef CONFIG_NAND_SPL
/* in the early stage of NAND flash booting, printf() is not available */
#define printf(fmt, args...)
 
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
       int i;
       struct nand_chip *this = mtd->priv;
 
       for (i = 0; i < len; i++)
              buf[i] = readb(this->IO_ADDR_R);
}
#endif
 
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
//     struct nand_chip *chip = mtd->priv;
       struct s3c2410_nand *nand = s3c2410_get_base_nand();
 
       debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
 
       if (ctrl & NAND_CTRL_CHANGE) {
    //  ulong IO_ADDR_W = (ulong) nand;
              IO_ADDR_W = (ulong)nand;
 
              if (!(ctrl & NAND_CLE))
                     IO_ADDR_W |= S3C2410_ADDR_NCLE;
              if (!(ctrl & NAND_ALE))
                     IO_ADDR_W |= S3C2410_ADDR_NALE;
 
//            chip->IO_ADDR_W = (void *)IO_ADDR_W;
 
#if defined(CONFIG_S3C2410)&&!defined(CONFIG_S3C2440)
              if (ctrl & NAND_NCE)
                     writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,
                            &nand->NFCONF);
              else
                     writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,
                            &nand->NFCONF);
       }
#endif
#if defined(CONFIG_S3C2440)
              if (ctrl & NAND_NCE)
                     writel(readl(&nand->NFCONT) & ~S3C2410_NFCONT_nFCE,
                            &nand->NFCONT);
              else
                     writel(readl(&nand->NFCONT) | S3C2410_NFCONT_nFCE,
                            &nand->NFCONT);
       }
#endif
 
       if (cmd != NAND_CMD_NONE)
       // writeb(cmd, chip->IO_ADDR_W);
              writeb(cmd, (void *)IO_ADDR_W);
}
static int s3c2410_dev_ready(struct mtd_info *mtd)
{
       struct s3c2410_nand *nand = s3c2410_get_base_nand();
       debugX(1, "dev_ready\n");
       return readl(&nand->NFSTAT) & 0x01;
}
 
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
       struct s3c2410_nand *nand = s3c2410_get_base_nand();
       debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
#if defined(CONFIG_S3C2410)&&!defined(CONFIG_S3C2440)
       writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);
#endif
 
#if defined(CONFIG_S3C2440)
       writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC, &nand->NFCONT);
#endif
}
 
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                                  u_char *ecc_code)
{
       struct s3c2410_nand *nand = s3c2410_get_base_nand();
       ecc_code[0] = readb(&nand->NFECC);
       ecc_code[1] = readb(&nand->NFECC + 1);
       ecc_code[2] = readb(&nand->NFECC + 2);
       debugX(1, "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 s3c2410_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("s3c2410_nand_correct_data: not implemented\n");
       return -1;
}
#endif
 
int board_nand_init(struct nand_chip *nand)
{
       u_int32_t cfg;
       u_int8_t tacls, twrph0, twrph1;
       struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
       struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
 
       debugX(1, "board_nand_init()\n");
 
       writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
 
#if defined(CONFIG_S3C2410)&&!defined(CONFIG_S3C2440)
       /* initialize hardware */
       twrph0 = 3;
       twrph1 = 0;
       tacls = 0;
 
       cfg = S3C2410_NFCONF_EN;
       cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
       cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
       cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
       writel(cfg, &nand_reg->NFCONF);
 
       /* initialize nand_chip data structure */
       nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
#endif
#if defined(CONFIG_S3C2440)
       twrph0 = 4;
       twrph1 = 2;
       tacls = 0;
 
       cfg = 0;
       cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
       cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
       cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
       writel(cfg, &nand_reg->NFCONF);
 
       cfg = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);
       writel(cfg, &nand_reg->NFCONT);
       /* initialize nand_chip data structure */
       nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
#endif
 
       nand->select_chip = NULL;
 
       /* read_buf and write_buf are default */
       /* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
       nand->read_buf = nand_read_buf;
#endif
 
       /* hwcontrol always must be implemented */
       nand->cmd_ctrl = s3c2410_hwcontrol;
 
       nand->dev_ready = s3c2410_dev_ready;
 
#ifdef CONFIG_S3C2410_NAND_HWECC
       nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
       nand->ecc.calculate = s3c2410_nand_calculate_ecc;
       nand->ecc.correct = s3c2410_nand_correct_data;
       nand->ecc.mode = NAND_ECC_HW;
       nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
       nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
       nand->ecc.mode = NAND_ECC_SOFT;
#endif
 
#ifdef CONFIG_S3C2410_NAND_BBT
       nand->options = NAND_USE_FLASH_BBT;
#else
       nand->options = 0;
#endif
 
       debugX(1, "end of nand_init\n");
 
       return 0;
}
  
在s3c24x0.h里添加S3C2440相关nand_flash的结构体,修改代码如下:
#gedit include/asm/arch-s3c24x0/s3c24x0.h
#if defined(CONFIG_S3C2440)
struct s3c2410_nand {
  u32 NFCONF;
  u32 NFCONT;
  u32 NFCMD;
  u32 NFADDR;
  u32 NFDATA;
  u32 NFMECCD0;
  u32 NFMECCD1;
  u32 NFSECCD;
  u32 NFSTAT;
  u32 NFESTAT0;
  u32 NFESTAT1;
  u32 NFMECC0;
  u32 NFMECC1;
  u32 NFSECC;
  u32 NFSBLK;
  u32 NFEBLK;
};
#endif
#if defined(CONFIG_S3C2410)&&!defined(CONFIG_S3C2440)
/* NAND FLASH (see S3C2410 manual chapter 6) */
struct s3c2410_nand {
       u32  NFCONF;
       u32  NFCMD;
       u32  NFADDR;
       u32  NFDATA;
       u32  NFSTAT;
       u32  NFECC;
};
#endif
  在mini2440.h里添加nand_flash相关宏定义
#gedit include/configs/mini2440.h
#define CONFIG_CMD_NAND
/* NAND flash settings */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_NAND_BASE            0x4E000000 //Nand配置寄存器基地址
#define CONFIG_SYS_MAX_NAND_DEVICE      1 
#define CONFIG_MTD_NAND_VERIFY_WRITE    1 
//#define NAND_SAMSUNG_LP_OPTIONS       1  //注意:我们这里是M的Nand Flash,所以不、//用,如果是M的大块Nand Flash,则需加上
#endif
       
在mini2440.h里添加saveenv命令的支持
#gedit include/configs/mini2440.h
//#define CONFIG_ENV_IS_IN_FLASH   1   /*屏蔽Nor Flash saveenv相关宏定义*/
//#define CONFIG_ENV_SIZE          0x10000  /* Total Size of Environment Sector */
#define   CONFIG_ENV_IS_IN_NAND  1
#define   CONFIG_ENV_OFFSET        0x60000
#define   CONFIG_ENV_SIZE          0x20000
#define   CONFIG_CMD_SAVEENV
    对于我这样的只想了解流程的人来说,这样复制已经足够了,因为这些具体芯片的操作细节,查看相关芯片数据手册是完全没有问题的,建议初学者,而且没有想继续在u-boot这里深入下去的人,就不要纠缠于为什么了。

    编译,通过supervivi的a命令下载,然后从nand flash启动,运行了个help和写命令:

arm-linux移植手记(二)bootloader移植(中)_第1张图片


arm-linux移植手记(二)bootloader移植(中)_第2张图片

    四、增加DM9000驱动,参考《u-boot-2009.08在mini2440上的移植(四)---增加DM9000驱动和命令自动补全功能》
    我仅实现了DM9000的驱动,能够使用nfs命令下载内核。下面为复制内容,其采用的是u-boot-2009.08,和我的u-boot-2010.06基本一样。
    u-boot-2009.08版本已经对CS8900、RTL8019和DM9000X等网卡有比较完善的代码支持(代码在drivers/net/目录下),而且在S3C24XX系列中默认对CS8900网卡进行配置使用。而mini2440开发板使用的则是DM9000网卡芯片,所以只需在开发板上添加对DM9000的支持即可。还有一点,以前的 U-boot 对于网络延时部分有问题,需要修改许多地方。但是现在的U-boot 网络
部分已经基本不需要怎么修改了,只有在DM9000 的驱动和NFS 的TIMEOUT 参数上需要稍微修改一下。
    (1)DM9000驱动代码修改
【1】修改static int dm9000_init函数中部分代码,如果不修改这一部分,在使用网卡的时候会报“could not establish link”的错误。
打开/drivers/net/dm9000x.c,定位到377行,修改如下:(我这里没有修改,参考文章后面又改回来了!)
【2】对于NFS,增加了延时,否则会出现“*** ERROR: Cannot mount”的错误。
打开/net/nfs.c,定位到36行,修改如下:

#define HASHES_PER_LINE 65	/* Number of "loading" hashes per line	*/
#define NFS_RETRY_COUNT 30
#define NFS_TIMEOUT (CONFIG_SYS_HZ/1000*2000UL)
//#define NFS_TIMEOUT 2000UL
【3】添加网卡芯片(DM9000)的初始化函数
打开board/samsung/mini2440/mini2440.c,定位到194行附近,文件末尾处,修改如下:
extern int dm9000_initialize(bd_t *bis);//implicit declaration of function 'dm9000_initialize'
#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
【4】修改配置文件,在mini2440.h中加入相关定义
打开/include/configs/mini2440.h,定位到60行附近,修改如下:
#define CONFIG_NET_MULTI  1
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x20000300 //网卡片选地址
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE+4) //网卡数据地址
#define CONFIG_DM9000_NO_SROM  1
//#define CONFIG_DM9000_USE_16BIT
#undef CONFIG_DM9000_DEBUG
注意:
u-boot-2009.08 可以自动检测DM9000网卡的位数,根据开发板原理图可知网卡的数据位为16位,并且网卡位
于CPU的BANK4上,所以只需在 board/samsung/mini2440/lowlevel_init.S中设置 #define B4_BWSCON (DW16) 即
可,不需要此处的 #define CONFIG_DM9000_USE_16BIT 1
给u-boot加上ping命令,用来测试网络通不通
/*
 * Command line configuration.
 */
#include <config_cmd_default.h>

#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_PING /*ping command support*/

恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址以及内核启动参数:

#define CONFIG_BOOTDELAY 3
//这里的地址和IP都可以自己设定,只要在一个网段内即可
#define CONFIG_ETHADDR	08:00:3e:26:0a:5b 
#define CONFIG_NETMASK          255.255.255.0
#define CONFIG_IPADDR		192.168.0.223
#define CONFIG_SERVERIP		192.168.0.224
#define CONFIG_GATEWAYIP 192.168.0.1
#define CONFIG_OVERWRITE_ETHADDR_ONCE

    这里稍稍变下,我跳过参考文章的错误步骤,直接记录正确配置。

【5】保持网卡打开

打开drivers/net/dm9000x.c ,定位到456行附近,屏蔽掉dm9000_halt函数中的内容:

/*
  Stop the interface.
  The interface is stopped when it is brought.
*/
static void dm9000_halt(struct eth_device *netdev)
{
#if 0
	DM9000_DBG("%s\n", __func__);

	/* RESET devie */
	phy_write(0, 0x8000);	/* PHY RESET */
	DM9000_iow(DM9000_GPR, 0x01);	/* Power-Down PHY */
	DM9000_iow(DM9000_IMR, 0x80);	/* Disable all interrupt */
	DM9000_iow(DM9000_RCR, 0x00);	/* Disable RX */
#endif
}
*
   Write a word to phyxcer
*/
#if 0
static void
phy_write(int reg, u16 value)
{


	/* Fill the phyxcer register into REG_0C */
	DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);


	/* Fill the written data into REG_0D & REG_0E */
	DM9000_iow(DM9000_EPDRL, (value & 0xff));
	DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff));
	DM9000_iow(DM9000_EPCR, 0xa);	/* Issue phyxcer write command */
	udelay(500);			/* Wait write complete */
	DM9000_iow(DM9000_EPCR, 0x0);	/* Clear phyxcer write command */
	DM9000_DBG("phy_write(reg:0x%x, value:0x%x)\n", reg, value);
}
#endif
/* function declaration ------------------------------------- */
static int dm9000_probe(void);
static u16 phy_read(int);
//static void phy_write(int, u16);
static u8 DM9000_ior(int);
static void DM9000_iow(int reg, u8 value);
  Read a word from phyxcer
*/
static u16
phy_read(int reg)
{
	u16 val;
	/* Fill the phyxcer register into REG_0C */
	DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
	DM9000_iow(DM9000_EPCR, 0xc);	/* Issue phyxcer read command */
	udelay(1000);// udelay(100);			/* Wait read complete */
	DM9000_iow(DM9000_EPCR, 0x0);	/* Clear phyxcer read command */
	val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);
我这里没有保存我运行的结果,大致是一样的,我ping的是192.168.0.225和224,也就是主机winxp和虚拟机linux的ip,结果和参考文章类似:

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at unknown: 0 mode
*** ERROR: `ethaddr' not set
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at unknown: 0 mode
ping failed; host 10.1.0.128 is not alive
//一开始要是mac和ip地址都不对了可以按下面方式改,运行saveenv,以后就不用了
[u-boot@MINI2440]# setenv ipaddr 10.1.0.129
[u-boot@MINI2440]# setenv serverip 10.1.0.128
[u-boot@MINI2440]# setenv setenv ethaddr 12:34:56:78:9A:BC
[u-boot@MINI2440]# saveenv
[u-boot@MINI2440]# setenv gatewayip 10.1.0.1   //看情况吧
[u-boot@MINI2440]# setenv ethaddr 12:34:56:78:9a:bc //MAC地址,随便设
[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 0 mode
Using dm9000 device
host 10.1.0.128 is alive
[u-boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x4000000000002 --   0% complete.
Writing to Nand... done

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 0 mode
Using dm9000 device
host 10.1.0.128 is alive

    这篇就先到这里了,已经很多了,后面具体介绍制作uImage,下载到nand flash中,并引导启动。感觉基本全是复制别人的,就当留个纪念了,非常感谢那些把自己所做的记录并发布到网上的人,让我学习了这么多,少走很多弯路。由于代码变颜色我用不好,这里没有变,希望谅解了。

参考文章地址:

1.u-boot-2010-06在mini2440上的移植 

 http://www.linuxidc.com/Linux/2011-03/33476.htm

2.u-boot-2009.08在mini2440上的移植(四)---增加DM9000驱动和命令自动补全功能 

 http://singleboy.blog.163.com/blog/static/5490019420114981651831/


你可能感兴趣的:(arm-linux移植手记(二)bootloader移植(中))