在之前的章节我们已经介绍了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: %08lxn", 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 MiBn", 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_initn");

    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%02xn", 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%02xn", 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_readyn");
    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%02xn",
          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 implementedn");
    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_initn");

    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函数:

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

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/zyly/p/15780695.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!

相关课程