fl2440 移植u-boot-2010.09全纪录4---------u-boot支持从nandflash启动

之前我们的u-boot虽然能够运行,但是是在sdram中,并没有烧写到flash中,因此,如果我们重启开发板,之前的u-boot就丢失了。如果我们想要让u-boot上电就启动,那么我们必须要把u-boot烧写到flash中保存起来。

这里我将u-boot固化到nandflash,使u-boot支持从nandflash启动。

一、添加NOR Flash启动和NAND Flash启动的识别

识别u-boot是从nandflash启动还是norflash启动,这里我总结了两种方法:

1 是通过nandflashdatasheet中的记载,其OM[1:0]规定了启动的地点,当OM的低两位为:00时,从nandflash启动,01或者10都为norflash启动,因此可以通过判断这两位的值来判断

代码如下:

 /*OM[1:0] != 0, 跳转到NOR FLASH 启动处,OM[1:0] = 0,向下执行代码,从nandflash启动 */  

#define BWSCON 0x48000000   

ldr     r0, =BWSCON  

ldr     r0, [r0]  

ands    r0, r0, #0x6  

tst     r0, #0x0

bne     norflash_boot            

 

2是将地址4000003C置为0,如果在地址0000003C中读出的值为0,则代表u-bootnandflash启动,如果读出的值是0xdeadbeaf,则代表u-bootnorflash启动(原理如下:s3c24x0在上电后,会把内部的sram映射到0x40000000地址上,如果从nandflash启动,sram还将映射到0x00000000地址上。因为0x3c之前的地址空间都被使用,在程序开始的时候做中断向量跳转去了,而且这个地址的值是确定的,也不属于程序,所以我们选择0x0000003c这个地址空间作为检测。如果我们往0x4000003c这个地址上写0,检测到0x0000003c0,代表是从nandflash启动,如果检测到0xdeadbeaf,代表从norflash启动)

代码如下:

    ldr r1, =( (4<<28)|(3<<4)|(3<<2) )     /*载入地址0x4000003C*/

    mov r0, #0      /* r0 = 0 */    

    str r0, [r1]                      /*R1所代表的地址(0x4000003C)写0*/

 

    mov r1, #0x3c       /*载入地址 0x0000003C*/

    ldr r0, [r1]

    cmp r0, #0      /*0x0000003C中的值和0做比较,然后跳转*/

bne NORFLASH_BOOT 

 

二,添加nandflash拷贝代码:

#define LENGTH_UBOOT   0x60000
NANDFLASH_BOOT:

    /*Get ready to call C functions for nand_read_ll()*/

    ldr sp, DW_STACK_START  @ setup stack pointer

    mov fp, #0  @ no previous frame, so fp=0

 

    /* Read u-boot from Nandflash to SDRAM address $TEXT_BASE */

    ldr r0, =TEXT_BASE     /*nand_read_ll() 1st argument*/

    mov r1, #0x0           /*nand_read_ll() 2nd argument*/

    mov r2, #LENGTH_UBOOT  /*nand_read_ll() 3rd argument*/

    bl  nand_read_ll 

  /*跳转到nand_read_ll函数中,完成相应的操作,注意:nand_read_ll函数需要三个参数,这三个参数分别放在R0R1R2*/

    tst r0, #0x0           /*Check nand_read_ll() return value*/

    bne infinite_loop     /*nand_read_ll() not return 0, then goto dead loop*/

 

nand_read_ok:       

    /*Then verify the read data validation

读取前4K代码,和sram中的代码做比较,确认读取的代码有效,*/

    mov r0, #0             /*The first 4K data in internal SRAM*/

    ldr r1, =TEXT_BASE     /*The first 4K data read from Nandflash in SDRAM*/

    mov r2, #0x400         /*The compare data length*/

 

compare_next_byte:

    ldr r3, [r0], #4

    ldr r4, [r1], #4

    teq r3, r4

    bne infinite_loop       /*如果出现数据不一样,跳转到infinite_loop,死循环*/

 

 

    subs    r2, r2, #4

    beq stack_setup

    bne compare_next_byte

 

