移植u-boot-1.3.4到S3C2440

    技术2022-05-14  13

    移植 u-boot-1.3.4 S3C2440

    一.预备知识:

    1.        首先, U-Boot1.3.4 还没有支持 s3c2440 ,移植仍是用 2410 的文件稍作修改而成的。

    2.        2440 2410 的区别:

    2440 2410 的区别主要是 2440 的主频更高,增加了摄像头接口和 AC‘97 音频接口;寄存器方面,除了新增模 块的寄存器外,移植所要注意的是 NAND FlASH 控制器的寄存器有较大的变化、芯片的时钟频率控制寄存器(芯片 PLL 的寄存器)有一定的变化 。其他寄存器基本是兼容的。

    3.  你开发板的 boot 方式是什么,开发板上电以后是怎么执行的。

    一般来说三星的开发板有三种启动方式: nand nor ram

    具体用那一种方式来启动决定于 CPU 0M[0:1] 这两个引脚,具体请参考 S3C2440 datasheet

     

    nand :对于 2440 来说, CPU 是不给 nand-flash 分配地址空间的, nand-flash 只相当于 CPU 的一个外设, S3C2440 做了一个从 nand-flash 启动的机制。开发板一上电, CPU 就自动复制

          nand-flash 里面的前 4K-Bytes 内容到 S3C2440 内部集成的 SDRAM ,然后把 4K 内容所在          RAM 映射到 S3C2440 0 地址,从 0 地址开始执行。这 4K 的内容主要负责下面这些工          作:初始化中断矢量、设定 CPU 的工作模式为 SVC32 模式、屏蔽看门狗、屏蔽中断、           初始化时钟、把整个 u-boot 重定向到外部 SDRAM 、跳到主要的 C 函数入口。

    nor:  早期的时候利用 nor-flash 启动的方式比较多,就是把 u-boot 烧写到 nor-flash 里面,        直接把 nor-flash 映射到 S3C2440 0 地址,上电从 0 地址开始执行。

    ram:  直接把 u-boot 放到外部 SDRAM 上跑,这一般 debug 时候用到。

    4.  u-boot 程序的入口地址问题

        要理解程序的入口地址,自然想到的是连接文件,首先看看开发板相对于某个开发板的连接文件 "/board/ 你的开发板 /u-boot.lds", 看一个 2410 的例子:

    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 = .;

    }

    (1) ENTRY(_start) 可以看出 u-boot 的入口函数是 _start, 这个没错

    (2) . = 0x00000000 也许可以看出 _start 的地址是 0x00000000, 事实并不是这样的,这里的 0x00000000 没效,在连接的时候最终会被 TETX_BASE 所代替的,具体请参考 u-boot 根目录下的 config.mk.

    (3) 网上很多说法是 _start=TEXT_BASE ,我想这种说法也是正确的,但没有说具体原因。

    本人的理解是这样的, TEXT_BASE 表示 text 段的起始地址,而从

    .text       :

    {

      cpu/arm920t/start.o    (.text)

      *(.text)

    }

    看,放在 text 段的第一个文件就是 start.c 编译后的内容,而 start.c 中的第一个函数就是

    _start ,所以 _start 应该是放在 text 段的起始位置,因此说 _start=TEXT_BASE 也不为过。

    5.  一直不明白的 U-BOOT 是怎样从 4Ksteppingstone 跳到 RAM 中执行的,现在终于明白了。关键在于:

                  ldr   pc, _start_armboot

    _start_armboot:    .word start_armboot

    这两条语句, ldr       pc, _start_armboot 指令把 _start_armboot 这个标签的地方存放的内容 ( 也即是 start_armboot) 移到 PC 寄存器里面, start_armboot 是一个函数地址,在编译的时候给分配了一个绝对地址,所以上面语句实际上是完成了一个绝对地址的跳转。而我一直不明白的为什么在 start.S 里面有很多 BL,B 跳转语句都没有跳出 4Ksteppingstone 原因是他们都是相对于 PC 的便宜的跳转,而不是绝对地址的跳转。还有要补充一下 LDR,MOV,LDR 伪指令的区别。

    LDR      R0,0x12345678   // 把地址 0x12345678 存放的内容放到 R0 里面

    MOV    R0,#x                   // 把立即数 x 放到 R0 里面, x 必须是一个 8 bits 的数移到偶数次得到的数。

    LDR      R0,=0x12345678        // 把立即数 0x12345678 放到 R0 里面

    6.  在移植 u-boot-1.3.3 以上版本的时候要注意:

           u-boot1.3.3 及以上版本 Makefile 有一定的变化,使得对于 24x0 处理器从 nand 启动的遇到问题。也就是网上有人说的:无法运行过 lowlevel_init 。其实这个问题是由于编译器将我们自己添加的用于 nandboot 的子函数 nand_read_ll 放到了 4K 之后造成的(到这不理解的话,请仔细看看 24x0 处理器 nandboot 原理)。我是在运行失败后,利用 mini2440 4 LED 调试发现 u-boot 根本没有完成自我拷贝,然后看了 uboot 根目录下的 System.map 文件就可知道原因。

    解决办法其实很简单: __LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD)) 改为 __LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))

    7.    然后说一下跳转指令。 ARM 有两种跳转方式。

    1 mov p c < 跳转地址〉

      这种向程序计数器 PC 直接写跳转地址,能在 4GB 连续空间内任意跳转。

    2 )通过 B BL BLX BX 可以完成在当前指令向前或者向后 32MB 的地址空间的跳转(为什么是 32MB 呢?寄存器是 32 位的,此时的值是 24 位有符号数,所以 32MB )。

    B 是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址—— 是相对当前 PC 值的一个偏移量,它的值由汇编器计算得出。

    BL 非常常用。它在跳转之前会在寄存器 LR(R14) 中保存 PC 的当前内容。 BL 的经典用法如下:

            bl NEXT  ; 跳转到NEXT

           ……

        NEXT

           ……

           mov pc, lr   ; 从子程序返回。

    二.开始上机移植: ( 红色字体为添加的内容,蓝色字体为修改的内容,下同 )

    给自己的开发板取名为 qljt2440

    1.        随便找个目录解压 u-boot

    $tar –xjvf u-boot-1.3.4.tar.gz2

    2.        进入 u-boot 目录修改 Makefile ( 你要编译 u-boot 那当然少不了配置啦 )

    $cd u-boot-1.3.4

    [uboot@localhost u-boot-1.3.4]$ vim Makefile  修改内容如下:

    __LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD)) 改为

    __LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))

     

    sbc2410x_config: unconfig @$(MKCONFIG) $(@:_config=) arm arm920t sbc2410x NULL s3c24x0

    qljt2440_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t qljt2440 qljt s3c24x0

    /*

    各项的意思如下 :

           qljt2440_config : 这个名字是将来你配置板子时候用到的名字,参见 make qljt2440_config 命令。

    arm: CPU 的架构 (ARCH)

    arm920t: CPU 的类型 (CPU) ,其对应于 cpu/arm920t 子目录。

    qljt2440: 开发板的型号 (BOARD) ,对应于 board/qljt/qljt2440 目录。

    qljt: 开发者 / 或经销商 (vender) s3c24x0: 片上系统 (SOC)

    */

    4. /board 子目录中建立自己的开发板 qljt2440 目录

    由于我在上一步板子的开发者 / 或经销商 (vender) 中填了 qljt ,所以开发板 qljt2440 目录一定要建在 /board 子目录中的 qljt 目录下 ,否则编译会出错。

    [uboot@localhost u-boot-1.3.4] $ cd board

    [uboot@localhost board] $ mkdir qljt qljt/qljt2440

    [uboot@localhost board] $ cp -arf sbc2410x/* qljt/qljt2440/

    [uboot@localhost board] $ cd qljt/qljt2440/

    [uboot@localhost qljt2440] $ mv sbc2410x.c qljt2440.c

    [uboot@localhost qljt2440] $ ls 可以看到下面这些文件:

           config.mk  flash.c  lowlevel_init.s     Makefile    qljt2440.c  u-boot.lds

    [uboot@localhost qljt2440] $ vim Makefile

           COBJS := qljt2440.o flash.o

    5. include/configs/ 中建立开发板所需要的配置头文件

    [uboot@localhost qljt2440] $ cd ../../..

    [uboot@localhost u-boot-1.3.4] $ cp include/configs/sbc2410x.h include/configs/qljt2440.h

    6. 测试交叉编译能否成功

    1 )配置

           [uboot@localhost u-boot-1.3.4] $ make qljt2440_config

           Configure for qljt2440 board…

    (2) 测试编译

           [uboot@localhost u-boot-1.3.4] $ make

    详细信息如下:

    编译信息最后两行:

           arm-linux-objcopy --gap-fill=0xff -O srec u-boot u-boot.srec

    arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

    到此交叉编译成功。

    三.开始针对自己的开发板移植

    1.   修改 /cpu/arm920t/start.S

    1.1 修改寄存器地址定义

    #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)          /* turn off the watchdog */ #if defined(CONFIG_S3C2400) # define pWTCON        0x15300000 # define INTMSK        0x14400008    /* Interupt-Controller base addresses */ # define CLKDIVN    0x14800014    /* clock divisor register */ #else

    # define pWTCON        0x53000000       /* 该地址用来屏蔽看门狗 */ # define INTMSK        0x4A000008    /* Interupt-Controller base addresses 该地址用来屏蔽中断 */ # define INTSUBMSK    0x4A00001C     /* 该地址用来屏蔽子中断 */ # define CLKDIVN    0x4C000014    /* clock divisor register 该地址用来决定 FCLK HCLK PCLK 的比例 */

    #define CLK_CTL_BASE        0x4c000000  /* qljt S3C2440A.pdf 中可以看出该寄存器是存放 Mpll Upll P254 */ #if defined(CONFIG_S3C2440)       

    #define MDIV_405       0x7f << 12   /* qljt   参见 P255 表,同时要知道本开发板的 Fin 12MHz ,需要的 Fclk( 也就

    Mpll) 405MHz */ #define PSDIV_405       0x21       /* qljt 同上,同时设定 PDIV SDIV 的值, PDIV SDIV 参见 S3C2440A.pdf */ #endif #endif

    1.2  修改中断禁止部分   # if defined(CONFIG_S3C2410)     ldr    r1, =0x 7 ff   // 根据 2410 芯片手册, INTSUBMSK 11 位可用,                        //vivi 也是 0x7ff ,不知为什么 U Boot 一直没改过来。但是由于芯片复位默认

    // 所有的终端都是被屏蔽的,所以这个不影响工作     ldr    r0, =INTSUBMSK     str    r1, [r0] # endif # if  defined(CONFIG_S3C2440)     ldr    r1, =0x7fff   // 根据 2440 芯片手册, INTSUBMSK 15 位可用     ldr    r0, =INTSUBMSK     str    r1, [r0] # endif

    1.3 修改时钟设置

    /* 时钟控制逻辑单元能够产生 s3c2440 需要的时钟信号,包括 CPU 使用的主频 FCLK,AHB 总线使用的 HCLK,APB 总线设备使用的 PCLK 2440 里面的两个锁相环 (PLL) ,其中一个对应 FCLK HCLK PCLK ,另外一个对应 UCLK(48MHz)*/

    /* 注意: AHP APB 总线的简介参见“ AHB APB 总线 .doc */

    /* FCLK:HCLK:PCLK = 1:4:8 */     ldr    r0, =CLKDIVN      mov    r1, #5     str    r1, [r0] /* 这三条协处理器命令确实不知道什么意思,在 ATXJGYBC_ql.pdf 中搜 p15 c1 ,只知道它们执行以后会把协处理器 p15 的寄存器 c1 的最高两位置 1 ,但 c1 的最高两位是没有意义啊,弄不懂它的真正意思

    不过我却知道这三条语句是从哪里出来的,详细请参考 s3c2440 datasheet s3c2440datasheet 中的 R1_nF R1_iA.doc */     mrc    p15, 0, r1, c1, c0, 0        /*read ctrl register   qljt*/     orr    r1, r1, #0xc0000000        /*Asynchronous  qljt 改变总线模式为异步模式网上某位朋友说不知到在哪里看到过

    如果 FCLK HCLK 不同的话就要选择这种模式的 */     mcr    p15, 0, r1, c1, c0, 0      /*write ctrl register qljt*/ #if defined(CONFIG_S3C2440)    // 2440 的主频可达 533MHz ,但听说设到 533MHz 时系统

    // 很不稳定,不知是不是 SDRAM 和总线配置的影响,所以现在先设到 //405MHz ,以后在改进。)     /*now, CPU clock is 405.00 Mhz   qljt*/     mov    r1, #CLK_CTL_BASE    /* qljt*/     mov    r2, #MDIV_405                   /* mpll_405mhz    qljt*/     add    r2, r2, #PSDIV_405             /* mpll_405mhz    qljt*/     str    r2, [r1, #0x04]               /* MPLLCON qljt 实际上是设置寄存器 CLK_CTL_BASE+0x04=0x4c000004 的值 */ #endif #endif    /* CONFIG_S3C2400 || CONFIG_S3C2410|| CONFIG_S3C2440 */

     

    1.4 将从 Flash 启动改成从 NAND Flash 启动 。(特别注意:这和 2410 的程序有不同,不可混用!!!是拷贝 vivi 的代码。) 将以下 U Boot 的重定向语句段:

    @#if ndef CONFIG_AT91RM9200

    #if  0 #ifndef CONFIG_SKIP_RELOCATE_UBOOT 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   /*CONFIG_AT91RM9200 */ 然后添加: /* 下载了一个 vivi 源代码看了一下,还真的有下面哪一段代码 */ #ifdef CONFIG_S3C2440_NAND_BOOT   @qljt@@@@@@@@@@@@@@@@SSSSSSSSSSSSS     @ reset NAND

    /* 往下四段内容都是针对 S3C2440 的关于 NAND-FLASH 的寄存器的设置,具体有什么作用,看了 datasheet ,有些明白有些不明白 */     mov    r1, #NAND_CTL_BASE                ldr    r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )     str    r2, [r1, #oNFCONF]             /* 这些宏是在 include/configs/qljt2440.h 中被定义的 */     ldr    r2, [r1, #oNFCONF]  /* 还是弄不懂为什么上面一句 str 以后还要有这句的 ldr 命令? why ?难道是多余的? */     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] /*delay 一段时间 */   mov r3, #0                   @ wait nand1:   add  r3, r3, #0x1   cmp r3, #0xa   blt   nand1 /* 等待 nand-flash 的复位完毕信号 */ nand2:   ldr   r2, [r1, #oNFSTAT]      @ wait ready   tst    r2, #0x4   beq  nand2   ldr    r2, [r1, #oNFCONT]   orr    r2, r2, #0x2        @ Flash Memory Chip Disable  /* 在这里先 Display fansh CE 先,在 C 函数中对 falsh 进行 */

    str    r2, [r1, #oNFCONT]                           /* 操作的时候才 enable ,为什么这样操作不太清楚 */     /* 下面这段用来初始化栈指针 sp 和帧指针 fp, 至于它们的定义和作用参考文件夹 栈指针 sp 和帧指针 fp” 里面的内容

    记住它们都是与函数调用时候相关的。简单来讲就是子函数被调用以后是通过指针的相对位置来查找调用参数和局部变量的,但是由于 sp 经常变化,所以需要 fp 来协助。 */ @ get ready to call C functions (for nand_read())   ldr   sp, DW_STACK_START       @ setup stack pointer /* sp 是指堆栈指针 */   mov fp, #0                    @ no previous frame, so fp=0                 @ copy U-Boot to RAM               /*vivi 里面应该是有一段是针对 gpio 的程序,也许使用来 debug 用的信号灯,这里省略了 */          /* TEXT_BASE uboot 自己的入口地址,在 u-boot-1.3.4-board/qljt/qljt2440 config.mk 中定义

    有趣的是外国人的逆向思维很厉害,它们很灵活地把它放在 SDRAM 的最后 0x80000 地方,也就是 0x33F80000

    */

      ldr   r0, =TEXT_BASE /*r0 : u-boot 复制到 ram 的那个位置 */          mov     r1, #0x0                         /*r1 : falsh 的那个位置开始复制 */          mov r2, #0x20000                     /*r2 : 复制多大的内容 */          bl    nand_read_ll            /* 跳到执行 uboot 复制的程序入口 , 这个函数从哪里来?也是来自 vivi 的,没办法 */          tst    r0, #0x0                            /* 这里特别注意 r0 的值是指 nand_read_ll 执行完以后的返回值,而不是上面

    ldr   r0, =TEXT_BASE 的值,初学者往往在这里想不通 */              beq  ok_nand_read bad_nand_read:                  /* 如果读 nand_read 失败的话,那么 sorry ,重来,或者检查硬件 */ loop2:    b     loop2          @ infinite loop ok_nand_read: @ verify            

    /* 计算机就是好,很容易就可以检测我们放在 SDRAM 中的 u-boot 是不是 flash 中的 uboot

    本开发板使用的是 nand-falsh 的启动方式,板子一上电并不是马上进入 SDRAM 执行程序的。是这样的:板子一上电, S3C2440 自动把 nand-falsh 中从 0 地址开始的 4Kbytes 复制到 S3C2440 集成的某个缓冲区里面 ( 起始地址是 0x00) ,从那里开始执行,那 4K 程序负责把整个 uboot 复制到 SDRAM ,然后才跳到 SDRAM 开始正真的 UBOOT( 这个技术是有个专业名字的我忘记了 ),*/

    /* 下面这段程序的作用就是用开始执行的 4Kbytes 程序跟我们复制到 SDRAM 中的 uboot 的前 4K 程序进行比较,从而校验 */   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_S3C2440_NAND_BOOT  @qljt@@@@@@@@@@@@@@@@@@EEEEEEEEE

     

    1.5 在跳到 C 函数执行前,也就是跳出 start.S 前,添加几个 LED 灯的控制,说明程序跑到这里了,移植的第一阶段完成了。

    /* 本开发板上面有四个 LED 灯,分别接到 CPU GPIO_F[4:7] 这四个引脚上 * /

    #if defined(CONFIG_S3C2440)

    @  LED1 on u-boot stage 1 is ok!     mov    r1, #GPIO_CTL_BASE         add    r1, r1, #oGPIO_F     ldr    r2,=0x5500     str    r2, [r1, #oGPIO_CON]     mov    r2, #0xff     str    r2, [r1, #oGPIO_UP]     mov    r2, #0xdf     str    r2, [r1, #oGPIO_DAT]

    #endif

    1.6 “  _start_armboot:    .word start_armboot  ” 后加入:

    #if defined(CONFIG_S3C2440_NAND_BOOT) .align     2         /* ??? 这里我一直不明白为什么是 .align 2, 因为如果按照 ARM 的规则,意思是按照 2 2 次方 =4bit

    方式对齐,那么就是半个字节对齐,有可能吗? */ DW_STACK_START:  .word  STACK_BASE+STACK_SIZE-4    /* 从这里可以看出该堆栈是从高地址向低地址增长的

    注意这里的 STACK_BASE STACK_SIZE 还没定义,在 1.1 节中定义 */

    #endif

    2. 修改 include/configs/qljt2440.h 文件,在结尾处添加如下内容( 注意: s3c2410 s3c2440 Nand Flash 控制器寄存器不同,不能混用!! ): ...... /*  * Nandflash Boot  */ #define CONFIG_S3C2440_NAND_BOOT 1 #define STACK_BASE    0x33f00000 #define STACK_SIZE    0x8000 /* NAND Flash Controller */ #define NAND_CTL_BASE        0x4E000000 /* Offset */ #define oNFCONF            0x00 /* 这些宏是在 start.S 中被调用的 */ #define oNFCONT            0x04 #define oNFCMD            0x08 #define oNFADDR            0x0c #define oNFDATA            0x10 #define oNFSTAT            0x20 #define oNFECC            0x2c

    /* GPIO */ #define GPIO_CTL_BASE        0x56000000 #define oGPIO_F            0x50 #define oGPIO_CON       0x0   /* R/W, Configures the pins of the port */ #define oGPIO_DAT        0x4    /* R/W,    Data register for port */ #define oGPIO_UP        0x8    /* R/W, Pull-up disable register */ #endif    /* __CONFIG_H */

     

    3.  board/qljt/qljt2440 加入 NAND Flash 读函数文件,拷贝 vivi 中的 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 NFCONT        __REGi(NF_BASE + 0x4) #define NFCMD        __REGb(NF_BASE + 0x8) #define NFADDR        __REGb(NF_BASE + 0xC) #define NFDATA        __REGb(NF_BASE + 0x10) #define NFSTAT        __REGb(NF_BASE + 0x20) //#define GPDAT        __REGi(GPIO_CTL_BASE+oGPIO_F+oGPIO_DAT) #define NAND_CHIP_ENABLE  (NFCONT &= ~(1<<1)) #define NAND_CHIP_DISABLE (NFCONT |=  (1<<1)) #define NAND_CLEAR_RB      (NFSTAT |=  (1<<2)) #define NAND_DETECT_RB      { while(! (NFSTAT&(1<<2)) );} #define BUSY 4 inline void wait_idle(void) {     while(!(NFSTAT & BUSY));     NFSTAT |= BUSY; } #define NAND_SECTOR_SIZE    512 #define NAND_BLOCK_MASK        (NAND_SECTOR_SIZE - 1) /* low level nand read function */

    /* 下面 nand_read_ll 的三个参数来自 start.S 里面调用 nand_read_ll 前的 r0 r1 r2*/ int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) {     int i, j; /* 下面这个 if 保证对 flash 的读操作是从某一页的页头开始的,从直观来看是保证 start_addr[0:8] 位都为 0

    为什么呢?因为本 flash 的一页的大小位 512-bytes ,也就是从 0x0 0x1ff*/     if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {         return -1;    /* invalid alignment */     }     NAND_CHIP_ENABLE;     for(i=start_addr; i < (start_addr + size);) {         /* READ0 */         NAND_CLEAR_RB;                  /* 到此应该可以明白 s3c2440 nandflash 相关寄存器的确切含义了,就是说 s3c2440 里面已经集成了对 nand flash

    作的相关寄存器,只要你的 nand flash 接线符合 s3c2440 datasheet 的接法,就可以随便使用 s3c2440 对于 nand

    flash 的相关寄存器,例如如果你想像 nand flash 写一个命令,那么只要对命令寄存器写入你的命令就可以了, s3c2440 可以自动帮你完成所有的时序动作,写地址也是一样。反过来说如果没有了对 nand flash 的支持,那么我们对 nand falsh 的操作就会增加好多对 I/O 口的控制,例如对 CLE,ALE 的控制。 s3c2440 已经帮我们完成了这部分工作了 */

            NFCMD = 0;         /* Write Address */

    /* 下面这个送地址的过程可以说是这段程序里最难懂的一部分了,难就难于为什么送进 nand flash 的地址忽略了 bit8

    纵观整个 for(i) 循环, i 并不是一个随机的地址,而应该是每一页的首地址。其实 nand flash 并不是忽略了 bit 8 这个

    地址,而是 bit 8 早就被定下来了,什么时候定下来,就是上面的 NFCMD = 0; 语句,本 flash (K9F1208U0B) 支持从

    半页开始读取,从而它有两个读的命令,分别是 0x00( 从一页的上半页开始读 ) 0x01( 从一页的下半页开始读 )

    当取 0x00 时, bit 8=0 ,当取 0x01 bit 8=1.*/         NFADDR = i & 0xff;         NFADDR = (i >> 9) & 0xff;         NFADDR = (i >> 17) & 0xff;         NFADDR = (i >> 25) & 0xff;         NAND_DETECT_RB;         for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {             *buf = (NFDATA & 0xff); /* 每读一次 NANDFLASH 就往 IO 口送下一个 byte ,直到送

    NAND_SECTOR_SIZE 个为止 */             buf++;         }     }     NAND_CHIP_DISABLE;     return 0; }

    4. 修改 board/qljt/qljt2440/Makefile 文件,让刚刚添加的nand_read.c 编译进来 ...... C OBJS := qljt2440.o  nand_read.o flash.o ......

    /*===========================================================

    到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误

    ===========================================================*/

    5. 修改 board/qljt/qljt2440/lowlevel_init.S 文件     依照开发板的内存区的配置情况 , 修改 board/qljt/qljt2440/lowlevel_init.S 文件, : ...... /* REFRESH parameter 下面这 6 个配置都可以参考 s3c2440A datasheet P210 REFRESH 寄存器 */ #define REFEN             0x1    /* Refresh enable */ #define TREFMD             0x0    /* CBR(CAS before RAS)/Auto refresh */ #define Trp             0x01     /* 3clk 这个值可以参考本版子上的 SDRAM datasheet*/ #define Trc             0x3    /* 也就是 SDRAM datasheet 里面的 Tsrc 7clk 本来这个地方是 Trc ,但从 lowlevel_init.S 里面的调用来看,应该是寄存器 REFRESH

    Tsrc 才对,好多地方都没有改过来,我我觉得只是个名字而已,不影响结果

    注意:如果这里改了,那么下面这句中的 Trc 也要改为相应的 Tsrc

    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)*/

    #define Tchr             0x2    /* 3clk 这个从 lowlevel_init.S 里面的调用来看是属于 REFRESH 的保留位,不知道为什

    么还要给他赋值 */ #define REFCNT    1259 / * 这个值的算法参考 s3c2440A datasheet P210 Refresh Counter */

     

    /* 下面不厌其烦地解析一下 lowlevel_init.S 这个原文件 */

     

    #define BWSCON    0x48000000

    ……

    #define Tchr                        0x2   /* 3clk */

    #define REFCNT                         0x0459

    /**************************************/

    /*1. 要知道上面这些配置的最终会被用到下面 SMRDATA 这个数据池里面,所以必须要明白 SMRDATA 这个数据池是用

    来干什么的, SMRDATA 后面每一个 .word 后面防止的数据都是将要写入 BWSCON 开始的寄存器的,总共有 13 .work , 它们后面放置的值将会分别别写入 0x48000000 0x48000004 0x48000008… 一直到 0x48000030 13 个寄存器。 */

    /*2. 上面那些配置的值是怎样决定的呢,详细请参考 s3c2440A 和你所用 SDRAM datasheet 。细心找总是能找到的。 */

    /*3. 而上面的那些配置值最终是通过下面 lowlevel_init 后面的这段函数写到寄存器里面的,下面对该段函数逐一分析: */

    _TEXT_BASE:

             .word         TEXT_BASE

     

    .globl lowlevel_init

    lowlevel_init:

             /* 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  /* 其实明白了前三条语句这段程序就不难懂了,归根到底就是为什么将 SMRDATA 的值减

    _TEXT_BASE 的值?原因是这样的:我们使用的是从 nandflash boot 的方式,目前程序

    仍然在 4K-bytes ‘Steppingstone’( 这里为什么突然冒出个 Steppingstone’, 这个就是我前面提到从 nand flash 引导的方法,但不知道名字,后来重新看 s3c2440A  datasheet nand flash 那一章的开头才知道 ) 上面 运行,在 SMRDATA 后面的的内容仍然在 Steppingstone 里面。但是 SMRDATA 的值是相对于 _TEXT_BASE 值的地址,而且 _TEXT_BASE 是放置 u-boot 的开始地方,所以用 SMRDATA-_TEXT_BASE 就可以得到 SMRDATA 后面内容在 Steppingstone 里面 相对于地址 0x00000000 的放置的所在地方 ( 相对于 0x00 的地址值 ) */

    /* 从这三条语句可以看出前人为了实现从 nand flash 启动可谓费尽心思啊! */

             ldr    r1, =BWSCON         /* Bus Width Status Controller */

             add     r2, r0, #13*4  /* 总共 13 个寄存器 */

    0:

             ldr     r3, [r0], #4

             str     r3, [r1], #4

             cmp     r2, r0

             bne     0b

     

             /* everything is fine now */

             mov  pc, lr

     

             .ltorg         /* 数据缓冲池,上网可以查得资料 */

    /* the literal pools origin */

     

    SMRDATA:

       ……  

        .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

        .word 0xb2

        .word 0x30        /* 需要注意的是 CAS Latency 的值在这里直接配置 */

    .word 0x30

    /*===========================================================

    到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误

    ===========================================================*/

     

    6 修改 /board/qljt/qljt2440/qljt2440.c ,修改这个文件主要针对下面两点:

    (1) GPIO 的控制

    (2) PLL ,毕竟s3c2410s3c2440 不同

    修改其对 GPIO PLL 的配置 ( 请参阅 SBC2440 的硬件说明和 2440 芯片手册 ) ......

    #elif FCLK_SPEED==1 /* Fout = 405MHz */

    //#define M_MDIV 0x5c

    //#define M_PDIV 0x4

    //#define M_SDIV 0x0

    #define M_MDIV 0x7f

    #define M_PDIV 0x2

    #define M_SDIV 0x1

    #elif USB_CLOCK==1

    //#define U_M_MDIV 0x48

    //#define U_M_PDIV 0x3

    #define U_M_MDIV 0x38

    #define U_M_PDIV 0x2

    #define U_M_SDIV 0x2

    ......

    /* set up the I/O ports */

    gpio->GPACON = 0x007FFFFF;

    // gpio->GPFCON = 0x000055AA;

    gpio->GPFCON = 0x5500; /*for LED*/

    ......

    /* arch number of S3C2440 -Board */

    gd->bd->bi_arch_number = MACH_TYPE_S3C2440 ;

    /* adress of boot parameters */

    gd->bd->bi_boot_params = 0x30000100;

    icache_enable();

    dcache_enable();

    gpio->GPFDAT = 0xbf; /*for LED*/

    //int board_init (void) 设置完成后, LED1 LED2 会亮起!

    return 0;

    }

    /*===========================================================

    到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误

    ===========================================================*/

     

    7. 为了实现 NAND Flash 的读写,再次修改 /include/configs/qljt2440.h ...... /*  * High Level Configuration Options  * (easy to change)  */ #define CONFIG_ARM920T        1     /* This is an ARM920T Core    */

    //#define CONFIG_S3C2410           1    /* in a SAMSUNG S3C2410 SoC     */

    //#define CONFIG_SBC2410X        1    /* on a friendly-arm SBC-2410X Board  */

    #define    CONFIG_S3C2440        1   /* 在前面很多地方调用到 CONFIG_S3C2440 , 他是在这里定义   */ #define CONFIG_qljt2440    1  /* 针对一些本开发板配置的宏控制 */ ...... /***********************************************************  * Command definition  ***********************************************************/

    #define CONFIG_CMD_DHCP

    #define CONFIG_CMD_ELF

    #define CONFIG_CMD_PING      

    #define CONFIG_CMD_NAND           

    #define CONFIG_CMD_NET

    #define CONFIG_CMD_ENV

    /* this must be included AFTER the definition of CONFIG_COMMANDS (if any) */ #include <cmd_confdefs.h> #define    CFG_LONGHELP                /* undef to save memory        */

    #define    CFG_PROMPT   "[qljt2440]#" /* 这个就是你启动开发板后命令行显示的内容了 */ /*Monitor Command Prompt  */ #define    CFG_CBSIZE        256        /* Console I/O Buffer Size    */ ...... #define    CFG_LOAD_ADDR      0x30008000   /* 以后 linux kernel 就要放在这里执行 */  /* default load address    */ ......

    //#define CFG_ENV_IS_IN_FLASH        1 / 这里的 flash 应该是指 nor 了,都不知道外国人为什么这么默认 / #define    CFG_ENV_IS_IN_NAND    1 /* 定义这个宏的目的是为了调用 nand flash 类型的 saveenv

    因为还有其它类型存储器的 saveenv ,在 u-boot 中查看 saveenv

    的定义,有多少中定义就有多少种 */

    /* linux nand flash 分区的时候,给 u-boot 分配 256k 的空间 (0~0x40000)

    其中 u-boot.bin    [0x0~0x30000]  192K

       u-boot 的参数 [0x30000~0x40000] 64k

    */ #define CFG_ENV_OFFSET  0x30000          

    #define CFG_ENV_SIZE          0x10000

    /* 注意:网上很多地方都有关于 CONFIG_CMD_NAND CFG_NAND_LEGACY drivers/mtd/nand/nand.c 中的 nand_init() 函数以及 board/qljt/qljt2440/qljt2440.c 中的 nand_init() 函数这四个东西的关系,但大多说的不清不楚,我把它门的关系用表格一一列出来,请参考附录。 */ #define CFG_NAND_LEGACY                   1 /*----------------------------------------------------------------------  * NAND flash settings  */ #if defined (CONFIG_CMD_NAND) #define CFG_NAND_BASE 0x4E000000       /* 这个鬼东西在 drivers/mtd/nand/nand.c 中被调用 ,

    NAND 控制寄存器的基地址 */ /* NandFlash 控制器在 SFR 区起始寄存器地址 */ #define CFG_MAX_NAND_DEVICE 1  /* 支持的最在 Nand Flash 数据 */ #define SECTORSIZE 512 /* 1 页的大小 */ #define NAND_SECTOR_SIZE SECTORSIZE  /* 这两个东西好像也是多余的,备用吧,在次文章搜一下

    就知道其它用到的地方也有定义 */ #define NAND_BLOCK_MASK 511           /* flash 一个 block 的大小 -1*/ /* 页掩码 */ #define ADDR_COLUMN 1 /* 意思是你所用的 nandflash Column 地址占多少个字节 */ /* 一个字节的 Column 地址 */

    #define ADDR_PAGE 3 /* 意思是你所用的 nandflash (row)page 地址占多少个字节 */ /* 3 字节的页块地址 !!!!!*/ #define ADDR_COLUMN_PAGE 4 /* 意思是你所用的 nandflash column 地址 +page 地址共占多少个字节 */ /* 总共 4 字节的页块地址 !!!!! */ #define NAND_ChipID_UNKNOWN 0x00 /* 未知芯片的 ID */ #define NAND_MAX_FLOORS 1             /* 怎样算一 floor*/ #define NAND_MAX_CHIPS 1 /* Nand Flash 命令层底层接口函数 */

    #define rNFCONF (*(volatile unsigned int *)0x4e000000)

    #define rNFCONT (*(volatile unsigned int *)0x4e000004)

    #define rNFCMD (*(volatile unsigned char *)0x4e000008)

    #define rNFADDR (*(volatile unsigned char *)0x4e00000c)

    #define rNFDATA (*(volatile unsigned char *)0x4e000010)

    #define rNFSTAT (*(volatile unsigned int *)0x4e000020)

    #define rNFECC (*(volatile unsigned int *)0x4e00002c)

    /* 下面部分内容是修改的 */

    /* Nand Flash 命令层底层接口函数 */

    /*

    #define NAND_WAIT_READY(nand)       NF_WaitRB()

    #define NAND_DISABLE_CE(nand)         NF_SetCE(NFCE_HIGH)

    #define NAND_ENABLE_CE(nand) NF_SetCE(NFCE_LOW)

    #define WRITE_NAND_COMMAND(d, adr)    NF_Cmd(d)

    #define WRITE_NAND_COMMANDW(d, adr)         NF_CmdW(d)

    #define WRITE_NAND_ADDRESS(d, adr)       NF_Addr(d)

    #define WRITE_NAND(d, adr)          NF_Write(d)

    #define READ_NAND(adr)                          NF_Read()

    */

    #define WRITE_NAND_ADDRESS(d, adr) {rNFADDR = d;}

    #define WRITE_NAND(d, adr) {rNFDATA = d;}

    #define READ_NAND(adr) (rNFDATA)

    #define NAND_WAIT_READY(nand) {while(!(rNFSTAT&(1<<0)));}

    #define WRITE_NAND_COMMAND(d, adr) {rNFCMD = d;}

    #define WRITE_NAND_COMMANDW(d, adr)    NF_CmdW(d)

     

    # if defined(CONFIG_S3C2440)

    #define NAND_DISABLE_CE(nand) {rNFCONT |= (1<<1);}

    #define NAND_ENABLE_CE(nand) {rNFCONT &= ~(1<<1);}

    #endif

    # if defined(CONFIG_S3C2410)

    #define NAND_DISABLE_CE(nand) {rNFCONF |= (1<<11);}

    #define NAND_ENABLE_CE(nand) {rNFCONF &= ~(1<<11);}

    #endif

    /* 允许 Nand Flash 写校验 打开下面宏定义 */ #define CONFIG_MTD_NAND_VERIFY_WRITE 1 ......

    #endif    /* __CONFIG_H */

    8. /board/qljt/qljt2440/qljt2440.c 文件的末尾添加对 Nand Flash 的初始化函数(在后面 Nand Flash 的操作都要用到)

    #if defined(CONFIG_CMD_NAND) /* 大概在 145 */ 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_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 u32 NF_Read_ECC(void) {     S3C2410_NAND * const nand = S3C2410_GetBase_NAND();     return(nand->NFECC); } #if defined(CONFIG_S3C2440) static inline void NF_Cont(u16 cont) {     S3C2410_NAND * const nand = S3C2410_GetBase_NAND();     nand->NFCONT = cont; } static inline void NF_SetCE(NFCE_STATE s) {     S3C2410_NAND * const nand = S3C2410_GetBase_NAND();     switch (s) {     case NFCE_LOW:         nand->NFCONT &= ~(1<<1);         break;     case NFCE_HIGH:         nand->NFCONT |= (1<<1);         break;     } } static inline void NF_Init_ECC(void) {     S3C2410_NAND * const nand = S3C2410_GetBase_NAND();     nand->NFCONT |= (1<<4); } #else 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_Init_ECC(void) {     S3C2410_NAND * const nand = S3C2410_GetBase_NAND();     nand->NFCONF |= (1<<12); } #endif /* 对应 #if defined(CONFIG_S3C2440)*/ static inline void NF_Init(void) { #if 0 #define TACLS 0 #define TWRPH0 3 #define TWRPH1 0 #else #define TACLS 0 #define TWRPH0 4 #define TWRPH1 2 #endif #if defined(CONFIG_S3C2440)     NF_Conf((TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4));     NF_Cont((0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0)); #else      NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));     /*nand->NFCONF = (1<<15)|(1<<14)|(1<<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 */ #endif      NF_Reset(); } #endif

    9. cpu/arm920t/s3c24x0/ Nand.c ,很多人说 u-boot-1.3.4 已经不支持 CFG_NAND_LEGACY 了,但其实还是支持的,定义了 CFG_NAND_LEGACY Nand.c 要做如下修改:

    #error "U-Boot legacy NAND support not available for S3C2410"

    改成

    // #error "U-Boot legacy NAND support not available for S3C2410"

    /*===========================================================

    到这里,编译是不能通过的,原因上一节中 CONFIG_S3C2410 这个宏定义被注释掉,下面要用 CONFIG_S3C2440 这个宏打开 CONFIG_S3C2410 所打开的内容 ===========================================================*/

     

    10.  S3C2440 s3c2410 能够共用的文件中添加 “CONFIG_S3C2440” ,使得原来 s3c2410 的代码可以编译进来。

    1 /include/common.h 文件的第 492 行: /* 一些公用的常用函数,例如 get_fclk()*/

    #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_LH7A40X) || defined(CONFIG_S3C2440)

    2 /include/s3c24x0.h :文件的第 85 95 99 110 148 404 行: /* 一些关于 S3C2440 寄存器的结构体 */

    #if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    3 /cpu/arm920t/s3c24x0/interrupts.c 文件的第 33 行: /* 主要把一些头文件包含进去 */

    #if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB) || defined (CONFIG_S3C2440)

    38 行:

    #elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    4 /cpu/arm920t/s3c24x0/serial.c 文件的第 22 行: /* 主要把一些头文件包含进去 */

    #if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB) || defined (CONFIG_S3C2440)

    26 行:

    #elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    5 /cpu/arm920t/s3c24x0/speed.c 文件的第 33 行:

    #if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB) || defined (CONFIG_S3C2440)

    37 行:

    #elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    顺便修改源代码,以匹配 s3c2440

    static ulong get_PLLCLK(int pllreg) {    ......     m = ((r & 0xFF000) >> 12) + 8;     p = ((r & 0x003F0) >> 4) + 2;     s = r & 0x3; //qljt   /* 这两个 PLL 的算法参见 S3C2440datasheet 254 */ #if defined(CONFIG_S3C2440)    if (pllreg == MPLL)     return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /* CONFIG_SYS_CLK_FREQ qljt2440.h 中定义 */     else if (pllreg == UPLL) #endif //qljt     return((CONFIG_SYS_CLK_FREQ * m) / (p << s)); }

    ......

    /* return FCLK frequency */

    ulong get_FCLK(void)

    {

        return(get_PLLCLK(MPLL));

    }

     

    /* return HCLK frequency */

    ulong get_HCLK(void)

    {

        S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

     /* 看看 s3c2410 s3c2440 datasheet 就知道 s3c2440 HCLK 可选择的值多很多 */

      if (clk_power->CLKDIVN & 0x6)   

      { /* 这里注意:编译的时候发现 CLKDIVN ,这个将会在 12 节解决 */

           if ((clk_power->CLKDIVN & 0x6)==2)        return(get_FCLK()/2);

    if ((clk_power->CLKDIVN & 0x6)==6)        return((clk_power->CAMDIVN & 0x100) ? get_FCLK()/6 : get_FCLK()/3);         /* 注意这里的 CAMDIVN 还没有被定义,在 /include/s3c24x0.h 中定义 */

          if ((clk_power->CLKDIVN & 0x6)==4)        return((clk_power->CAMDIVN & 0x200) ? get_FCLK()/8 : get_FCLK()/4);        

            return(get_FCLK());

      } 

      else   {

            return(get_FCLK());

         }

    //    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());

    }

    ......

    6 /cpu/arm920t/s3c24x0/usb_ohci.c 文件的第 45 行:

    #elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)

    7 drivers/rtc/s3c24x0_rtc.c 文件的第 35 行:

    #elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)  

    8 )在文件中添加 “defined(CONFIG_qljt2440)” ,使得原来 SBC2410X 开发板的代码可以编译进来,

    /cpu/arm920t/s3c24x0/interrupts.c 文件的第 181 行:  

        #elif defined(CONFIG_SBC2410X) || /

          defined(CONFIG_SMDK2410) || /

          defined(CONFIG_VCMA9) || defined(CONFIG_qljt2440)

             tbclk = CFG_HZ;      /* 对于 CFG_HZ 的值,结合 uboot 的说明和 s3c2440 datasheet 就比较容易理解 */

    #else

    9 /cpu/arm920t/s3c24x0/usb.c 文件的第 31 行: #elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    10 /cpu/arm920t/s3c24x0/i2c.c 文件的第 35 行: #elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) 66 85 142 150 174 行: “#ifdef CONFIG_S3C2410” 改为 #if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)

    11 drivers/usb/usb_ohci.c 文件的第 68 行附近: #if defined(CONFIG_ARM920T) || /     defined(CONFIG_S3C2400) || /     defined(CONFIG_S3C2410) || /      defined(CONFIG_S3C2440) || /     defined(CONFIG_440EP) || /     defined(CONFIG_PCI_OHCI) || /     defined(CONFIG_MPC5200)

    11. /include/s3c24x0.h 中加入2440 NAND FLASH 寄存器定义和CAMDIVN 定义:

    typedef struct {

             S3C24X0_REG32   LOCKTIME;

             S3C24X0_REG32   MPLLCON;

             S3C24X0_REG32   UPLLCON;

             S3C24X0_REG32   CLKCON;

             S3C24X0_REG32   CLKSLOW;

             S3C24X0_REG32   CLKDIVN;

             S3C24X0_REG32   CAMDIVN;

    } S3C24X0_CLOCK_POWER;

    ......

    #if defined(CONFIG_S3C2410)  //2440  NAND FLASH 寄存器

    typedef struct {

             S3C24X0_REG32   NFCONF;

             S3C24X0_REG32   NFCMD;

             S3C24X0_REG32   NFADDR;

             S3C24X0_REG32   NFDATA;

             S3C24X0_REG32   NFSTAT;

             S3C24X0_REG32   NFECC;

    } S3C2410_NAND;

    #endif

    #if defined (CONFIG_S3C2440)

    typedef struct {

             S3C24X0_REG32   NFCONF;

             S3C24X0_REG32   NFCONT;

             S3C24X0_REG32   NFCMD;

             S3C24X0_REG32   NFADDR;

             S3C24X0_REG32   NFDATA;

             S3C24X0_REG32   NFMECC0;

             S3C24X0_REG32   NFMECC1;

             S3C24X0_REG32   NFSECC;

             S3C24X0_REG32   NFSTAT;

             S3C24X0_REG32   NFESTAT0;

             S3C24X0_REG32   NFESTAT1;

             S3C24X0_REG32   NFECC;

    } S3C2410_NAND;

    #endif

    12. 修改 /lib_arm 中的 board.c ...... #include <common.h> #include <command.h> #include <malloc.h> #include <devices.h> #include <version.h> #include <net.h> #include <s3c2410.h>     ......

    13. 修改 common/env_nand.c ...... #ifdef CONFIG_INFERNO #error CONFIG_INFERNO not supported yet #endif 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 */ nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; ...... #else /* ! CFG_ENV_OFFSET_REDUND */ int saveenv(void) {     size_t total;     int ret = 0;     nand_erase_options_t nand_erase_options;     nand_erase_options.length = CFG_ENV_RANGE;     nand_erase_options.quiet = 0;     nand_erase_options.jffs2 = 0;     nand_erase_options.scrub = 0;     nand_erase_options.offset = CFG_ENV_OFFSET;     if (CFG_ENV_RANGE < CFG_ENV_SIZE)         return 1;     puts ("Erasing Nand.../n");

    /* 248 行附近 */ //    if (nand_erase_opts(&nand_info[0], &nand_erase_options))   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;

    /* 254 行附近 */ //    if (writeenv(CFG_ENV_OFFSET, (u_char *) env_ptr)) { //        puts("FAILED!/n"); //        return 1; //    }

    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 */ ....... /*  * The legacy NAND code saved the environment in the first NAND device i.e.,  * nand_dev_desc + 0. This is also the behaviour using the new NAND code.  */ void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED)     size_t total;     int ret;     total = CFG_ENV_SIZE;

    /* 360 行附近 */ //    ret = readenv(CFG_ENV_OFFSET, (u_char *) env_ptr);      ret = nand_legacy_rw(nand_dev_desc + 0, 0x01 | 0x02, CFG_ENV_OFFSET,CFG_ENV_SIZE, &total, (u_char*)env_ptr); /*edited by yaoyi 20090314,1.3.4 是先进入到 readenv, 而非直接调用 nand_legacy_rw 因此干脆就不用到 readenv 了,直接注释掉,添加以上代码 */     if (ret || total != CFG_ENV_SIZE)         return use_default();     if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)         return use_default(); #endif /* ! ENV_IS_EMBEDDED */ }

    /* u-boot 运行至第二阶段进入 start_armboot() 函数。其中 nand_init() 函数是对 nand flash 的最初初始化函数。 Nand_init() 函数在两个文件中实现。其调用与 CFG_NAND_LEGACY 宏有关,如果没有定义这个宏,系统调用 drivers/nand/nand.c 中的 nand_init() ;否则调用自己在 board/qljt/qljt2440/qljt2440.c 中的 nand_init() 函数。这里我选择第二种方式。 */

    14. 修改 include/nand.h ....... //#ifndef CFG_NAND_LEGACY #include <linux/mtd/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> .......

    //#endif /* !CFG_NAND_LEGACY */

    /*===========================================================

    到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误

    ===========================================================*/

    9 、在 include/linux/mtd/nand_ids.h 的结构体 nand_flash_ids 加入 /* 至于这个结构体的值怎么得来,有待研究 */

    static struct nand_flash_dev nand_flash_ids[] = {

    ...... /* 结构体 nand_flash_dev doc2000.h 中定义 */

    /* 厂家 型号,生产商编号,本模块的编号, 总共容纳地址的位数,存储页字节数是否为256 地址需要多少字节数减一( 行列地址总共) ,擦除 1 block 的大小, 是否为16 位总线 */     {"Samsung KM29N16000",NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000, 0},     {"Samsung K9F1208U0B",  NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},     {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000, 0}, ......

    };

    /* 下面说说上面结构体的 8 个参数是怎么得出来的,以便日后再次移植的时候会更换 nand flash*/

    /*

    1. “厂家 型号”:这个从 nand flash datasheet 就可以直接找到了吧。

    2. 生产商的编号:也就是 datasheet 里面的 Maker code ,它也同时被存放在 nand flash 里面的 ID(nand flash 应该有一个读 ID 命令的 ) 信息里面)。

    3. 本模块的编号:也就是 datasheet 里面的 device code ,跟 Maker code 一样它也被放到 ID 信息里面。

    4. 总共容纳的地址位数:也就是有效的地址位数。针对于本 flash(K9F1208U0M) 可以参考它的 datasheet 7 页。

    5. 一页所存储的字节数是否为 256 个:针对于本 flash(K9F1208U0M) 可以参考它的 datasheet 7 页。

    6. 地址需要多少字节数减一 ( 行列地址总共 ) :举个例子可能更容易明白,第 4 点中可以知道本 flash(K9F1208U0M) 26 位,而对本 flash 地址的写入每次只能写 8 位,所以至少要写 4 次才能把 26 位地址写入本 flash 4 次的写入针对于编程来说就是 [0:3] ,所以本 falsh 相对于该结构体的该变量的值是 3.

    7. 擦除 1 block 的大小:简单来说就是 1 block 的大小,本 flash 1block=32 pages 1 page=512 bytes, 所以 1 block=512x32=16 k-bytes ,也就是 0x4000

    8. 是否为 16 位总线:本 flash 地址和数据总线共用,都是 8 位的,所以上面值为 0

    */

    15. 修改 /lib_arm 中的 board.c 。添加几个 debug 信息   (这一步可以不用修改)

    ......

    #include <common.h> #include <command.h> #include <malloc.h> #include <devices.h> #include <version.h> #include <net.h>

     

    ......

     

    static int display_banner (void) {       

             S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

             gpio->GPFDAT = 0x8f;   //qljtninja

    // 在串口初始化和 console 初始化完成,串口输出信息之前, LED1 LED2 LED3 会亮起!

        printf ("/n/n%s/n/n", version_string);     debug ("U-Boot code: lX -> lX   BSS: -> lX/n",            _armboot_start, _bss_start, _bss_end);     printf ("U-Boot code: lX -> lX   BSS: -> lX/n",     //qljt         _armboot_start, _bss_start, _bss_end);       //qljt #ifdef CONFIG_MODEM_SUPPORT     debug ("Modem Support enabled/n"); #endif #ifdef CONFIG_USE_IRQ     debug ("IRQ Stack: lx/n", IRQ_STACK_START);     debug ("FIQ Stack: lx/n", FIQ_STACK_START); #endif     return (0); }

     

    ......

    void start_armboot (void)

    {

             init_fnc_t **init_fnc_ptr;

             char *s;

    #ifndef CFG_NO_FLASH

             ulong size;

    #endif

    #if defined(CONFIG_VFD) || defined(CONFIG_LCD)

             unsigned long addr;

    #endif

             S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

    ......

             gpio->GPFDAT = 0x7f;  //qljtninja

    // 在进入命令提示符之前,四个 LED 会同时亮起!

             /* main_loop() can return to retry autoboot, if so just run it again. */

             for (;;) {

                       main_loop ();

             }

     

             /* NOTREACHED - no way out of command loop except booting */

    }

    /*===========================================================

    到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误

    ===========================================================*/

     

    16. 裁减flash 的支持 (这一步也可以不执行)

    (1) board/qljt/qljt2440/flash.c 的头部加上: #if 0 ,尾部加上:#endif

    (2) include/configs/qq2440.h 加上:

    #undef CONFIG_CMD_FLASH

    #undef CONFIG_CMD_IMLS

    ….

    #define CFG_NO_FLASH  1

    (3) common/cmd_bootm.c”#include” 语句后加上

    #ifdef CONFIG_CMD_IMLS

    #undef CONFIG_CMD_IMLS

    #endif

     

    附录:

    一.

    U-boot 的命令默认配置存放在/include/config_cmd_default.h 里,可以修改该文件或者在qq2440.h 里添加#undef 里裁减不需要的内容

    二.

    1. u-boot-1.3.2 ( 不含 u-boot-1.3.2)nand_init 函数的调用关系,它的调用是被“ CONFIG_COMMANDS&

    CFG_CMD_NAND ”和“ CFG_NAND_LEGACY ”控制的, 1 :表示该值为真, 0 :表示该值为假

    CONFIG_COMMANDS&

    CFG_CMD_NAND

    CFG_NAND_LEGACY

    /drivers/mtd/nand/nand.c 中的

    nand_init() 函数

    /board/qljt/qljt2440/qljt2440.c 中的 nand_init() 函数

    0

    0

    0

    0

    0

    1

    0

    0

    1

    0

    1

    1

    1

    1

    0

    1

     

     

    2. u-boot-1.3.2 ( u-boot-1.3.2)nand_init 函数的调用关系,它的调用是被“ CONFIG_CMD_NAND ”和“ CFG_NAND_LEGACY ”控制的, 1 :表示该值为真, 0 :表示该值为假

    CONFIG_CMD_NAND

    CFG_NAND_LEGACY

    /drivers/mtd/nand/nand.c 中的

    nand_init() 函数

    /board/qljt/qljt2440/qljt2440.c 中的 nand_init() 函数

    0

    0

    0

    0

    0

    1

    0

    0

    1

    0

    1

    1

    1

    1

    0

    1


    最新回复(0)