yiffer的个人空间 https://blog.eetop.cn/edesign [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

ARM9 2410移植之Nand flash 驱动的编写与移植

已有 1831 次阅读| 2010-6-14 23:21 |个人分类:linux驱动开发

1 Nand flash 工作原理

    S3C2410 板的Nand Flash 支持由两部分组成:Nand Flash 控制器(集成在S3C2410 CPU)和Nand Flash 存储

芯片(K9F1208U0B)两大部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命

令才能完成。所以, Nand Flash相当于S3C2410的一个外设,而不位于它的内存地址区.

1.1 Nand flash 芯片工作原理

     Nand flash 芯片型号为Samsung K9F1208U0B,数据存储容量为64MB ,采用块页式存储管理。8 个I/O

引脚充当数据、地址、命令的复用端口。

1.1.1 芯片内部存储布局及存储操作特点

    一片Nand flash 为一个设备(device), 其数据存储分层为:

      1设备(Device) = 4096 块(Blocks)

      1块(Block) = 32 页/ (Pages/rows) ;页与  是相同的意思,叫法不一样

      1块(Page) = 528 字节(Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes)

    在每一页中,最后16 个字节 (又称OOB)用于Nand Flash 命令执行完后设置状态用,剩余512 个字节又

分为前半部分和后半部分。可以通过Nand Flash 命令00h/01h/50h 分别对前半部、后半部、OOB 进行定位通过

Nand Flash 内置的指针指向各自的首地址。

存储操作特点:

1. 擦除操作的最小单位是块。

2. Nand Flash 芯片每一位(bit)只能从1 变为0 ,而不能从0 变为 1,所以在对其进行写入操作之前要一定将相应

块擦除(擦除即是将相应块得位全部变为 1).

3. OOB 部分的第六字节(即517 字节)标志是否是坏块,如果不是坏块该值为FF ,否则为坏块。

4. 除OOB 第六字节外,通常至少把OOB 的前3 个字节存放Nand Flash 硬件ECC 码(关于硬件ECC 码请参看

Nandflash 控制器一节).

1.1.2 重要芯片引脚功能

    I/O0-I/O7:复用引脚。可以通过它向nand flash 芯片输入数据、地址、nand flash 命令以及输出数据和操作

状态信息。

    CLE(Command Latch Enable):  命令锁存允许

    ALE(Address Lactch Enable): 地址锁存允许

    -CE: 芯片选择    

    -RE: 读允许

    -WE: 写允许

    -WP: 在写或擦除期间,提供写保护

    R/-B: 读/忙输出

1.1.3 寻址方式

   Samsung K9F1208U0B Nand Flash 片内寻址采用26 位地址形式。从第0 位开始分四次通过I/O0 -I/O7 进

传送,并进行片内寻址。具体含义如下:

   0 -7 位:字节在上半部、下半部及OOB 内的偏移地址

   8 位:值为0 代表对一页内前256 个字节进行寻址

         值为1 代表对一页内后256 个字节进行寻址

   9-13位:对页进行寻址

   14-25 位:对块进行寻址

   当传送地址时,从位0 开始

1.1.4 Nand flash主要内设命令详细介绍

Nand Flash 命令执  是通过将命令字送到Nand Flash 控制器的命令寄存器来执           。

Nand Flash 的命令是分周期执行的,每条命令都有一个或多个执  周期,每个执  周期都有相映代码表示该周

期将要执行的动作。

主要命令有:Read 1 、Read 2 、Read ID 、Reset 、Page Program 、Block Erase 、Read Status 。

详细介绍如下:

1. Read 1:

功能:表示将要读取Nand flash 存储空间中一个页的前半部分,并且将内置指针定位到前半部分的第一个字节。

命令代码:00h

2. Read 2:

功能:表示将要读取Nand flash 存储空间中一个页的后半部分,并且将内置指针定位到后半部分的第一个字节。

命令代码:01h

3. Read ID:

功能:读取Nand flash 芯片的ID 号

命令代码:90h

4. Reset:

功能:重启芯片。

命令代码:FFh

5. Page Program:

功能:对页进行编程命令, 用于写操作。

命令代码:首先写入00h(A 区)/01h(B 区)/05h(C 区), 表示写入那个区; 再写入80h 开始编程模式(写入模式) ,接

下来写入地址和数据; 最后写入 10h 表示编程结束.

6. Block Erase

功能:块擦除命令。

命令代码:首先写入60h 进入擦写模式,然后输入块地址; 接下来写入D0h, 表示擦写结束.

7. Read Status

功能:读取内部状态寄存器值命令。

命令代码:70h

1.2 Nand Flash 控制器工作原理

   对Nand Flash 存储芯片进行操作, 必须通过Nand Flash 控制器的专用寄存器才能完成。所以,不能对Nand

Flash 进 总线操作。而Nand Flash 的写操作也必须块方式进          。对Nand Flash 的读操作可以按字节读取。

1.2.1 Nand Flash控制器特性

1. 支持对Nand Flash 芯片的读、检验、编程控制

