u-boot-2009.08在mini2440上的移植(三)---增加nand flash功能

u-boot-2009.08在mini2440上的移植(三)---增加nand flash功能  

转载补充,谢谢分享:http://blog.163.com/liuqiang_mail@126/blog/static/109968875201176214200/

移植环境

1,主机环境:VMare下CentOS 5.5 ,1G内存。

2,集成开发环境:Elipse IDE

3,编译编译环境:arm-linux-gcc v4.4.3,arm-none-eabi-gcc v4.5.1。

4,开发板:mini2440,2M nor flash,128M nand flash。

5,u-boot版本:u-boot-2009.08

6,参考文章:

http://blogold.chinaunix.net/u3/101649/showart.php?id=2105215

http://blog.chinaunix.net/space.php?uid=23787856&do=blog&id=115382

http://blogimg.chinaunix.net/blog/upfile2/100811115954.pdf

目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。在做u-boot移植的时候,多数人使用的是Nand flash启动或Nar Flash启动。这样u-boot就只能在Nand flash或Nor flash。那么我们如何让我们的u-boot在Nand flash或Nor flash都能使用。

3.1,Nand flash或Nor flash双启动原理

首先,看一下我们熟悉的u-boot启动的时候执行的一段程序,这段程序一般存放在Nand flash中或Nor flash中。我们所说的Nand flash启动或Nor flash启动主要是涉及到一段搬移代码。这段搬移代码的功能是u-boot自己把自己搬移到内存中执行。如下是Nor flash启动中的这段搬移代码(这里以s3c2410为例)

  relocate:                        /* relocate U-Boot to RAM */
  adr r0, _start        /* r0 <- current position of code */
  ldr r1, _TEXT_BASE        /* test if we run from flash or RAM */
  cmp r0, r1                        /* don't reloc during debug */
  beq stack_setup
  ldr r2, _armboot_start
  ldr r3, _bss_start
  sub r2, r3, r2                /* r2 <- size of armboot */
  add r2, r0, r2                /* r2 <- source end address */
  copy_loop:
  ldmia r0!, {r3-r10}                /* copy from source address [r0] */
  stmia r1!, {r3-r10}                /* copy to target address [r1] */
  cmp r0, r2                /* until source end addreee [r2] */
  ble copy_loop
  #endif        /* CONFIG_SKIP_RELOCATE_UBOOT */
  #endif

上面这段代码就是把u-boot搬移到内存。而不同的启动方式区别也就在这段代码上,如果我们这里是Nand flash启动的话我们也需要写相同功能的代码,不同的是对于Nand的操作和Nor的操作是完全不同的,选择Nor flash启动是将Nor flash映射到片选0上也就是0x0地址而选择Nand flash启动则是将CPU的片内RAM(4K)映射到0地址,通过Nand flash控制器操作Nand flash。我们这里讨论如何实现Nand 和 Nor双启动。下面我们看看这两种启动的映射关系。

u-boot-2009.08在mini2440上的移植(三)---增加nand flash功能_第1张图片
同时我们可以看到: 总线宽度和等待控制寄存器:
 
u-boot-2009.08在mini2440上的移植(三)---增加nand flash功能_第2张图片
 
 在系统重启时会扫描BWSCON的状态,而BWSCON的其他位的初始状态都是0,只有DW0(BWSCON[2:1])的值由OM[1:0]来决定,通过上面的2个图我们可以发现,我们可以通过判断BWSCON的第2位、第3位   {DW0(BWSCON[2:1])}的值,判断是Nor flash启动还是Nand Flash启动。可以启动代码之前添加如下代码,来判断是Nor flash启动还是Nand Flash启动。

 # define BWSCON   0x48000000 
        ldr r0,=BWSCON
        ldr r0,[r0]
        and r0,r0,#6
        cmp  r0, #0  
        bne  relocate

 ///////////////////////////////////////////////////
     //nand_boot
  //Nand 搬移代码
