移植u-boot-2010.09到三星SC32442平台(一):时钟和nand启动

本文详细记录了U-Boot在三星SC32442平台的移植过程及NAND闪存启动的实现方法。包括配置项创建、时钟设定、串口调试、NAND控制器初始化和数据读取等关键步骤。

我之前毕业设计是在学校实验室里做的嵌入式开发和fpga设计。当时摸索过程中碰到了许多问题,绕了些弯路,现在早已解题,觉得有必要把我所做的内容发出来,抛砖引玉,为后人提供个参考。在此也感谢帮助过我的前人们,及其博文。写这些博文也是为了将互助的精神发扬光大。

 

本文参考了以下博客:

http://blog.sina.com.cn/s/blog_3e4774e30100hgyd.html

http://www.cublog.cn/u3/104447/showart_2205510.html

 

先是移植u-boot-2010.09到三星SC32442平台

 

 

 

U-boot 版本 2010.09

 

1新建2442配置选项并编译:

解压缩到~/development目录下,进入目录:

hu@hu-laptop:~$ cd ~/development/u-boot-2010.09

 

创建针对2442板子的配置项:

hu@hu-laptop:~/development/u-boot-2010.09$ cd board/samsung

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ mkdir board2442

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ cp -rf smdk2410/* board2442

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ ls

board2442  goni  smdk2400  smdk2410  smdk6400  smdkc100

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung$ cd board2442

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung/board2442$ mv smdk2410.c board2442.c

hu@hu-laptop:~/development/u-boot-2010.09/board/samsung/board2442$ cd ../../../

hu@hu-laptop:~/development/u-boot-2010.09$ cp include/configs/smdk2410.h include/configs/board2442.h

hu@hu-laptop:~/development/u-boot-2010.09$ gedit board/samsung/board2442/Makefile

修改 COBJS := smdk2410.o flash.o

为 COBJS := board2442.o flash.o

 

这个版本的u-boot和以前版本不一样了,新建配置选项不需要修改顶层目录下的Makefile,只需修改board.cfg,在smdk2410下面照样子添加

board2442 arm arm920t - samsung s3c24x0

 

打开顶层Makefile,设置编译工具链。

# set default to nothing for native builds

ifeq ($(HOSTARCH),$(ARCH))

CROSS_COMPILE ?=

endif

在此之后加上一行:

CROSS_COMPILE := arm-unknown-linux-gnueabi-

 

之后就可以编译了,命令行进入顶层目录,配置目标板:

make board2442_config

编译:

make all

之后就能在顶层目录下编译出u-boot, u-boot.bin, u-boot.srec, u-boot.map文件。

2.修改时钟,低级初始化等设定,使串口成功输出信息:

修改arch/cpu/arm920t/start.S:

注释掉无关的LED灯初始化语句:

//bl coloured_LED_init

//bl red_LED_on

 

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

    /* 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_S3C2442b)//添加s3c2440的中断禁止部分

    ldr  r1, =0xfff        //由2442手册设置,与2440不一样,2440是0x7fff  

    ldr  r0, =INTSUBMSK

    str  r1, [r0]

# endif

# if defined(CONFIG_S3C2442b)   //添加2442时钟部分

#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  //设置系统主频为400MHz

    ldr  r1, =0x3F040  //63 4 0 这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分

    str  r1, [r0]

    ldr  r0, =UPLLCON  //设置USB时钟频率为47.98MHz

    ldr  r1, =0x1A041  //26 4 1这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分

    str  r1, [r0]

# else

    /* 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 */

 

修改

 

修改include/configs/board2442.h头文件:

添加CONFIG_S3C2442b配置宏

#define CONFIG_ARM920T 1     /* This is an ARM920T Core   */

#define CONFIG_S3C24X0  1     /* in a SAMSUNG S3C24x0-type SoC       */

#define CONFIG_S3C2410   1     /* specifically a SAMSUNG S3C2410 SoC       */

#define CONFIG_SMDK2410      1     /* on a SAMSUNG SMDK2410 Board  */

#define CONFIG_S3C2442b  1

 

//#define CONFIG_SYS_CLK_FREQ       12000000/* the SMDK2410 has 12MHz input clock */

#define CONFIG_SYS_CLK_FREQ         16934400 //我们板子的晶振频率是16.934

#define  CONFIG_SYS_PROMPT             "board2442 # "           //修改提示符信息

 

修改board/samsung/board2442/board2442.c文件:

修改机器码,为以后启动Linux内核做准备

       /* arch number of SMDK2410-Board */

