移植uboot2012.04.01到JZ2440(长文,详细记录)

1.下载uboot2012.04.01,并尝试编译smdk2410

    a.官网下载

    b.tar解压

    c.配置:make smdk2410_config

    d.编译:make

遇到问题:

    make: *** [u-boot] Error 139

    编译器版本低

故:换arm-linux-gcc-4.3.2

    e.再次编译:

 遇到问题:

    error trying to exec 'cc1': execvp: No such file or directory

    编译器位置的问题

故:将编译器解压到 /

    

    d.再次编译:编译成功,达到uboot.bin可执行文件

2.基于smdk2410创建单板目录和配置文件

    a.创建jz2440  board目录

    在u-boot-2012.04.01\board\samsung\  目录下创建jz2440目录,并将同目录下smdk2410中的文件全部拷贝到jz2440

    同时将smdk2410.c改成jz2440.c  ,并在同目录下将Makefile 中的 COBJS    := smdk2410.o    改为COBJS    := jz2440.

    b.创建jz2440.h配置文件

    在u-boot-2012.04.01\include\configs中copy smdk2410.h文件并重命名为jz2440.h粘贴到该目录下。

    c.在/board.cfg中添加:

    jz2440                     arm         arm920t     -                   samsung        s3c2440

    d./Makefile中:

    

    添加:arm-linux-

    

    目标:成功编译新board

                make jz2440_config

                make

                (成功)

3.创建SI-project,分析启动流程,了解内存布局,及各关键部位

    a.创建SI-project

    b.分析启动流程

        从arch/arm/cpu/arm920t/start.S开始:

        start_code:

                            set the cpu to SVC32 mode -> .....详情见移植过程......

    

    c.内存布局

    d.各关键部位

4.修改时钟和存储控制器,与board适配,并debugSDRAM是否可用

step1:修改时钟和存储控制器   

 set the cpu to SVC32 mode 

->#if    defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)     //delete  这部分

->turn off the watchdog    //2410   2440看门狗继存器位置相同,不改动

->mask all IRQs by setting all bits in the INTMR - default   //屏蔽IRQs   INTSUBMSK  2440也有,将CONFIG_S3C2410换成                      CONFIG_S3C2440    并将CONFIG_S3C2440在jz2440中定义

->第一个重要post1:时钟配置(MPLL){

        MPLL和UPLL在board/sansung/jz2440.c中int board_early_init_f(void)有:

        

        将以上MPLL注释,UPLL保留;在post1处手动添加MPLL的配置,如下

        a.set FCLK 和 Fin的关系

        b.set FCLK、HCLK、PCLK三者关系

        c.改变总线模式,使cpu核时钟使用FCLK(默认使用HCLK)

        

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    }

->CONFIG_SKIP_LOWLEVEL_INIT jz2440不定义,bl    cpu_init_crit 

    flush v4 I/D caches  、 disable MMU stuff and caches、bl    lowlevel_init(执行存储控制器的配置)

->bl    lowlevel_init:在jz2440/lowlevel.S中:

首先检查BWSCON  存储控制器寄存器组的基址:0x48000000  没错

分析配置过程,没错

更改SMRDATA:的值(与jz2440适配,注意时钟的变化,修改刷新reg)

step2:验证SDRAM是否可用:

使用JTAG物理调试(类似于gdb),采用openOCD作为上位机应用程序:

    a.烧写一个移植好的uboot,采用dnw(win7) +  usb + uboot 下载自己的uboot到Nor / Nand

    b.在uboot启动过程中,q进入命名模式:

        usb 1 30000000                    //采用usb下载   1-一直等待至下载完成    30000000  下载目的地址

        对Nor Flash:

        protect  off  all            //解除写保护

        erase 0 7FFFF                //擦除Nor 512K的大小(从0x0地址到0x7FFFF)

        cp.b  30000000  0  80000            //从0x30000000处复制 0-0x80000内容到Nor

        对Nand Flash:

        nand  erase 0 80000            //擦除nand 从0x0  到 0x80000

        nand write 30000000 0 80000   //从0x30000000复制0-0x80000的内容写入nand

    c.win7安装OpenOCD(包括驱动) + OpenJTAG  + telent进行debug

    d.打开telent功能(控制面板 - 程序与功能 - ... )

    e.打开openocd,OPJTAG连接开发板和PC,启动开发板,connect,识别成功后telnet到cmd中:

        reset halt 

        halt

        step 0x0            //从0x0开始运行

        step                    //单步运行    pc += 4

        bp  0xb0   4  hw        //在0xb0处设置断点,使其完成存储 控制器的配置   

        resume         //继续运行到断点0xb0

        mdw   0x30000000        //查看0x30000000处原来的值

        mww  0x30000000   0x11112222        //向0x30000000写入0x11112222

        mdw   0x30000000        //查看0x30000000处现在的值,若= 0x11112222则读写入成功(SDRAM可用)

    小结:在start.S中配置MPLL,在lowlevel.S中配置存储控制器,并在jz2440.c中注释MPLL的配置,以及在s3c24x0.h中注释s3c2410的GPIO结构体,(下一步:在jz2440.h中添加CONFIG_S3C2440宏)

    目标:MPLL配置成功,SDRAM可用,串口有输出(不配置CONFIG_S3C2440则为乱码)

    

    (成功)

5.修改时钟获取函数(如get_PCLK()等),使baudrate正常,使串口能输出

->Set stackpointer in internal RAM to call board_init_f(在SDRAM中设置栈指针,并调用board_init_f 第一阶段的初始化工作)

->board_init_f:主要做一些第一阶段的初始化工作:

->serial_init源码追溯

int serial_init(void)

{

    return serial_init_dev(UART_NR);

}

static int serial_init_dev(const int dev_index)

{

    //获得UART控制器基址

    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);  

#ifdef CONFIG_HWFLOW        //不管

    hwflow = 0;    /* turned off by default */

#endif

    /* FIFO enable, Tx/Rx FIFO clear */

    writel(0x07, &uart->ufcon);

    writel(0x0, &uart->umcon);

    /* Normal,No parity,1 stop,8 bit */                    //8N1

    writel(0x3, &uart->ulcon);

    /*

     * tx=level,rx=edge,disable timeout int.,enable rx error int.,

     * normal,interrupt or polling

     */

    writel(0x245, &uart->ucon);        //interrupt or polling

#ifdef CONFIG_HWFLOW

    writel(0x1, &uart->umcon);    /* rts up */

#endif

    /* FIXME: This is sooooooooooooooooooo ugly */

#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)

    /* we need auto hw flow control on the gsm and gps port */

    if (dev_index == 0 || dev_index == 1)

        writel(0x10, &uart->umcon);

#endif

    _serial_setbrg(dev_index);

    return (0);

}

void _serial_setbrg(const int dev_index)

{

    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);

    unsigned int reg = 0;

    int i;

    /* value is calculated so : (int)(PCLK/16./baudrate) -1 */

    reg = get_PCLK() / (16 * gd->baudrate) - 1;

    writel(reg, &uart->ubrdiv);

    for (i = 0; i < 100; i++)

        /* Delay */ ;

}

/*划重点了*/

ulong get_PCLK(void)

{

    struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();

    return (readl(&clk_power->clkdivn) & 1) ? get_HCLK() / 2 : get_HCLK();

}

ulong get_HCLK(void)