////////////////////////////////////////////////////
  relocate:
       //nor_boot
      //Nor 搬移代码
////////////////////////////////////////////////////
有了上面这段代码,就可以实现双启动了,只要再适当的添加对应的功能我们的uboot就完成了

 3.2,为u-boot增加对nand flash支持实际操作

【1】修改代码重定向部分,增加Nor flash 和Nand flash 双启动

修改cpu/arm920t/start.S文件,为使u-boot从Nand Flash启动,需要将下面注释掉的CPU和DRAM初始化部分还原。

用gedit打开cpu/arm920t/start.S,定位到199行附近,找到如下代码

//#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 //bl cpu_init_crit
//#endif

将注释掉的这段代码恢复,修改如下:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit
#endif

Tekkaman Ninja从2009.08 开始就在启动时增加了启动时检测自身是否已经在SDRAM中(通过OpenJTAG载入),以及芯片是Norboot还是Nandboot的机制,来决定代码重定向的方式,使得编译出的bin文件可以同时烧入Nand Flash和Nor flash,以及被OpenJTAG载入进行调试。至于这部分的原理,在Tekkaman Ninja的博客文章《在U-boot下实现自动识别启动Flash的原理(针对S3C24x0)》中有详细叙述。这部分代码修改后结果如下:

(1)判断当前代码位置,如果在内存,直接跳到stack_setup

//#ifndef CONFIG_SKIP_RELOCATE_UBOOT
//relocate: /* relocate U-Boot to RAM */
/***************** Check the code position begain*******************************/
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
 cmp     r0, r1          /* don't reloc during debug  */
 beq     stack_setup
/****************** Check the code position end ********************************/

(2)如果不是在代码当前位置不再内存中,就判断启动的Flash:Nand 或者Nor
/***************** Check the boot flash begain **********************************/
# define BWSCON  0x48000000 
        ldr r0,=BWSCON
        ldr r0,[r0]
        ands r0,r0,#6
        cmp  r0, #0  
        bne  relocate


 /* recovery  */
 ldr r0, =(0xdeadbeef)
 ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
 str r0, [r1]
/***************** check the boot flash end ************************************/

(3)如果判断是在Nand Flash中启动的话,那么nand  Flash搬移代码如下:
定义u-boot在nand flash中存放的长度为#define LENGTH_UBOOT 0x60000,可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。

// copy U-Boot to RAM  form   Nand Flash

/***************** NAND BOOT start *************************************************/

#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000