//     gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

       gd->bd->bi_arch_number = MACH_TYPE_SMDK2440;

 

除此之外:

#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 = 400MHz */

#define M_MDIV    0x3F     //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置

#define M_PDIV    0x4

#define M_SDIV    0x0

#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 = 47.98MHz */

#define U_M_MDIV    0x1A   //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置

#define U_M_PDIV    0x4

#define U_M_SDIV    0x1

#endif

 

修改cpu/arm920t/s3c24x0/speed.c

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_S3C2442b)

    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_S3C2442b)

    return(get_FCLK()/4);

#endif

    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());

}

 

之后可以吧u-boot通过openOCD下载到RAM里运行了,不过若要u-boot在ram中运行的话最好事先在board.h头文件里定义两个宏:

#define CONFIG_SKIP_LOWLEVEL_INIT   1

#define CONFIG_SKIP_RELOCATE_UBOOT      1

这样u-boot就不会重复执行底层初始化的工作,不然容易使CPU产生异常

 

通过openOCD将u-boot下载到内存:

 

 

 

 

接着就能在串口看到提示信息了

 

这次设置时钟和串口花了不少功夫。首先根据了2442手册上推荐的时钟频率列表选择两个合适的MPLL UPLL频率。

 

 

 

 

 

实验室板子的晶振频率是16.9344M,所以选择47.98 和 400两个频率。可以看得出来,2442比2440的主频要低一些,记得2440是支持533主频的。之后我把编译好的镜像通过j-tag下载到RAM中运行,发现串口终于有输出了,不过是乱码,肯定还是哪里时钟没设置对。值得一提的是,要让开发板能在ram运行uboot镜像,需要进行底层的初始化,这个过程可以通过把u-boot.bin再烧到nand中重启开发板来实现。由于此时的u-boot是不支持nand的,所以启动后只能初始化下cpu,串口是看不到信息的。

 

针对串口输出是乱码的问题,我上网查阅了很多文章,大部分遇到同样问题的人都是PLL和ULL没有设置好,或者是speed.c里get_PLLCLK()和get_HCLK()两个函数没有进行修改。不过我反复检查了自己的代码,没有他们所说的问题,一时间u-boot开发进度停止不前。后来很偶然地发现问题出在board.h里CONFIG_SYS_CLK_FREQ.默认的值是12000000,开发板的晶振是16934400,改过来问题就解决了.

 

串口能输入信息的话后面的移植工作就方便很多了,可以通过串口输出调试信息来进行调试.在此之前对u-boot的调试都是”摸黑”的,只能通过openocd从内存和cpu的寄存器值来分析问题.并且实验室的电路板不像大部分商业开发板上有4个LED,也无法通过”点灯大法”来调试.

 

 

3.添加nand启动

在board.h文件中添加有关nand控制器的定义:

(在最后一句#endif  /* __CONFIG_H */之前):

/*

 * Nand flash register and envionment variables

 */

#define CONFIG_S3C2442b_NAND_BOOT  1

#define NAND_CTL_BASE  0x4E000000  //Nand Flash配置寄存器基地址,查2440手册可得知

#define STACK_BASE  0x33F00000     //定义堆栈的地址

#define STACK_SIZE  0x8000         //堆栈的长度大小

#define oNFCONF  0x00

#define oNFCONT  0x04 //控制寄存器的基地址(0x4E000004)

#define oNFADDR  0x0c //地址寄存器的基地址(0x4E00000c)

#define oNFDATA  0x10 //数据寄存器的基地址(0x4E000010)

#define oNFCMD   0x08 //指令寄存器的基地址(0x4E000008)

#define oNFSTAT  0x20 //状态寄存器的基地址(0x4E000020)

#define oNFECC   0x2c //ECC寄存器的基地址(0x4E00002c)

 

另外,这时要把忽略低级初始化的两个宏去掉了,这样才能自己启动

//#define CONFIG_SKIP_LOWLEVEL_INIT 1

//#define CONFIG_SKIP_RELOCATE_UBOOT    1

 

修改arch/cpu/arm920t/start.s文件,添加nand启动的代码

 

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

#if 0                           //屏蔽掉从flash启动部分

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   

 

 

#ifdef CONFIG_S3C2442b_NAND_BOOT

    mov r1, #NAND_CTL_BASE   //以下是nand控制寄存器的一些初始化

    ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

    str r2, [r1, #oNFCONF]   //复位nand控制器

    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           

    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代码准备堆栈,堆栈的定义在下面

    mov fp, #0              

    //copy U-Boot to RAM

    ldr r0, =TEXT_BASE     //C代码第一个参数,u-boot在ram中地址,也就是0x33f80000

    mov r1, #0x0     //u-boot在nand中起始地址

    mov r2, #0x30000  //u-boot大小

    bl nand_read_ll  //跳转到C函数nand_read_ll

    tst r0, #0x0

    beq ok_nand_read

bad_nand_read:

    loop2: b loop2    //infinite loop

 

ok_nand_read: //读取成功

    mov r0, #0

    ldr r1, =TEXT_BASE

    mov r2, #0x400           //4 bytes * 1024 = 4K-bytes

go_next:     //核对4k启动内存中数据和nand中是不是一样

    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_S3C2442b_NAND_BOOT

#endif //CONFIG_SKIP_RELOCATE_UBOOT

 

_start_armboot: .word start_armboot //在这一句的下面加上DW_STACK_START的定义

.align 2

DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

 

接下来,在board/samsung/gec2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:

 

#include <config.h>

#define NF_BASE   0x4E000000 

#define __REGb(x) (*(volatile unsigned char *)(x))

#define __REGi(x) (*(volatile unsigned int  *)(x))

#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 NFSTAT __REGb(NF_BASE + 0x20) 

#define NAND_CHIP_ENABLE  (NFCONT &= ~(1<<1)) 

#define NAND_CHIP_DISABLE (NFCONT |= (1<<1))  

#define NAND_CLEAR_RB     (NFSTAT |= (1<<2))

#define NAND_DETECT_RB    { while(! (NFSTAT&(1<<2)) );}

#define NAND_SECTOR_SIZE 0x800            //这块nand页面大小变成2k了

#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)

/* low level nand read function */

int 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 -1;                 //地址不对齐,退出

    }

    NAND_CHIP_ENABLE;           //片选

    for(i=start_addr; i < (start_addr + size);)

    {

       

        NAND_CLEAR_RB;

        NFCMD = 0;          //read0指令

       

        NFADDR = 0 & 0xFF;                               //Column (A[7:0]) = 0

        NFADDR = 0 & 0xFF;                              // A[11:8]

        NFADDR = (i >> 11) & 0xFF;                  // A[19:12]

        NFADDR = (i >> 19) & 0xFF;                  // A[27:20]

       NFCMD = 0x30;             //read0x30指令

        NAND_DETECT_RB;

        for(j=0; j < NAND_SECTOR_SIZE; j++, i++)

        {

            *buf = (NFDATA & 0xFF);

            buf++;

        }

    }

    NAND_CHIP_DISABLE;

    return 0;

}

 

接下来,在board/samsung/board2442/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:

COBJS    := gec2440.o flash.o nand_read.o

 

还有一个重要的地方要修改,在arch/cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:

.text :

{

    cpu/arm920t/start.o    (.text)

    board/samsung/board2442/lowlevel_init.o (.text)

    board/samsung/board2442/nand_read.o (.text)

    *(.text)

}

 

现在终于能启动了

 

 

2442的nand和以前移植的2440不一样。不仅仅是一个片内,一个片外的区别,2442的nand是K9F1g08,一个page 2k字节,一个block 64个page,一共又有1024个block,所以一共有128m的大小。2440的nand是k9S1208,一个page 512字节, 总共64M。除了页面,大小不一样外,2442的nand读数据要多一个时钟周期,不仅需要read0指令,也需要read 0x30 指令。刚开始做nand的时候不知道指令周期的区别,只是简单的运用2440的读nand程序,没有read 0x30指令,数据读不出来,使得nand一直处于等待状态,u-boot停止启动。

 

我后来意识到有可能是nand芯片不一样,上网搜索了一下,参考了个天津大学的硕士论文,加上了read 0x30指令。nand终于能读了,不过u-boot每次进入start_armboot就会出现数据异常的错误,还是启动不了。通过调试工具的单步运行,检查寄存器的值,我发现内存0x33f80850的值很奇怪,是0x09090909. 打印内存后才发现,只有nand前2k的数据正确搬移了,后面出现了大面积的数据错误。

 

仔细静下心来分析,发现u-boot只成功搬移了前一个page的数据,后面的数据要不错位了(比如ram中0x800的数据又重复了nand中0x400的),要不就是空的(16个字的0xffffffff连着八个字的0x09090909)。这个问题说明是2442的nand页面配置不同导致了其寻址方式和2440nand也不同了。上网找了半天,找到个2442thumb汇编的bootloader,里面有读nand的c程序。对比了以下,果然nand地址寄存器的赋值方式不一样。对照着改了一下,nand终于正常读数了,u-boot也终于启动了。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值