{

    struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();

#ifdef CONFIG_S3C2440                    //需要定义这个宏,在include/configs/jz2440.h中

    switch (readl(&clk_power->clkdivn) & 0x6) {

    default:

    case 0:

        return get_FCLK();

    case 2:

        return get_FCLK() / 2;

    case 4:

        return (readl(&clk_power->camdivn) & (1 << 9)) ?

            get_FCLK() / 8 : get_FCLK() / 4;

    case 6:

        return (readl(&clk_power->camdivn) & (1 << 8)) ?

            get_FCLK() / 6 : get_FCLK() / 3;

    }

#else

    return (readl(&clk_power->clkdivn) & 2) ? get_FCLK() / 2 : get_FCLK();

#endif

}

串口的支持其实就是追溯到/arch/arm/cpu/arm920t/s3c24x0/speed.c中    影响get_PLLCLK函数和get_HCLK函数并在自己的配置文件中添加宏定义CONFIG_S3C2440,

去掉CONFIG_S3C2410

->编译:

q1.在s3c2410_nand.c  中出错:

对照源码:

可见是因为把CONFIG_S3C2410这个宏去掉了,导致条件编译时没有生成struct s3c2410_nand结构体,后续的所有配置也就是错误的。

故:

    暂时添加CONFIG_S3C2410这个宏,因为在speed.c中只要配置了CONFIG_S3C2440这个宏即可,其余问题后续再解决。

(解决)

q2:

大概是个结构体吧,对照源码:从cpu_info.c追溯到:s3c24x0_cpu.h

可以看出CONFIG_S3C2410  又在作怪了。。。

故:

    那就将以上源码的CONFIG_S3C2440与CONFIG_S3C2410位置对换,且同时修改.h文件

(未解决,到s3c24x0.h中看:)

可见二者不能同时存在

故:

    直接把2410的部分结构体成员注释把。

(解决)

q3:

以上未定义的函数,在s3c2410.h中定义。。。。。

故:

    

在/driver/mtd/nand/Makefile中修改如上,s3c2440.o备用

(解决)

q4:

故:暂时注释吧

终于:(编译成功)

    小结:其实可以直接在jz2440.h中注释CONFIG_NAND_S3C2410  使得在/drivers/mtd/nand/Makefile中不编译s3c2410.o;然后注释掉2410的nand结构体。

              也可以不注释CONFIG_S3C2410,直接添加CONFIG_S3C2440,并在s3c24x0.h中注释2410的GPIO结构体。因为串口获取时钟只要定义CONFIG_S3C2440即可。

->烧写、启动、观察串口输出:

这个结果其实很好得到,就是要注意MPLLCON的配置顺序!!!!

小结:

    MPLL的配置;ICache开启;添加CONFIG_S3C2440(使得获取总线时钟函数获取到2440的正确时钟)才能使波特率正常。

6.修改启动方式之  支持Nand Flash启动

    原本,uboot是默认Nor Flash启动,不管是*.lds中的链接地址还是重定位代码,都是针对Nor Flash;

    现在,要支持Nand Flash启动,必然涉及修改*.lds中的链接地址和修改重定位方式,并去掉针对Nor 启动的-pie选项(-pie会导致在*.lds中生成一些关于已初始化全局变量和静态变量的段,可在arch/arm/config.mk中修改)

    此外,要注意将重定位之前的代码和相关变量定位于前4K,/jz2440/Makefile中修改

    1.将原来的nand_drv.c复制到jz2440/下,并在jz2440/Makefile中做对应修改(主要是nand_init 和nand_read 用于nand初始化和重定位uboot)

    

    编译,烧写,测试:没问题

    

    在board.c中定义_DEBUG,查看输出:

    2.在进入第一阶段的初始前(调用board_init_f之前),进行nand_init 和 重定位代码

    如下:

    

其中CONFIG_SYS_TEXT_BASE是原本在jz2440.h中定义为0x0,表示自动生成的uboot.lds中指定链接地址为0;

现在,修改为:(更改链接地址了)

这样不好!!!因为还没有重定位,在jz2440.h中定义的值可能编译器输出在4K之外,所以将上面的r1修改:

关于_TEXT_BASE和_bss_start_ofs在start.S中:

4.去掉针对Nor 启动的-pie选项;去掉针对Nor 启动的重定位相关代码

在/arch/arm/config.mk中:

在arch/arm/lib/board.c中:

在arch/arm/cpu/arm920t/start.S:

以上直接去掉,直到清bss段。

3.清bss段

篇外:

    一个可执行程序有:text、data等段,bss段不在可执行文件里面。

    bss段里面存的是为初始化的全局变量和静态变量,是一个个值。当从定位代码的时候,只重定位了text开始。。。bss_start,另外的bss段可以不重定位,直接在重定位后clear(初始化为0)。

    所以,我们应该给程序留的最终大小应该是从text开始。。。bss_end。

uboot原本的:

是结合Nor 重定位的,直接自己写吧:

4.进入第二阶段的初始化工作(进入到board_init_r函数)

篇外:接clear_bss后的启动流程分析

经过以上,启动流程分析,走过了board_init_f,再经过一些Nor Flash的重定位和clear bss,进入到board_init_r第二阶段的初始化工作:

->board_init_r:这里面就超级重要了,主要是对外设的支持,比如Nand Flash、Nor Flash 、网卡、LCD等。

以及开启cache、设置parm参数地址 和 2410机器ID(需要修改)、环境变量重定位、设置栈等。

board_init_r有两个参数需要确定:

void board_init_r(gd_t *id, ulong dest_addr)

gd_t *id 在board_init_f中就确定了,直接使函数返回(返回值保存在r0中)   //在函数定义和声明处(/include/common.h)更改返回值类型

ulong dest_addr就是链接地址:_TEXT_BASE

所以:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl    cpu_init_crit            //完成储存控制器的配置

#endif

    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)

    bic    sp, sp, #7 /* 8-byte alignment for ABI compliance */

    bl nand_init_new        //Nand Flash 控制器初始化

    mov r0, #0x0

    ldr r1, _TEXT_BASE

    ldr r2, _bss_start_ofs

    bl copy_bootcode_to_sdram    //重定位nand中的uboot到SDRAM 0x33f00000

clear_bss:

    ldr    r0, _bss_start_ofs

    ldr    r1, _bss_end_ofs

    mov    r2, #0x00000000        /* clear                */

clbss_l:

    str    r2, [r0]        /* clear loop...            */

    add    r0, r0, #4

    cmp    r0, r1

    bne    clbss_l

    //bl coloured_LED_init

    //bl red_led_on

    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        //第一阶段的初始化,修改返回id参数,保存在r0

    ldr r1, =_TEXT_BASE

    bl board_init_r

/*

* We are done. Do not return, instead branch to second part of board

* initialization, now running from RAM.

*/

#if 0            //都是针对Nor启动的代码

#ifdef CONFIG_NAND_SPL

    ldr     r0, _nand_boot_ofs

    mov    pc, r0

_nand_boot_ofs:

    .word nand_boot

#else

    ldr    r0, _board_init_r_ofs

    adr    r1, _start

    add    lr, r0, r1

    add    lr, lr, r9

    /* setup parameters for board_init_r */

    mov    r0, r5        /* gd_t */

    mov    r1, r6        /* dest_addr */

    /* jump to it ... */

    mov    pc, lr

_board_init_r_ofs:

    .word board_init_r - _start

#endif

_rel_dyn_start_ofs:

    .word __rel_dyn_start - _start

_rel_dyn_end_ofs:

    .word __rel_dyn_end - _start

_dynsym_start_ofs:

    .word __dynsym_start - _start

#endif

5.修改jz2440/ 下的Makefile ,添加nand_drv.o

6.在arch/arm/cpu/在的uboot.lds中修改(将start.o、nand_drev.o、lowlevel.o等放到前4K的位置)