#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20

 @ reset NAND
 mov r1, #NAND_CTL_BASE
 ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
 str r2, [r1, #oNFCONF]
 ldr r2, [r1, #oNFCONF]

 ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
 str r2, [r1, #oNFCONT]
 ldr r2, [r1, #oNFCONT]

 ldr r2, =(0x6) @ RnB Clear
 str r2, [r1, #oNFSTAT]
 ldr r2, [r1, #oNFSTAT]

 mov r2, #0xff @ RESET command
 strb r2, [r1, #oNFCMD]

 mov r3, #0 @ wait
nand1:
 add r3, r3, #0x1
 cmp r3, #0xa
 blt nand1

nand2:
 ldr r2, [r1, #oNFSTAT] @ wait ready
 tst r2, #0x4
 beq nand2


 ldr r2, [r1, #oNFCONT]
 orr r2, r2, #0x2 @ Flash Memory Chip Disable
 str r2, [r1, #oNFCONT]

 @ get read to call C functions (for nand_read())
 ldr sp, DW_STACK_START @ setup stack pointer
 mov fp, #0 @ no previous frame, so fp=0

 @ copy U-Boot to RAM
 ldr r0, =TEXT_BASE
 mov r1, #0x0
 mov r2, #LENGTH_UBOOT
 bl nand_read_ll
 tst r0, #0x0
 beq ok_nand_read

bad_nand_read:
loop2:
 b loop2 @ infinite loop
ok_nand_read:
 @ verify
 mov r0, #0
 ldr r1, =TEXT_BASE
 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
 ldr r3, [r0], #4
 ldr r4, [r1], #4
 teq r3, r4
 bne notmatch
 subs r2, r2, #4
 beq stack_setup
 bne go_next

notmatch:
loop3:
 b loop3 @ infinite loop
#endif

#ifdef CONFIG_S3C2410

/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10

 @ reset NAND
 mov r1, #NAND_CTL_BASE
 ldr r2, =0xf830 @ initial value
 str r2, [r1, #oNFCONF]
 ldr r2, [r1, #oNFCONF]
 bic r2, r2, #0x800 @ enable chip
 str r2, [r1, #oNFCONF]
 mov r2, #0xff  @ RESET command
 strb r2, [r1, #oNFCMD]


 mov r3, #0 @ wait
nand1:
 add r3, r3, #0x1
 cmp r3, #0xa
 blt nand1

nand2:
 ldr r2, [r1, #oNFSTAT] @ wait ready
 tst r2, #0x1
 beq nand2

 ldr r2, [r1, #oNFCONF]
 orr r2, r2, #0x800 @ disable chip
 str r2, [r1, #oNFCONF]

 @ get read to call C functions (for nand_read())
 ldr sp, DW_STACK_START @ setup stack pointer
 mov fp, #0 @ no previous frame, so fp=0

 @ copy U-Boot to RAM
 ldr r0, =TEXT_BASE
 mov r1, #0x0
 mov r2, #LENGTH_UBOOT
 bl nand_read_ll
 tst r0, #0x0
 beq ok_nand_read

bad_nand_read:
loop2:
 b loop2 @ infinite loop


ok_nand_read:
 @ verify
 mov r0, #0
 ldr r1, =TEXT_BASE
 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
 ldr r3, [r0], #4
 ldr r4, [r1], #4
 teq r3, r4
 bne notmatch
 subs r2, r2, #4
 beq stack_setup
 bne go_next

notmatch:
loop3:
 b loop3 @ infinite loop

#endif
/***************** NAND BOOT end *************************************************/


(4)如果判断是在Nor  Flash中启动的话,那么nor  Flash搬移代码如下:

// copy U-Boot to RAM  form   Nor  Flash

/***************** NOR BOOT start *************************************************/
relocate:    /* relocate U-Boot to RAM     */
      /*********** CHECK_FOR_MAGIC_NUMBER***************/
 ldr r1, =(0xdeadbeef)
 cmp r0, r1
 bne loop3
      /*********** CHECK_FOR_MAGIC_NUMBER***************/
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
 ldr r2, _armboot_start
 ldr r3, _bss_start
 sub r2, r3, r2  /* r2 <- size of armboot            */
 add r2, r0, r2  /* r2 <- source end address         */

copy_loop:
 ldmia r0!, {r3-r10}  /* copy from source address [r0]    */
 stmia r1!, {r3-r10}  /* copy to   target address [r1]    */
 cmp r0, r2   /* until source end addreee [r2]    */
 ble copy_loop
//#endif /* CONFIG_SKIP_RELOCATE_UBOOT */ 
/***************** NOR BOOT end********************************************/


 /* Set up the stack          */
stack_setup:
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
 sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area             */
 sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo 

... ...

【2】在上面添加的代码中有一个跳转:bl nand_read_ll ,它跳入是新增的C 语言文件board/samsung/mini2440/nand_read.c中的函数,这个文件原本是用vivi 的代码,后来经过openmoko 的修改,并支持不同的Nand Flash 芯片,我又这里多加了几个个芯片ID以支持所有mini2440 的Nand Flash。代码如下:

/*
 * 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 <[email protected]>
 * Date  : $Date: 2004/02/04 10:37:37 $
 *
 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
 * Author: Harald Welte <[email protected]>
 */

#include <common.h>
#include <linux/mtd/nand.h>


#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(void)
{
 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 */
           ||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 == 0xecda  /* Samsung K9F2G08U0B */
     ||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) {//warning: suggest parentheses around comparison in operand of '&'
 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;
}

然后保存为nand_read.c,还要记得路径是在board/samsung/mini2440/目录下。

注意:上面这段代码中对Nand进行寻址的部分,这跟具体的Nand Flash的寻址方式有关。根据我们开发板上的Nand Flash(K9F1G08U0C)数据手册得知,片内寻址是采用27位地址形式。从第0位开始分四次通过I/O0-I/O7进行传送,并进行片内寻址。具体含义和结构图如下(相关概念参考Nand数据手册):

0-11位:Y-Buffers偏移地址

12-27位:X-Buffers偏移地址


【3】在board/samsung/mini2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,修改后代码如下:
 
include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
COBJS :=  nand_read.o mini2440.o flash.o
SOBJS := lowlevel_init.o
【4】在mini2440.h头文件中加入Nand要用到的宏和寄存器的定义。
用gedit打开include/configs/mini2440.h头文件,定位到文件末尾处加入下列代码:
/*-----------------------------------------------------------------------
 * NAND flash settings
 */

#define CONFIG_S3C2410_NAND_SKIP_BAD 1

【5】在cpu/arm920t/u-boot.lds中添加下面两行代码,主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的,修改后代码如下:
 .text :
 {
     cpu/arm920t/start.o (.text)
     board/samsung/mini2440/lowlevel_init.o (.text)
     board/samsung/mini2440/nand_read.o (.text)
     *(.text)
 }
 

3.3,重新编译
 这里发生一个错误,错误原因是有一个变量没有定义,这个错误发生在【1】修改代码重定向部分,增加Nor flash 和Nand flash 双启动,错误提示信息如下:

Error: Internal_relocation (type 182) not fixed up (OFFSET_IMM)
编译 start.S 文件时出错。错误位置

340 @ get read to call C functions (for nand_read())
341 ldr sp, DW_STACK_START @ setup stack pointer
342 mov fp, #0 @ no previous frame, so fp=0
反复排查问题,原来宏DW_STACK_START 原作者在文章中没有贴出定义,加上以下代码在cpu/arm920t/start.S的459行附件

ldrpc, _start_armboot
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .wordSTACK_BASE+STACK_SIZE-4 

重新编译,成功,编译成功后生成u-boot.bin文件。下载时先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,打开电源就从Nand Flash启动了,启动结果如下:
... ...
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 14:17:44)
DRAM:  64 MB
Flash:  2 MB
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]#nand info
Unknown command nand' - try 'help'
[u-boot@MINI2440]#

从上面的运行结果看,显然现在的Nand还不能做任何事情,而且也没有显示有关Nand的任何信息,所以只能说明上面的这些步骤只是完成了Nand移植的Stage1部分。下面我们来添加我们开发板上的Nand Flash(K9F1G08U0B)的Stage2部分的有关操作支持。
3.4,在U-boot 启动的第一阶段,初始化了Nand Flash 控制器。但到第二阶段start_armboot函数还是会再次初始化Nand Flash 控制器。因为第二阶段和第一阶段的代码基本是独立的,第一阶段的代码基本只起到代码重定位的作用,到了第二阶段才是真正U-boot 的开始,以前的初始化过程还会重做一遍,比如始化Nand Flash 控制器、CPU 频率等。因为 S3C2440 和S3C2410 之间的很大差别就是:S3C2410 的Nand Flash 控制器只支持512B+16B 的Nand Flash,而S3C2440 还支持2KB+64B 的大容量Nand Flash。所以在Nand Flash 控制器上寄存器和控制流程上的差别很明显,底层驱动代码的修改也是必须的。具体的差别还是需要对比芯片数据手册的,下面我们结合代码来分析一下u-boot在第二阶段的执行流程:
<1>.lib_arm/board.c文件中的start_armboot函数调用了drivers/mtd/nand/nand.c文件中的nand_init函数,如下:
  #if defined(CONFIG_CMD_NAND) //可以看到CONFIG_CMD_NAND宏决定了Nand的初始化
      puts ("NAND: ");
      nand_init();
  #endif
<2>.nand_init调用了同文件下的nand_init_chip函数;
<3>.nand_init_chip函数调用drivers/mtd/nand/s3c2410_nand.c文件下的board_nand_init函数,然后再调用drivers/mtd/nand/nand_base.c函数中的nand_scan函数;
<4>.nand_scan函数调用了同文件下的nand_scan_ident函数等。

【1】Nand Flash 相关代码的修改
打开/drivers/mtd/nand/s3c2410_nand.c,定位到37行,修改如下:
#define __REGb(x) (*(volatile unsigned char *)(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 S3C2410_ADDR_NALE 4
#define S3C2410_ADDR_NCLE 8
#endif
#if defined(CONFIG_S3C2440)
#define S3C2410_ADDR_NALE 0x08
#define S3C2410_ADDR_NCLE 0x0c
#define NFCONF  __REGi(NF_BASE + 0x0)
#define NFCONT    __REGb(NF_BASE + 0x4)   
#define NFCMD  __REGb(NF_BASE + 0x8)
#define NFADDR  __REGb(NF_BASE + 0xc)
#define NFDATA  __REGb(NF_BASE + 0x10)
#define NFMECCD0   __REGb(NF_BASE + 0x14)
#define NFMECCD1   __REGb(NF_BASE + 0x18)
#define NFSECCD   __REGb(NF_BASE + 0x1c)
#define NFSTAT  __REGb(NF_BASE + 0x20)
#define NFESTAT0 __REGb(NF_BASE + 0x24)
#define NFESTAT1 __REGb(NF_BASE + 0x28)
#define NFMECC0  __REGb(NF_BASE + 0x2c)
#define NFMECC1  __REGb(NF_BASE + 0x30)
#define NFSECC  __REGb(NF_BASE + 0x34)
#define NFSBLK  __REGb(NF_BASE + 0x38)
#define NFEBLK  __REGb(NF_BASE + 0x3c)
#define NFECC0  __REGb(NF_BASE + 0x2c)
#define NFECC1  __REGb(NF_BASE + 0x2d)
#define NFECC2  __REGb(NF_BASE + 0x2e)
#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)
#endif
ulong IO_ADDR_W = NF_BASE;  
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
 //struct nand_chip *chip = mtd->priv;
 DEBUGN("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
 if (ctrl & NAND_CTRL_CHANGE) {
  //ulong IO_ADDR_W = NF_BASE;//应注释掉这个局部变量否则找不到ID "No NAND device found!!!" 
   IO_ADDR_W = NF_BASE; 
 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 defined(CONFIG_S3C2410)
  if (ctrl & NAND_NCE)
   NFCONF &= ~S3C2410_NFCONF_nFCE;
  else
   NFCONF |= S3C2410_NFCONF_nFCE;
#endif
#if defined(CONFIG_S3C2440)
  if (ctrl & NAND_NCE)
   NFCONT &= ~S3C2410_NFCONT_nFCE;
  else
   NFCONT |= S3C2410_NFCONT_nFCE;
#endif
 }
 if (cmd != NAND_CMD_NONE)
  //writeb(cmd, chip->IO_ADDR_W);
  writeb(cmd, (void *)IO_ADDR_W);
}
... ...
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
 DEBUGN("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
#if defined(CONFIG_S3C2410)
 NFCONF |= S3C2410_NFCONF_INITECC;
#endif
#if defined(CONFIG_S3C2440)
 NFCONT |= S3C2410_NFCONT_INITECC;
#endif
}
... ...
#endif
int board_nand_init(struct nand_chip *nand)
{
 u_int32_t cfg;
 u_int8_t tacls, twrph0, twrph1;
 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
 DEBUGN("board_nand_init()\n");
 clk_power->CLKCON |= (1 << 4);
 /* initialize hardware */
#if defined(CONFIG_S3C2410)
  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);
  NFCONF = cfg;
 /* initialize nand_chip data structure */
 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e00000c;
#endif
#if defined(CONFIG_S3C2440)
  twrph0 = 4; twrph1 = 2; tacls = 0;
  cfg = 0;
  cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
  cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
  cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
  NFCONF = cfg;
  NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);
  /* initialize nand_chip data structure */
  nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
#endif
 /* read_buf and write_buf are default */
 /* read_byte and write_byte are default */


【2】在mini2440.h里添加nand_flash相关宏定义
打开include/configs/mini2440.h,定位到98行附近,加入下列代码:
/*
 * Command line configuration.
 */
#include <config_cmd_default.h>
#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND
#define CONFIG_CMD_JFFS2  /* JFFS2 Support*/
... ...
在文件末尾处加入下列代码:
/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_NAND_BASE 0x4E000000 
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices  */
#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 */
#define CONFIG_S3C2410_NAND_SKIP_BAD 1
 
3.5,重新编译,按上面3.3中方法下载,运行结果如下:
 
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 19:52:47)
DRAM:  64 MB
Flash:  2 MB
NAND:  128 MiB
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]# nand info
Device 0: NAND 128MiB 3,3V 8-bit, sector size 128 KiB
[u-boot@MINI2440]# saveenv
Saving Environment to Flash...
Un-Protected 16 sectors
Erasing Flash...Erasing sector 64 ...
从上面输出信息可以看到保存环境变量并没有成功,而且它是将把环境变量保存到Flash即Nor flash中,显然这不正确,我们是要保存到Nand中。原来,u-boot在默认的情况下把环境变量都是保存到Nor Flash中的,所以我们要修改代码,让它保存到Nand中,如下:
打开include/configs/mini2440.h,定位到193行附近,注释掉下列代码:

/* timeout values are in ticks */
#define CONFIG_SYS_FLASH_ERASE_TOUT (5*CONFIG_SYS_HZ) /* Timeout for Flash Erase */
#define CONFIG_SYS_FLASH_WRITE_TOUT (5*CONFIG_SYS_HZ) /* Timeout for Flash Write */

//#define CONFIG_ENV_IS_IN_FLASH 1
//#define CONFIG_ENV_SIZE  0x20000 /* Total Size of Environment Sector */

然后加入下列代码:

/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#define CONFIG_ENV_IS_IN_NAND  1
#define CONFIG_ENV_OFFSET      0x40000 //将环境变量保存到nand中的0x40000位置必,须在块的起始位置
#define CONFIG_ENV_SIZE        0x20000 /*必须为块大小的整数倍 , 否则会提示下面的信息,将擦除整个块*/
#if defined(CONFIG_CMD_NAND)
 
Warning: Erase size 0x00010000 smaller than one erase block 0x00020000
         Erasing 0x00020000 instead
NAND 128MiB 3,3V 8-bit: MTD Erase failure: -22
Writing to Nand... FAILED!
 
然后保存,重新编译、拨到nor 档下载,在拨到nand 档上电运行:
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 22:30:25)
DRAM:  64 MB
Flash:  2 MB
NAND:  128 MiB
*** Warning - bad CRC or NAND, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x4000000000002 --   0% complete.
Writing to Nand... done
[u-boot@MINI2440]#
可以看到环境变量保存成功,将开发板重新上电后不会再有bad CRC告警,nand flash 移植成功。

接下来将进入u-boot的第四阶段,为u-boot-2009.08增加网卡DM9000驱动支持。

有时间要研究代码呀,一定要,待续。。。。。

你可能感兴趣的:(uboot)