UBOOT-2010-03在S3C2440上的移植<四>------------支持NANDFLASH

来源:互联网 发布:中小企业优化解决方案 编辑:程序博客网 时间:2024/06/08 07:41

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

        上文连接:

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

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 )

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

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

该文档下载链接:点击下载该文档

下接:               

UBOOT-2011-03在S3C2440上的移植<四>------------支持NANDFLASH<续>








原创粉丝点击