经编译后,在jz2440/下:没有对应的start.o、nand_drev.o、lowlevel.o,却有一个libjz2440.o,可见uboot将他们做成了一个库。

所以将arch/arm/cpu/下的uboot.lds:

修改为:

7.总结修改了哪些文件:

start.S   jz2440/  jz2440.h   /arch/arm/config.mk    arch/arm/lib/board.c   /include/common.h   arch/arm/cpu/下的uboot.lds  

8.编译:

q1:

(解决)

q2:

注意英文逗号。。。。。

(解决)

q3:

board前面没有/  

(解决)

(编译成功)

9.烧写结果:

!!!!!不断重启或者Nor Flash哪里没有输出,不能正常终止或hang!!!!

如下:

纠错:

问题:做好移植后测试,根据串口信息,SDRAM,时钟,Flash,串口等基本判定能正常工作,但是在某个位置会重启或有单句调试信息不能正常输出。

    排查:

        出错点的上下文逻辑;

        整体配置;

        从SDRAM,时钟,串口等设置断点等方式逐一排查;

        检查关键点参数配置;

        内存分布,最终检查出在board_init_f中有个addr参数的设置没修改为Nand 启动的链接地址,导致了堆区上移,在board_init_r中执行堆的初始化时将已经重定位的前300K左右部分一并初始化了,当后面的代码执行时,程序自然就跑飞了。

再次编译测试:

(解决,已正常支持Nand 启动)

7.支持Nor Flash

添加宏 DEBUG (在linclude/common.h中),这样可以使所有的debug信息输出:

可见用的是CFI接口(在Nor Flash中也能找到支持CFI字样)

继续在board.c  board_init_r中分析:

进入flash_init()(cfi_flash.c中):

unsigned long flash_init (void)

{

    unsigned long size = 0;

    int i;

//没定义

#ifdef CONFIG_SYS_FLASH_PROTECTION

    /* read environment from EEPROM */

    char s[64];

    getenv_f("unlock", s, sizeof(s));

#endif

    /* Init: no FLASHes known */

    //CONFIG_SYS_MAX_FLASH_BANKS  jz2440.h中定义 = 1(只有一片Nor Flash)

    for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {

        /*  include/flash.c中 = 0xFFFF  先初始化Nor Flash id  */

        flash_info[i].flash_id = FLASH_UNKNOWN;

        /* Optionally write flash configuration register 写操作reg config*/

        cfi_flash_set_config_reg(cfi_flash_bank_addr(i),    //0x0

                     cfi_flash_config_reg(i));//0xFFFF

        //以上无作用

        //JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,

        //如果芯片不支持CFI,就需使用JEDEC了

        if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))// 0 0    USE CFI FLASH

            flash_get_size(cfi_flash_bank_addr(i), i);    //USE SMI FLASH

        size += flash_info[i].size;

        if (flash_info[i].flash_id == FLASH_UNKNOWN) {

//没定义,执行

#ifndef CONFIG_SYS_FLASH_QUIET_TEST

            printf ("## Unknown flash on Bank %d "

                "- Size = 0x%08lx = %ld MB\n",

                i+1, flash_info[i].size,

                flash_info[i].size >> 20);

#endif /* CONFIG_SYS_FLASH_QUIET_TEST */

        }

#ifdef CONFIG_SYS_FLASH_PROTECTION

        else if ((s != NULL) && (strcmp(s, "yes") == 0)) {

            /*

             * Only the U-Boot image and it's environment

             * is protected, all other sectors are

             * unprotected (unlocked) if flash hardware

             * protection is used (CONFIG_SYS_FLASH_PROTECTION)

             * and the environment variable "unlock" is

             * set to "yes".

             */

            if (flash_info[i].legacy_unlock) {

                int k;

                /*

                 * Disable legacy_unlock temporarily,

                 * since flash_real_protect would

                 * relock all other sectors again

                 * otherwise.

                 */

                flash_info[i].legacy_unlock = 0;

                /*

                 * Legacy unlocking (e.g. Intel J3) ->

                 * unlock only one sector. This will

                 * unlock all sectors.

                 */

                flash_real_protect (&flash_info[i], 0, 0);

                flash_info[i].legacy_unlock = 1;

                /*

                 * Manually mark other sectors as

                 * unlocked (unprotected)

                 */

                for (k = 1; k < flash_info[i].sector_count; k++)

                    flash_info[i].protect[k] = 0;

            } else {

                /*

                 * No legancy unlocking -> unlock all sectors

                 */

                flash_protect (FLAG_PROTECT_CLEAR,

                           flash_info[i].start[0],

                           flash_info[i].start[0]

                           + flash_info[i].size - 1,

                           &flash_info[i]);

            }

        }

#endif /* CONFIG_SYS_FLASH_PROTECTION */

    }

    flash_protect_default();

#ifdef CONFIG_FLASH_CFI_MTD

    cfi_mtd_init();

#endif

    return (size);

}

进入flash_detect_legacy函数(本文件中):

static int flash_detect_legacy(phys_addr_t base, int banknum)

{

    flash_info_t *info = &flash_info[banknum];

    if (board_flash_get_legacy(base, banknum, info)) {

        debug("detect_legacy......\n");

        /* board code may have filled info completely. If not, we

           use JEDEC ID probing. */

        debug("detect_legacy......info->vendor = %d\n",(unsigned short)(info->vendor));

        if (!info->vendor) {

            

            //厂家ID

            int modes[] = {

                CFI_CMDSET_AMD_STANDARD,    //在下面的for中两次循环用到

                CFI_CMDSET_INTEL_STANDARD    //AMD 和 INTEL是JEDEC查询采用的两个命令集

            };                                

            int i;

            debug("sizeof(modes) = %d\n",(int)sizeof(modes));

            for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) {

                info->vendor = modes[i];    //更改info->vendor = 2

                //Flash 0x0 addr

                info->start[0] =

                    (ulong)map_physmem(base,

                               info->portwidth,

                               MAP_NOCACHE);

                //info->portwidth = 2

                debug("detect_legacy......info->portwidth = %d\n",(uchar)(info->portwidth));

                if (info->portwidth == FLASH_CFI_8BIT

                    && info->interface == FLASH_CFI_X8X16) {

                    info->addr_unlock1 = 0x2AAA;

                    info->addr_unlock2 = 0x5555;

                } else {//here 2  FLASH_CFI_16BIT = 2

                    info->addr_unlock1 = 0x5555;

                    info->addr_unlock2 = 0x2AAA;

                }

                flash_read_jedec_ids(info);

                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]))        //重点查找对应的Nor Flash

                    break;

                else

                    unmap_physmem((void *)info->start[0],

                              MAP_NOCACHE);

            }

        }

        switch(info->vendor) {

        case CFI_CMDSET_INTEL_PROG_REGIONS:

        case CFI_CMDSET_INTEL_STANDARD:

        case CFI_CMDSET_INTEL_EXTENDED:

            info->cmd_reset = FLASH_CMD_RESET;

            break;

        case CFI_CMDSET_AMD_STANDARD:

        case CFI_CMDSET_AMD_EXTENDED:

        case CFI_CMDSET_AMD_LEGACY:

            info->cmd_reset = AMD_CMD_RESET;

            break;

        }

        info->flash_id = FLASH_MAN_CFI;

        debug("return 1  not use CFI\n");        //here

        return 1;

    }

    debug("return 0  use CFI\n");

    return 0; /* use CFI */

}

