U-Boot-2010-03在S3C2440上的移植

来源:互联网 发布:dbaccess declare sql 编辑:程序博客网 时间:2024/05/20 18:16

一、准备移植

1.1)平台及硬件介绍

u-boot版本:u-boot-2011-03
Linux平台:Ubutu 10.10
交叉编译工具:arm-linux-gcc-4.3.2

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

arm-linux-gcc-4.3.2.tgz下载在Linux公社的1号FTP服务器里,下载地址:

FTP地址:ftp://www.linuxidc.com

用户名:www.linuxidc.com

密码:www.muu.cc

在 2011年LinuxIDC.com\4月\Ubuntu 10.10 建立交叉编译工具4.3.2

下载方法见 http://www.linuxidc.net/thread-1187-1-1.html

U-Boot源代码下载地址 http://www.linuxidc.com/Linux/2011-07/38897.htm

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

目标板子:             Micro2440
        CPU:           S3C2440
        SDRAM:      64M
        Nor Flash:   39F1601 2M
        Nand Flash: K9F2G08 256M
        网卡:           DM9000EP

1.2)实现功能:

a):支持NORFLASH读写

b):支持NANDFLASH读写

c):支持NORFLASH引导Linux内核

d):支持NANDFLASH引导内核

e):支持DM9K网卡

f):支持TFTP、TFTP下载

g):支持YAFFS文件系统

h):支持JFFS2文件系统

1.3)获取u-boot-2011-03源码 u-boot-2011-03.tar.bz在:点击获取u-boot-2011-03源码

       Uboot启动流程图如下

二、建立属于自己的开发板项目<eilian240>

2.1)源码解压进入Uboot主目录<大家都懂的>

#tar jaxvf u-boot-2010.03.tar.bz2
#cd cd u-boot-2010.03

2.2)克隆目标板<借签smdk2410>

a)创建eilian240文件

#cd board/samsung/

#mkdir eilian240

#cp -fr smdk2410 eilian240

#cd eilian240

#mv smdk2410.c eilian240.c

2.3)修改eilian240下的Makefile<为什么我想大家都懂的>

       修改COBJS    := smdk2410.o flash.o --->COBJS    := eilian240.o flash.o

2.4)创建板级头文件

#cd include/configs/

#cp -fr smdk2410.h eilian240.h

2.5)修改Uboot根目录下的Makefile文件

#vi Makefile

       修改约164行:CROSS_COMPILE?= arm-linux-

       在约3049行添加:

eilian240_config    :    unconfig
    @$(MKCONFIG) $(@:_config=) arm arm920t eilian240 samsung s3c24x0

*说明:arm    :CPU的架构(ARCH)

             arm920t:CPU的类型

            eilian240:对应在board目录下建立新的开发板项目的目录

             samsung:新开发板项目目录的上级目录,如直接在board下建立新的开发板项目的目录,则这里就为NULL

             s3c24x0:CPU型号

2.6)测试编译新建的<eilian240>项目

a)回到Uboot主目录

b)测试:

#make eilian240_config

   如果出现Configuring for eilian240 board...表示设置成功

#make

    编译完成后在uboot主目录下生成uboot.bin文件:至此uboot移植第一步完成


三、修改时钟频率和中断的配置

Notes:下面绿色色部分代码既是需要修改的代码

UBOOT-2010-03在S3C2440上的移植<一>------------项目搭建

http://www.linuxidc.com/Linux/2011-11/46993.htm

3.1)修改eilian240开发板u-boot第一个要运行的程序cpu/arm920t/start.S(即u-boot的 stage1部分)  

3.1.1) 添加CPU频率初始化设置:

    大概在star.S文件的第146行代码后面添加如下内容

/***定义时钟配置寄存器****change by eilianlau*************************/
#define CLK_CTL_BASE    0x4C000000
#define MDIV_405    0x7f << 12
#define PSDIV_405    0x21
#define MDIV_200    0xa1 << 12
#define PSDIV_200    0x31