2. 如果支持从Nand Flash 启动, 在每次重启后自动将前Nand Flash 的前4KB 数据搬运到ARM 的内部RAM 中

3. 支持ECC 校验

1.2.2 Nand Flash控制器工作原理

   Nand Flash 控制器在其专用寄存器区(SFR)地址空间中映射有属于自己的特殊功能寄存器,就是通过将Nand

Flash 芯片的内设命令写到其特殊功能寄存器中,从而实现对Nand flash 芯片读、检验和编程控制的。特殊功能

寄存器有:NFCONF 、NFCMD 、NFADDR 、NFDATA 、NFSTAT 、NFECC 。寄存详细说明见下一节。

1.3 Nand flash 控制器中特殊功能寄存器详细介绍

1. 配置寄存器(NFCONF)

      功能:用于对Nand Flash 控制器的配置状态进行控制。

      在地址空间中地址:0x4E000000,其中:

      Bit15:Nand Flash 控制器使能位,置0 代表禁止Nand Flash 控制器,置 1 代表激活Nand Flash 控制器;

要想访问Nand Flash 芯片上存储空间,必须激活Nand Flash 控制器。在复位后该位自动置0 ,因此在初始化时

必须将该位置为 1 。

      Bit12:初始化ECC 位,置1 为初始化ECC;置0 为不初始化ECC 。

      Bit11:Nand Flash 芯片存储空间使能位,置0 代表可以对存储空间进行操作;置 1 代表禁止对存储空

间进行操作。在复位后,该位自动为 1 。

      Bit10-8:TACLS 位。根据此设定CLE&ALE 的周期。TACLS 的值范围在0 -7 之间。

      Bit6-4 、2-0 分别为:TWRPH0 、TWRPH1 位。设定写操作的访问周期。其值在0 -7 之间。

2. 命令寄存器(NFCMD)

      功能:用于存放Nand flash 芯片内设的操作命令。

      在地址空间中地址:0x4E000004 ,其中:

      Bit0 -7:存放具体Nand flash 芯片内设的命令值。其余位保留以后用。

3. 地址寄存器(NFADDR)

      功能:用于存放用于对Nand flash 芯片存储单元寻址的地址值。

      在地址空间中地址:0x4E000008 ,其中:

      Bit0 -7:用于存放地址值。因为本款Nand flash 芯片只有I/O0 -7 的地址/数据复用引脚且地址是四周

期每次8 位送入的,所以这里只用到8 位。其余位保留待用。

4. 数据寄存器(NFDATA)

      功能:Nand flash 芯片所有内设命令执行后都会将其值放到该寄存器中。同时,读出、写入Nand flash

存储空间的值也是放到该寄存器。

      在地址空间中地址:0x4E00000C ,其中:

      Bit0 -7:用于存放需要读出和写入的数据。其余位保留代用。

5. 状态寄存器(NFSTAT)

      功能:用于检测Nand flash 芯片上次对其存储空间的操作是否完成。

      在地址空间中地址:0x4E000010 ,其中:

      Bit0:置0 表示Nand flash 芯片正忙于上次对存储空间的操作;置 1 表示Nand flash 芯片准备好接收新

的对存储空间操作的请求。

6. ECC 校验寄存器(NFECC)

      功能:ECC 校验寄存器

      在地址空间中地址:0x4E000014 ,其中:

        Bit0-Bit7: ECC0

        Bit8-Bit15: ECC1

        Bit16-Bit23: ECC2

1.4 Nand Flash 控制器中的硬件 ECC 介绍

1.4.1 ECC产生方法

   ECC 是用于对存储器之间传送数据正确进行校验的一种算法,分硬件ECC 和软件ECC 算法两种,在

S3C2410 的Nand Flash 控制器中实现了由硬件电路 (ECC 生成器)实现的硬件ECC 。

1.4.2 ECC生成器工作过程

   当写入数据到Nand flash 存储空间时, ECC 生成器会在写入数据完毕后自动生成ECC 码,将其放入到

ECC0 -ECC2 。当读出数据时Nand Flash 同样会在读数据完毕后,自动生成ECC 码将其放到ECC0 -ECC2 当

中。

1.4.3 ECC 的运用

    当写入数据时,可以在每页写完数据后将产生的ECC 码放入到OOB 指定的位置(Byte 6)去,这样就完成了

ECC 码的存储。这样当读出该页数据时,将所需数据以及整个OOB 读出,然后将指定位置的ECC 码与读出数

据后在ECC0 -ECC1 的实际产生的ECC 码进  对比,如果相等则读出正确,若不相等则读取错误需要进行重

读。

2 在ADS 下 flash烧写程序

2.1 ADS 下flash烧写程序原理及结构

    基本原理:在windows 环境下借助ADS 仿真器将在SDRAM 中的一段存储区域中的数据写到Nand flash 存

储空间中。烧写程序在纵向上分三层完成:

    第一层: 主烧写函数 (完成将在SDRAM 中的一段存储区域中的数据写到Nand flash 存储空间中);

    第二层: 为第一层主烧写函数提供支持的对Nand flash 进行操作的页读、写,块擦除等函数;

    第三层:为第二层提供具体Nand flash 控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正的