进入(jedec_flash_match函数(jedec_flash.c中):

/*-----------------------------------------------------------------------

* match jedec ids against table. If a match is found, fill flash_info entry

*/

int jedec_flash_match(flash_info_t *info, ulong base)

{

    int ret = 0;

    int i;

    ulong mask = 0xFFFF;

    if (info->chipwidth == 1)

        mask = 0xFF;

    //jedec_table数组存储着要匹配的Nor Flash参数,没有则自己对应datasheet添加

    for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {

        if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&

            (jedec_table[i].dev_id & mask) == (info->device_id & mask)) {

            fill_info(info, &jedec_table[i], base);

            ret = 1;

            break;

        }

    }

    return ret;

}

进入jedec_table数组:

这下应该可以了,在include/common.h中取消DEBUG的定义,编译测试:

OK!

上述有个ERROR: too many flash sectors(扇区太多了)

在ulong flash_get_size (phys_addr_t base, int banknum)函数中找到:

以上问题就是说检测到的扇区个数 sect_cnt > 程序中定义的扇区个数(在jz2440.h中)

再次编译测试:

ERROR没有了!OK!

现在,开发板已经支持Nor Flash的各种操作了,意味着在uboot里能用一些操作Nor Flash的命令了

可以看到shell交互成功(flinfo 查看Nor flash)!

其中的RO是只读的意思,是由于前一个uboot在进行烧写Nor Flash的时候,将烧写区进行保护,要写该区域就要执行protect命令。

执行erase 命令时有如下不必要的输出:

在cfi_flash.c   flash_is_busy函数中找到:

本文件的DEBUG没有取消,那就取消吧!

最后一个问题:

因为此时我们的栈还停留在SDRAM的下部:

应该设置重新设置在上部(board_init_f 中已经定义的addr_sp ,只不过我们由于修改为Nand 启动没有调用原来的relocate_code(addr_sp, id, addr);    故还没有使用)

会造成什么问题呢?

最直观的就是在uboot命令行交互时:(情景再现)

    上电后由于SDRAM不保存数据,会将内存数据初始化为0xff;

    当在uboot 中使用命令cp.b 30000000 80000 10000拷贝一部分数据到Nor flash时,uboot的命令时C函数实现,则必然要改变栈区数据,又同时在复制数据,当复制的区域与栈区重叠时,数据就会出现差异,导致程序奔溃。

    情景2:

        uboot向内核传递的参数在0x30000100,也有可能栈溢出改变参数,那样内核肯定启动不了。

所以我们要修改栈区:

    a.在start.S中定义一个全局变量,好在board.c中使用

    

    b.在board.c board_init_f 中:

    

    

    c.在start.S中修改栈:

    

再次编译测试:

不过此时已经可以用自己的uboot下载程序了,虽然还不能使用usb,但可以用loady命令:

用help命令查看找到:

其中loadb 可以使用kermit协议下载二进制文件;loady可以使用ymodem协议下载二进制文件,那就找一个支持这两个协议的串口工具吧:

(CRT是真的强,支持ymodem、xmodem、ASCII)

因为现在使用的是sp = 0x30000F80 所以下载到0x32000000,避免命令造成栈数据和下载数据的差异;(loady address,然后CRT选项:传输-发送ymodem)

现在直接使用reset命令重启开发板:

OK!再测试栈有没有改变:

就这样吧。。。

8.支持Nand Flash

     在board_init_r中:

进入nand_init函数(drivers/mtd/nand/nand.c):

void nand_init(void)

{

//没定义

#ifdef CONFIG_SYS_NAND_SELF_INIT

    board_nand_init();

#else

    int i;

    //重点,CONFIG_SYS_MAX_NAND_DEVICE = 1(只有一块NAND Flash)

    for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)

        nand_init_chip(i);    //0    

#endif

    printf("%lu MiB\n", total_nand_size / 1024);

//没定义

#ifdef CONFIG_SYS_NAND_SELECT_DEVICE

    /*

     * Select the chip in the board/cpu specific driver

     */

    board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);

#endif

}

进入nand_init_chip(0)本文件:

这里有两个重点函数:

board_nand_init: 这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数。

nand_scan:

先进入board_nand_init(在上s3c2410_nand.c中):

int board_nand_init(struct nand_chip *nand)

{/*这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数*/

    u_int32_t cfg;

    u_int8_t tacls, twrph0, twrph1;

    //获得时钟和nand控制器的基址0x4E000000

    struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();

    struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();

    debug("board_nand_init()\n");

    //使能nand flash

    writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);

    /* initialize hardware 时序、ECC、禁止选中等*/

#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)

    tacls  = CONFIG_S3C24XX_TACLS;

    twrph0 = CONFIG_S3C24XX_TWRPH0;

    twrph1 =  CONFIG_S3C24XX_TWRPH1;

#else

    tacls = 4;

    twrph0 = 8;

    twrph1 = 8;

#endif

    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 初始化 读写 指向NFDATA */

    nand->IO_ADDR_R = (void *)&nand_reg->nfdata;

    nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

    //初始化选中芯片的函数,可以自己写

    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;    //软件设置ECC

#endif

#ifdef CONFIG_S3C2410_NAND_BBT

    nand->options = NAND_USE_FLASH_BBT;

#else

    nand->options = 0;        //

#endif

    debug("end of nand_init\n");

    return 0;

}

再进入nand_scan(nand_base.c)函数:

/**

* nand_scan - [NAND Interface] Scan for the NAND device

* @mtd:    MTD device structure

* @maxchips:    Number of chips to scan for

*

* This fills out all the uninitialized function pointers

* with the defaults.

* The flash ID is read and the mtd/chip structures are

* filled with the appropriate values.

* The mtd->owner field must be set to the module of the caller

*

*/

int nand_scan(struct mtd_info *mtd, int maxchips)

{

    int ret;

    ret = nand_scan_ident(mtd, maxchips, NULL);//正常情况返回0

    if (!ret)

        ret = nand_scan_tail(mtd);    //ECC。。。

    return ret;

}

进入nand_scan_ident函数(本文件):

/**

* nand_scan_ident - [NAND Interface] Scan for the NAND device

* @mtd:         MTD device structure

* @maxchips:         Number of chips to scan for

* @table:         Alternative NAND ID table

*

* This is the first phase of the normal nand_scan() function. It

* reads the flash ID and sets up MTD fields accordingly.

*

* The mtd->owner field must be set to the module of the caller.

*/

int nand_scan_ident(struct mtd_info *mtd, int maxchips,

            const struct nand_flash_dev *table)

{

    int i, busw, nand_maf_id, nand_dev_id;

    struct nand_chip *chip = mtd->priv;        //

    const struct nand_flash_dev *type;

    /* Get buswidth to select the correct functions */

    busw = chip->options & NAND_BUSWIDTH_16;    //0

    /* Set the default functions */

    nand_set_defaults(chip, busw);//各种默认的函数指针,不执行其他函数

    /* Read the flash type 检测NAND芯片 类型并填充info*/

    type = nand_get_flash_type(mtd, chip, busw,

                &nand_maf_id, &nand_dev_id, table);

    if (IS_ERR(type)) {

#ifndef CONFIG_SYS_NAND_QUIET_TEST

        printk(KERN_WARNING "No NAND device found!!!\n");

#endif

        chip->select_chip(mtd, -1);

        return PTR_ERR(type);

    }

    /* Check for a chip array */

    for (i = 1; i < maxchips; i++) {

        chip->select_chip(mtd, i);//选中芯片

        /* See comment in nand_get_flash_type for reset */

        chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);//向NAND 写复位命令

        /* Send the command for reading device ID */

        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//读设备ID

        /* Read manufacturer and device IDs */

        if (nand_maf_id != chip->read_byte(mtd) || //找到就退出for

            nand_dev_id != chip->read_byte(mtd))

            break;

    }

    

