主题 : u-boot-2012-07移植到mini2440开发板上 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 68445
精华: 0
发帖: 6
金钱: 30 两
威望: 6 点
贡献值: 0 点
综合积分: 12 分
注册时间: 2012-04-23
最后登录: 2014-07-22
楼主  发表于: 2012-09-19 23:32

 u-boot-2012-07移植到mini2440开发板上

移植u-boot-2012.07到mini2440开发板的笔记
<1363976004@qq.com>
下载、解压、编译、u-boot源码
    tar xjf u-boot-2012.07.tar.bz2
    cd u-boot-2012.04.01
    make smdk2410_config
    Make
    烧写到mini2440开发板的Nor flash中,启动开发板,观察现象。

创建mini2440单板
    将u-boot-2012.07/board/samsung目录下的smdk2410复制为mini2440,将该目录下的smdk2410.c命名为mini2440.c,修改mini2440目录下的Makefile,将COBJS:= smdk2410.o改为COBJS:= mini2440.o
    在board.cfg的65行的
smdk2410      arm      arm920t     -           samsung        s3c24x0
后面添加
mini2440       arm      arm920t     -           samsung        s3c24x0
    在u-boot-2012.07/include/configs中的smdk2410.h复制为mini2440.h
    
阅读u-boot代码分析启动过程并修改代码
    set the cpu to SVC32 mode
    turn off the watchdog
    设置时钟分频系数,使用默认的时钟120MHz
    设置内存控制器
    board_init_f(start.S)
    -->for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { (board.c)
            if ((*init_fnc_ptr)() != 0) {
                hang ();}
      -->board_early_init_f(smdk2410.c) (该函数中设置系统时钟)
           -->serial_init(serial_s3c24x0.c)
    -->serial_init_dev(serial_s3c24x0.c)
      -->_serial_setbrg(serial_s3c24x0.c)
    -->get_PCLK(speed.c)
      -->get_HCLK(speed.c)
    ........(发现宏CONFIG_S3C2440未定义,故使用的是2410的时钟,故应定义宏CONFIG_S3C2440)
    【通过以上的分析可知:在设置时钟与设置内存控制器的顺序是先设置内存控制器,再设置系统时钟,故存在问题。故应先设置时钟,在已设置的时钟下,在设置内存控制器,让内存工作在合适的频率下,使其SDRAM正常工作】
    更改:
    在start.S中:
    将
    /* FCLK:HCLK:PCLK = 1:2:4 */
        /* default FCLK is 120 MHz ! */
        ldr    r0, =CLKDIVN
        mov    r1, #3
        str    r1, [r0]
    改为
    #define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))
    
    /* 2. 设置时钟 */
        ldr r0, =0x4c000014
        mov r1, #0x05;              // FCLK:HCLK:PCLK=1:4:8
        str r1, [r0]
    
        /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
        mrc    p15, 0, r1, c1, c0, 0        /* 读出控制寄存器 */
        orr    r1, r1, #0xc0000000    /* 设置为“asynchronous bus mode” */
        mcr    p15, 0, r1, c1, c0, 0        /* 写入控制寄存器 */
    
        /* MPLLCON = S3C2440_MPLL_200MHZ */
        ldr r0, =0x4c000004
        ldr r1, =S3C2440_MPLL_400MHZ
        str r1, [r0]
    
        /* 启动ICACHE */
        mrc p15, 0, r0, c1, c0, 0    @ read control reg
        orr r0, r0, #(1<<12)
        mcr    p15, 0, r0, c1, c0, 0   @ write it back
    

    在mini2440.c的board_early_init_f函数中去掉:
    writel(0xFFFFFF, &clk_power->locktime);
    writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
          &clk_power->mpllcon);
    (在start.S中已经设置了时钟)

    通过之前的分析可知:u-boot的源代码中已经带有了2440的串口初始化函数,只需在配置文件mini2440.h中定义宏CONFIG_S3C2440,去掉CONFIG_S3C2410即可。
