|  | 
 
| 5.2  U-Boot移植 
 5.2.1  Bootloader介绍
 
 1.概念
 
 简单地说,Bootloader就是在操作系统内核运行之前运行的一段程序,它类似于PC机中的BIOS程序。通过这段程序,可以完成硬件设备的初始化,并建立内存空间的映射关系,从而将系统的软硬件环境带到一个合适的状态,为最终加载系统内核做好准备。
 
 通常,Bootloader比较依赖于硬件平台,特别是在嵌入式系统中,更为如此。因此,在嵌入式世界里建立一个通用的Bootloader是一件比较困难的事情。尽管如此,仍然可以对Bootloader归纳出一些通用的概念来指导面向用户定制的Bootloader设计与实现。
 
 (1)Bootloader所支持的CPU和嵌入式开发板。
 每种不同的CPU体系结构都有不同的Bootloader。有些Bootloader也支持多种体系结构的CPU,如后面要介绍的U-Boot支持ARM、MIPS、PowerPC等众多体系结构。除了依赖于CPU的体系结构外,Bootloader实际上也依赖于具体的嵌入式板级设备的配置。
 
 (2)Bootloader的存储位置。
 系统加电或复位后,所有的CPU通常都从某个由CPU制造商预先安排的地址上取指令。而基于CPU构建的嵌入式系统通常都有某种类型的固态存储设备(比如ROM、EEPROM或Flash等)被映射到这个预先安排的地址上。因此在系统加电后,CPU将首先执行Bootloader程序。
 
 (3)Bootloader的启动过程分为单阶段和多阶段两种。通常多阶段的Bootloader能提供更为复杂的功能,以及更好的可移植性。
 
 (4)Bootloader的操作模式。大多数Bootloader都包含两种不同的操作模式:“启动加载”模式和“下载”模式,这种区别仅对于开发人员才有意义。
 
 n 启动加载模式:这种模式也称为“自主”模式。也就是Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。这种模式是嵌入式产品发布时的通用模式。
 
 n 下载模式:在这种模式下,目标机上的Bootloader将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被Bootloader保存到目标机的RAM中,然后再被Bootloader写入到目标机上的Flash类固态存储设备中。Bootloader的这种模式在系统更新时使用。工作于这种模式下的Bootloader通常都会向它的终端用户提供一个简单的命令行接口。
 
 (5)Bootloader与主机之间进行文件传输所用的通信设备及协议,最常见的情况就是,目标机上的Bootloader通过串口与主机之间进行文件传输,传输协议通常是xmodem/ ymodem/zmodem等。但是,串口传输的速度是有限的,因此通过以太网连接并借助TFTP等协议来下载文件是个更好的选择。
 
 2.Bootloader启动流程
 
 Bootloader的启动流程一般分为两个阶段:stage1和stage2,下面分别对这两个阶段进行讲解。
 
 (1)Bootloader的stage1。
 在stage1中Bootloader主要完成以下工作。
 n 基本的硬件初始化,包括屏蔽所有的中断、设置CPU的速度和时钟频率、RAM初始化、初始化外围设备、关闭CPU内部指令和数据cache等。
 n 为加载stage2准备RAM空间,通常为了获得更快的执行速度,通常把stage2加载到RAM空间中来执行,因此必须为加载Bootloader的stage2准备好一段可用的RAM空间。
 n 复制stage2到RAM中,在这里要确定两点:①stage2的可执行映像在固态存储设备的存放起始地址和终止地址;②RAM空间的起始地址。
 n 设置堆栈指针sp,这是为执行stage2的C语言代码做好准备。
 
 (2)Bootloader的stage2。
 在stage2中Bootloader主要完成以下工作。
 n 用汇编语言跳转到main入口函数。
 由于stage2的代码通常用C语言来实现,目的是实现更复杂的功能和取得更好的代码可读性和可移植性。但是与普通C语言应用程序不同的是,在编译和链接Bootloader这样的程序时,不能使用glibc库中的任何支持函数。
 n 初始化本阶段要使用到的硬件设备,包括初始化串口、初始化计时器等。在初始化这些设备之前可以输出一些打印信息。
 n 检测系统的内存映射,所谓内存映射就是指在整个4GB物理地址空间中指出哪些地址范围被分配用来寻址系统的内存。
 n 加载内核映像和根文件系统映像,这里包括规划内存占用的布局和从Flash上复制数据。
 n 设置内核的启动参数。
 
 5.2.2  U-Boot概述
 
 1.U-Boot简介
 
 U-Boot(UniversalBootloader)是遵循GPL条款的开放源码项目。它是从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,而且还支持NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS等嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD、NetBSD、FreeBSD、4.4BSD、Linux、SVR4、Esix、Solaris、Irix、SCO、Dell、NCR、VxWorks、LynxOS、pSOS、QNX、RTEMS、ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前为止,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。
 
 2.U-Boot特点
 
 U-Boot的特点如下。
 n 开放源码;
 n 支持多种嵌入式操作系统内核,如Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS;
 n 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;
 n 较高的可靠性和稳定性;
 n 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求和产品发布等;
 n 丰富的设备驱动源码,如串口、以太网、SDRAM、Flash、LCD、NVRAM、EEPROM、RTC、键盘等;
 n 较为丰富的开发调试文档与强大的网络技术支持。
 
 3.U-Boot主要功能
 
 U-Boot可支持的主要功能列表。
 n 系统引导:支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统。支持NFS挂载,并从Flash中引导压缩或非压缩系统内核。
 n 基本辅助功能:强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤其对Linux支持最为强劲;支持目标板环境参数多种存储方式,如Flash、NVRAM、EEPROM;CRC32校验,可校验Flash中内核、RAMDISK映像文件是否完好。
 n 设备驱动:串口、SDRAM、Flash、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持。
 n 上电自检功能:SDRAM、Flash大小自动检测;SDRAM故障检测;CPU型号。
 n 特殊功能:XIP内核引导。
 
 5.2.3  U-Boot源码导读
 
 1.U-Boot源码结构
 
 U-Boot源码结构如图5.27所示。
 
 
         图5.27  U-Boot源码结构 
           n board:和一些已有开发板有关的代码,比如makefile和U-Boot.lds等都和具体开发板的硬件和地址分配有关。
 n common:与体系结构无关的代码,用来实现各种命令的C程序。
 n cpu:包含CPU相关代码,其中的子目录都是以U-BOOT所支持的CPU为名,比如有子目录arm926ejs、mips、mpc8260和nios等,每个特定的子目录中都包括cpu.c和interrupt.c,start.S等。其中cpu.c初始化CPU、设置指令Cache和数据Cache等;interrupt.c设置系统的各种中断和异常,比如快速中断、开关中断、时钟中断、软件中断、预取中止和未定义指令等;汇编代码文件start.S是U-BOOT启动时执行的第一个文件,它主要是设置系统堆栈和工作方式,为进入C程序奠定基础。
 
 n disk:disk驱动的分区相关代码。
 n doc:文档。
 n drivers:通用设备驱动程序,比如各种网卡、支持CFI的Flash、串口和USB总线等。
 n fs:支持文件系统的文件,U-BOOT现在支持cramfs、fat、fdos、jffs2和registerfs等。
 n include:头文件,还有对各种硬件平台支持的汇编文件,系统的配置文件和对文件系统支持的文件。
 n net:与网络有关的代码,BOOTP协议、TFTP协议、RARP协议和NFS文件系统的实现。
 n lib_arm:与ARM体系结构相关的代码。
 n tools:创建S-Record格式文件和U-BOOT images的工具。
 
 2.U-Boot重要代码
 
 (1)cpu/arm920t/start.S
 这是U-Boot的起始位置。在这个文件中设置了处理器的状态、初始化中断向量和内存时序等,从Flash中跳转到定位好的内存位置执行。
 .globl_start (起始位置:中断向量设置)
 _start:     b         reset
 ldr    pc, _undefined_instruction
 ldr    pc, _software_interrupt
 ldr    pc, _prefetch_abort
 ldr    pc, _data_abort
 ldr    pc, _not_used
 ldr    pc, _irq
 ldr    pc, _fiq
 
 _undefined_instruction:   .word undefined_instruction
 _software_interrupt:   .word software_interrupt
 _prefetch_abort:   .word prefetch_abort
 _data_abort:       .word data_abort
 _not_used:      .word not_used
 _irq:            .word irq
 _fiq:            .word fiq
 
 _TEXT_BASE: (代码段起始位置)
 .word   TEXT_BASE
 
 .globl _armboot_start
 _armboot_start:
 .word _start
 
 /*
 * These are defined in the board-specific linker script.
 */
 .globl _bss_start (BSS段起始位置)
 _bss_start:
 .word __bss_start
 
 .globl _bss_end
 _bss_end:
 .word _end
 reset: (执行入口)
 /*
 * set the cpu to SVC32 mode;使处理器进入特权模式
 */
 mrs    r0,cpsr
 bic    r0,r0,#0x1f
 orr    r0,r0,#0xd3
 msr    cpsr,r0
 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
 
 /* Set up the stack;设置堆栈  */
 stack_setup:
 ldr   r0, _TEXT_BASE      /* upper 128 KiB: relocated uboot   */
 sub   r0, r0, #CFG_MALLOC_LEN    /* malloc area                      */
 sub   r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
 
 clear_bss: (清空BSS段)
 ldr    r0, _bss_start     /* find start of bss segment        */
 ldr    r1, _bss_end        /* stop here                        */
 mov     r2, #0x00000000     /* clear                            */
 
 clbss_l:str    r2, [r0]        /* clear loop...                    */
 add   r0, r0, #4
 cmp   r0, r1
 bne   clbss_l
 ldr   pc, _start_armboot
 
 _start_armboot:     .word start_armboot
 
 (2)interrupts.c
 这个文件是处理中断的,如打开和关闭中断等。
 
 #ifdef CONFIG_USE_IRQ
 /* enable IRQ interrupts;中断使能函数 */
 void enable_interrupts (void)
 {
 unsigned long temp;
 __asm__ __volatile__("mrs %0, cpsr\n"
 "bic %0, %0, #0x80\n"
 "msr cpsr_c, %0"
 : "=r" (temp)
 :
 : "memory");
 }
 
 /*
 * disable IRQ/FIQ interrupts;中断屏蔽函数
 * returns true if interrupts had been enabled before we disabled them
 */
 int disable_interrupts (void)
 {
 unsigned long old,temp;
 __asm__ __volatile__("mrs %0, cpsr\n"
 "orr %1, %0, #0xc0\n"
 "msr cpsr_c, %1"
 : "=r" (old), "=r" (temp)
 :
 : "memory");
 return (old & 0x80) == 0;
 }
 #endif
 void show_regs (struct pt_regs *regs)
 {
 unsigned long flags;
 const char *processor_modes[] = {
 "USER_26", "FIQ_26",  "IRQ_26",  "SVC_26",
 "UK4_26",  "UK5_26",  "UK6_26",  "UK7_26",
 "UK8_26",  "UK9_26",  "UK10_26", "UK11_26",
 "UK12_26", "UK13_26", "UK14_26", "UK15_26",
 "USER_32", "FIQ_32",  "IRQ_32",  "SVC_32",
 "UK4_32",  "UK5_32",  "UK6_32",   "ABT_32",
 "UK8_32",  "UK9_32",  "UK10_32", "UND_32",
 "UK12_32", "UK13_32", "UK14_32", "SYS_32",
 };
 …
 }
 /* 在U-Boot启动模式下,在原则上要禁止中断处理,所以如果发生中断,当作出错处理 */
 void do_fiq (struct pt_regs *pt_regs)
 {
 printf ("fast interrupt request\n");
 show_regs (pt_regs);
 bad_mode ();
 }
 
 void do_irq (struct pt_regs *pt_regs)
 {
 printf ("interrupt request\n");
 show_regs (pt_regs);
 bad_mode ();
 }
 
 (3)cpu.c
 这个文件是对处理器进行操作,如下所示:
 
 int cpu_init (void)
 {
 /*
 * setup up stacks if necessary;设置需要的堆栈
 */
 #ifdef CONFIG_USE_IRQ
 DECLARE_GLOBAL_DATA_PTR;
 
 IRQ_STACK_START=_armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
 FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
 #endif
 return 0;
 }
 int cleanup_before_linux (void) /* 准备加载linux */
 {
 /*
 * this function is called just before we call linux
 * it prepares the processor for linux
 *
 * we turn off caches etc ...
 */
 unsigned long i;
 disable_interrupts ();
 
 /* turn off I/D-cache:关闭cache */
 asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i));
 i &= ~(C1_DC | C1_IC);
 asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i));
 
 /* flush I/D-cache */
 i = 0;
 asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));
 return (0);
 }
 
 OUTPUT_ARCH(arm)
 ENTRY(_start)
 SECTIONS
 {
 . = 0x00000000;
 . = ALIGN(4);
 .text      :
 {
 cpu/arm920t/start.o (.text)
 *(.text)
 }
 
 . = ALIGN(4);
 .rodata : { *(.rodata) }
 
 . = ALIGN(4);
 .data : { *(.data) }
 
 . = ALIGN(4);
 .got : { *(.got) }
 
 __u_boot_cmd_start = .;
 .u_boot_cmd : { *(.u_boot_cmd) }
 __u_boot_cmd_end = .;
 
 . = ALIGN(4);
 __bss_start = .;
 .bss : { *(.bss) }
 _end = .;
 }
 
 (4)memsetup.S
 这个文件是用于配置开发板参数的,如下所示:
 
 /* memsetup.c */
 /* memory control configuration */
 /* make r0 relative the current location so that it */
 /* reads SMRDATA out of FLASH rather than memory ! */
 ldr     r0, =SMRDATA
 ldr r1, _TEXT_BASE
 sub r0, r0, r1
 ldr r1, =BWSCON /* Bus Width Status Controller */
 add     r2, r0, #52
 0:
 ldr     r3, [r0], #4
 str     r3, [r1], #4
 cmp     r2, r0
 bne     0b
 
 /* everything is fine now */
 mov pc, lr
 
 .ltorg
 
 5.2.4  U-Boot移植主要步骤
 
 (1)建立自己的开发板类型。
 阅读makefile文件,在makefile文件中添加两行,如下所示:
 
 fs2410_config: unconfig
 @./mkconfig $(@:_config=) arm arm920t fs2410
 
 其中“arm”为表示处理器体系结构的种类,“arm920t”表示处理器体系结构的名称,“fs2410”为主板名称。
 
 在board目录中建立fs2410目录,并将smdk2410目录中的内容(cp –a smdk2410/*  fs2410)复制到该目录中。
 
 n 在include/configs/目录下将smdk2410.h复制到(cp smdk2410.h fs2410.h)。
 n 修改ARM编译器的目录名及前缀(都要改成以“fs2410”开头)。
 n 完成之后,可以测试配置。
 
 $ make fs2410_config;make
 
 (2)修改程序链接地址。
 在board/s3c2410中有一个config.mk文件,它是用于设置程序链接的起始地址,因为会在U-Boot中增加功能,所以留下6MB的空间,修改33F80000为33A00000。
 为了以后能用U-Boot的“go”命令执行修改过的用loadb或tftp下载的U-Boot,需要在board/ s3c2410的memsetup.S中标记符”0:”上加入5句:
 
 mov r3, pc
 ldr r4, =0x3FFF0000
 and r3, r3, r4 (以上3句得到实际代码启动的内存地址)
 aad r0, r0, r3 (用go命令调试u-boot时,启动地址在RAM)
 add r2, r2, r3 (把初始化内存信息的地址,加上实际启动地址)
 
 (3)将中断禁止的部分应该改为如下所示(/cpu/arm920t/start.S):
 
 # if defined(CONFIG_S3C2410)
 ldr    r1, =0x7ff
 ldr    r0, =INTSUBMSK
 str    r1, [r0]
 # endif
 
 (4)因为在fs2410开发板启动时是直接从Nand Flash加载代码,所以启动代码应该改成如下所示(/cpu/arm920t/start.S):
 
 #ifdef CONFIG_S3C2410_NAND_BOOT   @START
 @ 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, #0x20000
 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 @ CONFIG_S3C2410_NAND_BOOT  @END
 
 在 “ _start_armboot:    .word start_armboot  ” 后加入:
 
 .align     2
 DW_STACK_START:  .word  STACK_BASE+STACK_SIZE-4
 
 (5)修改内存配置(board/fs2410/lowlevel_init.S)。
 
 #define BWSCON     0x48000000
 #define PLD_BASE   0x2C000000
 #define SDRAM_REG  0x2C000106
 
 /* BWSCON */
 #define DW8              (0x0)
 #define DW16            (0x1)
 #define DW32            (0x2)
 #define WAIT            (0x1<<2)
 #define UBLB            (0x1<<3)
 
 /* BANKSIZE */
 #define BURST_EN        (0x1<<7)
 
 #define B1_BWSCON      (DW16 + WAIT)
 #define B2_BWSCON      (DW32)
 #define B3_BWSCON      (DW32)
 #define B4_BWSCON      (DW16 + WAIT + UBLB)
 #define B5_BWSCON      (DW8 + UBLB)
 #define B6_BWSCON      (DW32)
 #define B7_BWSCON      (DW32)
 /* BANK0CON */
 #define B0_Tacs             0x0 /*  0clk */
 #define B0_Tcos             0x1 /*  1clk */
 #define B0_Tacc             0x7 /*  14clk */
 #define B0_Tcoh             0x0 /*  0clk */
 #define B0_Tah       0x0   /*  0clk */
 #define B0_Tacp            0x0     /* page mode is not used */
 #define B0_PMC          0x0 /* page mode disabled */
 
 /* BANK1CON */
 #define B1_Tacs             0x0 /*  0clk */
 #define B1_Tcos             0x1 /*  1clk */
 #define B1_Tacc             0x7 /*  14clk */
 #define B1_Tcoh             0x0 /*  0clk */
 #define B1_Tah          0x0 /*  0clk */
 #define B1_Tacp            0x0     /* page mode is not used */
 #define B1_PMC          0x0 /* page mode disabled */
 ……
 /* REFRESH parameter */
 #define REFEN           0x1 /* Refresh enable */
 #define TREFMD          0x0 /* CBR(CAS before RAS)/Auto refresh */
 #define Trp         0x0 /* 2clk */
 #define Trc         0x3 /* 7clk */
 #define Tchr            0x2 /* 3clk */
 #define REFCNT           1113 /*period=15.6us,HCLK=60Mhz, (2048+1-15.6*60) */
 ......
 .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
 .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
 .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
 .word 0x32
 .word 0x30
 .word 0x30
 
 (6)加入Nand Flash读函数(创建board/fs2410/nand_read.c文件)。
 
 #include <config.h>
 #define __REGb(x) (*(volatile unsigned char *)(x))
 #define __REGi(x) (*(volatile unsigned int *)(x))
 #define NF_BASE  0x4e000000
 #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 BUSY 1
 inline void wait_idle(void)
 {
 Int i;
 while(!(NFSTAT & BUSY))
 {
 for (i = 0; i < 10; i++);
 }
 }
 
 /* low level nand read function */
 int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
 {
 int i, j;
 if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
 {
 return -1; /* invalid alignment */
 }
 /* chip Enable */
 NFCONF &= ~0x800;
 for (i = 0; i < 10; i++);
 for (i = start_addr; i < (start_addr + size);)
 {
 /* READ0 */
 NFCMD = 0;
 /* Write Address */
 NFADDR = i & 0xff;
 NFADDR = (i >> 9) & 0xff;
 NFADDR = (i >> 17) & 0xff;
 NFADDR = (i >> 25) & 0xff;
 wait_idle();
 for (j = 0; j < NAND_SECTOR_SIZE; j++, i++)
 {
 *buf = (NFDATA & 0xff);
 buf++;
 }
 }
 /* chip Disable */
 NFCONF |= 0x800; /* chip disable */
 return 0;
 }
 
 修改board/fs2410/makefile文件,以增加nand_read()函数。
 
 OBJS := fs2410.o  flash.o  nand_read.o
 
 (7)加入Nand Flash的初始化函数(board/fs2410/fs2410.c)。
 
 #if (CONFIG_COMMANDS & CFG_CMD_NAND)
 typedef enum
 {
 NFCE_LOW,
 NFCE_HIGH
 } NFCE_STATE;
 static inline void NF_Conf(u16 conf)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCONF = conf;
 }
 static inline void NF_Cmd(u8 cmd)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCMD = cmd;
 }
 static inline void NF_CmdW(u8 cmd)
 {
 NF_Cmd(cmd);
 udelay(1);
 }
 static inline void NF_Addr(u8 addr)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFADDR = addr;
 }
 static inline void NF_SetCE(NFCE_STATE s)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 switch (s)
 {
 case NFCE_LOW:
 nand->NFCONF &= ~(1<<11);
 break;
 case NFCE_HIGH:
 nand->NFCONF |= (1<<11);
 break;
 }
 }
 static inline void NF_WaitRB(void)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 while (!(nand->NFSTAT & (1<<0)));
 }
 static inline void NF_Write(u8 data)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFDATA = data;
 }
 static inline u8 NF_Read(void)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 return(nand->NFDATA);
 }
 static inline void NF_Init_ECC(void)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCONF |= (1<<12);
 }
 static inline u32 NF_Read_ECC(void)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 return(nand->NFECC);
 }
 #endif
 /*
 * NAND flash initialization.
 */
 #if (CONFIG_COMMANDS & CFG_CMD_NAND)
 extern ulong nand_probe(ulong physadr);
 static inline void NF_Reset(void)
 {
 int i;
 NF_SetCE(NFCE_LOW);
 NF_Cmd(0xFF); /* reset command */
 for (i = 0; i < 10; i++); /* tWB = 100ns. */
 NF_WaitRB(); /* wait 200~500us; */
 NF_SetCE(NFCE_HIGH);
 }
 static inline void NF_Init(void)
 {
 #define TACLS 0
 #define TWRPH0 4
 #define TWRPH1 2
 NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)
 |(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));
 /* 1 1 1 1, 1 xxx, r xxx, r xxx */
 /* En 512B 4step ECCR nFCE=H tACLS tWRPH0 tWRPH1 */
 NF_Reset();
 }
 void nand_init(void)
 {
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 NF_Init();
 #ifdef DEBUG
 printf("NAND flash probing at 0x%.8lX\n", (ulong)nand);
 #endif
 printf ("%4lu MB\n", nand_probe((ulong)nand) >> 20);
 }
 #endif
 
 (8)修改GPIO配置(board/fs2410/fs2410.c)。
 
 /* set up the I/O ports */
 gpio->GPACON = 0x007FFFFF;
 gpio->GPBCON = 0x002AAAAA;
 gpio->GPBUP = 0x000002BF;
 gpio->GPCCON = 0xAAAAAAAA;
 gpio->GPCUP = 0x0000FFFF;
 gpio->GPDCON = 0xAAAAAAAA;
 gpio->GPDUP = 0x0000FFFF;
 gpio->GPECON = 0xAAAAAAAA;
 gpio->GPEUP = 0x000037F7;
 gpio->GPFCON = 0x00000000;
 gpio->GPFUP = 0x00000000;
 gpio->GPGCON = 0xFFEAFF5A;
 gpio->GPGUP = 0x0000F0DC;
 gpio->GPHCON = 0x0018AAAA;
 gpio->GPHDAT = 0x000001FF;
 gpio->GPHUP = 0x00000656
 
 (9)提供nand flash相关宏定义(include/configs/fs2410.h),具体参考源码。
 
 (10)加入Nand Flash设备(include/linux/mtd/nand_ids.h)
 
 static struct nand_flash_dev nand_flash_ids[] =
 {
 ......
 {"Samsung KM29N16000",NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000, 0},
 {"Samsung K9F1208U0M",  NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},
 {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000, 0},
 ......
 {NULL,}
 };
 
 (11)设置Nand Flash环境(common/env_nand.c)
 
 int nand_legacy_rw (struct nand_chip* nand, int cmd,
 size_t start, size_t len,
 size_t * retlen, u_char * buf);
 extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
 extern int nand_legacy_erase(struct nand_chip *nand,
 size_t ofs, size_t len, int clean);
 
 /* info for NAND chips, defined in drivers/nand/nand.c */
 extern nand_info_t nand_info[CFG_MAX_NAND_DEVICE];
 ......
 #else /* ! CFG_ENV_OFFSET_REDUND */
 int saveenv(void)
 {
 ulong total;
 int ret = 0;
 puts ("Erasing Nand...");
 if (nand_legacy_erase(nand_dev_desc + 0,
 CFG_ENV_OFFSET, CFG_ENV_SIZE, 0))
 {
 return 1;
 }
 puts ("Writing to Nand... ");
 total = CFG_ENV_SIZE;
 ret = nand_legacy_rw(nand_dev_desc + 0, 0x00 | 0x02, CFG_ENV_OFFSET,
 CFG_ENV_SIZE, &total, (u_char*)env_ptr);
 if (ret || total != CFG_ENV_SIZE)
 {
 return 1;
 }
 puts ("done\n");
 return ret;
 ......
 #else /* ! CFG_ENV_OFFSET_REDUND */
 void env_relocate_spec (void)
 {
 #if !defined(ENV_IS_EMBEDDED)
 ulong total;
 int ret;
 total = CFG_ENV_SIZE;
 ret = nand_legacy_rw(nand_dev_desc + 0, 0x01 | 0x02, CFG_ENV_OFFSET,
 CFG_ENV_SIZE, &total, (u_char*)env_ptr);
 | 
 |