#ifdef DEBUG

    if (i > 1)

        printk(KERN_INFO "%d NAND chips detected\n", i);

#endif

    /* Store the number of chips and calc total size for mtd */

    chip->numchips = i;        //i是NAND芯片个数,下面计算NAND大小会用到

    //  chip->chipsize应该在上面nand_get_flash_type里面读到了大小

    mtd->size = i * chip->chipsize;    

    return 0;

}

以上就是主要的分析流程,可见主要涉及的文件:board.c ->nand.c -> s3c2410_nand.c -> nand_base.c

nand.c之uboot自己的nand 协议层,实际的操作在s3c2410_nand.c -> nand_base.c中;

s3c2410_nand.c 是针对2410的nand文件,我们cp并命名为s3c2440_nand.c,并在对应Makefile中做修改:

Makefile:

#COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o

COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o

在jz2440.h中用宏开关控制这个Makefile的选择:

/*

* NAND configuration

*/

#ifdef CONFIG_CMD_NAND

#ifdef CONFIG_S3C2410

#define CONFIG_NAND_S3C2410

#define CONFIG_SYS_S3C2410_NAND_HWECC

#define CONFIG_SYS_MAX_NAND_DEVICE    1

#define CONFIG_SYS_NAND_BASE        0x4E000000

#else

#define CONFIG_NAND_S3C2440

#define CONFIG_SYS_S3C2440_NAND_HWECC

#define CONFIG_SYS_MAX_NAND_DEVICE    1

#define CONFIG_SYS_NAND_BASE        0x4E000000

#endif

#endif

然后根据流程分析,修改s3c2440_nand.c:

board_nand_init:

static void

s3c2440_nand_select(struct mtd_info *mtd, int chipnr)//自己写的

{

    //struct nand_chip *chip = mtd->priv;

    struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();

    switch (chipnr) {

    case -1://-1 diselect

        //chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);

        nand_reg->nfcont |= (1 << 1);

        break;

    case 0:

        nand_reg->nfcont &= ~(1 << 1);

        break;

    default:

        BUG();

    }    

}

int board_nand_init(struct nand_chip *nand)

{/*这个函数是准备基本函数、初始化NAND 和 填充信息,不跳到其他函数*/

    u_int32_t cfg;

    u_int8_t tacls, twrph0, twrph1;

    //获得时钟和nand控制器的基址0x4E000000

    struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();

    struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();

    debug("board_nand_init()\n");

    //使能nand flash 时钟 2440 2410同

    writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);

    /* initialize hardware 时序、ECC、禁止选中等*/

#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)

    tacls  = CONFIG_S3C24XX_TACLS;

    twrph0 = CONFIG_S3C24XX_TWRPH0;

    twrph1 =  CONFIG_S3C24XX_TWRPH1;

#else

    tacls = 0;    

    twrph0 = 1;    

    twrph1 = 0;

#endif

#if 0

    cfg = S3C2410_NFCONF_EN;

    cfg |= S3C2410_NFCONF_TACLS(tacls - 1);

    cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

    cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

#endif

    cfg = (tacls << 12) | (twrph0 << 8) | (twrph1 << 4);

    writel(cfg, &nand_reg->nfconf);

    cfg = (1 << 4) | (1 << 1) | (1 << 0);

    writel(cfg, &nand_reg->nfcont);

    /* initialize nand_chip data structure 初始化 读写 指向NFDATA */

    nand->IO_ADDR_R = (void *)&nand_reg->nfdata;

    nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

    //初始化选中芯片的函数,可以自己写

    nand->select_chip = s3c2440_nand_select;

    /* 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 = s3c2440_hwcontrol;//重要函数,要修改

    nand->dev_ready = s3c2440_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;    //软件设置ECC

#endif

#ifdef CONFIG_S3C2410_NAND_BBT

    nand->options = NAND_USE_FLASH_BBT;

#else

    nand->options = 0;        //

#endif

    debug("end of nand_init\n");

    return 0;

}

修改s3c2410_hwcontrol和s3c2410_dev_ready:

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

    if( ctrl & NAND_CLE ){

        writeb(cmd, &nand->nfcmd);

    }else{

        if( ctrl & NAND_ALE )

            writeb(cmd, &nand->nfaddr);

    }

#if 0

    debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

//以下就是先区分地址还是命令,确定NFCMMD 和 NFDATA 基址

//再发送cmd (可能是命令或数据              ctrl 决定)

    ulong IO_ADDR_W = (ulong)nand;    //2440nand 结构体的首地址4E000000

    if ((ctrl & NAND_CLE))

            //S3C2410_ADDR_NCLE = 8(则IO_ADDR_W = 4E000008  就是NFCMMD地址)

            //IO_ADDR_W |= S3C2410_ADDR_NCLE;

        IO_ADDR_W |= 8;

    if ((ctrl & NAND_ALE))

            //IO_ADDR_W |= S3C2410_ADDR_NALE;        

        IO_ADDR_W |= 0xC;

    

    chip->IO_ADDR_W = (void *)IO_ADDR_W;

    if (ctrl & NAND_NCE)    //选中芯片

        writel(readl(&nand->nfcont) & ~(1 << 1),

                   &nand->nfcont);

    else                    //取消选中

        writel(readl(&nand->nfcont) | (1 << 1),

                   &nand->nfcont);

    if (cmd != NAND_CMD_NONE)

        //因为chip->IO_ADDR_W已经确定了是NFDATA / NFCMMD ,所以既可以写命令也可以写地址

        writeb(cmd, chip->IO_ADDR_W);

#endif

}

static int s3c2440_dev_ready(struct mtd_info *mtd)

{

    struct s3c2440_nand *nand = s3c2440_get_base_nand();

    debug("dev_ready\n");

    return readl(&nand->nfstat) & 0x01;

}

再看nand_scan(nand_base.c)本不修改函数:

/**

* nand_scan - [NAND Interface] Scan for the NAND device

* @mtd:    MTD device structure

* @maxchips:    Number of chips to scan for

*

* This fills out all the uninitialized function pointers

* with the defaults.

* The flash ID is read and the mtd/chip structures are

* filled with the appropriate values.

* The mtd->owner field must be set to the module of the caller

*

*/

int nand_scan(struct mtd_info *mtd, int maxchips)

{

    int ret;

    ret = nand_scan_ident(mtd, maxchips, NULL);//正常情况返回0

    if (!ret)

        ret = nand_scan_tail(mtd);    //ECC。。。

    return ret;

}

修改nand_scan_ident函数:

/**

* nand_scan_ident - [NAND Interface] Scan for the NAND device

* @mtd:         MTD device structure

* @maxchips:         Number of chips to scan for

* @table:         Alternative NAND ID table

*

* This is the first phase of the normal nand_scan() function. It

* reads the flash ID and sets up MTD fields accordingly.

*

* The mtd->owner field must be set to the module of the caller.

*/

int nand_scan_ident(struct mtd_info *mtd, int maxchips,

            const struct nand_flash_dev *table)