将#define CONFIG_S3C2410改为 #define CONFIG_S3C2440
    放在linux编译,发现错误,找出出现错误的文件,并找到该目录下的Makefile,找出控制该文件的宏,在mini2440.h文件中去掉该宏。然后在编译,如再有错误在重复以上步骤,直到编译成功。(只需去掉宏CONFIG_CMD_NAND的定义)
    将生成的u-boot.bin烧如Nor flash中启动开发板,可以看到开发板可以看到有信信息输出,证明该u-boot的对2440的时钟以及串口的支持已经正常。

     在串口上打印的信息中有 ### ERROR ### Please RESET the board ###
    搜索 Please RESET the board可以直到该信息是在board.c中答应出来的
    分析代码可知:
    if (flash_size > 0) {
    ....................
    }
    else
    {
    puts(failed);
    hang();
    -->    puts("### ERROR ### Please RESET the board ###\n");
        for (;;);
    }
    可知该u-boot只支持nor flash启动,当不能识别nor flash时就打印错误,并让程序进入死循环,但是mini2440的开发板既支持nor flash启动也支持nand flash启动。当开发板从nand flash启动时,cpu看到的零地址是片内内存的地址,这是cpu根本就不能看到nor flash。
    故应去掉board.c中:
    puts(failed);
        hang();
    并加上:
    puts("0  KB\n");  (当不能识别nor  flash时打印出flash: 0KB,但程序还是继续运行。)

    当从nand flash启动时,在上电后在硬件上,会将nand flash中前4k的代码复制到s3c2440的片内内存中,cpu此时看到的零地址是片内内存,即从片内内存开始执行,又由于u-boot的代码远大于4k,故在复制的4k的代码中应完成以下几件事:
    初始化内存控制器,是的SDRAM能正常工作
    将nand flash中的代码复制到SDRAM中
    当以nor flash启动时,nor flash能像读内存一样的读,不能像写内存一样的写,cpu可以直接在nor flash上取得指令执行,故在开始就要做得事情就是要将nor flash上的代码复制到SDRAM中,让代码能正常运行。
    通过分析u-boot的源代码可知,代码中的重定位代码是从内存的最高地址,减去需要使用的地址,算出代码重点位的起始地址。然后将代码修改后复制到SDRAM中,但是这种做法,很复杂,从定位代码大于4k,故不适用源代码中的方式,而采用将代码固定的复制到某个地址,在前4K的代码使用位置无关码。这样就使代码简单,在4k的代码中可以完成。
    当以nand flash启动时,要将nand flash中的代码复制到SDRAM中,故应该提供nand flash的读函数,即在单板目录下添加mini2440_init.c文件并修改该目录下的Makefile,代码如下:
    
    /* NAND FLASH控制器 */
    #define NFCONF (*((volatile unsigned long *)0x4E000000))
    #define NFCONT (*((volatile unsigned long *)0x4E000004))
    #define NFCMMD (*((volatile unsigned char *)0x4E000008))
    #define NFADDR (*((volatile unsigned char *)0x4E00000C))
    #define NFDATA (*((volatile unsigned char *)0x4E000010))
    #define NFSTAT (*((volatile unsigned char *)0x4E000020))
    
    void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
    
    static int isBootFromNorFlash(void)
    {
        volatile int *p = (volatile int *)0;
        int val;
    
        val = *p;
        *p = 0x12345678;
        if (*p == 0x12345678)
        {
            /* 写成功,是nand启动 */
            *p = val;
            return 0;
        }
        else
        {
            /* NOR不能像内存一样写 */
            return 1;
        }
    }
    
    void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
    {    
        int i = 0;
        
        /* 如果是nor启动 */
        if (isBootFromNorFlash())
        {
            while (i < len)
            {
                dest = src;
                i++;
            }
        }
        else
        {
            nand_read((unsigned int)src, dest, len);
        }
    }
    
    void clear_bss(void)
    {
        extern int __bss_start, __bss_end__;
        int *p = &__bss_start;
        
        for (; p < &__bss_end__; p++)
            *p = 0;
    }
    
    void my_nand_init(void)
    {
    #define TACLS   0
    #define TWRPH0  1
    #define TWRPH1  0
        /* 设置时序*/
        NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
        /* 使能nand flash控制器,初始化ECC,禁止片选 */
        NFCONT = (1<<4)|(1<<1)|(1<<0);    
    }
    
    static void nand_select(void)
    {
        NFCONT &= ~(1<<1);    
    }
    
    static void nand_deselect(void)
    {
        NFCONT |= (1<<1);    
    }
    
    static void nand_cmd(unsigned char cmd)
    {
        volatile int i;
        NFCMMD = cmd;
        for (i = 0; i < 10; i++);
    }
    
    static void nand_addr(unsigned int addr)
    {
        unsigned int col  = addr % 2048;
        unsigned int page = addr / 2048;
        volatile int i;
    
        NFADDR = col & 0xff;
        for (i = 0; i < 10; i++);
        NFADDR = (col >> 8) & 0xff;
        for (i = 0; i < 10; i++);
        
        NFADDR  = page & 0xff;
        for (i = 0; i < 10; i++);
        NFADDR  = (page >> 8) & 0xff;
        for (i = 0; i < 10; i++);
        NFADDR  = (page >> 16) & 0xff;
        for (i = 0; i < 10; i++);    
    }
    
    static void nand_wait_ready(void)
    {
        while (!(NFSTAT & 1));
    }
    
    static unsigned char nand_data(void)
    {
        return NFDATA;
    }
    
    void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
    {
        int col = addr % 2048;
        int i = 0;
            
        /* 1.选中 */
        nand_select();
    
        while (i < len)
        {
            /* 2. 发出读命令00h */
            nand_cmd(0x00);
    
            /* 3. 发出地址(分5步发出) */
            nand_addr(addr);
    
            /* 4. 发现读命令30h */
            nand_cmd(0x30);
    
            /* 5. 判断状态 */
            nand_wait_ready();
    
            /* 6. 读数据*/
            for (; (col < 2048) && (i < len); col++)
            {
                buf = nand_data();
                i++;
                addr++;
            }
            
            col = 0;
        }
    
        /* 7. 取消选中*/        
        nand_deselect();
    }
    
    将board.c中的void board_init_f(ulong bootflag)函数改为返回id的具有返回值的函数
    并在start.S中定义一个变量,在board.c函数中赋值,该变量用于存放栈指针。
    因为在start.S中已经完成了重定位,故在board_init_f函数中去掉relocate_code
    
    将start.S中的