将数据能够在SDRAM 和Nand flash 之间实现传送的函数。

    下面对其三层进行分述:

2.2 第三层实现说明

2.1.1 特殊功能寄存器定义

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

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

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

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

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

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

#define rNFECC0 (*(volatile unsigned char  *)0x4e000014)

#define rNFECC1 (*(volatile unsigned char *)0x4e000015)

#define rNFECC2 (*(volatile unsigned char *)0x4e000016)

2.1.2 操作的函数实现

1. 发送命令

#define NF_CMD(cmd)    {rNFCMD=cmd;}

2. 写入地址

#define NF_ADDR(addr)  {rNFADDR=addr;}

3. Nand Flash 芯片选中

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

4. Nand Flash 芯片不选中

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

5. 初始化ECC

#define NF_RSTECC()    {rNFCONF|=(1<<12);}

6. 读数据

#define NF_RDDATA()         (rNFDATA)

7. 写数据

#define NF_WRDATA(data) {rNFDATA=data;}

8. 获取Nand Flash 芯片状态

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

0/假: 表示Nand Flash 芯片忙状态

1/真:表示Nand Flash 已经准备好

2.3 第二层实现说明

2.3.1 Nand Flash 初始化

void NF_Init(void)

{     /* 设置Nand Flash 配置寄存器, 每一位的取值见1.3 节 */

    rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);

    /* 复位外部Nand Flash 芯片 */

    NF_Reset();

}

2.3.2 Nand flash复位

static void NF_Reset(void)

{

    int i;

    

    NF_nFCE_L();        /* 片选Nand Flash 芯片*/

    NF_CMD(0xFF);       /* 复位命令          */

    for(i=0;i<10;i++);  /* 等待tWB = 100ns.      */

    NF_WAITRB();        /* wait 200~500us;     */

    NF_nFCE_H();        /* 取消Nand Flash 选中*/

}

2.3.3 获取 Nand flash ID

返回值为Nand flash 芯片的ID 号

unsigned short  NF_CheckId(void)

{

    int i;

    unsigned short id;

    

    NF_nFCE_L();            /* 片选Nand Flash 芯片*/

    NF_CMD(0x90);           /* 发送读ID 命令到Nand Flash 芯片 */

    NF_ADDR(0x0);           /* 指定地址0x0 ,芯片手册要求    */

    for(i=0;i<10;i++);       /* 等待tWB = 100ns.           */

    id=NF_RDDATA()<<8;  /* 厂商ID(K9S1208V:0xec)           */

    id|=NF_RDDATA();        /* 设备ID(K9S1208V:0x76)  */

    NF_nFCE_H();             /* 取消Nand Flash 选中*/

    return id;

}

2.3.4 Nand flash 写入

以页为单位写入.

参数说明:block       块号

          page   页号

          buffer  指向内存中待写入Nand flash 中的数据起始位置

返回值:      0 :写错误

         1:写成功

static int NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer)

{

    int i;

    unsigned int blockPage = (block<<5)+page;

    unsigned char *bufPt = buffer;

    NF_RSTECC();     /* 初始化 ECC             */

    NF_nFCE_L();     /* 片选Nand Flash 芯片*/

    NF_CMD(0x0);     /* 从A 区开始写      */

    NF_CMD(0x80);  /* 写第一条命令      */

    NF_ADDR(0);      /* A0~A7 位(Column Address)     */

    NF_ADDR(blockPage&0xff);      /* A9-A16, (Page Address) */

    NF_ADDR((blockPage>>8)&0xff);      /* A17-A24, (Page Address) */

    NF_ADDR((blockPage>>16)&0xff);  /* A25,    (Page Address) */

    for(i=0;i<512;i++)

    {

        NF_WRDATA(*bufPt++);            /* 写一个页512 字节到Nand Flash 芯片 */

    }

    

    /*

    * OOB 一共16 Bytes, 每一个字节存放什么由程序员自己定义, 通常,

    * 我们在Byte0-Byte2 存ECC 检验码. Byte6 存放坏块标志.

    */

    seBuf[0]=rNFECC0;  /* 读取ECC 检验码0 */

    seBuf[1]=rNFECC1;  /* 读取ECC 检验码 1 */

    seBuf[2]=rNFECC2;  /* 读取ECC 检验码2 */

    seBuf[5]=0xff;       /* 非坏块标志       */

    

    for(i=0;i<16;i++)

    {

        NF_WRDATA(seBuf[i]); /* 写该页的OOB 数据块  */

    }

    NF_CMD(0x10);     /* 结束写命令 */

    /* 等待Nand Flash 处于准备状态 */

    for(i=0;i<10;i++);

    NF_WAITRB();

 

    /* 发送读状态命令           Nand Flash  */

    NF_CMD(0x70);

    for(i=0;i<3;i++);

    

    if (NF_RDDATA()&0x1)

    {   /*如果写有错, 则标示为坏块    */

        NF_nFCE_H();  /* 取消Nand Flash 选中*/

        NF_MarkBadBlock(block);

        return 0;

    } else { /* 正常退出 */

        NF_nFCE_H(); /* 取消Nand Flash 选中*/

        return 1;

    }

}