/***************这些寄存器的定义S3C2440数据手册上是说的再清楚不过了如果不知道怎么配置请看下面贴图

3.1.2)添加中断禁止部分: 因为2410和2440的中断配置不同

# if defined(CONFIG_S3C2410)
    ldr    r1, =0x3ff
    ldr    r0, =INTSUBMSK
    str    r1, [r0]
#endif                          //
/***添加中断禁止部分****changeby eilianlau*************************/

# if defined(CONFIG_S3C2440)

 ldr r1, =0x7fff

 ldr r0, =INTSUBMSK

 str r1, [r0]

# endif

3.1.3) 修改时钟设置(2440的主频为405MHz)

          原来的时钟设置函数是

/* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr    r0, =CLKDIVN
    mov    r1, #3
    str    r1, [r0]

          修改上述代码如下

/***修改时钟设置(2440的主频为405MHz)****change by eilianlau***FCLK:HCLK:PCLK=1:4:8******/
/*********************************/
#if defined(CONFIG_S3C2440)
    /* FCLK:HCLK:PCLK = 1:4:8 */
    ldr    r0, =CLKDIVN
    mov    r1, #5
    str    r1, [r0]
    
    mrc    p15, 0, r1, c1, c0, 0    
    orr    r1, r1, #0xc0000000        
    mcr    p15, 0, r1, c1, c0, 0    
    /****上面这三句是非常的重要的对于S3C2440,CLKDIVN的第[2:1]位为HDIVN,如果HDIVN非0,CPU总线模   式应该从“fast bus mode”转换为“asynchronous bus mode”,可以通过上面的3句程序实现。否则
CPU的工作频率将自动变成HCLK,不再是FCLK。这对SDRAM的初始化至关重要。****/
    mov    r1, #CLK_CTL_BASE    
    mov    r2, #MDIV_405    
    add    r2, r2, #PSDIV_405    
    str    r2, [r1, #0x04]        /* MPLLCON*/
/************************************/
#else
    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr    r0, =CLKDIVN
    mov    r1, #3
    str    r1, [r0]
/*************************************/    
    mrc    p15, 0, r1, c1, c0, 0    
    orr    r1, r1, #0xc0000000    
    mcr    p15, 0, r1, c1, c0, 0    
    
    mov    r1, #CLK_CTL_BASE    
    mov    r2, #MDIV_200    
    add    r2, r2, #PSDIV_200    
    str    r2, [r1, #0x04]
#endif
/**************没什么可说的还是S3C2440数据手册********************/
#endif    /* CONFIG_S3C24X0 */

3.2)修改cpu/arm920t/s3c24x0/ speed.c

       我直接添代码吧<不知道要说什么。。>

3.2.1)修改static ulong get_PLLCLK(int pllreg)

static ulong get_PLLCLK(int pllreg)
{
    struct s3c24x0_clock_power *clk_power =s3c24x0_get_base_clock_power();
    ulong r, m, p, s;

    if (pllreg == MPLL)
        r = readl(&clk_power->MPLLCON);
    else if (pllreg == UPLL)
        r = readl(&clk_power->UPLLCON);
    else
        hang();

    m = ((r & 0xFF000) >> 12) + 8;
    p = ((r & 0x003F0) >> 4) + 2;
    s = r & 0x3;
#if defined(CONFIG_S3C2440)
    if(pllreg == MPLL)
    {   //参考S3C2440芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2s)
        return((CONFIG_SYS_CLK_FREQ * m * 2)/ (p << s));
    }
#endif


    return (CONFIG_SYS_CLK_FREQ * m) / (p << s);
}

3.2.2)修改ulong get_FCLK(void)

/* return HCLK frequency */
ulong get_HCLK(void)
{
    struct s3c24x0_clock_power *clk_power =s3c24x0_get_base_clock_power();
#if defined(CONFIG_S3C2440)
    return(get_FCLK()/4);
#endif

    return (readl(&clk_power->CLKDIVN) & 2) ?get_FCLK() / 2 : get_FCLK();
}

