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 启动时的问题解决记录

  • 相关阅读:
    python 返回函数的使用
    你的服务器还在裸奔吗?
    云计算产品vSwitch原理
    网卡创建Bond
    UI自动化框架介绍
    常用底层linux命令
    Linux Bridge基本概念
    磁盘格式化及设置自动挂载
    Linux vi文本编辑器常用命令
    MySQL5.7安装方式
  • 原文地址:https://www.cnblogs.com/zyly/p/15780695.html
Copyright © 2011-2022 走看看