2.3.5 Nand flash读取

参数说明:block:块号

          page:页号

          buffer:指向将要读取到内存中的起始位置

返回值:1:读成功

        0:读失败

static int NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer)

{

    int i;

    unsigned int blockPage;

    unsigned char ecc0, ecc1, ecc2;

    unsigned char *bufPt=buffer;

    unsigned char se[16];

    

    page=page&0x1f;

    blockPage=(block<<5)+page;

    NF_RSTECC();      /* 初始化 ECC             */

    NF_nFCE_L();      /* 片选Nand Flash 芯片*/

    NF_CMD(0x00);      /* 从A 区开始读      */

    NF_ADDR(0);       /* A0~A7 位(Column Address)      */

    NF_ADDR(blockPage&0xff);       /* A9-A16, (Page Address) */

    NF_ADDR((blockPage>>8)&0xff);        /* A17-A24, (Page Address) */

    NF_ADDR((blockPage>>16)&0xff);  /* A25,      (Page Address) */



    /* 等待Nand Flash 处于再准备状态 */

    for(i=0;i<10;i++);

    NF_WAITRB();      /*等待 tR(max 12us) */

    /* 读整个页, 512 字节             */

    for(i=0;i<512;i++)

    {

        *bufPt++=NF_RDDATA();

    }

    

    /* 读取ECC 码 */

    ecc0=rNFECC0;

    ecc1=rNFECC1;

    ecc2=rNFECC2;

    

    /* 读取该页的OOB 块 */

    for(i=0;i<16;i++)

    {

         se[i]=NF_RDDATA();

    }

    

    NF_nFCE_H();        /* 取消Nand Flash 选中*/



    /* 校验ECC 码, 并返回 */

    if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2])

           return 1;

    else

          return 0;

}

2.3.6 Nand flash标记坏块

如果是坏块, 通过写OOB 块的Byte6 把该块标记为坏块。

参数说明:block 块号

返回值:1:ok ,成功完成标记。

        0:表示写OOB 块正确.

static int NF_MarkBadBlock(unsigned int block)

{

    int i;

    unsigned int blockPage=(block<<5);

 

    seBuf[0]=0xff;

    seBuf[1]=0xff;

    seBuf[2]=0xff;

    seBuf[5]=0x44;      /* 设置坏块标记 */

    

    NF_nFCE_L();       /* 片选Nand Flash 芯片*/

    NF_CMD(0x50);         /* 从C 区开始写    */

    NF_CMD(0x80);         /* 发送编程命令, 让Nand Flash 处理写状态 */

    NF_ADDR(0x0);  /*        A0~A7 位(Column Address)         */

    NF_ADDR(blockPage&0xff);           /* A9-A16, (Page Address) */

    NF_ADDR((blockPage>>8)&0xff);            /* A17-A24, (Page Address) */

    NF_ADDR((blockPage>>16)&0xff);  /* A25,            (Page Address) */

    

    /* 写OOB 数据块 */

    for(i=0;i<16;i++)

    {

         NF_WRDATA(seBuf[i]);

    }

    NF_CMD(0x10);         /* 结束写命令 */

    

    /* 等待NandFlash 准备好 */

    for(i=0;i<10;i++);  /* tWB = 100ns.      */

    NF_WAITRB();

    /*读NandFlash 的写状态 */

    NF_CMD(0x70);

    for(i=0;i<3;i++);  /* twhr=60ns  */

    if (NF_RDDATA()&0x1)

    {

                NF_nFCE_H(); /* 取消Nand Flash 选中*/

                return 0;

     } else {

                NF_nFCE_H(); /* 取消Nand Flash 选中*/

    }

    return 1;

}

2.3.7 Nand Flash检查坏块

检查指定块是否是坏块.

参数说明:block:块号

返回值:1:指定块是坏块

        0:指定块不是坏块。

static int NF_IsBadBlock(U32 block)

{

    int i;

    unsigned int blockPage;

    U8 data;

        

    blockPage=(block<<5);

    NF_nFCE_L();        /* 片选Nand Flash 芯片*/

    NF_CMD(0x50);       /* Read OOB 数据块   */

    NF_ADDR(517&0xf);  /* A0~A7 位(Column Address)         */

    NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) */

    NF_ADDR((blockPage>>8)&0xff);       /* A17-A24, (Page Address) */

    NF_ADDR((blockPage>>16)&0xff);  /* A25,     (Page Address) */



    /* 等待NandFlash 准备好 */

    for(i=0;i<10;i++);  /* wait tWB(100ns)  */

    NF_WAITRB();

    /* 读取读出值 */

    data=NF_RDDATA();

    NF_nFCE_H();     /* 取消Nand Flash 选中*/

    /* 如果data 不为0xff 时, 表示该块是坏块 */

    if(data != 0xff)

        return 1;

    else

        return 0;

}