3.3)修改board/Samsung/eilian240/ eilian240.c 

3.3.1)修改FCLK代码中带详细解释

/**#define FCLK_SPEED 1 *默认是加载FCLK_SPEED==1但2440的FCLK输出时钟为405Mhz该设置请参考2440数据手册255页那个表*/
#define FCLK_SPEED 2
/**change by eilianlau********/
#if FCLK_SPEED==0        /* Fout = 203MHz, Fin =12MHz for Audio */
#define M_MDIV    0xC3
#define M_PDIV    0x4
#define M_SDIV    0x1
#elif FCLK_SPEED==1        /* Fout = 202.8MHz */
#define M_MDIV    0xA1
#define M_PDIV    0x3
#define M_SDIV    0x1

/**change by eilianlau********/
#elif FCLK_SPEED==2       /* Fout = 405MHz */
#define M_MDIV    0x7f
#define M_PDIV    0x2
#define M_SDIV    0x1
#endif

/**change by eilianlau********/

3.3.2)修改USB的时钟

/**#define USB_CLOCK 1*道理同上*/
#define USB_CLOCK 2

#if USB_CLOCK==0
#define U_M_MDIV    0xA1
#define U_M_PDIV    0x3
#define U_M_SDIV    0x1
#elif USB_CLOCK==1
#define U_M_MDIV    0x48
#define U_M_PDIV    0x3
#define U_M_SDIV    0x2

/**change by eilianlau********/
#elif USB_CLOCK==2
#define U_M_MDIV    0x38
#define U_M_PDIV    0x2
#define U_M_SDIV    0x2
#endif

3.3.3)修改引导Linux内核的机器码<机器码必须和Linux内核中的机器码相同 这个是自己可以定义> cpu/arm920t/s3c24x0/ speed.c

    /* arch number of eilian240-Board */
    /* 这个机器码用于和Linux内核配对*/
    gd->bd->bi_arch_number =MACH_TYPE_EILIAN240;

    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100;

    icache_enable();

3.4)定义3.3.3的机器码MACH_TYPE_EILIAN240

3.4.1)修改u-boot-2010.03/include/asm-arm/mach-types.h    <机器码都定义在这个文件中呢。。>

      在第2702行添加下面的宏定义就O了

#defineMACH_TYPE_EILIAN240           8000

3.5)在include/configs/eilian240.h头文件中添加之前用到的CONFIG_S3C2440宏
       第40行添加代码如下:
#define CONFIG_S3C2440 1   /* on aeilian240 Board  */ 

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

3.6)另外为了更好理解S3C2440的时钟体系 上传几个图来理解希望对大家有所帮助<移植S3C3440芯片手册是必须要要参看的>

S3C2440时钟体系<一>


S3C2440时钟体系<二>

 

S3C2440时钟启动<一>

S3C2440时钟启动<二>

 

S3C2440时钟时钟相关寄存器LOCKTIME


S3C2440时钟时钟相关寄存器MPLLCON


S3C2440时钟时钟相关寄存器CLKDIVN


至此硬件初始化的工作就完成了。。可以测试编译下看有没有错误<编译通过 No problem> 如发现有错误请指出 十分感谢。。。



四、自动识别启动模式Nand Or NorNotes:1)上文连接

UBOOT-2010-03在S3C2440上的移植<二>------------硬件初始化

http://www.linuxidc.com/Linux/2011-11/46993p2.htm

Notes:2)下面代码段红色部分为删除部分

Notes:3)下面代码段绿色部分为添加部分

4.1)Notes:以下资源来自大神Tekkaman Ninja博客点击查看原文

移植之前还是请大家先看S3C2440数据手册<第五章 存储器控制器>中的<Figure 5-1. S3C2440A Memory Map after Reset>(也就是那个映射图),从理论上来讲,对于0x40000000以后的内存,只有在Nor boot的时候才存在;而在nand