infinite_loop:

    b   infinite_loop

三,添加C语言从NAND Flash搬移代码部分,刚刚我们看到了汇编部分会调用一个C函数nand_read_ll,这个函数就在nand_read.c文件中

首先在board/fl2440目录下新建一个名为nand_read.c的文件,内容如下

/*

 * nand_read.c: Simple NAND read functions for booting from NAND

 *

 * This is used by cpu/arm920/start.S assembler code,

 * and the board-specific linker script must make sure this

 * file is linked within the first 4kB of NAND flash.

 *

 * Taken from GPLv2 licensed vivi bootloader,

 * Copyright (C) 2002 MIZI Research, Inc.

 *

 * Author: Hwang, Chideok

 * Date  : $Date: 2004/02/04 10:37:37 $

 *

 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.

 * Author: Harald Welte

 *

 *Author: hurryliu

 

 */

 

#include

#include

 

 

#define __REGb(x)   (*(volatile unsigned char *)(x))

#define __REGw(x)   (*(volatile unsigned short *)(x))

#define __REGi(x)     (*(volatile unsigned int *)(x))

#define NF_BASE               0x4e000000

#if defined(CONFIG_S3C2410)

#define NFCONF                __REGi(NF_BASE + 0x0)

#define NFCMD         __REGb(NF_BASE + 0x4)

#define NFADDR                __REGb(NF_BASE + 0x8)

#define NFDATA                __REGb(NF_BASE + 0xc)

#define NFSTAT                 __REGb(NF_BASE + 0x10)

#define NFSTAT_BUSY     1

#define nand_select()        (NFCONF &= ~0x800)

#define nand_deselect()   (NFCONF |= 0x800)

#define nand_clear_RnB()do {} while (0)

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

#define NFCONF                __REGi(NF_BASE + 0x0)

#define NFCONT                __REGi(NF_BASE + 0x4)

#define NFCMD         __REGb(NF_BASE + 0x8)

#define NFADDR                __REGb(NF_BASE + 0xc)

#define NFDATA                __REGb(NF_BASE + 0x10)

#define NFDATA16   __REGw(NF_BASE + 0x10)

#define NFSTAT                 __REGb(NF_BASE + 0x20)

#define NFSTAT_BUSY     1

#define nand_select()        (NFCONT &= ~(1 << 1))

#define nand_deselect()   (NFCONT |= (1 << 1))

#define nand_clear_RnB()(NFSTAT |= (1 << 2))

#endif

 

static inline void nand_wait(void)

{

         int i;

 

         while (!(NFSTAT & NFSTAT_BUSY))

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

}

 

struct boot_nand_t {

         int page_size;

         int block_size;

         int bad_block_offset;

//       unsigned long size;

};

 

#if 0

#if defined(CONFIG_S3C2410) || defined(CONFIG_MINI2440)

/* configuration for 2410 with 512byte sized flash */

#define NAND_PAGE_SIZE                 512

#define BAD_BLOCK_OFFSET 5

#define NAND_BLOCK_MASK            (NAND_PAGE_SIZE - 1)

#define NAND_BLOCK_SIZE              0x4000

#else

/* configuration for 2440 with 2048byte sized flash */

#define NAND_5_ADDR_CYCLE

#define NAND_PAGE_SIZE                 2048

#define BAD_BLOCK_OFFSET NAND_PAGE_SIZE

#define      NAND_BLOCK_MASK                 (NAND_PAGE_SIZE - 1)

#define NAND_BLOCK_SIZE              (NAND_PAGE_SIZE * 64)

#endif

 

/* compile time failure in case of an invalid configuration */

#if defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)

#error "S3C2410 does not support nand page size != 512"

#endif

#endif

 

static int is_bad_block(struct boot_nand_t * nand, unsigned long i)