2.3.8 擦除指定块中数据

参数说明:block 块号

返回值:0:擦除错误。(若是坏块直接返回0;若擦除出现错误则标记为坏块然后返回0)

          1 :成功擦除。

 static int NF_EraseBlock(unsigned int block)

 {

     unsigned int blockPage=(block<<5);

     int i;



     /* 如果该块是坏块, 则返回 */

     if(NF_IsBadBlock(block))

         return 0;

     NF_nFCE_L();      /* 片选Nand Flash 芯片*/

     NF_CMD(0x60);     /* 设置擦写模式      */

     NF_ADDR(blockPage&0xff);      /* A9-A16, (Page Address) , 是基于块擦*/

     NF_ADDR((blockPage>>8)&0xff);       /* A17-A24, (Page Address) */

     NF_ADDR((blockPage>>16)&0xff);  /* A25, (Page Address) */

     NF_CMD(0xd0);     /* 发送擦写命令, 开始擦写 */

     /* 等待NandFlash 准备好 */

     for(i=0;i<10;i++); /* tWB(100ns) */

     NF_WAITRB();

     /* 读取操作状态         */

     NF_CMD(0x70);

     if (NF_RDDATA()&0x1)

     {

                 NF_nFCE_H(); /* 取消Nand Flash 选中*/

                 NF_MarkBadBlock(block); /* 标记为坏块 */

                 return 0;

     }  else  {

                 NF_nFCE_H(); /* 取消Nand Flash 选中*/

                 return 1;

     }

 }

 2.4   第一层的实现

 2.4.1 NandFlash 烧写主函数说明

 参数说明: block 块号

            srcAddress SDRAM 中数据起始地址

            fileSize 要烧写的数据长度

 返回值: 无

 void K9S1208_Program(unsigned int block, unsigned int srcAddress, unsigned int fileSize)

 {

      int  i;

       int  programError=0;

       U32  blockIndex;

       U8  *srcPt, *saveSrcPt;

       srcPt=(U8 *)srcAddress; /* 文件起始地址 */

      blockIndex = block; /* 块号 */

       while(1)

       {

              saveSrcPt=srcPt;

            /* 如果当前块是坏块,  跳过当前块 */

              if(NF_IsBadBlock(blockIndex))

              {

                    blockIndex++;   /* 到下一个块 */

                 continue;

              }

            /* 在写之前, 必须先擦除, 如果擦除不成功, 跳过当前块 */

            if(!NF_EraseBlock(blockIndex))

              {

                  blockIndex++;  /* 到下一个块 */

                     continue;

              }



            /* 写一个块, 一块有32 页 */

              for(i=0;i<32;i++)

              {

                 /* 写入一个页, 如果出错, 停止写当前块 */

                 if(!NF_WritePage(blockIndex,i,srcPt))

                   {

                       programError=1;

                       break;

                 }

                 /* 如果操作正常, 文件的写位置加上1 页偏移,到下一页的起始位置 */

                    srcPt+=512;

                 /* 如果写地址没有超过文件长度, 继续; 超出则终止写 */

                  if((U32)srcPt>=(srcAddress+fileSize))

                           break;

            }

         

              /* 如果写一个块时, 其中某一页写失败, 则把写地址恢复写该块之前, 并跳过当前块 */

             if(programError==1)

             {

                  blockIndex++;

                  srcPt=saveSrcPt;

                  programError=0;

                  continue;

             }

             /* 如果写地址没有超过文件长度, 继续; 超出则终止写 */

             if((U32)srcPt >= (srcAddress+fileSize))

                  break;

             /* 如果正常写成功, 继续写下一个块 */

             blockIndex++;

       }

 }

3 在 U-BOOT对Nand Flash 的支持

3.1 U-BOOT 对从Nand Flash启动的支持

3.1.1 从Nand Flash启动 U-BOOT的基本原理

1. 前4K 的问题

    如果S3C2410 被配置成从Nand Flash 启动(配置由硬件工程师在电路板设置), S3C2410 的Nand Flash 控制器

有一个特殊的功能, 在S3C2410 上电后, Nand Flash 控制器会自动的把Nand Flash 上的前4K 数据搬移到4K 内部

RAM 中, 并把0x00000000 设置内部RAM 的起始地址, CPU 从内部RAM 的0x00000000 位置开始启动。这个过

程不需要程序干涉。

    程序员需要完成的工作,是把最核心的启动程序放在Nand Flash 的前4K 中。

2.  启动程序的安排

     由于Nand Flash 控制器从Nand Flash 中搬移到内部RAM 的代码是有限的,所以, 在启动代码的前4K 里,我

们必须完成S3C2410 的核心配置以及把启动代码(UBOOT)剩余部分搬到RAM 中运                                 。以UBOOT 为例, 前4K

完成的主要工作, 见第四部分的2.2 节。