{

    int i, busw, nand_maf_id, nand_dev_id;

    struct nand_chip *chip = mtd->priv;        //mtd->priv前面已给出

    const struct nand_flash_dev *type;

    /* Get buswidth to select the correct functions */

    busw = chip->options & NAND_BUSWIDTH_16;    //0

    /* Set the default functions */

    nand_set_defaults(chip, busw);//各种默认的函数指针,不执行其他函数,不修改

    /* Read the flash type 检测NAND芯片 类型并填充info,可以不修改,不过最好查看检测时使用的命令是否与Nand Flash一致*/

    type = nand_get_flash_type(mtd, chip, busw,

                &nand_maf_id, &nand_dev_id, table);

    if (IS_ERR(type)) {

#ifndef CONFIG_SYS_NAND_QUIET_TEST

        printk(KERN_WARNING "No NAND device found!!!\n");

#endif

        chip->select_chip(mtd, -1);

        return PTR_ERR(type);

    }

    /* Check for a chip array */

    for (i = 1; i < maxchips; i++) {

        chip->select_chip(mtd, i);//选中芯片

        /* See comment in nand_get_flash_type for reset */

        chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);//向NAND 写复位命令

        /* Send the command for reading device ID */

        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//读设备ID

        /* Read manufacturer and device IDs */

        if (nand_maf_id != chip->read_byte(mtd) || //找到就退出for

            nand_dev_id != chip->read_byte(mtd))

            break;

    }

    

#ifdef DEBUG

    if (i > 1)

        printk(KERN_INFO "%d NAND chips detected\n", i);

#endif

    /* Store the number of chips and calc total size for mtd */

    chip->numchips = i;        //i是NAND芯片个数,下面计算NAND大小会用到

    //  chip->chipsize应该在上面nand_get_flash_type里面读到了大小

    mtd->size = i * chip->chipsize;    

    return 0;

}

nand_set_defaults里面指定默认的chip->cmdfunc = nand_command,但是这是used for small page devices (256/512 Bytes per page),

不过在第一次调用nand_command时,函数里面会发命令检测Nand Flash的各种参数,并做适当的修改,比如将大页 换成

chip->cmdfunc = nand_command_lp,以下是nand_command_lp发命令和地址,注意其中有将NAND_CTRL_CHANGE取消,所以在更底层的s3c2440_hwcontrol中要注意一下。

编译测试:

以上是在Nor 中运行,可以识别出Nor 和 Nand。(Nor 启动时可以访问Nor 和 Nand)

再次测试:

在先将Nor中的uboot写入Nand。然后nand read 到SDRAM,最后间接比较Nor 和 Nand中的数据是否一致,可见是一致的,nand write /read等命令成功。

最后使用Nand 启动:

因为Nand启动无法访问Nor ,所以我自己给出的0MiB,但是成功识别了Nand Flash,并且到了命令行,一切正常。

9.支持DM9000网卡

在board_init_r中,在NAND FLASH后面紧接着可以有ONENAND、DATAFLASH、MMC、MISC、PCI等用于存储类的接口设备初始化。。。都不必用就没定义

来到下一个重点:

前言:dm9000和cs8900等网卡协议层和底层驱动uboot都已经写好了,我们只需要做适当的改动。

在drivers/net/Makefile中可以看到:

uboot把Makefile都写好了,只需要我们用宏开关(CONFIG_CS8900、CONFIG_DRIVER_DM9000)来控制Makefile即可:

在jz2440.h中配置CONFIG_DRIVER_DM9000:

仅仅是这样肯定不行的,DM9000是内存类接口的设备,要访问他,至少也要确定它的时序,位宽和基地址吧;

如此我们知道要设置存储控制器:

在原理图:

可以知道:

    jz2440选择的片选信号是nGCS4,也就是BANK4,基地址 = 0x20000000;

    只有一个LADDR2接到DM9000C的CMD接口,这是IO接口吧(在DM9000中看到这是区分发COMMAND和INDEX的)

    有LDATA0 - LDATA15共16根数据线  ,则位宽是16位。

设置存储控制寄存器BWSCON

ST4是设置SRAM的不管,WS4是设置等待信号,在原理图上没接WAIT信号(芯片手册上好像也没有wait引脚),不管,WD4是位宽 = 01

BANKCON4:

再配合DM9000C的datasheet中的时序图和S3C2440中的时序图在DM9000datasheet中找到对应的时序参数设置存储控制寄存器BANKCON4,以下是S3C2440和DM9000中对时序的要求:

对比两个图可以看出:

    tacs就是0ns   (nGCS4 = CS#,  A[24:0] = CMD) = 0ns   则在BANKCON4中设置tacs = 0 (0clock);

    tcos就是T1    (A[24:0] = CMD   nOE = IOR# ) = 0ns    则在BANKCON4中设置tcos = 0 (0clock);

    tacc就是T2(write)      (nOW低电平时间) = 10ns(max)      则在BANKCON4中设置tacc = 0 (1clock = 10ns(HCLK=100M));

    tcoh  = T5  (nOE上升,nGCS变化)  = 0ns                     则在BANKCON4中设置tacp = 0 (0 lock )

    tcah 类似tacs = 0ns                                                           则在BANKCON4中设置tcah = 0 (0clock);

    tacp = 是page的周期 = CMD的周期(read) = T1 + T2 + T5 >= 10ns             则在BANKCON4中设置tacp = 0 (2 lock = 20ns )

    最后一个PMC有些特殊。。。在DM9000中查找到:

    DM9000 1个周期只能处理1个数据,所以PMC应该为normal(1data)

则BANKCON4 = 0x0,不过可以采用默认的0x00000700

以上是硬件访问相关,下面进入eth_initialize函数(net/eth.c):

eth_initialize函数是uboot中实现的net协议层,其中的board_eth_init才是我们配置自己单板的函数(在jz2440.c中):

可见,这个函数此时的CONFIG_CS8900在开始被注释了,那么就是空函数,下面仿照格式添加修改:

小结:修改了jz2440.h 添加配置宏;修改了lowlevel.S 配置了相关的存储控制器reg;修改了jz2440.c 更改dm9000初始化函数。

编译测试:

Q1:缺少定义DM9000_DATA

看看其他board怎么用的吧:

可见我们要在jz2440.h中定义DM9000_DATA 和 CONFIG_DM9000_BASE:

再次编译:

看来来需要定义DM9000_IO:

再次编译:OK!

测试:

能识别DM9000了,再次测试uboot能不能ping通PC(PC的IP: 192.168.1.101)

先设置ipaddr 和ethaddr 环境变量,再ping,同时可以用tftp功能(要在PC上打开tftp软件)。

10.裁剪和修改默认参数

环境变量相关:

    a.有哪些环境变量?

    例如:

    

    还有ethaddr(MAC)、bootcmd、bootags

    bootags是要传递给内核的参数         bootm是在bootdelay变为0后自动执行的一连串命令(如读内核,bootcmd启动内核等)       

    b.环境变量代码在哪些文件?

        

    c.环境变量存储在flash的什么位置?

    

    代码分析:

    在board_init_f的初始化中:

    

    这个函数在各个存储器相关的文件里都有,那么该用哪个呢=====因为各个存储器的文件都在common/下,查看Makefile:

    

    在jz2440.h中又有:

    

    那么用的就是common/env_flash.c:

    主要就是CRC效验(去flash读环境变量,判断格式,是否可用,返回一个值),用这个值确定gd->env_valid以备重定位时使用。

    

    在board_init_r的初始化中:

    

    

void env_relocate(void)            //common/env_common.c

{

#if defined(CONFIG_NEEDS_MANUAL_RELOC)

    env_reloc();

#endif

    if (gd->env_valid == 0) {//在board_init_f的env_init初始化中将gd->env_valid = 0

#if defined(CONFIG_ENV_IS_NOWHERE)    /* Environment not changable */

        set_default_env(NULL);

#else

        //前面判断在flash上的环境变量不可用,返回bad CRC , 

        bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); //bootstage.h中枚举 = 60

        set_default_env("!bad CRC");

#endif

    } else {

        env_relocate_spec();

    }

}

void set_default_env(const char *s)

{

    if (sizeof(default_environment) > ENV_SIZE) {

        puts("*** Error - default environment is too large\n\n");

        return;

    }

    if (s) {

        if (*s == '!') {

            printf("*** Warning - %s, "

                "using default environment\n\n",

                s + 1);

        } else {

            puts(s);

        }

    } else {

        puts("Using default environment\n\n");

    }

    if (himport_r(&env_htab, (char *)default_environment,        //环境变量数组

            sizeof(default_environment), '\0', 0) == 0)

        error("Environment import failed: errno = %d\n", errno);

    gd->flags |= GD_FLG_ENV_READY;

}

这个数组的取值是宏定义,可以在jz2440.h这个配置文件中设置:

此时环境变量使用的还是默认的,数据保存在uboot的data中,而根据jz2440.h中定义的ENV_ADDR = 0x70000(大约是450K左右)

如果在uboot交互时使用save命令就把数据保存到flash上了,但是必然会破坏Flash中的UBOOT,那程序不就蹦了嘛

所在一般在NAND FLASH中采用分区的形式:

那么根据在common/Makefile:需要注释掉env_flash的相关配置,添加env_nand相关的配置:

其他是否还需要配置,看env_nand.c

int env_init(void)

{

#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)

    int crc1_ok = 0, crc2_ok = 0;

    env_t *tmp_env1;

#ifdef CONFIG_ENV_OFFSET_REDUND

    env_t *tmp_env2;

    tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);

    crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;

#endif

    tmp_env1 = env_ptr;

    crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;

    if (!crc1_ok && !crc2_ok) {

        gd->env_addr    = 0;

        gd->env_valid    = 0;

        return 0;

    } else if (crc1_ok && !crc2_ok) {

        gd->env_valid = 1;

    }

#ifdef CONFIG_ENV_OFFSET_REDUND

    else if (!crc1_ok && crc2_ok) {

        gd->env_valid = 2;

    } else {

        /* both ok - check serial */

        if (tmp_env1->flags == 255 && tmp_env2->flags == 0)

            gd->env_valid = 2;

        else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)

            gd->env_valid = 1;

        else if (tmp_env1->flags > tmp_env2->flags)

            gd->env_valid = 1;

        else if (tmp_env2->flags > tmp_env1->flags)

            gd->env_valid = 2;

        else /* flags are equal - almost impossible */

            gd->env_valid = 1;

    }

    if (gd->env_valid == 2)

        env_ptr = tmp_env2;

    else