boot 的时候他被映射到了0x00000000,在0x40000000以后不存在内存。如果我们在启动的时候,将一些特定的数据写入0x40000000~0x40001000之间,那么按照数据手册上的说法,如果回读的结果和写入的一致说明是nor boot,否则就是nand boot!<写这个文章的时候我又看了一次。。。。。。。。。>

从上图我们可以看出,无论是Nor boot还是nand boot ,这4K的内部SRAM都被映射到了0x40000000,而在nand boot的时候,这块内存同时还被映射到了0x00000000那么一开始提出的办法就不可行了,而且在nand boot 的时候写入0x40000000~0x40001000还会破坏自身的程序。

但是通过上面的图,我想到了解决的办法:
在启动的时候,用程序
0x40000000~0x40001000中的某些位置清零,如果回读0x00000000~0x00001000中的相应位置后为零,说明是Nand boot,如果是原来的数据(一定要选非零的位置)就是Nor boot。判断完后如果是nand boot,还要恢复被改动的数据,再进入自拷贝阶段。
   只要检测的位置合理,这方法是可行的。现在的关键是选什么位置的数据最好呢?经过查看源码,我选择了在start.S文件开头,全局中断向量之后的:



.balignl 16,0xdeadbeef


选这个数据作为检测位置的理由如下:
(1)他是非零数,而且数据是确定的:0xdeadbeef;
(2)他的位置是固定的:
0x0000003c0x4000003c);
(3)他在检测程序之前,不会影响程序的向下运行;
(4)他不属于程序,他是一个程序中的魔数(Magic Number),用魔数来检测也比较合理


所以我最后的检测步骤是:
在启动的时候,将0x4000003c位置开始的四个字节清零,然后读取0x0000003c位置开始的四个字节。如果回读的结果为零,说明是nand boot,否则就是Nor boot(为了保险还可以判断是否为0xdeadbeef,不是的话就说明有未知错误,死循环!)。但是最后有一点很重要:如果是Nand boot,必须要复原清零的数据。原因是:在nand boot过后,会核对内部SRAM中的4K程序,和从Nand中拷贝到SDRAM的前4K程序是否一致,如果不一致会进入死循环。

/**************好了看完这一对白虽然明白了很多但是跟随着的问题也多了很多,我就在想.balignl 16,0xdeadbeef 这个东东究竟是起到什么作用的,你又怎么知道它的位置就是固定的而且又是0x0000003c(0x4000003c)呢?为此我纠结了好久还好通过一番纠结我终于找到了答案<这得感谢我哥:baiu and google>1)点击查看答案and2)点击查看答案 那么接下来是时候动手代码的移植了

4.1.1)打开之前我们修改的那个start.S文件 第90行左右添加一个Flash启动标志

/************************标志位从Nand启动时将其设置为0,从Nor启动时将其设置为1*****/
.globl bBootFrmNORFlash
bBootFrmNORFlash:
 .word 0

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

 /*
     * we do sys-critical inits only at reboot,
     * not when booting from ram!
     */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl    cpu_init_crit
#endif  /***找到这段代码在下面添加:***/

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

4.1.3)如果代码当前位置不在内存中,就判断启动方式为Nand Flash或者Nor Flash 

Notes:没有说明就默认在以上代码的后面添加

/***************** CHECK_BOOT_FLASH ******************************************/
    ldr    r1, =( (4<<28)|(3<<4)|(3<<2) )        /* address of Internal SRAM  0x4000003C*/
    mov    r0, #0        /* r0 = 0 */
    str    r0, [r1]


    mov    r1, #0x3c        /* address of men  0x0000003C*/
    ldr    r0, [r1]
    cmp    r0, #0
    bne    relocate

    /* recovery  */
    ldr    r0, =(0xdeadbeef)
    ldr    r1, =( (4<<28)|(3<<4)|(3<<2) )
    str    r0, [r1]