3.1.2 支持 Nand Flash启动代码说明

    首先在include/configs/crane2410.h 中加入CONFIG_S3C2410_NAND_BOOT, 如下:

#define CONFIG_S3C2410_NAND_BOOT       1

支持从Nand Flash 中启动.

1. 执   Nand Flash 初始化

下面代码在cpu/arm920t/start.S 中

#ifdef CONFIG_S3C2410_NAND_BOOT

copy_myself:

       mov r10, lr

       ldr sp, DW_STACK_START    @安装栈的起始地址

       mov fp, #0                 @初始化帧指针寄存器

       bl nand_reset              @跳到复位C 函数去执

...

DW_STACK_START:

        .word  STACK_BASE+STACK_SIZE-4

2. nand_reset C 代码

下面代码被加在/board/crane2410/crane2410.c 中

void nand_reset(void)

{

        int i;

       /* 设置Nand Flash 控制器 */

       rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);

        /*   Nand Flash 芯片发送复位命令 */

        NF_nFCE_L();

        NF_CMD(0xFF);

        for(i=0; i<10; i++);

        NF_WAITRB();

        NF_nFCE_H();

}



3. 从Nand Flash 中把UBOOT 拷贝到RAM

@read U-BOOT from Nand Flash to RAM

         ldr r0, =UBOOT_RAM_BASE          @ 设置第 1 个参数: UBOOT 在RAM 中的起始地址

         mov   r1, #0x0                   @ 设置第2 个参数:Nand Flash 的起始地址

         mov   r2, #0x20000               @ 设置第3 个参数: UBOOT 的长度(128KB)

         bl  nand_read_whole              @ 调用nand_read_whole(), 该函数在board/crane2410/crane2410.c 中

         tst r0, #0x0                     @ 如果函数的返回值为0,表示执行成功.

         beq  ok_nand_read                @ 执行内存比较

4. 从Nand Flash 中把数据读入到RAM 中

int nand_read_whole(unsigned char *buf, unsigned long start_addr, int size)

{

         int i, j;

         /* 如果起始地址和长度不是512 字节(1 页)的倍数, 则返回错误代码 */

         if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {

                 return -1;

         }

         /* 激活Nand Flash */

         NF_nFCE_L();

         for(i=0; i<10; i++);

         i = start_addr;

         while(i < start_addr + size) {

                 /* 读A 区 */

                 rNFCMD = 0;



                 /* 写入读取地址 */

                 rNFADDR = i & 0xff;

                 rNFADDR = (i >> 9) & 0xff;

                 rNFADDR = (i >> 17) & 0xff;

                 rNFADDR = (i >> 25) & 0xff;



                 NF_WAITRB();

               /* 读出一页(512 字节) */

                 for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {

                          *buf = (rNFDATA & 0xff);

                          buf++;

                 }

         }

         /* 停止驱动Nand Flash */

         NF_nFCE_H();

         return 0;

}

5.  校查搬移后的数据

把RAM 中的前4K 与内部中前4K 进                   比较, 如果完全相同, 则表示搬移成功.

ok_nand_read:

        mov r0, #0x00000000            @内部RAM 的起始地址

        ldr r1, =UBOOT_RAM_BASE       @UBOOT 在RAM 中的起始地址

        mov r2, #0x400                 @比较1024 次, 每次4 字节, 4 bytes * 1024 = 4K-bytes

go_next:   @ 比较1024 次, 每次4 个字节

        ldr r3, [r0], #4

        ldr r4, [r1], #4

        teq  r3, r4

        bne  notmatch

        subs r2, r2, #4

        beq  done_nand_read

        bne  go_next

notmatch:

        1:b   1b

done_nand_read:

        mov pc, r10

3.2 U-BOOT 对Nand Flash 命令的支持

    在U-BOOT 下对Nand Flash 的支持主要是在命令行下实现对nand flash 的操作。对nand flash 实现的命令

为:nand info 、nand device 、nand read 、nand write 、nand erease 、nand bad 。

    用到的主要数据结构有:struct nand_flash_dev 、struct nand_chip 。前者包括主要的芯片型号、存储容量、

设备ID 、I/O 总线宽度等信息;后者是具体对nand flash 进行操作时用到的信息。

3.2.1 主要数据结构介绍

1. struct nand_flash_dev 数据结构

该数据结构在include/linux/mtd/nand.h 中定义,在include/linux/mtd/nand_ids.h 中赋初值。

struct nand_flash_dev {

        char *name;             /* 芯片名称 */

        int manufacture_id;     /* 厂商ID     */

        int model_id;           /* 模式ID     */

        int chipshift;          /* Nand Flash 地址位数 */

        char page256;           /* 表明是否时256 字节一页。1:是;0:否。*/

        char pageadrlen;        /* 完成一次地址传送需要               NFADDR 中传送几次。*/

        unsigned long erasesize;  /* 一次块擦除可以擦除多少字节 */

        int bus16;              /* 地址线是否是16位,1:是;0:否 */

};

2. struct nand_chip 数据结构

该数据结构在include/linux/mtd/nand.h 中定义. 该结构体定义出一个Nand Flash 设备数组:

        struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];