{

         unsigned char data;

         unsigned long page_num;

 

         nand_clear_RnB();

         if (nand->page_size == 512) {

                  NFCMD = NAND_CMD_READOOB; /* 0x50 */

                  NFADDR = nand->bad_block_offset & 0xf;

                  NFADDR = (i >> 9) & 0xff;

                  NFADDR = (i >> 17) & 0xff;

                  NFADDR = (i >> 25) & 0xff;

         } else if (nand->page_size == 2048) {

                  page_num = i >> 11; /* addr / 2048 */

                  NFCMD = NAND_CMD_READ0;

                  NFADDR = nand->bad_block_offset & 0xff;

                  NFADDR = (nand->bad_block_offset >> 8) & 0xff;

                  NFADDR = page_num & 0xff;

                  NFADDR = (page_num >> 8) & 0xff;

                  NFADDR = (page_num >> 16) & 0xff;

                  NFCMD = NAND_CMD_READSTART;

         } else {

                  return -1;

         }

         nand_wait();

         data = (NFDATA & 0xff);

         if (data != 0xff)

                  return 1;

 

         return 0;

}

 

static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)

{

         unsigned short *ptr16 = (unsigned short *)buf;

         unsigned int i, page_num;

 

         nand_clear_RnB();

 

         NFCMD = NAND_CMD_READ0;

 

         if (nand->page_size == 512) {

                  /* Write Address */

                  NFADDR = addr & 0xff;

                  NFADDR = (addr >> 9) & 0xff;

                  NFADDR = (addr >> 17) & 0xff;

                  NFADDR = (addr >> 25) & 0xff;

         } else if (nand->page_size == 2048) {

                  page_num = addr >> 11; /* addr / 2048 */

                  /* Write Address */

                  NFADDR = 0;

                  NFADDR = 0;

                  NFADDR = page_num & 0xff;

                  NFADDR = (page_num >> 8) & 0xff;

                  NFADDR = (page_num >> 16) & 0xff;

                  NFCMD = NAND_CMD_READSTART;

         } else {

                  return -1;

         }

         nand_wait();

 

#if defined(CONFIG_S3C2410)

         for (i = 0; i < nand->page_size; i++) {

                  *buf = (NFDATA & 0xff);

                  buf++;

         }

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

         for (i = 0; i < (nand->page_size>>1); i++) {

                  *ptr16 = NFDATA16;

                  ptr16++;

         }

#endif

 

         return nand->page_size;

}

 

static unsigned short nand_read_id()

{

         unsigned short res = 0;

         NFCMD = NAND_CMD_READID;

         NFADDR = 0;

         res = NFDATA;

         res = (res << 8) | NFDATA;

         return res;

}

 

extern unsigned int dynpart_size[];

 

/* low level nand read function */

int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)

{

         int i, j;

         unsigned short nand_id;

         struct boot_nand_t nand;

 

         /* chip Enable */

         nand_select();

         nand_clear_RnB();

        

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

                  ;

         nand_id = nand_read_id();

         if (0) { /* dirty little hack to detect if nand id is misread */

                  unsigned short * nid = (unsigned short *)0x31fffff0;

                  *nid = nand_id;

         }       

 

       if (nand_id == 0xec76 ||           /* Samsung K91208 on SD2410 board */

           nand_id == 0xad76 ) {      /*Hynix HY27US08121A*/

                  nand.page_size = 512;

                  nand.block_size = 16 * 1024;

                  nand.bad_block_offset = 5;

         //       nand.size = 0x4000000;

         } else if (nand_id == 0xecf1 ||      /* Samsung K9F1G08U0B */

           nand_id == 0xadda || /*  Hynix HY27UF082G2B on FL2440 board */

                     nand_id == 0xecda ||     /* Samsung K9F2G08U0B on FL2440 board */

                     nand_id == 0xecd3 )      { /* Samsung K9K8G08 */

                  nand.page_size = 2048;

                  nand.block_size = 128 * 1024;

                  nand.bad_block_offset = nand.page_size;

         //       nand.size = 0x8000000;

         } else {

                  return -1; // hang

         }

         if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))

                  return -1;  /* invalid alignment */

 

         for (i=start_addr; i < (start_addr + size);) {

#ifdef CONFIG_S3C2410_NAND_SKIP_BAD

                  if (i & (nand.block_size-1)== 0) {

                           if (is_bad_block(&nand, i) ||

                               is_bad_block(&nand, i + nand.page_size)) {

                                    /* Bad block */

                                    i += nand.block_size;

                                    size += nand.block_size;

                                    continue;

                           }

                  }

#endif

                  j = nand_read_page_ll(&nand, buf, i);

                  i += j;

                  buf += j;

         }

 

         /* chip Disable */

         nand_deselect();

 

         return 0;

}