/***************** CHECK_BOOT_FLASH ******************************************/

4.1.4)在Nand Flash中启动的话,那么Nand Flash搬移代码如下:

Notes:定义u-boot在Nand flash中存放的长度为#define LENGTH_UBOOT 0x100000<1M>,可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。

/***************** NAND_BOOT *************************************************/

#define LENGTH_UBOOT 0x100000
#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
/***************** NAND_BOOT *************************************************/
 

4.1.5)在Nor Flash中启动的话,那么Nor Flash搬移代码如下:

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

SetBootFlag:
    ldr r0, =bBootFrmNORFlash
    mov r1, #1
    str r1, [r0]
/***************** NOR_BOOT *************************************************/

4.1.6)删除下面这段代码

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

4.2.1)在_start_armboot: .word start_armboot 后面添加:

#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
 .align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4

/**********************************到此完了。。。。********/

Notes1):如果你不是第一次移植UBOOT那么建议好好的理解一下上面这些代码<当然加入你还是第一次的话那么好了直接COPY吧很管用的>

Notes2):上面用到了一个nand_read_ll函数,该函数用来读NANDFLASH的<下一篇就是实现这个UBOOT对NAND的支持了先休息一下吧>


四、修改 Nand flash 相关代码,使 U-BOOT 支持 Nand flash

        上文连接:

UBOOT-2010-03在S3C2440上的移植<三>------------自动识别启动模式Nand Or Nor

http://www.linuxidc.com/Linux/2011-11/46993p3.htm

5.1)start.S中Nand Boot需要nand_read_ll函数, 在board/samsung/eilian240下新建文件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 <hwang@mizi.com>
 * Date  : $Date: 2004/02/04 10:37:37 $
 *
 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
 * Author: Harald Welte <laforge@openmoko.org>
 */

#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
#define NFCONF        __REGi(NF_BASE + 0x0)/**0x4e000000**NAND配置寄存器**/
#define NFCONT        __REGi(NF_BASE + 0x4)/**0x4e000004**NAND控制寄存器**/
#define NFCMD        __REGb(NF_BASE + 0x8)/*0x4e000008**NAND命令寄存器Bit7-0存放芯片内设命令值*/
#define NFADDR        __REGb(NF_BASE + 0xc)/**0x4e00000C**NAND地址寄存器Bit7-0存放**/
#define NFDATA        __REGb(NF_BASE + 0x10)/**0x4e000010**NAND数据寄存器8位IO**/
#define NFDATA16    __REGw(NF_BASE + 0x10)/**0x4e000010**NAND数据寄存器16位IO**/
#define NFSTAT        __REGb(NF_BASE + 0x20)/**0x4e000020**NAND状态寄存器**/
#define NFSTAT_BUSY    1
#define nand_select()    (NFCONT &= ~(1 << 1))  /***芯片片选**/
#define nand_deselect()    (NFCONT |= (1 << 1))/**取消芯片片选**/
#define nand_clear_RnB()    (NFSTAT |= (1 << 2)) /**NFSTAT状态寄存器的Bit2 写1
/**************上面这些都是一些S3C2440NANDFLASH寄存器的定义怎么定义数据手册很详细的*******/
/****我再一次的打开了S3C2440数据手册***/
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;
};


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();

    for (i = 0; i < (nand->page_size>>1); i++) {
        *ptr16 = NFDATA16;
        ptr16++;
    }

    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 */
           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) {
            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;
}

/**********************接下来我想对这些代码进行分析这才是我本次移植的目的*******************/