该数组在nand_probe()中对其进行初始化.

struct nand_chip {

        int     page_shift;      /* Page 地址位数         */

        u_char  *data_buf;       /* 本次读出的一页数据    */

        u_char  *data_cache;     /* 读出的一页数据        */

        int     cache_page;      /* 上次操作的页号        */

        u_char  ecc_code_buf[6]; /* ECC 校验码            */

        u_char  reserved[2];

        char ChipID;             /* 芯片ID 号             */

        struct Nand *chips;      /* Nand Flash 芯片列表, 表示支持几个芯片为一个设备*/

        int chipshift;

        char* chips_name;        /* Nand Flash 芯片名称     */

        unsigned long erasesize; /* 块擦写的大小           */

        unsigned long mfr;      /* 厂商ID                      */

        unsigned long id;       /* 模式ID                      */

        char* name;             /* 设备名称               */

        int numchips;           /* 有几块Nand Flash 芯片   */

        char page256;           /* 一页是256 字节, 还是512 字节 */

        char pageadrlen;        /* 页地址的长度           */

        unsigned long IO_ADDR;  /* 用于对nand flash 进行寻址的地址值存放处 */

        unsigned long totlen; /* Nand Flash 总共大小       */

        uint oobblock;        /* 一页的大小。本款nand flash 为512                     */

        uint oobsize;         /* spare array 大小。本款nand flash 为16             */

        uint eccsize;         /* ECC 大小                 */

        int bus16;            /* 地址线是否是16位,1:是;0:否        */

};

3.2.2 支持的命令函数说明

1. nand info/nand device

功能:显示当前nand flash 芯片信息。

函数调用关系如下(按先后顺序):

static void nand_print(struct nand_chip *nand) ;

2. nand erase

功能:擦除指定块上的数据。

函数调用关系如下(按先后顺序):

int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean);

3. nand bad

功能:显示坏块。

函数调用关系如下(按先后顺序):

static void nand_print_bad(struct nand_chip* nand);

int check_block (struct nand_chip *nand, unsigned long pos);

4. nand read

功能:读取nand flash 信息到SDRAM 。

函数调用关系如下(按先后顺序):

int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf);

static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len,

                     size_t * retlen, u_char *buf, u_char *ecc_code);

static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr);

READ_NAND(adr);

5. nand write

功能:从SDRAM 写数据到nand flash 中。

函数调用关系如下(按先后顺序):

int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf);

static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len,

                      size_t * retlen, const u_char * buf, u_char * ecc_code);

static int nand_write_page (struct nand_chip *nand, int page, int col, int last, u_char * ecc_code);

WRITE_NAND(d , adr);

3.2.3 U-BOOT支持 Nand Flash命令移植说明

1. 设置配置选项

  在CONFIG_COMMANDS 中, 打开CFG_CMD_NAND 选项.

  #define CONFIG_COMMANDS \

                        (CONFIG_CMD_DFL         | \

                        CFG_CMD_CACHE           | \

                        CFG_CMD_NAND            | \

                       /*CFG_CMD_EEPROM |*/ \

                       /*CFG_CMD_I2C            |*/ \

                       /*CFG_CMD_USB            |*/ \

                        CFG_CMD_PING       | \

                        CFG_CMD_REGINFO  | \

                        CFG_CMD_DATE            | \

                        CFG_CMD_ELF)

#if (CONFIG_COMMANDS & CFG_CMD_NAND)

#define CFG_NAND_BASE               0x4E000000    /* Nand Flash 控制器在SFR 区中起始寄存器地址 */

#define CFG_MAX_NAND_DEVICE         1            /* 支持的最在Nand Flash 数据 */

#define SECTORSIZE                  512          /* 1 页的大小 */

#define NAND_SECTOR_SIZE             SECTORSIZE

#define NAND_BLOCK_MASK             (NAND_SECTOR_SIZE – 1)  /* 页掩码 */

#define ADDR_COLUMN                 1   /* 一个字节的Column 地址 */

#define ADDR_PAGE                   3  /* 3 字节的页块地址, A9-A25*/

#define ADDR_COLUMN_PAGE            4  /*  总共4 字节的页块地址   */

#define NAND_ChipID_UNKNOWN         0x00 /* 未知芯片的ID 号 */

#define NAND_MAX_FLOORS             1

#define NAND_MAX_CHIPS              1

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

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

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

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

#define READ_NAND(adr)          (rNFDATA)

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

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

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

/* 下面一组操作对Nand Flash 无效 */

#define NAND_CTL_CLRALE(nandptr)

#define NAND_CTL_SETALE(nandptr)

#define NAND_CTL_CLRCLE(nandptr)

#define NAND_CTL_SETCLE(nandptr)

/* 允许Nand Flash 写校验 */

#define CONFIG_MTD_NAND_VERIFY_WRITE 1

#endif  /* CONFIG_COMMANDS & CFG_CMD_NAND*/

