zoukankan      html  css  js  c++  java
  • Mini440之uboot移植之实践NAND启动(四)

    在之前的章节我们已经介绍了u-boot如何支持我们的NOR FLASH,以及DM9000网卡的支持。

    这一节我们将会在Young / s3c2440_project[u-boot-2016.05-nor-flash】代码的基础上新建u-boot-2016.05-nand-flash项目,使得我们项目支持NAND,由于u-boot默认采用的NOR启动,这里我们需要修改u-boot实现NAND启动。

    一、NAND启动

    如果想支持NAND启动,我们需要考虑以下问题,S3C2440在NAND启动时S3C2440的NAND 控制器会自动把NAND FLASH中的前4K代码数据搬到内部SRAM中(在S3C2440片内有一块被称为SteppingStone的片内SRAM,它的大小为4K),片内SRAM被映射到nGCS0片选的空间(nGCS0片选的空间,即0x00000000),CPU从0x00000000位置开始运行程序。

    然而我们的uboot程序是远远大于4kb的,因此程序大于4kb的时候需要从 NAND FLASH中拷贝到 SDRAM中去运行。自然可以想到烧到 NAND FLASH中的程序前面一部分代码应该是初始化SDRAM(程序最终需要拷贝到SDRAM中去运行)和 将NAND FLASH中的剩余的程序拷贝到SDRAM中去(全考过去也行,方便点),然后跳转到SDRAM中执行。

    既然我们已经有了思路,那么接下来就是如何去实现。

    在分析Mini440之uboot移植之源码分析start.S(一)中我们介绍到CPU上点首先执行的是start.S文件(arch/arm/cpu/arm920t),在内核级配置和初始化过程中会调用cpu_init_crit函数。

    我们可以在cpu_init_crit函数执行完之后进行适当扩充,添加以下功能:

    • NAND初始化;
    • 代码重定位,将uboot拷贝到SDRAM,并进行跳转运行;
    • 屏蔽u-boot原生重定位代码;

    1.1 新建init.c文件

    新建init.c文件并将文件放在board/samsung/smdk2440路径下,并修改当前路径下的Makefile:

    obj-y    := smdk2440.o init.o
    obj-y    += lowlevel_init.o

    init.c代码如下:

    /**************************************************************************
     *
     *  FileName  : init.c
     *  Function  : 一些初始化相关的函数
     *  Author    : zy
     *
     *************************************************************************/
    #include <init.h>
    
    
    /**************************************************************************
     *
     *  Function    :  nand flash读写相关函数
     *
     *************************************************************************/
    
    /* 等待NAND Flash就绪 */
    void _nand_wait_idle(void)
    {
        int i;
        while(!(NFSTAT & BUSY));
        for(i=0; i<10; i++);
    }
    
    /* 发出片选信号 */
    void _nand_select_chip(void)
    {
        int i;
        NFCONT &= ~(1<<1);                   /* set CE=0 */
        for(i=0; i<10; i++);    
    }
    
    /* 取消片选信号 */
    void _nand_deselect_chip(void)
    {
        NFCONT |= (1<<1);                   /* set CE=1 */
    }
    
    /* 发出命令 */
    void _nand_write_cmd(unsigned char cmd)
    {
        int i;
        NFCMMD = cmd;
        for(i=0; i<10; i++);
    }
    
    /* 发出地址 */
    void _nand_write_addr_byte(unsigned char addr)
    {
        volatile int i;
        NFADDR = addr;
        for(i=0; i<10; i++);
    }
    
    /* 发出地址,5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址 */
    void _nand_write_addr(unsigned int addr)
    {
        int col, page;
    
        col = addr & NAND_PAGE_MASK_SIZE;
        page = addr / NAND_PAGE_SIZE;     /* 块号  */
    
        _nand_write_addr_byte(col & 0xff);            /* Column Address A0~A7 */
        _nand_write_addr_byte((col >> 8) & 0x0f);     /* Column Address A8~A11 */
        _nand_write_addr_byte(page & 0xff);            /* Row Address A12~A19 */
        _nand_write_addr_byte((page >> 8) & 0xff);    /* Row Address A20~A27 */
        _nand_write_addr_byte((page >> 16) & 0x01);    /* Row Address A28 */
    }
    
    /* 复位 */
    void _nand_reset(void)
    {
        _nand_select_chip();
        _nand_write_cmd(0xff);          // 复位命令
        _nand_wait_idle();
        _nand_deselect_chip();
    }
    
    /* 读取数据 */
    unsigned char _nand_read_data(void)
    {
        return NFDATA;
    }
    
    /* 写入数据 */
    void _nand_write_data(unsigned char data)
    {
        int i;
        NFDATA = data;
        for(i=0; i<10; i++);
    }
    
    
    /* 初始化and Flash */
    void nand_init_ll(void)
    {
        #define TACLS   0
        #define TWRPH0  1
        #define TWRPH1  0
    
        /* 设置时序 */
        NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 使能NAND控制器 */
        NFCONT = (1<<4)|(1<<1)|(1<<0);
    
        /* 复位NAND Flash */
        _nand_reset();
    }
    
    
    /* 发送读取数据命令,将nand start_addr处数据读取到buf中,读取长度为size, start_addr参数必须按页对齐   success:0 fail:-1  */
    int nand_read_data(unsigned char *buf, unsigned int start_addr, unsigned int size)
    {
        int i = 0;
        int j = 0;
    
        if (start_addr & NAND_PAGE_MASK_SIZE ) {
            return -1;    /* 地址或长度不对齐 */
        }
    
        /* 选中芯片 */
        _nand_select_chip();
    
        /* 按页读取 */
        while (i < size) {
          /* 发出READ命令 */
          _nand_write_cmd(0);
    
          /* write address */
          _nand_write_addr(start_addr+i);
    
          /* write cmd */
          _nand_write_cmd(0x30);
    
          /* wait */
          _nand_wait_idle();
    
          /* 读取一页数据 */
          for(; (j < NAND_PAGE_SIZE) &&  (i < size); j++)
          {
              buf[i++] = _nand_read_data();
          }
          j = 0;
        }
    
        /* 取消片选信号 */
        _nand_deselect_chip();
        
        return 0;
    }
    
    
    
    /**************************************************************************
     *
     *  Function    :  自动区分是nand启动还是nor启动
     *                 NAND启动,此时内部4k SRAM映射到0x00处,才可以访问该内存
     *                 NOR启动,此时内部2M NOR FLASH映射到地址0x00处,此时无法写入内存,片内SRAM映射到0x40000000地址处
     *
     *************************************************************************/
    int is_boot_from_nor_flash(void)
    {
        volatile unsigned int *p = (volatile unsigned int *)0x00;
    
        unsigned int val = *p;          /* get value from address 0x00 */
        *p = 0x12345678;                /* write value to address 0x00 */
    
        /* 写成功, 对应nand启动 */
        if(*p == 0x12345678)
        {
            *p = val;    
            return 0;
        }
    
        return 1;
    }
    
    /**************************************************************************
     *
     *  Function    :  清除bss段
     *
     *************************************************************************/
    void clear_bss(void)
    {
        /* 要从lds文件中获得 __bss_start, __bss_end
         */
        extern int __bss_end, __bss_start;
    
        int *p = &__bss_start;
        for (; p < &__bss_end; p++)
            *p = 0;
    
    }
    
    
    
    /**************************************************************************
     *
     *  Function    :  将代码从nand/nor falsh复制到sdram
     *
     *************************************************************************/
    void copy_code_to_sdram(void)
    {
        /* 要从lds文件中获得 __image_copy_start, __image_copy_end 然后从0地址把数据复制到__image_copy_start */
        extern int __image_copy_start, __image_copy_end;
        volatile unsigned int *dest = (volatile unsigned int *)&__image_copy_start;
        volatile unsigned int *end = (volatile unsigned int *)&__image_copy_end;
        volatile unsigned int *src = (volatile unsigned int *)0;
        unsigned int len = (unsigned int)(&__image_copy_end) - (unsigned int)(&__image_copy_start);
    
        /* nor falsh boot:  nor flash address 0x00 */
        if (is_boot_from_nor_flash())
        {
            /* 把nor flash的内容全部copy到sdram */
             while (dest < end)
             {
                 *dest++ = *src++;
              }
        }
        else 
        {
            // 将nand flash内容复制到SDRAM
            nand_init_ll();
            nand_read_data(dest, (unsigned int)src, len);
        }
    }

    在include目录下新建init.h头文件,代码如下:

    #ifndef __S3C2440_INIT__
    #define __S3C2440_INIT__
    
    #define GSTATUS1        (*(volatile unsigned long *)0x560000B0)
    #define BUSY            1
    
    /* page size 2048byte */
    #define NAND_PAGE_SIZE           2048
    #define NAND_PAGE_MASK_SIZE      (NAND_PAGE_SIZE - 1)
    #define NAND_BLOCK_SIZE          (64*NAND_PAGE_SIZE)
    #define NAND_BLOCK_MASK_SIZE     (NAND_BLOCK_SIZE-1)
    
     /* NAND FLASH (see S3C2440 manual chapter 6, www.100ask.net) */
    #define     NFCONF        (*(volatile unsigned int *)0x4e000000)        // 配置寄存器
    #define     NFCONT       (*(volatile unsigned int *)0x4e000004)        // 控制寄存器
    #define     NFCMMD       (*(volatile unsigned char *)0x4e000008)        // 命令寄存器
    #define     NFADDR       (*(volatile unsigned char *)0x4e00000C)        // 地址寄存器
    #define     NFDATA       (*(volatile unsigned char *)0x4e000010)        // 数据寄存器
    #define     NFMECCD0     (*(volatile unsigned int *)0x4e000014)
    #define     NFMECCD      (*(volatile unsigned int *)0x4e000018)
    #define     NFSECCD      (*(volatile unsigned int *)0x4e00001C)
    #define     NFSTAT       (*(volatile unsigned int *)0x4e000020)
    #define     NFESTAT0     (*(volatile unsigned int *)0x4e000024)
    #define     NFESTAT1     (*(volatile unsigned int *)0x4e000028)
    #define     NFMECC0      (*(volatile unsigned int *)0x4e00002C)
    #define     NFMECC1      (*(volatile unsigned int *)0x4e000030)
    #define     NFSECC       (*(volatile unsigned int *)0x4e000034)
    #define     NFSBLK       (*(volatile unsigned int *)0x4e000038)
    #define     NFEBLK       (*(volatile unsigned int *)0x4e00003C)
    
    /* 函数声明 */
    extern void copy_code_to_sdram();
    extern void clear_bss(void);
    
    #endif

    修改include/configs/smdk2440.h文件,将CONFIG_SYS_TEXT_BASE宏改为0x33f00000,也就是uboot重定位后的位置, 这里没有设置成0x33f80000,而是留了1M空间供给uboot重定位,主要是因为我们的u-boot可能大于512kb。

    具体可以看考Mini440之uboot移植之源码分析u-boot重定位(三)

    1.2 start.S修改(arch/arm/cpu/arm920t/start.S)

    lowlevel_init函数执行完之后就是执行bl _main(arch/arm/lib/crt0.S),因此我们可以在cpu_init_crit执行之后,并加入NAND初始化,以及重定位相关代码。修改之后的start.S代码:

    #ifndef CONFIG_SKIP_LOWLEVEL_INIT
        bl    cpu_init_crit
    #endif
    
        ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)      @ 设置栈    0x30000f50
        bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
       
        bl copy_code_to_sdram                   @ 代码重定位  
        bl clear_bss                            @ 清除bss段
        ldr pc,=_main                           @ 绝对跳转,跳转到SDRAM运行 

    最后我们使用ldr进行了绝对跳转,跳转到SDRAM中运行。

    1.3 屏蔽u-boot重定位代码

    因为我们已经将代码从NAND FLASH拷贝到了SDRAM中,因此需要屏蔽掉u-boot的重定位、以及BSS清除相关的代码。屏蔽掉_main(arch/arm/lib/crt0.S)中如下代码:

        ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */
        b    relocate_code

    修改reserve_uboot(common/board_f.c):

    static int reserve_uboot(void)
    {
        /*
         * reserve memory for U-Boot code, data & bss
         * round down to next 4 kB limit
         */
        gd->relocaddr = CONFIG_SYS_TEXT_BASE;
    
    
        debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10,
              gd->relocaddr);
    
        gd->start_addr_sp = gd->relocaddr;
    
        return 0;
    }

    1.4 修改链接脚本

    start.S, init.c(实现重定位), lowlevel.S(实现初始化SDRAM)等文件放在内存空间最前面。修改arch/arm/cpu/u-boot.lds部分信息如下:

        .text :
        {
            *(.__image_copy_start)
            *(.vectors)
            CPUDIR/start.o (.text*)
            board/samsung/smdk2440/built-in.o (.text*)  
            *(.text*)
        }

    build-in.o是将smdk2440单板目录下的所有*.c,*S文件编译后,连接成一个库文件。

    1.5 去掉 "-pie"选项

    在u-boot源码分析重定位中,我们说过在arm-linux-ld时加了"-pie"选项, 使得u-boot.bin里多了"*(.rel*)", "*(.dynsym)",从而程序非常大,不利于从NAND启动,所以我们修改代码,并取消"-pie"选项。运行:

     grep "\-pie"  *  -nR

    我们修改arch/arm/config.mk,屏蔽掉下面代码:

    LDFLAGS_u-boot += -pie

    此外,我们还需要屏蔽掉顶层Makefile文件中的重定位规则检验的代码,否则编译汇报如下错误:

     我们定位到1387行,修改为如下:

    checkarmreloc: u-boot
        @RELOC="`$(CROSS_COMPILE)readelf -r -W $< | cut -d ' ' -f 4 | \
            grep R_A | sort -u`"; \
    #    if test "$$RELOC" != "R_ARM_RELATIVE" -a \
    #         "$$RELOC" != "R_AARCH64_RELATIVE"; then \
    #        echo "$< contains unexpected relocations: $$RELOC"; \
    #        false; \
    #    fi

    修改arch/arm/cpu/u-boot.lds,将如下内容:

        .bss_start __rel_dyn_start (OVERLAY) : {
            KEEP(*(.__bss_start));
            __bss_base = .;
        }
    
        .bss __bss_base (OVERLAY) : {
            *(.bss*)
             . = ALIGN(4);
             __bss_limit = .;
        }
    
        .bss_end __bss_limit (OVERLAY) : {
            KEEP(*(.__bss_end));
        }

    修改为:

    . = ALIGN(4);
    /* 定义变量,保存.bss段起始地址,可以在汇编代码中之间使用 */
    __bss_start = .;
    
    /* 全局的未初始化变量存在于.bss段中 */
    .bss :
    {
        *(.bss*)
    }
    
     __bss_end = .;

    为什么修改这个呢,主要是因为源代码配置的bss和rel.dyn使用的同一个段空间,而我们编译时取消了-pie选项,会导致bss段起始地址变成0x00。具体可以参考u-boot中bss段的使用

    二、NAND识别

    u-boot在启动过程中,同样是识别不了NAND的,这一小节,我们将修改代码,使其能够识别NAND,并正常输出其大小。

    2.1 添加NAND FLASH操作文件

    拷贝drivers/mtd/nand/s3c2410_nand.c为s4c2440_nand.c到同目录下。

    cd drivers/mtd/nand
    cp s3c2410_nand.c s3c2440_nand.c

    修改当前目录下的Makefile如下:

    2.2 分析nand初始化调用流程

    Mini440之uboot移植之源码分析board_init_r(四)我们介绍到board_init_r()函数会进行nand的初始化。具体在 initr_nand()函数执行过程中,该函数位于common/board_r.c文件:

    static int initr_nand(void)
    {
        puts("NAND:  ");
        nand_init();
        return 0;
    }

    初始化nand flash,并输出nand falsh信息。其中nand_init()定义在drivers/mtd/nand/nand.c文件中,去掉无用代码如下:

    void nand_init(void)
    {
        int i;
        for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
            nand_init_chip(i);
    
        printf("%lu MiB\n", total_nand_size / 1024);
    }

    nand_init_chip()函数代码如下(driver/mtd/nand/nand.c文件中):

    static void nand_init_chip(int i)
    {
        struct mtd_info *mtd = &nand_info[i];
        struct nand_chip *nand = &nand_chip[i];
        ulong base_addr = base_address[i];
        int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
    
        if (maxchips < 1)
            maxchips = 1;
    
        mtd->priv = nand;
        nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
    
        if (board_nand_init(nand))
            return;
    
        if (nand_scan(mtd, maxchips))
            return;
    
        nand_register(i);
    }

    其中board_nand_init()函数与nand_scan()函数最终都会调用drivers/mtd/nand/s3c2440_nand.c里的函数,所以需要修改s3c2440_nand.c文件来支持我们的NAND FLASH。

    2.3 修改s3c2440_nand.c

    1. 将该文件所有的2410都替换为2440;

    2. 添加s3c24x0_nand_select()函数;

    在board_nand_init()函数前添加s3c24x0_nand_select()函数,函数代码如下:

    static void s3c24x0_nand_select(struct mtd_info *mtd, int chipnr)
    {
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
     
        switch (chipnr) {
        case -1:  //取消选择
            nand->nfcont |= (1<<1);
        break;
        case 0:   /*选中*/
            nand->nfcont&=~(1<<1);
            break;
     
        default:
            BUG();
        }
    }

     3. 修改board_nand_init()函数:

    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 s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
    
        debug("board_nand_init()\n");
    
        writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
    
        /* initialize hardware */
    #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 = S3C2440_NFCONF_TACLS(tacls - 1);
        cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
        cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
        writel(cfg, &nand_reg->nfconf);           // 设置时序
    
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
        writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);
    
        /* initialize nand_chip data structure */
        nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
        nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
    
        nand->select_chip = s3c24x0_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 = s3c24x0_hwcontrol;  // 发出命令 或者发出地址
    
        nand->dev_ready = s3c24x0_dev_ready;
    
    #ifdef CONFIG_S3C2440_NAND_HWECC
        nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;
        nand->ecc.calculate = s3c24x0_nand_calculate_ecc;
        nand->ecc.correct = s3c24x0_nand_correct_data;
        nand->ecc.mode = NAND_ECC_HW;
        nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
        nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
        nand->ecc.strength = 1;
    #else
        nand->ecc.mode = NAND_ECC_SOFT;  // 软件ecc校验
    #endif
    
    #ifdef CONFIG_S3C2440_NAND_BBT
        nand->bbt_options |= NAND_BBT_USE_FLASH;
    #endif
    
        debug("end of nand_init\n");
    
        return 0;
    }

    修改宏定义:

    #define S3C2440_NFCONT_INITECC     (1<<4)
    #define S3C2440_NFCONF_TACLS(x)    ((x)<<12)
    #define S3C2440_NFCONF_TWRPH0(x)   ((x)<<8)
    #define S3C2440_NFCONF_TWRPH1(x)   ((x)<<4)
    
    // 地址寄存器偏移
    #define S3C2440_ADDR_NALE 0x0C
    // 命令寄存器偏移
    #define S3C2440_ADDR_NCLE 0x08

    4.修改s3c2440_hwcontrol区分命令和地址

    static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
    {
         struct nand_chip *chip = mtd->priv;
         struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
    
         debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
    
        if (ctrl & NAND_CTRL_CHANGE) {
            ulong IO_ADDR_W = (ulong)nand;
    
            if (ctrl & NAND_CLE)
                IO_ADDR_W |= S3C2440_ADDR_NCLE;
            if (ctrl & NAND_ALE)
                IO_ADDR_W |= S3C2440_ADDR_NALE;
            if(cmd ==NAND_CMD_NONE)
                IO_ADDR_W = &nand->nfdata;
    
            chip->IO_ADDR_W = (void *)IO_ADDR_W;
        }
    
        if (cmd != NAND_CMD_NONE)
            writeb(cmd, chip->IO_ADDR_W);
    
    }

    在这个函数中,除了修改寄存器的值以及设置写命令和写地址的IO端口外,我们还增加了if(cmd == NAND_CMD_NONE)判断语句。

    如果不加这个判断语句,向NAND FLASH内写数据是写不进去的,尽管系统不会提示任何错误,并显示“OK”,但其实数据是没有被写入的,因此一定要加上这条语句。

    这是因为在写完命令和地址后,一定还要把IO端口的地址重新设置为寄存器NFDATA。

    需要说明的是,由于系统没有定义CONFIG_S3C2440_NAND_HWECC,因此我们暂时先不对s3c24x0_nand_enable_hwecc函数、s3c24x0_nand_calculate_ecc函数和s3c24x0_nand_correct_data函数进行修改。

    完整代码:

    /*
     * (C) Copyright 2006 OpenMoko, Inc.
     * Author: Harald Welte <laforge@openmoko.org>
     *
     * SPDX-License-Identifier:    GPL-2.0+
     */
    
    #include <common.h>
    
    #include <nand.h>
    #include <asm/arch/s3c24x0_cpu.h>
    #include <asm/io.h>
    
    #define S3C2440_NFCONT_INITECC     (1<<4)
    #define S3C2440_NFCONF_TACLS(x)    ((x)<<12)
    #define S3C2440_NFCONF_TWRPH0(x)   ((x)<<8)
    #define S3C2440_NFCONF_TWRPH1(x)   ((x)<<4)
    
    // 地址寄存器偏移
    #define S3C2440_ADDR_NALE 0x0C
    // 命令寄存器偏移
    #define S3C2440_ADDR_NCLE 0x08
    
    #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 s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
    {
         struct nand_chip *chip = mtd->priv;
         struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
    
         debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
    
        if (ctrl & NAND_CTRL_CHANGE) {
            ulong IO_ADDR_W = (ulong)nand;
    
            if (ctrl & NAND_CLE)
                IO_ADDR_W |= S3C2440_ADDR_NCLE;
            if (ctrl & NAND_ALE)
                IO_ADDR_W |= S3C2440_ADDR_NALE;
            if(cmd ==NAND_CMD_NONE)
                IO_ADDR_W = &nand->nfdata;
    
            chip->IO_ADDR_W = (void *)IO_ADDR_W;
        }
    
        if (cmd != NAND_CMD_NONE)
            writeb(cmd, chip->IO_ADDR_W);
    
    }
    
    static int s3c24x0_dev_ready(struct mtd_info *mtd)
    {
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        debug("dev_ready\n");
        return readl(&nand->nfstat) & 0x01;
    }
    
    #ifdef CONFIG_S3C2440_NAND_HWECC
    void s3c24x0_nand_enable_hwecc(struct mtd_info *mtd, int mode)
    {
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        debug("s3c24x0_nand_enable_hwecc(%p, %d)\n", mtd, mode);
        writel(readl(&nand->nfconf) | S3C2440_NFCONT_INITECC, &nand->nfconf);
    }
    
    static int s3c24x0_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                          u_char *ecc_code)
    {
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
        ecc_code[0] = readb(&nand->nfecc);
        ecc_code[1] = readb(&nand->nfecc + 1);
        ecc_code[2] = readb(&nand->nfecc + 2);
        debug("s3c24x0_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 s3c24x0_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("s3c24x0_nand_correct_data: not implemented\n");
        return -1;
    }
    #endif
    
    
    static void s3c24x0_nand_select(struct mtd_info *mtd, int chipnr)
    {
        struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
     
        switch (chipnr) {
        case -1:  //取消选择
            nand->nfcont |= (1<<1);
        break;
        case 0:   /*选中*/
            nand->nfcont&=~(1<<1);
            break;
     
        default:
            BUG();
        }
    }
    
    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 s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
    
        debug("board_nand_init()\n");
    
        writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
    
        /* initialize hardware */
    #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 = S3C2440_NFCONF_TACLS(tacls - 1);
        cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
        cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
        writel(cfg, &nand_reg->nfconf);
    
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
        writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);
    
        /* initialize nand_chip data structure */
        nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
        nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
    
        nand->select_chip = s3c24x0_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 = s3c24x0_hwcontrol;
    
        nand->dev_ready = s3c24x0_dev_ready;
    
    #ifdef CONFIG_S3C2440_NAND_HWECC
        nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;
        nand->ecc.calculate = s3c24x0_nand_calculate_ecc;
        nand->ecc.correct = s3c24x0_nand_correct_data;
        nand->ecc.mode = NAND_ECC_HW;
        nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
        nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
        nand->ecc.strength = 1;
    #else
        nand->ecc.mode = NAND_ECC_SOFT;
    #endif
    
    #ifdef CONFIG_S3C2440_NAND_BBT
        nand->bbt_options |= NAND_BBT_USE_FLASH;
    #endif
    
        debug("end of nand_init\n");
    
        return 0;
    }
    View Code

    2.4 硬件ECC支持

    在没有开启CONFIG_S3C2440_NAND_HWECC配置的情况下,对NAND读写操作进行的都是软件ECC校验,即用软件编程的方法实现ECC。

    如果想要实现硬件ECC、我们需要配置CONFIG_S3C2440_NAND_HWECC,并对代码进行修改,具体参考u-boot-2016.03 在mini2440上移植之nandflash 硬件ecc,这里就不介绍了。

    三、 编译下载运行

    3.1 配置编译

    make distclean
    make smdk2440_defconfig
    make ARCH=arm CROSS_COMPILE=arm-linux- V=1

    3.2 查看内存分布

    直接反汇编u-boot文件,可以得到反汇编代码:

    arm-linux-objdump -D u-boot > u-boot.dis

    查看u-boot.dis,可以看到代码的链接起始地址为0x33f00000:

    继续查看u-boot.dis,找到NAND初始化代码:

     可以看到NAND相关代码均没有超过4kb,执行完代码重定位后,执行了代码跳转,跳转到SDRAM中运行__main函数:

    2.3 下载到NAND FALSH

    我们将代码下载到NAND FLASH中,并从NAND启动。这里有两种方式下载到NAND:

    • 使用MiniTools下载程序,我这u-boot后编译还有400kb+,MiniTools无法下载成功,好像是MiniTools不支持下载大于256KB的u-boot。
    • 使用网络下载,由于我这台开发板网卡坏掉了没发通过tftp下载到NAND。

    所以这里就不展示了,但是在后面的章节我们会介绍u-boot才裁切,裁切之后u-boot只有两百多kb,这时候就能使用MiniTools下载到NAND FLASH中了。Mini440之uboot移植之裁剪、分区与环境变量设置(五)

    3.4 下载到NOR FALSH

    我们利用Jlink将代码下载到NOR FLASH中,并从NOR启动。串口输出信息如下:

    initcall: 33f5dc2c
    
    
    U-Boot 2016.05 (Jan 14 2022 - 22:22:01 +0800)
    
    initcall: 33f0f3c0
    U-Boot code: 33F00000 -> 33F80000  BSS: -> 33FCF870
    initcall: 33f007a8
    CPUID: 32440001
    FCLK:      400 MHz
    HCLK:      100 MHz
    PCLK:       50 MHz
    initcall: 33f0f620
    DRAM:  initcall: 33f00518
    initcall: 33f0f570
    Monitor len: 000CF870
    Ram size: 04000000
    Ram top: 34000000
    initcall: 33f0f1b0
    initcall: 33f0f368
    TLB table from 33ff0000 to 33ff4000
    initcall: 33f0f1c8
    initcall: 33f0f330
    Reserving 830k for U-Boot at: 33f00000
    initcall: 33f0f304
    Reserving 4160k for malloc() at: 33af0000
    initcall: 33f0f524
    Reserving 80 Bytes for Board Info at: 33aeffb0
    initcall: 33f0f1d0
    initcall: 33f0f2d0
    Reserving 168 Bytes for Global Data at: 33aeff08
    initcall: 33f0f258
    initcall: 33f0f204
    initcall: 33f0f1d8
    initcall: 33f0f610
    initcall: 33f0f49c
    
    RAM Configuration:
    Bank #0: 30000000 64 MiB
    
    DRAM:  64 MiB
    initcall: 33f0f23c
    New Stack Pointer is: 33aefee0
    initcall: 33f0f460
    initcall: 33f0f3f0
    Relocation Offset is: 00000000
    Relocating to 33f00000, new gd at 33aeff08, sp at 33aefee0
    initcall: 33f0f69c
    initcall: 33f0f6a4
    initcall: 33f0f888 (relocated to 33f0f888)
    WARNING: Caches not enabled
    initcall: 33f0f6bc (relocated to 33f0f6bc)
    initcall: 33f0f6e4 (relocated to 33f0f6e4)
    initcall: 33f0f86c (relocated to 33f0f86c)
    using memory 0x33af0000-0x33f00000 for malloc()
    initcall: 33f0f6ec (relocated to 33f0f6ec)
    initcall: 33f0f678 (relocated to 33f0f678)
    initcall: 33f0f858 (relocated to 33f0f858)
    initcall: 33f184b4 (relocated to 33f184b4)
    initcall: 33f0f848 (relocated to 33f0f848)
    initcall: 33f0f7b0 (relocated to 33f0f7b0)
    Now running in RAM - U-Boot at: 33f00000
    initcall: 33f0f6f4 (relocated to 33f0f6f4)
    initcall: 33f0f7cc (relocated to 33f0f7cc)
    Flash: fwc addr 00000000 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 00000000 cmd f0 00f0 16bit x 16 bit
    JEDEC PROBE: ID 1 2249 0
    Found JEDEC Flash: AMD AM29LV160DB
    unlock address index 1
    unlock addresses are 0x555/0x2aa
    erase_region_count = 1 erase_region_size = 16384
    erase_region_count = 2 erase_region_size = 8192
    erase_region_count = 1 erase_region_size = 32768
    erase_region_count = 31 erase_region_size = 65536
    flash_protect ON: from 0x00000000 to 0x0007F213
    protect on 0
    protect on 1
    protect on 2
    protect on 3
    protect on 4
    protect on 5
    protect on 6
    protect on 7
    protect on 8
    protect on 9
    protect on 10
    flash_protect ON: from 0x00070000 to 0x0007FFFF
    protect on 10
    2 MiB
    initcall: 33f0f794 (relocated to 33f0f794)
    NAND:  board_nand_init()
    end of nand_init
    hwcontrol(): 0xff 0x83
    hwcontrol(): 0xffffffff 0x81
    dev_ready
    hwcontrol(): 0x90 0x83
    hwcontrol(): 0x00 0x85
    hwcontrol(): 0xffffffff 0x81
    dev_ready
    hwcontrol(): 0x90 0x83
    hwcontrol(): 0x00 0x85
    hwcontrol(): 0xffffffff 0x81
    dev_ready
    hwcontrol(): 0x90 0x83
    hwcontrol(): 0x40 0x85
    hwcontrol(): 0xffffffff 0x81
    dev_ready
    256 MiB
    initcall: 33f0f764 (relocated to 33f0f764)
    *** Warning - bad CRC, using default environment
    
    Destroy Hash Table: 33f79a34 table = 00000000
    Create Hash Table: N=79
    INSERT: table 33f79a34, filled 1/79 rv 33af50d8 ==> name="bootdelay" value="5"
    INSERT: table 33f79a34, filled 2/79 rv 33af4f98 ==> name="baudrate" value="115200"
    INSERT: table 33f79a34, filled 3/79 rv 33af4f48 ==> name="ipaddr" value="192.168.0.188"
    INSERT: table 33f79a34, filled 4/79 rv 33af5100 ==> name="serverip" value="192.168.0.200"
    INSERT: table 33f79a34, filled 5/79 rv 33af513c ==> name="ethaddr" value="08:00:3e:26:0a:5b"
    INSERT: table 33f79a34, filled 6/79 rv 33af5394 ==> name="netmask" value="255.255.255.0"
    INSERT: free(data = 33af4e88)
    INSERT: done
    initcall: 33f0f68c (relocated to 33f0f68c)
    initcall: 33f18574 (relocated to 33f18574)
    initcall: 33f0f754 (relocated to 33f0f754)
    initcall: 33f161c8 (relocated to 33f161c8)
    In:    serial
    Out:   serial
    Err:   serial
    Initial value for argc=3
    Final value for argc=3
    Initial value for argc=3
    Final value for argc=3
    Initial value for argc=3
    Final value for argc=3
    initcall: 33f00fc4 (relocated to 33f00fc4)
    initcall: 33f0f744 (relocated to 33f0f744)
    initcall: 33f0f724 (relocated to 33f0f724)
    initcall: 33f0f708 (relocated to 33f0f708)
    Net:   Initial value for argc=3
    Final value for argc=3
    dm9000
    initcall: 33f0f6fc (relocated to 33f0f6fc)
    ### main_loop entered: bootdelay=5
    
    ### main_loop: bootcmd="<UNDEFINED>"
    SMDK2440 # 

    如果屏蔽掉调试信息:

    四、代码下载

    Young / s3c2440_project[u-boot-2016.05-nand-flash】

    参考文章

    [1]S3C2440移植uboot之支持NAND启动

    [2]三,移植uboot-支持NAND启动

    [3]移植u-boot-2016.11到JZ2440(三:修改源码之实现NOR启动与NAND启动)

    [4]u-boot从nand 启动时的问题解决记录

  • 相关阅读:
    I.MX6 Surfaceflinger 机制
    理解 Android Fragment
    RPi 2B DDNS 动态域名
    RPi 2B IPC webcam server
    理解 Android MVP 开发模式
    I.MX6 system.img unpack repack
    can't set android permissions
    VMware Ubuntu 共享文件夹
    解决oracle数据库连接不上的问题
    perfect-scrollbar示例
  • 原文地址:https://www.cnblogs.com/zyly/p/15780695.html
Copyright © 2011-2022 走看看