call_board_init_f:
    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
    bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */
    ldr    r0,=0x00000000
    bl    board_init_f
改为:
    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
    bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */
    
    bl  my_nand_init  /* 初始化nand 控制器 */
    
    mov r0, #0              // 源
    ldr r1, _TEXT_BASE      // 目的
    ldr r2, _bss_start_ofs  // 长度
    
    bl copy_code_to_sdram   // 从定位代码
    bl clear_bss            // 清bss段

    ldr pc,=call_board_init_f  // 跳到SDRAM中执行
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:    
    ldr    r0,=0x00000000

    bl    board_init_f    
    ldr r1, _TEXT_BASE   // call_board_init_f 函数的返回值存放在r0中,刚好让                             // board_init_r函数使用
                            // 函数使用
    ldr sp, sp_addr_base  // 重新设置栈
    bl board_init_r       // 进入第二阶段
    
    在mini2440.h中将
    #define CONFIG_SYS_TEXT_BASE    0x0
    改为
    #define CONFIG_SYS_TEXT_BASE    0x33f80000  // 重定位代码的基地址为33f80000                                            (在高地址处留出512k供u-boot运行)
    原来的代码在链接时加了"-pie"选项, 使得u-boot.bin里多了"*(.rel*)", "*(.dynsym)"
    故应去掉 "-pie"选项
    arch/arm/config.mk中LDFLAGS_u-boot += -pie 去掉这行

    修改链接脚本: 把start.S, init.c, lowlevel.S等文件放在最前面
    即在arch/arm/cpu/u-boot.lds文件在CPUDIR/start.o后面添加:
          board/samsung/smdk2440/libsmdk2440.o
    
    移植u-boot支持nor flash的烧写与擦除
    通过之前的移植,在串口上有输出,为了得到了nor flash的厂家id和设备id,所以在代码中添加了一些打印信息,而这些打印信息是通过宏开关决定的,分析代码可知,只需在cfi_flash.c文件中添加
    #define DEBUG  1
    #define _DEBUG  1
    就可以打开宏开关,得到打印信息,然后编译烧写,观察串口的打印信息。信息如下:
    Flash: fwc addr (null) cmd f0 00f0 16bit x 16 bit
    fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit
    fwc addr 00005554 cmd 55 0055 16bit x 16 bit
    fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit
    fwc addr (null) cmd f0 00f0 16bit x 16 bit
    JEDEC PROBE: ID 1 2249 0
    fwc addr (null) cmd ff 00ff 16bit x 16 bit
    fwc addr (null) cmd 90 0090 16bit x 16 bit
    fwc addr (null) cmd ff 00ff 16bit x 16 bit
    JEDEC PROBE: ID 14 ea00 0
    *** Warning - bad CRC, using default environment
    
    可以得到nor flash的厂家id和设备id分别为1、 0x2249
    在代码中搜索JEDEC PROBE可以知道,该信息在代码中cfi_flash.c文件中打印出来的,代码为:
    
    debug("JEDEC PROBE: ID %x %x %x\n",
                            info->manufacturer_id,
                            info->device_id,
                            info->device_id2);
                    if (jedec_flash_match(info, info->start[0]))
                        break;
    
    -->jedec_flash_match
     -->for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
            if ((jedec_table.mfr_id & mask) == (info->manufacturer_id & mask) &&
                (jedec_table.dev_id & mask) == (info->device_id & mask)) {
                fill_info(info, &jedec_table, base);
                ret = 1;
                break;
            }
    
    可知:识别出ID后,在与数组jedec_table比较的时候没有匹配的,故应在jedec_table的定义处添加一项符合该nor flash的信息,即添加
        {
            .mfr_id        = (u16)AMD_MANUFACT,  
            .dev_id        = 0x2249,
            .name        = "ST Micro M29F400BB",
            .uaddr        = {
                [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
            },
            .DevSize        = SIZE_2MiB,
            .CmdSet            = CFI_CMDSET_AMD_LEGACY,
            .NumEraseRegions    = 4,
            .regions        = {
                ERASEINFO(0x04000, 1),
                ERASEINFO(0x02000, 2),
                ERASEINFO(0x08000, 1),
                ERASEINFO(0x10000, 31),
            }
        },
    在编译烧写,发现会打印出  ERROR: too many flash sectors
    搜索代码可知是由于:
    if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) {
                        printf("ERROR: too many flash sectors\n");
                        break;
    故需要修改宏CONFIG_SYS_MAX_FLASH_SECT的值,将配置文件中mini2440.h中的
    #define CONFIG_SYS_MAX_FLASH_SECT    (19)
    改为
    #define CONFIG_SYS_MAX_FLASH_SECT    (50)  
    (只需这个值打于35即可,因为该nor flash一共35个扇区)
    
    为了使u-boot的命令行输入命令时,可以通过TAB键补全,只需在配置文件    mini2440.h中定义宏
    #define CONFIG_AUTO_COMPLETE
    
    修改输入命令行前面的提示SMDK2410 # :只需将配置文件    mini2440.h中定义宏
    #define CONFIG_SYS_PROMPT    "SMDK2410 # "
    改为
    #define CONFIG_SYS_PROMPT    "[mini2440@u-boot]# "
    
    这样在命令行前面的提示就变成了[mini2440@u-boot]#
    
    修改u-boot,使其支持nand flash的烧写与读写工作。
    在drivers/mtd/nand 目录下将s3c2410_nand.c复制为s3c2440_nand.c,并修改Makefile,通过宏CONFIG_NAND_S3C2440控制s3c2440_nand.c文件是否编译,在mini2440.h中添加
    #define CONFIG_NAND_S3C2440
    去掉
    #define CONFIG_NAND_S3C2410
    
    分析board.c,在board_init_r函数中发现:
    #if defined(CONFIG_CMD_NAND)
        puts("NAND:  ");
        nand_init();        /* go init the NAND */
    #endif
    所以在nand_init函数中初始化nand flash。
    nand_init  (board.c)
      -->board_nand_init  (nand.c)
      -->nand_init_chip   (nand.c)
    -->nand_scan     (nand_base.c)
      -->nand_scan_ident  (nand_base.c)
    -->nand_set_defaults  (nand_base.c)
    -->nand_get_flash_type  (nand_base.c)
      -->nand_scan_tail   (nand_base.c)
    
    阅读s3c2440的芯片手册可知,应修改nand 控制器的时序:
    将
    #define S3C2440_NFCONF_TACLS(x)    ((x)<<8)
    #define S3C2440_NFCONF_TWRPH0(x)   ((x)<<4)
    #define S3C2440_NFCONF_TWRPH1(x)   ((x)<<0)
    改为
    #define S3C2440_NFCONF_TACLS(x)    ((x)<<12)
    #define S3C2440_NFCONF_TWRPH0(x)   ((x)<<8)
    #define S3C2440_NFCONF_TWRPH1(x)   ((x)<<4)
    
    去掉:
    cfg = S3C2440_NFCONF_EN;
    
    为了搞nand flash的烧写速度,可以将
    tacls = 4;
    twrph0 = 8;
    twrph1 = 8;
    改为:
    tacls = 4;
    twrph0 = 8;
    twrph1 = 8;
    
    为了使能NAND Flash控制器, 初始化ECC, 禁止片选
    应添加:    cfg = (1<<4)|(1<<1)|(1<<0);
    writel(cfg, &nand_reg->nfcont);
    
    将board_nand_init函数中的
    nand->select_chip = NULL;
    改为
    nand->select_chip = nand_select_chip;
    并添加函数:
    static void nand_select_chip(struct mtd_info *mtd, int chipnr)
    {
        struct s3c2440_nand *nand = s3c2440_get_base_nand();
    
        switch (chipnr) {
        case -1:
            /* 取消选中 */
            nand->nfcont |= (1<<1);
            break;
        case 0:
            //chip->cmd_ctrl(mtd, 0, 0 | NAND_CTRL_CHANGE);
            /* 选中 */
            nand->nfcont &= ~(1<<1);
            break;
    
        default:
            BUG();
        }
    
    将函数S3C2440_hwcontrol改为:
    static void S3C2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
    {
        struct s3c2440_nand *nand = s3c2440_get_base_nand();
    
        if (ctrl & NAND_CLE)
        {
            /* 写命令 */
            writeb(dat, &nand->nfcmd);
        }
        else if(ctrl & NAND_ALE)
        {
            /* 写地址 */
            writeb(dat, &nand->nfaddr);        
        }
    }
    
    在配置文件中定义宏:
    #define  CONFIG_s3c2440_nand_HWECC
    
    修改u-boot支持DM9000网卡
    阅读代码发现,在u-boot的源代码中已经支持了DM9000的代码,只需添加进去即可。
    查看 drivers/net/Makefile,发现dm9000x的编译由宏CONFIG_DRIVER_DM9000控制,所以必须在配置文件中定义宏。
    修改配置文件mini2440.h
    将
    #define CONFIG_CS8900        
    #define CONFIG_CS8900_BASE        0x19000300
    #define CONFIG_CS8900_BUS16    */
    改为:
    #define  CONFIG_DRIVER_DM9000
    #define CONFIG_DM9000_BASE      0x20000000
    #define DM9000_IO                CONFIG_DM9000_BASE
    #define DM9000_DATA             (CONFIG_DM9000_BASE + 4)
     在mini2440.c中的函数board_eth_init中添加
    dm9000_initialize(dis);
    
    添加默认参数:
     在mini2440.h的配置文件中添加默认参数:
    #define CONFIG_NETMASK        255.255.255.0
    #define CONFIG_IPADDR            192.168.1.128
    #define CONFIG_SERVERIP        192.168.1.125
    #define CONFIG_ETHADDR        00:0c:29:4d:e4:f4
    #define CONFIG_BOOTARGS   "console=ttySAC0,115200 root=/dev/mtdblock3"
    #define CONFIG_BOOTCOMMAND     "nand read 30000000 kernel;bootm 30000000"

    设置环境变量的保存地址
    在u-boot中保存环境变量的命令时saveenv,在代码中搜索saveenv知道,要想使环境变量保存在nand flash中,则必须将env_nand.c编译内u-boot中,查看common/Makefile 知道由宏CONFIG_ENV_IS_IN_NAND控制env_nand.c的编译,故应在配置文件mini2440.h中
    #define  CONFIG_ENV_IS_IN_NAND
    而u-boot源码提供环境变量的保存位置是在nor flash中,故应去掉宏
    #define CONFIG_ENV_ADDR            (CONFIG_SYS_FLASH_BASE + 0x070000)
    #define CONFIG_ENV_IS_IN_FLASH
    #define CONFIG_ENV_SIZE            0x10000
    #define CONFIG_ENV_OVERWRITE

    然后编译,发现错误,提示需要定义宏CONFIG_ENV_OFFSET、CONFIG_ENV_SIZE
    继续在mini2440.h的文件中定义
    #define  CONFIG_ENV_OFFSET  0x40000
    #define  CONFIG_ENV_SIZE     0x20000
    
    添加分区
    在配置文件中添加宏:
    #define CONFIG_CMD_MTDPARTS
    #define CONFIG_MTD_DEVICE
    #define MTDIDS_DEFAULT        "nand0=mini2440-0"  /* 那一个设备 */
    #define MTDPARTS_DEFAULT    "mtdparts=mini2440-0:256k(u-boot),"    \
                            "128k(params),"        \
                            "5m(kernel),"    \
                            "-(rootfs)"    
    
    在board.c的board_init_r函数中添加代码:
    run_command("mtdparts default", 0);
    
    为了能启动mini2440的linux内核,必须修改mini2440.c文件中
    将 board_init函数中的
    gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
    改为:
    gd->bd->bi_arch_number = MACH_TYPE_MINI2440;
    
    修改u-boot支持yaffs文件系统的烧写
    在配置文件中定义宏:
    #define CONFIG_CMD_NAND_YAFFS
    
    在common/cmd_nand.c的第670行左右:
    将
    ret = nand_write_skip_bad(nand, off, &rwsize,
                            (u_char *)addr,
                            WITH_INLINE_OOB);
    改为:
        ret = nand_write_skip_bad(nand, off, &rwsize,
                                (u_char *)addr,
                                WITH_YAFFS_OOB);
    
    在drivers/mtd/nand/nand_util.c中的第556行左右:    将
    ops.mode = MTD_OOB_AUTO;
    改为:
    ops.mode = MTD_OOB_RAW;

         注:该比较是我一遍移植,一边写的,应该不会有什么大问题,自己认为写的还较详细,希望对有需要的朋友有所帮助,有什么错误或者问题可以联系<1363976004@qq.com>
并指出,本人将不胜感激。
级别: 新手上路
UID: 23181
精华: 0
发帖: 14
金钱: 70 两
威望: 14 点
贡献值: 0 点
综合积分: 28 分
注册时间: 2010-06-14
最后登录: 2013-05-13
1楼  发表于: 2012-10-16 16:33
good,慢慢看看
级别: 新手上路
UID: 74964
精华: 0
发帖: 29
金钱: 145 两
威望: 29 点
贡献值: 0 点
综合积分: 58 分
注册时间: 2012-07-31
最后登录: 2017-09-13
2楼  发表于: 2013-04-16 23:50
感谢分享!!!