四,修改board/fl2440/Makefile文件,使得nand_read.c能被编译到u-boot.bin中(28行)

COBJS   := fl2440.onand_read.o flash.o

五,修改文件arch/arm/cpu/arm920t/u-boot.lds,将u-bootnandflash搬运的代码链接到前4Ku-boot.bin中(40行左右)

    .text :

    {

        arch/arm/cpu/arm920t/start.o    (.text)

        board/fl2440/lowlevel_init.o (.text)

        board/fl2440/nand_read.o (.text)

        *(.text)

}

六,修改include/configs/fl2440.h文件,让u-boot支持nandflash串口操作

 

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

 * NAND flash settings

 */

#if defined(CONFIG_CMD_NAND)

#define CONFIG_NAND_S3C2410

#define CONFIG_SYS_MAX_NAND_DEVICE 1   /* Max number of NAND devices       */

#define CONFIG_SYS_NAND_BASE 0x4E000000

#define SECTORSIZE 512

#define SECTORSIZE_2K 2048

#define NAND_SECTOR_SIZE SECTORSIZE

#define NAND_SECTOR_SIZE_2K SECTORSIZE_2K

#define NAND_BLOCK_MASK 511

#define NAND_BLOCK_MASK_2K 2047

#define NAND_MAX_CHIPS 1

#define CONFIG_MTD_NAND_VERIFY_WRITE

#define CONFIG_SYS_64BIT_VSPRINTF      /* needed for nand_util.c */

#endif  /* CONFIG_CMD_NAND */

七,修改drivers/mtd/nand/s3c2410_nand.c文件,支持nandflash的读写(27行左右)

修改宏定义如下

#define NF_BASE        0x4e000000

#if defined(CONFIG_S3C2410)  

#define S3C2410_NFCONF_EN         (1<<15)

#define S3C2410_NFCONF_512BYTE    (1<<14)

#define S3C2410_NFCONF_4STEP      (1<<13)

#define S3C2410_NFCONF_INITECC    (1<<12)

#define S3C2410_NFCONF_nFCE       (1<<11)

#define S3C2410_NFCONF_TACLS(x)   ((x)<<8)

#define S3C2410_NFCONF_TWRPH0(x)  ((x)<<4)

#define S3C2410_NFCONF_TWRPH1(x)  ((x)<<0)

 

#define S3C2410_ADDR_NALE 4

#define S3C2410_ADDR_NCLE 8

#endif

 

#if defined(CONFIG_S3C2440)  

#define S3C2410_NFCONT_EN         (1<<0)  

#define S3C2410_NFCONT_INITECC    (1<<4)  

#define S3C2410_NFCONT_nFCE       (1<<1)  

#define S3C2410_NFCONT_MAINECCLOCK (1<<5)  

#define S3C2410_NFCONF_TACLS(x)   ((x)<<12)  

#define S3C2410_NFCONF_TWRPH0(x)  ((x)<<8)  

#define S3C2410_NFCONF_TWRPH1(x)  ((x)<<4)  

 

#define S3C2410_ADDR_NALE 0x08  

#define S3C2410_ADDR_NCLE 0x0c  

#endif

ulong IO_ADDR_W = NF_BASE;

 

#ifdef CONFIG_NAND_SPL

修改s3c2410_hwcontrol函数为如下

 