六、了解NANDFLASH的一些硬件特性 

             6.1.1)nand flash layout

            

             6.1.2)Nand Flash的物理存储单元的阵列组织结构以本开发板上的K9F2G08X0A为例:

            

            6.1.3)  Block 块
            一个 Nand Flash (的 chip,芯片) 由很多个块(Block)组成,块的大小一般是 128KB, 256KB, 512KB,此处是 128KB。其他的小于 128KB 的,比如 64KB,一般都是下面将要介绍到的small block的Nand Flash。  块 Block,是Nand Flash的擦除操作的基本/最小单位。
           6.1.4)  Page 页
           每个块里面又包含了很多页(page) 。每个页的大小,对于现在常见的Nand Flash多数是2KB,最新的Nand Flash的是4KB、8KB等,这类的页大小大于 2KB的Nand Flash,被称作 big block的 Nand Flash,对应的发读写命令地址,一共 5个周期(cycle),而老的 Nand Flash,页大小是 256B,512B,这类的 Nand Flash被称作 small block,地址周期只有 4个。 页 Page,是读写操作的基本单位。 不过,也有例外的是,有些 Nand Flash支持 subpage(1/2 页或 1/4页)子页的读写操作,不过一般很少见。

           6.1.5)  oob / Redundant Area / Spare Area
           每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而Linux 系统中,一般叫做 OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做 EDC(Error Detection Code)/所以设计了多余的区域,用于放置数据的校验值。Oob 的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了 oob。  
           关于 oob具体用途,总结起来有:
           标记是否是坏快
           存储ECC数据
           存储一些和文件系统相关的数据。如 jffs2 就会用到这些空间存储一些特定信息,而yaffs2 文件系统,会在 oob中,存放很多和自己文件系统相关的信息

           6.1.6)NANDFLASH常用命令设置

          

            从上图可以看到,如果要实现读一个页的数据,就要发送 Read的命令,而且是分两个周期(Cycle),即分两次发送对应的命令,第一次是 0x00h,第二次是 0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址。
            对应地,其他常见的一些操作,比如写一个页的数据(Page Program),就是先发送 0x80h,然后发生要写入的地址,再发送0x10h。

            6.1.7)PIN DESCRIPTION

             

                       

                        对上表进行中文翻译。。。。。。。。

                  

                      Notes:#代表低电平有效

                      6.1.8)理解NANDFLASH的操作时序图以Read Operation为例

                    

             Notes:此图来自三星的型号 K9F2G08的 Nand Flash的数据手册(datasheet)。
我们来一起看看,我在上图的特意标注的①边上的红色竖线。 红色竖线所处的时刻,是在发送读操作的第一个周期的命令 0x00之前的那一刻。 让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。  
            1)红色竖线穿过的第一行,是 CLE。前面介绍命令所存使能(CLE)的那个引脚将CLE 置 1,就说明你将要通过 I/O 复用端口发送进入Nand Flash的,是命令,而不是地址或者其他类型的数据。只有这样将 CLE 置 1,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将 CLE 置 1 使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。
            2)而第二行,是 CE#,那一刻的值是 0。这个道理很简单,你既然要向Nand Flash发命令,那么先要选中它,所以,要保证 CE#为低电平,使其有效,也就是片选有效。
            3)第三行是 WE#,意思是写使能。因为接下来是往 Nand Flash里面写命令,所以,要使
得 WE#有效,所以设为低电平。
           4)第四行,是 ALE 是低电平,而 ALE 是高电平有效,此时意思就是使其无效。而对应地,前面介绍的,使 CLE 有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的 0x30) ,而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使 CLE 无效了。
           5)第五行,RE#,此时是高电平,无效。可以看到,知道后面低 6 阶段,才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。
           6)第六行,就是我们重点要介绍的,复用的输入输出 I/O 端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址。
           7)第七行,R/B#,高电平,表示 R(Ready)/就绪,因为到了后面的第 5阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统在忙着干活,属于忙的阶段,所以,R/B#才变成低,表示 Busy忙的状态的。 其他的时序的就类似的理解。

                  8)以上资料摘字网络 经整理成为自己的东西。。。(I'm sorry I forgot to is the great god of the blog )

 七、好了经过上面这么一分析再回到第五步 那些代码是否能看懂了<至少我明白了很多>

/***********************************************到此结束*******************************************/


0 0
原创粉丝点击