#endif

    if (gd->env_valid == 1)

        env_ptr = tmp_env1;

    gd->env_addr = (ulong)env_ptr->data;

#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */

    gd->env_addr    = (ulong)&default_environment[0];

    gd->env_valid    = 1;

#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */

    return 0;

}

/* ! CONFIG_ENV_OFFSET_REDUND */

int saveenv(void)

{

    int    ret = 0;

    env_t    env_new;

    ssize_t    len;

    char    *res;

    nand_erase_options_t nand_erase_options;

    memset(&nand_erase_options, 0, sizeof(nand_erase_options));

    nand_erase_options.length = CONFIG_ENV_RANGE;

    nand_erase_options.offset = CONFIG_ENV_OFFSET;

    if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)

        return 1;

    res = (char *)&env_new.data;

    len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);

    if (len < 0) {

        error("Cannot export environment: errno = %d\n", errno);

        return 1;

    }

    env_new.crc = crc32(0, env_new.data, ENV_SIZE);

    puts("Erasing Nand...\n");

    if (nand_erase_opts(&nand_info[0], &nand_erase_options))

        return 1;

    puts("Writing to Nand... ");

    if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {

        puts("FAILED!\n");

        return 1;

    }

    puts("done\n");

    return ret;

}

在saveenv中又有4个宏CONFIG_ENV_RANGE、CONFIG_ENV_OFFSET、CONFIG_ENV_SIZE和ENV_SIZE

其中

在environment.h中有了定义,余下的三个在jz2440.h中自己配置:

相关地址可以确定mtd分区后确定。

现在,我们已经将环境变量的一些默认值确定了,也选择了保存在难度 flash中,现在编译测试:(目前只修改了jz2440.h文件)

编译成功,烧写测试:

可见环境变量已经起的作用,能倒计时,也能自动执行bootm命令启动内核,只是bootcmd环境变量的相关参数还没设置正确。

bad CRC还在,是因为环境变量还没有保存到NAND FLASH,执行save命令即可:

   

果然save成功。  说明我们前面的配置使环境变量七座了,且保存在NAND分区上了。

那么目前最激动人心的便是:正确配置和    mtd分区:

bootargs:内核打印信息输出采用串口0,挂载根文件系统在mtd的第三块区域

将uImage下载到0x30000000  再执行bootm 30000000启动内核,查看内核中的分区:

现在希望自动执行bootargs中的命令开自动启动内核:

先将uImage下载到NAND FLASH的  kernel分区0x60000

再改变环境变量

重启后:

MTD分区,虽然知道了Lunux中的分区,但UBOOT还没分区呢:

首先确定一点,mtd在drivers/mtd/Makefile中添加:

在jz2440.h:

可见目前是添加了mtd的,在uboot中查看mtd输出:

根据提示找到:(common/cmd_mtdparts.c)

int mtdparts_init(void)中:

那么看看Makefile中有没有添加这个文件:

可见是添加了的,那么是哪里的问题呢?根据代码推测是mtdids_default的问题:

由于MTDIDS_DEFAULT没有设置,也就输出我们看到的错误了,那么就设置默认的mtdids呗:

先看看别人怎么配置:

然后根据我们的分区信息在jz2440.h中配置如下:

更改了哪些文件:jz2440.h  

编译测试:

mtd命令生效,也有了分区信息,不过还是有错误信息,搜索:

在int mtdparts_init(void):

默认的变量虽然设置了,但是没有执行mtdparts default 做默认相关的工作,在uboot中看看:

果然是这样,那么就在错误之前添加:

run_command("mtdparts default", 0);

再次编译测试:

经验:在mtdparts_init函数调用之前设置run_command("mtdparts default", 0);,那就在board_init_r中吧:

再次编译测试:

解决最后一个问题:

现在UBOOT太大了,可以剪裁一下,其实主要就是在jz2440.h中注释宏:

先在uboot中看uboot有哪些可以不要功能:

功能太多,比如USB、操作文件系统:

编译测试:

只有249K了,也能放到NAND 的第一个分区了,太棒了。

11.yaffs映像和制作补丁

既然已经能启动内核了,那么就在fs分区下载一个文件系统,看能不能正确启动fs,并进入Linux shell:

先烧写jffs2文件系统:

    a.下载jffs2到SDRAM,然后写到NAND FLASH的fs分区:(注意这里的mtd分区已经做好)

    

    

    

    

    整个系统 = uboot2012 + Linux2.6 + jffs2

    下面测试烧写yaffs2文件系统:

    

    uboot执行nand write.yaffs2 命令时出错,在uboot中找到:

    在common/cmd_nand.c中有好几处Unknown nand command suffix ,但是都在一个do_nand函数里面,并且可以看出,是do_nand对下载不同fs的打印信息:

      分析程序,大概就是先对比nand read/write  确定是read 还是write ,再对比后缀,如write.jffs2  write.yaffs 等,其中关于对比yaffs的代码由宏开关决定,没有打开,所以肯定下载不能成功。

    

#ifdef CONFIG_CMD_NAND_YAFFS      //这个宏要打开,宏打开后用nand_write_skip_bad烧写!

        } 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

先在jz2440.h中定义一个吧:

再来查找一下Unknown nand command suffix在哪里输出,肯定不是上面,根据源码追到:

现在编译uboot再测试一下:

VFS成功挂载yaffs2文件系统,但是串口打不开,init进程也找不到!!!

根据韦老师找到的uboot  bug:

在uboot中根据nand dump查看烧写在nand上fs分区的内容,一页一页的比较与yaffs二进制文件的差别,发现nand write只烧写了1页!这是uboot自身的bug!!!下面改正:

#ifdef CONFIG_CMD_NAND_YAFFS      //宏打开后用nand_write_skip_bad烧写!

进入nand_write_skip_bad(nand, off, &rwsize,(u_char *)addr, WITH_YAFFS_OOB);        //drivers/mtd/nand/nand_util.c

/**

* nand_write_skip_bad:

*

* Write image to NAND flash.

* Blocks that are marked bad are skipped and the is written to the next

* block instead as long as the image is short enough to fit even after

* skipping the bad blocks.

*

* @param nand      NAND device

* @param offset    offset in flash

* @param length    buffer length

* @param buffer        buffer to read from

* @param flags        flags modifying the behaviour of the write to NAND

* @return        0 in case of success

*/

int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,

            u_char *buffer, int flags)

{

    int rval = 0, blocksize;

    size_t left_to_write = *length;

    u_char *p_buffer = buffer;

    int need_skip;

#ifdef CONFIG_CMD_NAND_YAFFS

    //WITH_YAFFS_OOB是作为参数传进来的

    if (flags & WITH_YAFFS_OOB) {

        if (flags & ~WITH_YAFFS_OOB)//肯定是不行的。。。

            return -EINVAL;

        int pages; //erasesize  writesize  在nand_get_flash_type中设定

        //128K / 2K = 64页

        pages = nand->erasesize / nand->writesize;

        //blocksize 就是一块总的大小 = 128K + (oobsize*64页)

        blocksize = (pages * nand->oobsize) + nand->erasesize;

        if (*length % (nand->writesize + nand->oobsize)) {

            printf ("Attempt to write incomplete page"

                " in yaffs mode\n");

            return -EINVAL;

        }

    } else

#endif

    {

        blocksize = nand->erasesize;//不执行

    }

    /*

     * nand_write() handles unaligned, partial page writes.

     *

     * We allow length to be unaligned, for convenience in

     * using the $filesize variable.

     *

     * However, starting at an unaligned offset makes the

     * semantics of bad block skipping ambiguous (really,

     * you should only start a block skipping access at a

     * partition boundary).  So don't try to handle that.

     */

    if ((offset & (nand->writesize - 1)) != 0) {//对齐检查

        printf ("Attempt to write non page aligned data\n");

        *length = 0;

        return -EINVAL;

    }

    /*check_skip_len返回值:

        当offset在nand flash中无效时,返回-1(程序员应该不会范这种低级错误。。。)

        当offset在nand flash中有效时,若在offset和length之间有坏块则返回1,否则返回0

    */

    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;

    }

    /*当need_skip = 0(无坏块) 且 flag中不存在WITH_DROP_FFS

        目前flag之没有WITH_DROP_FFS的,所以当没有坏块时就会在这里面执行nand_write,

        都不会到下面的while中取写了,所以下面的修改也就没意义了!!!

            重点:下面的while是针对有坏块的情况,涉及OOB的操作,所以可以在可以添加一个条件:

        !(WITH_YAFFS_OOB)即当没有定义WITH_YAFFS_OOB这个宏时。

    */

    //if (!need_skip && !(flags & WITH_DROP_FFS)) {

    if (!need_skip && !(flags & WITH_DROP_FFS) && !(WITH_YAFFS_OOB)) {

        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;

    }

    while (left_to_write > 0) {//循环写

        size_t block_offset = offset & (nand->erasesize - 1);

        size_t write_size, truncated_write_size;

        WATCHDOG_RESET ();

        if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {

            printf ("Skip bad block 0x%08llx\n",

                offset & ~(nand->erasesize - 1));

            offset += nand->erasesize - block_offset;

            continue;

        }

        if (left_to_write < (blocksize - block_offset))

            write_size = left_to_write;

        else

            write_size = blocksize - block_offset;

#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;//原来是MTD_OOB_AUTO;  

            ops.ooboffs = 0;

            pages = write_size / pagesize_oob;

            for (page = 0; page < pages; page++) {

                WATCHDOG_RESET();

                ops.datbuf = p_buffer;

                ops.oobbuf = ops.datbuf + pagesize;

                rval = nand->write_oob(nand, offset, &ops);

                //write_oob写第一页时,返回0,成功,但是下面的!会break写

                if (rval)        //原本为if(!rval)

                    break;

                offset += pagesize;

                p_buffer += pagesize_oob;

            }

        }

        else

#endif

        {

            truncated_write_size = write_size;

#ifdef CONFIG_CMD_NAND_TRIMFFS

            if (flags & WITH_DROP_FFS)

                truncated_write_size = drop_ffs(nand, p_buffer,

                        &write_size);

#endif

            rval = nand_write(nand, offset, &truncated_write_size,

                    p_buffer);

            offset += write_size;

            p_buffer += write_size;

        }

        if (rval != 0) {

            printf ("NAND write to offset %llx failed %d\n",

                offset, rval);

            *length -= left_to_write;

            return rval;

        }

        left_to_write -= write_size;

    }

    return 0;

以上修改了3个bug!

再次编译测试:

注意:对jffs文件系统:烧写时用nand write.jffs2;对yaffs文件系统:烧写时用nand write.yaffs   //必须是固定了,在代码中定死了。。。不然可能会写错误。

tftp 30000000 uImage_4.3;nand erase.part kernel;nand write 30000000 kernel

tftp 30000000 uImage_new;nand erase.part kernel;nand write 30000000 kernel

tftp 30000000 fs_mini_mdev_new.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize

tftp 30000000 fs_mini.jffs2;nand erase.part fs;nand write.jffs2 30000000 260000 $filesize

tftp 30000000 fs_mini_mdev_new.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize

tftp 30000000 fs_mini.yaffs2;nand erase.part fs;nand write.yaffs 30000000 260000 $filesize

tftp 30000000 u-boot_new;nand erase.part u-boot;nand write 30000000 u-boot

1.uboot2012 + uImage_4.3  2.6 + jffs2 

使用:fs_mini_mdev_new.jffs2 

能挂载,但是显示s3c2440-sdi s3c2440-sdi: powered down.

注意要内核正确启动,这个的machid = 16a

不能正常启动fs的原因可能是内核与fs的编译工具链不同

使用fs_mini.jffs2:

也有,s3c2440-sdi s3c2440-sdi: powered down.

但是能正常启动fs,显示shell

2.uboot2012 + uImage_4.3  2.6 + yaffs2

使用:fs_mini_mdev_new.yaffs2

能挂载

Warning: unable to open an initial console.

Kernel panic - not syncing: No init found.  Try passing init= option to kernel.

使用fs_mini.yaffs2:

相同错误

3.uboot2012 + uImage_new  3.4 + jffs2

使用machid = 7cf 内核分区才正确

使用fs_mini.jffs2:

Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000200

使用:fs_mini_mdev_new.jffs2

正常挂载,正常显示shell

ALSA 是音频驱动,显示没有声卡,这个没关系

4.uboot2012 + uImage_new  3.4 + yaffs2

使用machid = 7cf 内核分区才正确

使用fs_mini.yaffs2:

使用:fs_mini_mdev_new.yaffs2

制作补丁:

diff -urN 老的目录   新的目录 > name.patch

u是按照某种格式输出

r递归比较

N是把没有的文件当空文件

    

12.总结

    

猜你喜欢

转载自blog.csdn.net/Carl_0/article/details/87857346