static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)

{

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

    struct s3c2410_nand *nand = s3c2410_get_base_nand();

 

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

 

    if (ctrl & NAND_CTRL_CHANGE) {

        //ulong IO_ADDR_W = (ulong)nand;

        IO_ADDR_W = (ulong)nand;

 

        if (!(ctrl & NAND_CLE))

            IO_ADDR_W |= S3C2410_ADDR_NCLE;

        if (!(ctrl & NAND_ALE))

            IO_ADDR_W |= S3C2410_ADDR_NALE;

 

//      chip->IO_ADDR_W = (void *)IO_ADDR_W;

 

        if (ctrl & NAND_NCE)

#if defined(CONFIG_S3C2410)  /* modify by hurryliu */

            writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,&nand->NFCONF);

#elif defined(CONFIG_S3C2440)

            writel(readl(&nand->NFCONT) & ~S3C2410_NFCONT_nFCE,&nand->NFCONT);

#endif

        else

#if defined(CONFIG_S3C2410)  

            writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,&nand->NFCONF);

#elif defined(CONFIG_S3C2440)

            writel(readl(&nand->NFCONT) | S3C2410_NFCONT_nFCE,&nand->NFCONT);

#endif

    }

 

    if (cmd != NAND_CMD_NONE)

        //writeb(cmd, chip->IO_ADDR_W);

        writeb(cmd, (void *)IO_ADDR_W);

}

修改s3c2410_nand_enable_hwecc函数如下:

void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)

{

    struct s3c2410_nand *nand = s3c2410_get_base_nand();

    debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);

#if defined(CONFIG_S3C2410)

    writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);

#elif defined(CONFIG_S3C2440)              /*add by hurryliu*/

    writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC, &nand->NFCONT);

#endif

}

修改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 s3c2410_nand *nand_reg = s3c2410_get_base_nand();

 

    debugX(1, "board_nand_init()\n");

 

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

 

#if defined(CONFIG_S3C2410) 

    /* initialize hardware */

    twrph0 = 3;

    twrph1 = 0;

    tacls = 0;

 

    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 */

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

#elif defined(CONFIG_S3C2440)  

   /*  initialize hardware */

    tacls = 0;

    twrph0 = 4;

    twrph1 = 2;

 

 

        cfg = 0;

        cfg |= S3C2410_NFCONF_TACLS(tacls - 1);

        cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

        cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

        writel(cfg, &nand_reg->NFCONF);

 

        cfg = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);

        writel(cfg, &nand_reg->NFCONT);

        /*  initialize nand_chip data structure */

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

#endif

 

    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;

#endif

 

#ifdef CONFIG_S3C2410_NAND_BBT

    nand->options = NAND_USE_FLASH_BBT;

#else

    nand->options = 0;

#endif

 

    debugX(1, "end of nand_init\n");

 

    return 0;

}

添加nandflash寄存器的定义board/fl2440/lowlevel_init.S

#define S3C24X0_INTERRUPT_BASE      0x4A000000
#define S3C24X0_CLOCK_POWER_BASE    0x4C000000
#define S3C2410_NAND_BASE           0x4E000000
#define S3C24X0_WATCHDOG_BASE       0x53000000
#define S3C24X0_GPIO_BASE           0x56000000
#define GPBCON                      0x56000010
#define GPBDAT                      0x56000014
#define GPBUP                       0x56000018

#define INTMSK_OFFSET               0x08
#define INTSUBMSK_OFFSET            0x1c
#define MPLLCON_OFFSET              0x04
#define CLKDIVN_OFFSET              0x14
#define NFCONF_OFFSET               0x00
#define NFCONT_OFFSET               0x04
#define NFCMD_OFFSET                0x08
#define NFSTAT_OFFSET               0x20

#define MDIV_405                    0x7f << 12
#define PSDIV_405                   0x21
#define MDIV_200                    0xa1 << 12
#define PSDIV_200                   0x31

 

你可能感兴趣的:(嵌入式)