2. 加入自己的Nand Flash 芯片型号

    在include/linux/mtd/ nand_ids.h 中的对如下结构体赋值进行修改:

   static struct nand_flash_dev nand_flash_ids[] = {

       ......

      {"Samsung K9F1208U0B",     NAND_MFR_SAMSUNG, 0x76, 26, 0, 4, 0x4000, 0},

       .......

   }

   这样对于该款Nand Flash 芯片的操作才能正确执                     。

3. 编写自己的Nand Flash 初始化函数

在board/crane2410/crane2410.c 中加入nand_init()函数.

void nand_init(void)

{

       /* 初始化Nand Flash 控制器, 以及Nand Flash 芯片 */

        nand_reset();

       /* 调用nand_probe()来检测芯片类型 */

        printf ("%4lu MB\n", nand_probe(CFG_NAND_BASE) >> 20);

}

该函数在启动时被start_armboot()调用.

4 在 Linux对Nand Flash 的支持

4.1 Linux 下Nand Flash 调用关系

4.1.1 Nand Flash设备添加时数据结构包含关系

        struct mtd_partition          partition_info[]

    --> struct s3c2410_nand_set       nandset

    --> struct s3c2410_platform_nand  superlpplatfrom 

    --> struct platform_device        s3c_device_nand

         在该数据结构的name字段的初始化值"s3c2410-nand",必须与Nand Flash设备驱动注册时

        struct device_driver结构中的name字段相同,因为platfrom bus是依靠名字来匹配的.

    --> struct platform_device        *smdk2410_devices[]

4.1.2 Nand Flash设备注册时数据结构包含关系

        struct device_driver s3c2410_nand_driver 

    -->struct device *dev

       该数据构由系统分配.

    -->struct platform_device *pdev

    -->struct s3c2410_platform_nand *plat

    -->struct s3c2410_nand_set nset

    -->struct mtd_partition

4.1.3  当发生系统调用时数据结构调用关系

        struct mtd_info

        它的*priv指向chip

    -->struct nand_chip

        它的*priv指向nmtd

    -->struct s3c2410_nand_mtd

        它是s3c2410_nand_info 的一个字段

    -->s3c2410_nand_info

        它被设为Nand Flash设备驱动的私有数据结构,在Nand Flash设备驱动注册时分配空间.

    -->struct device

    

4.2 Linux 下Nand Flash 驱动主要数据结构说明

4.2.1 s3c2410专有数据结构

1. s3c2410_nand_set

struct s3c2410_nand_set {

        int                     nr_chips;      /* 芯片的数目 */

        int                     nr_partitions; /* 分区的数目 */

        char                    *name;         /* 集合名称   */

        int                     nr_map;        /* 可选, 底层逻辑到物理的芯片数目 */

        struct mtd_partition    partitions;    /* 分区列表   */

};

2. s3c2410_platform_and

struct s3c2410_platform_nand {

        /* timing information for controller, all times in nanoseconds */

        int     tacls;  /* 从CLE/ALE有效到 nWE/nOE 的时间 */

        int     twrph0; /* nWE/nOE 的有效时间 */

        int     twrph1; /* 从释放CLE/ALE 到nWE/nOE不活动的时间 */

        int     nr_sets; /* 集合数目 */

        struct s3c2410_nand_set sets; /* 集合列表 */

         /* 根据芯片编号选择有效集合 */

        void (*select_chip)(struct s3c2410_nand_set , int chip);

};

3. s3c2410_nand_mtd

在drivers/mtd/nand/s3c2410.c 中,

struct s3c2410_nand_mtd {

        struct mtd_info                 mtd;    /* MTD 信息 */

        struct nand_chip                chip;   /* nand flash 芯片信息 */

        struct s3c2410_nand_set         set;    /* nand flash 集合     */

        struct s3c2410_nand_info        *info;  /* nand flash 信息     */

        int                             scan_res;

}; 

4. s3c2410_nand_info

struct s3c2410_nand_info {

        /* mtd info */

        struct nand_hw_control          controller; /* 硬件控制器 */

        struct s3c2410_nand_mtd         *mtds;      /* MTD 设备表 */

        struct s3c2410_platform_nand    platform;   /* Nand 设备的平台 */

        /* device info */

        struct device                   *device;    /* 设备指针 */

        struct resource                 *area;      /* 资源指针 */

        struct clk                      *clk;       /* Nand Flash 时钟 */

        void __iomem                    *regs;      /* 寄存器基地址(map后的逻辑地址) */

        int                             mtd_count;  /* MTD的数目 */

        unsigned char                   is_s3c2440;

};

5. struct clk

在arch/arm/mach-s3c2410/clock.h 中

struct clk {

        struct list_head      list;     /* clock 列表结点 */

        struct module        *owner;    /* 所属模块       */ 

        struct clk           *parent;   /* 父结点         */

      


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 3

    粉丝
  • 0

    好友
  • 20

    获赞
  • 69

    评论
  • 3705

    访问数
关闭

站长推荐 上一条 /2 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-4-16 22:34 , Processed in 0.029048 second(s), 14 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部