zoukankan      html  css  js  c++  java
  • S3C2440 nand_flash驱动程序

    20150418 S3C2440 nand_flash驱动程序

    2015-04-18 Lover雪儿

    一、一个简单的nand_flash驱动

    1.定义nand_chip、mtd_info两个结构体

    如上图所示:

    nand_chip 结构体:是给nand_scan函数用的,而nand_scan函数提供了选中nand、发出命令、发出地址、发出数据、读取数据、判断状态等功能,所以nand_chip结构体上必须定义一系列实现上面功能能的函数,包括选中函数,负责发地址与命令的函数,以及判断状态的函数,最重要的就是io读取的虚拟地址。

    mtd_info结构体:MTD(Memory Technology Device)即内存技术设在linux内核中,引入mtd层为NOR Flash和NAND Flash设备提供统一的接口,将文件系统于底层Flash存储设备进行了隔离。

    MTD设备可以分为四层,从上到下依次为:设备节点层,MTD设备层,MTD原始设备层,Flash硬件驱动层。

    Flash硬件驱动层:负责对Flash硬件的读、写和擦除操作。MTD设备的NAND flash芯片的驱动在drivers/mtd/nand目录下,nor flash芯片驱动位于drivers/mtd/chips目录下。

    MTD原始设备层:用于描述MTD原始设备的数据结构体是mtd_info ,它定义了大量的关于MTD的数据和操作函数,其中mtdcore.c:实现原始设备接口的相关实现,mtdpart.c:实现mtd分区接口相关实现。

    MTD设备层: 基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),其中mtdchar.c实现mtd字符设备接口相关实现,mtdblock.c用于实现块设备接口相关实现。

    设备节点层:通过mknode在/dev子目录下建立MTD块设备节点,通过此设备节点即可访问MTD字符设备和块设备。

    2.在init函数中初始化结构体

     1 static int lhy_nand_init(void){
     2     
     3     /* 1.分配一个nand_chip结构体 */
     4     lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
     5     /* 2.设置 */
     6     /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
     7      *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
     8      */
     9     lhy_nand->select_chip = lhy_select_chip;      //选中,芯片选择函数
    10     lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
    11     lhy_nand->IO_ADDR_R   = "NFDATA 的虚拟地址";
    12     lhy_nand->IO_ADDR_R   = "NFDATA 的虚拟地址";
    13     lhy_nand->dev_ready      = lhy_dev_ready;
    14     /* 3.硬件相关的操作 */
    15     
    16     /* 4.使用nand_scan */
    17     lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    18     lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
    19     lhy_mtd->owner = THIS_MODULE;
    20     
    21     nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
    22     /* 5.add_mtd_partitions */
    23     
    24     
    25     return 0;
    26 }

    3.实现上述方法:

     1 /* 芯片选择 */
     2 static void lhy_select_chip(struct mtd_info *mtd,int chipnr)
     3 {
     4     if(chipnr == -1){
     5         /* 取消选中,NFCONT[1]设为0 */
     6     }else{
     7         /* 选中:NFCONT[1]设为1 */
     8     }
     9 }
    10 //发送命令,地址,数据
    11 static void lhy_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
    12 {
    13     if (ctrl & NAND_CLE){
    14         /* 发命令 : NFCMMD=dat*/
    15         writeb(cmd, host->io_base + (1 << host->board->cle));
    16     }else{
    17         writeb(cmd, host->io_base + (1 << host->board->ale));
    18     }
    19 }
    20 //判断状态
    21 static int lhy_dev_ready(struct mtd_info *mtd)
    22 {
    23     return "NFSTAT 的 bit[0]";
    24 }

    附上驱动程序nand_flash1:

      1 /* 
      2  * 参考:linux-2.6.31driversmtd
    ands3c2410.c   atmel_nand.c 
      3  */
      4 
      5 #include <linux/slab.h>
      6 #include <linux/module.h>
      7 #include <linux/moduleparam.h>
      8 #include <linux/platform_device.h>
      9 #include <linux/mtd/mtd.h>
     10 #include <linux/mtd/nand.h>
     11 #include <linux/mtd/partitions.h>
     12 #include <linux/clk.h>
     13 #include <linux/io.h>
     14 #include <mach/board.h>
     15 #include <mach/cpu.h>
     16 
     17 static struct nand_chip *lhy_nand;   
     18 static struct mtd_info *lhy_mtd;    //定义一个mtd_info结构体
     19 
     20 /* 芯片选择 */
     21 static void lhy_select_chip(struct mtd_info *mtd,int chipnr)
     22 {
     23     if(chipnr == -1){
     24         /* 取消选中,NFCONT[1]设为0 */
     25     }else{
     26         /* 选中:NFCONT[1]设为1 */
     27     }
     28 }
     29 //发送命令,地址,数据
     30 static void lhy_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
     31 {
     32     if (ctrl & NAND_CLE){
     33         /* 发命令 : NFCMMD=dat*/
     34         writeb(cmd, host->io_base + (1 << host->board->cle));
     35     }else{
     36         writeb(cmd, host->io_base + (1 << host->board->ale));
     37     }
     38 }
     39 //判断状态
     40 static int lhy_dev_ready(struct mtd_info *mtd)
     41 {
     42     return "NFSTAT 的 bit[0]";
     43 }
     44 
     45 static int lhy_nand_init(void){
     46     
     47     /* 1.分配一个nand_chip结构体 */
     48     lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
     49     /* 2.设置 */
     50     /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
     51      *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
     52      */
     53     lhy_nand->select_chip = lhy_select_chip;      //选中,芯片选择函数
     54     lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
     55     lhy_nand->IO_ADDR_R   = "NFDATA 的虚拟地址";
     56     lhy_nand->IO_ADDR_R   = "NFDATA 的虚拟地址";
     57     lhy_nand->dev_ready      = lhy_dev_ready;
     58     /* 3.硬件相关的操作 */
     59     
     60     /* 4.使用nand_scan */
     61     lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
     62     lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
     63     lhy_mtd->owner = THIS_MODULE;
     64     
     65     nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
     66     /* 5.add_mtd_partitions */
     67     
     68     
     69     return 0;
     70 }
     71 
     72 static void lhy_nand_exit(void){
     73     if(lhy_nand)
     74         kfree(lhy_nand);
     75     if(lhy_mtd)
     76         kfree(lhy_mtd);
     77 }
     78 
     79 module_init(lhy_nand_init);
     80 module_exit(lhy_nand_exit);
     81 MODULE_LICENSE("GPL");
     82 
     83 
     84 /*
     85 S3C2440 U-BOOT 的NAND操作
     86 
     87 1.读取ID
     88 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
     89 发出命令0x90            NFCMMD=0X90            mw.b 0x4E000008 0x90
     90 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
     91 读出数据得到0XEC          val=NFDATA            md.b 0x4E000010 1
     92 读数据得到device code    val=NFDATA             md.b 0x4E000010 1
     93 退出读ID状态            NFCMMD=0xff            mw.b 0x4E000008 0xff
     94 
     95 2.读内容 读0地址的数据
     96 输入命令: nand dump 0  得到nand
     97 
     98 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
     99 发出命令0x00            NFCMMD=0X00            mw.b 0x4E000008 0x00
    100 
    101 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    102 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    103 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    104 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    105 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    106 
    107 发出命令0x30            NFCMMD=0X00            mw.b 0x4E000008 0x30
    108 
    109 //接下来就是从0地址开始一个字节一个字节的读出数据,和前面nand dump 0 的数据一样
    110 读出数据得到0x17          val=NFDATA            md.b 0x4E000010 1
    111 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    112 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    113 
    114 退出读状态                NFCMMD=0xff            mw.b 0x4E000008 0xff
    115 
    116 3.NAND  flash 驱动层次  Atmel_nand.c  Mtdchar.c
    117 块设备:        知道怎么优化
    118 NAND Flash协议:知道发什么来读写,擦除,识别
    119 硬件相关:        知道怎样发命令/地址,读写数据
    120 
    121 硬件相关:
    122 ①分配nand_chip 结构体 
    123 ②设置nand_chip 
    124 ③硬件相关设备 
    125 ④使用 nand_scan / add_mtd_partitions
    126 
    127 */
    nandflash1.c

    二、完善前面的程序

    1.定义芯片的内存地址,由于其地址是互相相连的所以我们可以使用结构体来省事。

    //寄存器结构体
    struct lhy_nand_regs{
        unsigned long NFCONF   ; //偏移地址: S3C2410_NFREG(0x00)
        unsigned long NFCONT   ; //偏移地址: S3C2410_NFREG(0x04)
        unsigned long NFCMD    ; //偏移地址: S3C2410_NFREG(0x08)
        unsigned long NFADDR   ; //偏移地址: S3C2410_NFREG(0x0C)
        unsigned long NFDATA   ; //偏移地址: S3C2410_NFREG(0x10)
        unsigned long NFECCD0  ; //偏移地址: S3C2410_NFREG(0x14)
        unsigned long NFECCD1  ; //偏移地址: S3C2410_NFREG(0x18)
        unsigned long NFECCD   ; //偏移地址: S3C2410_NFREG(0x1C)
        unsigned long NFSTAT   ; //偏移地址: S3C2410_NFREG(0x20)
        unsigned long NFESTAT0 ; //偏移地址: S3C2410_NFREG(0x24)
        unsigned long NFESTAT1 ; //偏移地址: S3C2410_NFREG(0x28)
        unsigned long NFMECC0  ; //偏移地址: S3C2410_NFREG(0x2C)
        unsigned long NFMECC1  ; //偏移地址: S3C2410_NFREG(0x30)
        unsigned long NFSECC   ; //偏移地址: S3C2410_NFREG(0x34)
        unsigned long NFSBLK   ; //偏移地址: S3C2410_NFREG(0x38)
        unsigned long NFEBLK   ; //偏移地址: S3C2410_NFREG(0x3C)
    };

    2.映射寄存器地址内存并其配置

    static int lhy_nand_init(void){
        
        /* 1.分配一个nand_chip结构体 */
        lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
        
        lhy_nand_regs = ioremap(0x4E000000,sizeof(struct lhy_nand_regs)); //映射寄存器
        /* 2.设置 */
        /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
         *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
         */
        lhy_nand->select_chip = lhy_select_chip;          //选中,芯片选择函数
        lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
        lhy_nand->IO_ADDR_R   = lhy_nand_regs->NFDATA;    //读寄存器
        lhy_nand->IO_ADDR_W   = lhy_nand_regs->NFDATA;    //写寄存器
        lhy_nand->dev_ready      = lhy_dev_ready;            //判断状态
        /* 3.硬件相关的操作 根据nand flash的手册设置时间参数
         * HCLK = 100MHz
         * TACLS: 发出CLE/ALE之后多长时间发出nWE信号,从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
         * TWRPH0: nWE的信号的脉冲宽度,HCLK *(TWPRH0 + 1),从NAND手册可知它要>=12ns,所以TWRPH0>=1
         * TWRPH1:表示nWE信号变为高电平后,CLE/ALE多长时间才能变为低电平,从手册可知他要>=5ns,所以TWRPH1>=0
         */
    #define TACLS     0
    #define TWRPH0    1
    #define TWRPH1    0     
        lhy_nand_regs->NFCONF |= (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
        
        /* NFCONT的bit1设为1,表示片 选 */
        lhy_nand_regs->NFCONT =  (1<<1) | (1<<0);
        /* 4.使用nand_scan */
        lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
        lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
        lhy_mtd->owner = THIS_MODULE;
        
        nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
        /* 5.add_mtd_partitions */
        
        return 0;
    }

    附上驱动程序nand_flash2:

      1 /* 
      2  * 参考:linux-2.6.31driversmtd
    ands3c2410.c   atmel_nand.c 
      3  */
      4 
      5 #include <linux/slab.h>
      6 #include <linux/module.h>
      7 #include <linux/moduleparam.h>
      8 #include <linux/platform_device.h>
      9 #include <linux/mtd/mtd.h>
     10 #include <linux/mtd/nand.h>
     11 #include <linux/mtd/partitions.h>
     12 #include <linux/clk.h>
     13 #include <linux/io.h>
     14 #include <mach/board.h>
     15 #include <mach/cpu.h>
     16 
     17 //寄存器结构体
     18 struct lhy_nand_regs{
     19     unsigned long NFCONF   ; //偏移地址: S3C2410_NFREG(0x00)
     20     unsigned long NFCONT   ; //偏移地址: S3C2410_NFREG(0x04)
     21     unsigned long NFCMD    ; //偏移地址: S3C2410_NFREG(0x08)
     22     unsigned long NFADDR   ; //偏移地址: S3C2410_NFREG(0x0C)
     23     unsigned long NFDATA   ; //偏移地址: S3C2410_NFREG(0x10)
     24     unsigned long NFECCD0  ; //偏移地址: S3C2410_NFREG(0x14)
     25     unsigned long NFECCD1  ; //偏移地址: S3C2410_NFREG(0x18)
     26     unsigned long NFECCD   ; //偏移地址: S3C2410_NFREG(0x1C)
     27     unsigned long NFSTAT   ; //偏移地址: S3C2410_NFREG(0x20)
     28     unsigned long NFESTAT0 ; //偏移地址: S3C2410_NFREG(0x24)
     29     unsigned long NFESTAT1 ; //偏移地址: S3C2410_NFREG(0x28)
     30     unsigned long NFMECC0  ; //偏移地址: S3C2410_NFREG(0x2C)
     31     unsigned long NFMECC1  ; //偏移地址: S3C2410_NFREG(0x30)
     32     unsigned long NFSECC   ; //偏移地址: S3C2410_NFREG(0x34)
     33     unsigned long NFSBLK   ; //偏移地址: S3C2410_NFREG(0x38)
     34     unsigned long NFEBLK   ; //偏移地址: S3C2410_NFREG(0x3C)
     35 };
     36 
     37 static struct nand_chip *lhy_nand;   
     38 static struct mtd_info *lhy_mtd;            //定义一个mtd_info结构体
     39 static struct lhy_nand_regs *lhy_nand_res;    //定义寄存器的结构体指针
     40 
     41 /* 芯片选择 */
     42 static void lhy_select_chip(struct mtd_info *mtd,int chipnr)
     43 {
     44     if(chipnr == -1){
     45         /* 取消选中,NFCONT[1]设为0 */
     46         lhy_nand_regs->NFCONT |= (1<<1);
     47     }else{
     48         /* 选中:NFCONT[1]设为1 */
     49     lhy_nand_regs->NFCONT &= ~(1<<1);
     50     }
     51 }
     52 //发送命令,地址,数据
     53 static void lhy_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
     54 {
     55     if (ctrl & NAND_CLE){
     56         /* 发命令 : NFCMMD=dat*/
     57         lhy_nand_regs->NFCMD = cmd;
     58     }else{
     59         writeb(cmd, host->io_base + (1 << host->board->ale));
     60         lhy_nand_regs->NFADDR = cmd;
     61     }
     62 }
     63 //判断状态
     64 static int lhy_dev_ready(struct mtd_info *mtd)
     65 {
     66     return (lhy_nand_regs->NFSTAT & (1<<0));
     67 }
     68 
     69 static int lhy_nand_init(void){
     70     
     71     /* 1.分配一个nand_chip结构体 */
     72     lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
     73     
     74     lhy_nand_regs = ioremap(0x4E000000,sizeof(struct lhy_nand_regs)); //映射寄存器
     75     /* 2.设置 */
     76     /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
     77      *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
     78      */
     79     lhy_nand->select_chip = lhy_select_chip;          //选中,芯片选择函数
     80     lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
     81     lhy_nand->IO_ADDR_R   = lhy_nand_regs->NFDATA;    //读寄存器
     82     lhy_nand->IO_ADDR_W   = lhy_nand_regs->NFDATA;    //写寄存器
     83     lhy_nand->dev_ready      = lhy_dev_ready;            //判断状态
     84     /* 3.硬件相关的操作 根据nand flash的手册设置时间参数
     85      * HCLK = 100MHz
     86      * TACLS: 发出CLE/ALE之后多长时间发出nWE信号,从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
     87      * TWRPH0: nWE的信号的脉冲宽度,HCLK *(TWPRH0 + 1),从NAND手册可知它要>=12ns,所以TWRPH0>=1
     88      * TWRPH1:表示nWE信号变为高电平后,CLE/ALE多长时间才能变为低电平,从手册可知他要>=5ns,所以TWRPH1>=0
     89      */
     90 #define TACLS     0
     91 #define TWRPH0    1
     92 #define TWRPH1    0     
     93     lhy_nand_regs->NFCONF |= (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
     94     
     95     /* NFCONT的bit1设为1,表示片 选 */
     96     lhy_nand_regs->NFCONT =  (1<<1) | (1<<0);
     97     /* 4.使用nand_scan */
     98     lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
     99     lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
    100     lhy_mtd->owner = THIS_MODULE;
    101     
    102     nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
    103     /* 5.add_mtd_partitions */
    104     
    105     return 0;
    106 }
    107 
    108 static void lhy_nand_exit(void){
    109     if(lhy_nand)
    110         kfree(lhy_nand);
    111     if(lhy_mtd)
    112         kfree(lhy_mtd);
    113     if(lhy_nand_regs)
    114         iounmap(lhy_nand_regs);
    115 }
    116 
    117 module_init(lhy_nand_init);
    118 module_exit(lhy_nand_exit);
    119 MODULE_LICENSE("GPL");
    120 
    121 
    122 /*
    123 S3C2440 U-BOOT 的NAND操作
    124 
    125 1.读取ID
    126 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    127 发出命令0x90            NFCMMD=0X90            mw.b 0x4E000008 0x90
    128 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    129 读出数据得到0XEC          val=NFDATA            md.b 0x4E000010 1
    130 读数据得到device code    val=NFDATA             md.b 0x4E000010 1
    131 退出读ID状态            NFCMMD=0xff            mw.b 0x4E000008 0xff
    132 
    133 2.读内容 读0地址的数据
    134 输入命令: nand dump 0  得到nand
    135 
    136 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    137 发出命令0x00            NFCMMD=0X00            mw.b 0x4E000008 0x00
    138 
    139 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    140 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    141 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    142 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    143 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    144 
    145 发出命令0x30            NFCMMD=0X00            mw.b 0x4E000008 0x30
    146 
    147 //接下来就是从0地址开始一个字节一个字节的读出数据,和前面nand dump 0 的数据一样
    148 读出数据得到0x17          val=NFDATA            md.b 0x4E000010 1
    149 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    150 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    151 
    152 退出读状态                NFCMMD=0xff            mw.b 0x4E000008 0xff
    153 
    154 3.NAND  flash 驱动层次  Atmel_nand.c  Mtdchar.c
    155 块设备:        知道怎么优化
    156 NAND Flash协议:知道发什么来读写,擦除,识别
    157 硬件相关:        知道怎样发命令/地址,读写数据
    158 
    159 硬件相关:
    160 ①分配nand_chip 结构体 
    161 ②设置nand_chip 
    162 ③硬件相关设备 
    163 ④使用 nand_scan / add_mtd_partitions
    164 
    165 
    166 */
    nandflash2.c

    三、增加ECC校验码

    前面的程序是可以直接用的,但是加载驱动时会报ECC校验错误。

    在flash设备中,每一页都有64b是不参与编址的,这一块是不参与统一编址OBB(out of bank)。

    原因:nand flash内存中,数据很容易发生位反转,为了防止数据发生错误,引入了ECC校验。

    解决方案:写一页数据时,这一页的数据生成ECC校验码,然后把ECC校验码写入OBB中。

    读取数据时:首先读取page,读OOB里的ECC,根据page的内容实时计算ECC,看是否与OOB中的

    ECC相同,若是相同则说明数据没有错误,否则通过ECC校验码也可以算出是哪一位发生错误。

    实现方法:可以设置为软件实现或者硬件实现,只要在nand_chip中的ECC结构体的mode中设置为NAND_ECC_SOFT,就是开启了软件ECC校验。

    nand_chip->ecc.mode = NAND_ECC_SOFT;            /*使能ECC校验码 enable ECC */

    附上驱动程序nand_flash3.c

      1 /* 
      2  * 参考:linux-2.6.31driversmtd
    ands3c2410.c   atmel_nand.c 
      3  */
      4 
      5 #include <linux/slab.h>
      6 #include <linux/module.h>
      7 #include <linux/moduleparam.h>
      8 #include <linux/platform_device.h>
      9 #include <linux/mtd/mtd.h>
     10 #include <linux/mtd/nand.h>
     11 #include <linux/mtd/partitions.h>
     12 #include <linux/clk.h>
     13 #include <linux/io.h>
     14 #include <mach/board.h>
     15 #include <mach/cpu.h>
     16 
     17 //寄存器结构体
     18 struct lhy_nand_regs{
     19     unsigned long NFCONF   ; //偏移地址: S3C2410_NFREG(0x00)
     20     unsigned long NFCONT   ; //偏移地址: S3C2410_NFREG(0x04)
     21     unsigned long NFCMD    ; //偏移地址: S3C2410_NFREG(0x08)
     22     unsigned long NFADDR   ; //偏移地址: S3C2410_NFREG(0x0C)
     23     unsigned long NFDATA   ; //偏移地址: S3C2410_NFREG(0x10)
     24     unsigned long NFECCD0  ; //偏移地址: S3C2410_NFREG(0x14)
     25     unsigned long NFECCD1  ; //偏移地址: S3C2410_NFREG(0x18)
     26     unsigned long NFECCD   ; //偏移地址: S3C2410_NFREG(0x1C)
     27     unsigned long NFSTAT   ; //偏移地址: S3C2410_NFREG(0x20)
     28     unsigned long NFESTAT0 ; //偏移地址: S3C2410_NFREG(0x24)
     29     unsigned long NFESTAT1 ; //偏移地址: S3C2410_NFREG(0x28)
     30     unsigned long NFMECC0  ; //偏移地址: S3C2410_NFREG(0x2C)
     31     unsigned long NFMECC1  ; //偏移地址: S3C2410_NFREG(0x30)
     32     unsigned long NFSECC   ; //偏移地址: S3C2410_NFREG(0x34)
     33     unsigned long NFSBLK   ; //偏移地址: S3C2410_NFREG(0x38)
     34     unsigned long NFEBLK   ; //偏移地址: S3C2410_NFREG(0x3C)
     35 };
     36 
     37 static struct nand_chip *lhy_nand;   
     38 static struct mtd_info *lhy_mtd;            //定义一个mtd_info结构体
     39 static struct lhy_nand_regs *lhy_nand_res;    //定义寄存器的结构体指针
     40 
     41 /* 芯片选择 */
     42 static void lhy_select_chip(struct mtd_info *mtd,int chipnr)
     43 {
     44     if(chipnr == -1){
     45         /* 取消选中,NFCONT[1]设为0 */
     46         lhy_nand_regs->NFCONT |= (1<<1);
     47     }else{
     48         /* 选中:NFCONT[1]设为1 */
     49     lhy_nand_regs->NFCONT &= ~(1<<1);
     50     }
     51 }
     52 //发送命令,地址,数据
     53 static void lhy_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
     54 {
     55     if (ctrl & NAND_CLE){
     56         /* 发命令 : NFCMMD=dat*/
     57         lhy_nand_regs->NFCMD = cmd;
     58     }else{
     59         writeb(cmd, host->io_base + (1 << host->board->ale));
     60         lhy_nand_regs->NFADDR = cmd;
     61     }
     62 }
     63 //判断状态
     64 static int lhy_dev_ready(struct mtd_info *mtd)
     65 {
     66     return (lhy_nand_regs->NFSTAT & (1<<0));
     67 }
     68 
     69 static int lhy_nand_init(void){
     70     
     71     /* 1.分配一个nand_chip结构体 */
     72     lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
     73     
     74     lhy_nand_regs = ioremap(0x4E000000,sizeof(struct lhy_nand_regs)); //映射寄存器
     75     /* 2.设置 */
     76     /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
     77      *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
     78      */
     79     lhy_nand->select_chip = lhy_select_chip;          //选中,芯片选择函数
     80     lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
     81     lhy_nand->IO_ADDR_R   = lhy_nand_regs->NFDATA;    //读寄存器
     82     lhy_nand->IO_ADDR_W   = lhy_nand_regs->NFDATA;    //写寄存器
     83     lhy_nand->dev_ready      = lhy_dev_ready;            //判断状态
     84     nand_chip->ecc.mode = NAND_ECC_SOFT;            /*使能ECC校验码 enable ECC */
     85 
     86     /* 3.硬件相关的操作 根据nand flash的手册设置时间参数
     87      * HCLK = 100MHz
     88      * TACLS: 发出CLE/ALE之后多长时间发出nWE信号,从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
     89      * TWRPH0: nWE的信号的脉冲宽度,HCLK *(TWPRH0 + 1),从NAND手册可知它要>=12ns,所以TWRPH0>=1
     90      * TWRPH1:表示nWE信号变为高电平后,CLE/ALE多长时间才能变为低电平,从手册可知他要>=5ns,所以TWRPH1>=0
     91      */
     92 #define TACLS     0
     93 #define TWRPH0    1
     94 #define TWRPH1    0     
     95     lhy_nand_regs->NFCONF |= (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
     96     
     97     /* NFCONT的bit1设为1,表示片 选 */
     98     lhy_nand_regs->NFCONT =  (1<<1) | (1<<0);
     99     /* 4.使用nand_scan */
    100     lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    101     lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
    102     lhy_mtd->owner = THIS_MODULE;
    103     
    104     nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
    105     /* 5.add_mtd_partitions 添加分区 */
    106     //add_mtd_partitions(tiny_nand_mtd, tiny_nand_part, 3);
    107     return 0;
    108 }
    109 
    110 static void lhy_nand_exit(void){
    111     if(lhy_nand)
    112         kfree(lhy_nand);
    113     if(lhy_mtd)
    114         kfree(lhy_mtd);
    115     if(lhy_nand_regs)
    116         iounmap(lhy_nand_regs);
    117 }
    118 
    119 module_init(lhy_nand_init);
    120 module_exit(lhy_nand_exit);
    121 MODULE_LICENSE("GPL");
    122 
    123 
    124 /*
    125 S3C2440 U-BOOT 的NAND操作
    126 
    127 1.读取ID
    128 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    129 发出命令0x90            NFCMMD=0X90            mw.b 0x4E000008 0x90
    130 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    131 读出数据得到0XEC          val=NFDATA            md.b 0x4E000010 1
    132 读数据得到device code    val=NFDATA             md.b 0x4E000010 1
    133 退出读ID状态            NFCMMD=0xff            mw.b 0x4E000008 0xff
    134 
    135 2.读内容 读0地址的数据
    136 输入命令: nand dump 0  得到nand
    137 
    138 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    139 发出命令0x00            NFCMMD=0X00            mw.b 0x4E000008 0x00
    140 
    141 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    142 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    143 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    144 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    145 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    146 
    147 发出命令0x30            NFCMMD=0X00            mw.b 0x4E000008 0x30
    148 
    149 //接下来就是从0地址开始一个字节一个字节的读出数据,和前面nand dump 0 的数据一样
    150 读出数据得到0x17          val=NFDATA            md.b 0x4E000010 1
    151 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    152 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    153 
    154 退出读状态                NFCMMD=0xff            mw.b 0x4E000008 0xff
    155 
    156 3.NAND  flash 驱动层次  Atmel_nand.c  Mtdchar.c
    157 块设备:        知道怎么优化
    158 NAND Flash协议:知道发什么来读写,擦除,识别
    159 硬件相关:        知道怎样发命令/地址,读写数据
    160 
    161 硬件相关:
    162 ①分配nand_chip 结构体 
    163 ②设置nand_chip 
    164 ③硬件相关设备 
    165 ④使用 nand_scan / add_mtd_partitions
    166 
    167 
    168 4.ECC校验码  64byte的OOB内存
    169 flash每一页都有64B不参与编址,被称为OOB(out of bank)
    170 原因是,nand flash的内存中,数据很容易发生位反转,为了防止数据出错,引入了ECC校验,
    171 解决:①写一页的数据 ②用这一页的内容生成ECC校验码 ③把ECC校验码写入OOB中
    172 读取:①读page ②读OOB里的ECC ③根据page的内容算ECC 
    173       ④将算的ECC校验码和读取OOB中的校验码进行校验和校准,可以算出哪一位发生错误。
    174 ECC校验码生成:可以硬件也可以软件
    175 
    176 */
    nand flash3.c

    四、增加分区挂接

    要增加分区的话,首先要定义mtd_partition结构体,里面定义了分区的名字,起始地址,以及分区的大小等参数。

     1 //定义nand flash的分区
     2 static struct mtd_partition lhy_nand_part[] = {
     3     [0] = {
     4         .name    = "bootloader",
     5         .size    = 0x40000,
     6         .offset    = 0,
     7     },
     8     [1] = {
     9         .name    = "params",
    10         .offset = MTDPART_OFS_APPEND,  //大小紧跟真前面这个分区
    11         .size    = 0x20000,
    12     },
    13     [2] = {
    14         .name    = "kernel",
    15         .offset = MTDPART_OFS_APPEND,  //大小紧跟真前面这个分区
    16         .size    = 0x200000,
    17     },
    18     [3] = {
    19         .name    = "root",
    20         .offset = MTDPART_OFS_APPEND,
    21         .size    = MTDPART_SIZ_FULL,        //剩余的所有大小
    22     },
    23 };

    接着就是在init函数中添加分区结构体。

    1     /* 5.add_mtd_partitions 添加分区 */
    2     //如果想整块flash只作为一个分区,使用add_mtd_device就够了
    3     //add_mtd_device(lhy_mtd);
    4     //如果要创建多个分区的话,那么就要使用add_mtd_partitions
    5     add_mtd_partitions(lhy_nand_mtd, lhy_nand_part, 3);

    附上驱动程序nand_flash4.c

      1 /* 
      2  * 参考:linux-2.6.31driversmtd
    ands3c2410.c   atmel_nand.c 
      3  */
      4 
      5 #include <linux/slab.h>
      6 #include <linux/module.h>
      7 #include <linux/moduleparam.h>
      8 #include <linux/platform_device.h>
      9 #include <linux/mtd/mtd.h>
     10 #include <linux/mtd/nand.h>
     11 #include <linux/mtd/partitions.h>
     12 #include <linux/clk.h>
     13 #include <linux/io.h>
     14 #include <mach/board.h>
     15 #include <mach/cpu.h>
     16 
     17 //寄存器结构体
     18 struct lhy_nand_regs{
     19     unsigned long NFCONF   ; //偏移地址: S3C2410_NFREG(0x00)
     20     unsigned long NFCONT   ; //偏移地址: S3C2410_NFREG(0x04)
     21     unsigned long NFCMD    ; //偏移地址: S3C2410_NFREG(0x08)
     22     unsigned long NFADDR   ; //偏移地址: S3C2410_NFREG(0x0C)
     23     unsigned long NFDATA   ; //偏移地址: S3C2410_NFREG(0x10)
     24     unsigned long NFECCD0  ; //偏移地址: S3C2410_NFREG(0x14)
     25     unsigned long NFECCD1  ; //偏移地址: S3C2410_NFREG(0x18)
     26     unsigned long NFECCD   ; //偏移地址: S3C2410_NFREG(0x1C)
     27     unsigned long NFSTAT   ; //偏移地址: S3C2410_NFREG(0x20)
     28     unsigned long NFESTAT0 ; //偏移地址: S3C2410_NFREG(0x24)
     29     unsigned long NFESTAT1 ; //偏移地址: S3C2410_NFREG(0x28)
     30     unsigned long NFMECC0  ; //偏移地址: S3C2410_NFREG(0x2C)
     31     unsigned long NFMECC1  ; //偏移地址: S3C2410_NFREG(0x30)
     32     unsigned long NFSECC   ; //偏移地址: S3C2410_NFREG(0x34)
     33     unsigned long NFSBLK   ; //偏移地址: S3C2410_NFREG(0x38)
     34     unsigned long NFEBLK   ; //偏移地址: S3C2410_NFREG(0x3C)
     35 };
     36 
     37 static struct nand_chip *lhy_nand;   
     38 static struct mtd_info *lhy_mtd;            //定义一个mtd_info结构体
     39 static struct lhy_nand_regs *lhy_nand_res;    //定义寄存器的结构体指针
     40 
     41 //定义nand flash的分区
     42 static struct mtd_partition lhy_nand_part[] = {
     43     [0] = {
     44         .name    = "bootloader",
     45         .size    = 0x40000,
     46         .offset    = 0,
     47     },
     48     [1] = {
     49         .name    = "params",
     50         .offset = MTDPART_OFS_APPEND,  //大小紧跟真前面这个分区
     51         .size    = 0x20000,
     52     },
     53     [2] = {
     54         .name    = "kernel",
     55         .offset = MTDPART_OFS_APPEND,  //大小紧跟真前面这个分区
     56         .size    = 0x200000,
     57     },
     58     [3] = {
     59         .name    = "root",
     60         .offset = MTDPART_OFS_APPEND,
     61         .size    = MTDPART_SIZ_FULL,        //剩余的所有大小
     62     },
     63 };
     64 
     65 /* 芯片选择 */
     66 static void lhy_select_chip(struct mtd_info *mtd,int chipnr)
     67 {
     68     if(chipnr == -1){
     69         /* 取消选中,NFCONT[1]设为0 */
     70         lhy_nand_regs->NFCONT |= (1<<1);
     71     }else{
     72         /* 选中:NFCONT[1]设为1 */
     73     lhy_nand_regs->NFCONT &= ~(1<<1);
     74     }
     75 }
     76 //发送命令,地址,数据
     77 static void lhy_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
     78 {
     79     if (ctrl & NAND_CLE){
     80         /* 发命令 : NFCMMD=dat*/
     81         lhy_nand_regs->NFCMD = cmd;
     82     }else{
     83         writeb(cmd, host->io_base + (1 << host->board->ale));
     84         lhy_nand_regs->NFADDR = cmd;
     85     }
     86 }
     87 //判断状态
     88 static int lhy_dev_ready(struct mtd_info *mtd)
     89 {
     90     return (lhy_nand_regs->NFSTAT & (1<<0));
     91 }
     92 
     93 static int lhy_nand_init(void){
     94     
     95     /* 1.分配一个nand_chip结构体 */
     96     lhy_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
     97     
     98     lhy_nand_regs = ioremap(0x4E000000,sizeof(struct lhy_nand_regs)); //映射寄存器
     99     /* 2.设置 */
    100     /* 2.1设置nand_chip是给nand_scan函数用的,如果不知道怎么设置,先看nand_scan怎么用
    101      *         它应该提供:选中,发命令,发地址,发数据,读数据,判断状态等功能
    102      */
    103     lhy_nand->select_chip = lhy_select_chip;          //选中,芯片选择函数
    104     lhy_nand->cmd_ctrl       = lhy_nand_cmd_ctrl;        //负责发送地址,命令
    105     lhy_nand->IO_ADDR_R   = lhy_nand_regs->NFDATA;    //读寄存器
    106     lhy_nand->IO_ADDR_W   = lhy_nand_regs->NFDATA;    //写寄存器
    107     lhy_nand->dev_ready      = lhy_dev_ready;            //判断状态
    108     nand_chip->ecc.mode = NAND_ECC_SOFT;            /*使能软件ECC校验码 enable ECC */
    109 
    110     /* 3.硬件相关的操作 根据nand flash的手册设置时间参数
    111      * HCLK = 100MHz
    112      * TACLS: 发出CLE/ALE之后多长时间发出nWE信号,从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
    113      * TWRPH0: nWE的信号的脉冲宽度,HCLK *(TWPRH0 + 1),从NAND手册可知它要>=12ns,所以TWRPH0>=1
    114      * TWRPH1:表示nWE信号变为高电平后,CLE/ALE多长时间才能变为低电平,从手册可知他要>=5ns,所以TWRPH1>=0
    115      */
    116 #define TACLS     0
    117 #define TWRPH0    1
    118 #define TWRPH1    0     
    119     lhy_nand_regs->NFCONF |= (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
    120     
    121     /* NFCONT的bit1设为1,表示片 选 */
    122     lhy_nand_regs->NFCONT =  (1<<1) | (1<<0);
    123     /* 4.使用nand_scan */
    124     lhy_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    125     lhy_mtd->priv = lhy_nand;            //私有数据为我们的nand_chip结构体
    126     lhy_mtd->owner = THIS_MODULE;
    127     
    128     nand_scan(lhy_mtd,1);        //扫描识别nand flash,并且构造mtd,最大芯片个数为1
    129     /* 5.add_mtd_partitions 添加分区 */
    130     //如果想整块flash只作为一个分区,使用add_mtd_device就够了
    131     //add_mtd_device(lhy_mtd);
    132     //如果要创建多个分区的话,那么就要使用add_mtd_partitions
    133     add_mtd_partitions(lhy_nand_mtd, lhy_nand_part, 3);
    134     return 0;
    135 }
    136 
    137 static void lhy_nand_exit(void){
    138     del_mtd_partitions(lhy_nand_mtd);
    139     if(lhy_nand)
    140         kfree(lhy_nand);
    141     if(lhy_mtd)
    142         kfree(lhy_mtd);
    143     if(lhy_nand_regs)
    144         iounmap(lhy_nand_regs);
    145 }
    146 
    147 module_init(lhy_nand_init);
    148 module_exit(lhy_nand_exit);
    149 MODULE_LICENSE("GPL");
    150 
    151 
    152 /*
    153 S3C2440 U-BOOT 的NAND操作
    154 
    155 1.读取ID
    156 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    157 发出命令0x90            NFCMMD=0X90            mw.b 0x4E000008 0x90
    158 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    159 读出数据得到0XEC          val=NFDATA            md.b 0x4E000010 1
    160 读数据得到device code    val=NFDATA             md.b 0x4E000010 1
    161 退出读ID状态            NFCMMD=0xff            mw.b 0x4E000008 0xff
    162 
    163 2.读内容 读0地址的数据
    164 输入命令: nand dump 0  得到nand
    165 
    166 选中                    NFCONT的bit1设为0    md.1 0x4E000004 1;    mw.1 0x4e000004 1
    167 发出命令0x00            NFCMMD=0X00            mw.b 0x4E000008 0x00
    168 
    169 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    170 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    171 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    172 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    173 发出地址0x00            NFADD=0X00            mw.b 0x4E00000c 0x00
    174 
    175 发出命令0x30            NFCMMD=0X00            mw.b 0x4E000008 0x30
    176 
    177 //接下来就是从0地址开始一个字节一个字节的读出数据,和前面nand dump 0 的数据一样
    178 读出数据得到0x17          val=NFDATA            md.b 0x4E000010 1
    179 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    180 读出数据得到0x00          val=NFDATA            md.b 0x4E000010 1
    181 
    182 退出读状态                NFCMMD=0xff            mw.b 0x4E000008 0xff
    183 
    184 3.NAND  flash 驱动层次  Atmel_nand.c  Mtdchar.c
    185 块设备:        知道怎么优化
    186 NAND Flash协议:知道发什么来读写,擦除,识别
    187 硬件相关:        知道怎样发命令/地址,读写数据
    188 
    189 硬件相关:
    190 ①分配nand_chip 结构体 
    191 ②设置nand_chip 
    192 ③硬件相关设备 
    193 ④使用 nand_scan / add_mtd_partitions
    194 
    195 
    196 4.ECC校验码  64byte的OOB内存
    197 flash每一页都有64B不参与编址,被称为OOB(out of bank)
    198 原因是,nand flash的内存中,数据很容易发生位反转,为了防止数据出错,引入了ECC校验,
    199 解决:①写一页的数据 ②用这一页的内容生成ECC校验码 ③把ECC校验码写入OOB中
    200 读取:①读page ②读OOB里的ECC ③根据page的内容算ECC 
    201       ④将算的ECC校验码和读取OOB中的校验码进行校验和校准,可以算出哪一位发生错误。
    202 ECC校验码生成:可以硬件也可以软件
    203 
    204 5.测试3th
    205 nfs挂载
    206 
    207 1.去掉内核自带的NAND FLASH驱动
    208 -->Device Drivers
    209     -->Memory Technology Device (MTD) support
    210         -->NAND Device Support
    211         < > NAND Flash support for s3c2410 SoC
    212 make uImage    
    213 2.保存以前的根文件系统的bootargs
    214 nfs 30000000 192.168.1.5:/work/nfs_root/uImage_nonand
    215 set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.105:/work/nfs_root/first_fs ip=192.168.1.17:192.168.1.5:192.168.1.1:255.255.255.0::eth0:off
    216 
    217 3.ls /dev/mtd*  //没有分区
    218 4.insmod lhy_nand.ko
    219 5.ls /dev/mtd*    //四个分区,总共有四对设备节点,只读与可写
    220 6.格式化nand flash,工具:mtd_utils-06.7.23.tar.bz2
    221     进入utils目录,修改Makefile,#CROSS=ARM-linux 修改为 CROSS=ARM-linux,把#号去掉
    222     make
    223 7.cp flash_erase flash_eraseall 拷贝到网络文件系统即可
    224 8.flah_eraseall /dev/mtd2
    225 9.挂接mount -t yaffs /dev/mtdblock3 /mnt/,可以在里面创建文件
    226 10.接着重新启动开发板,重新装载模块,挂接,看看文件是否还在里面
    227 
    228 
    229 */
    nandflash4.c
  • 相关阅读:
    第4課 部屋に机といずがあらます。
    (转)三层结构设计与ERP部署规划
    多线程的一个问题(同步事件和等待句柄)转
    C#操作xml小结(转)
    再论Assembly Registration Tool (Regasm.exe)
    再论Type Library Importer (Tlbimp.exe)
    Blittable and NonBlittable Types
    再论Importing a Type Library as an Assembly
    .Net Remoting(基本操作) Part.2 (转)
    Monitor类示例(转)
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4438142.html
Copyright © 2011-2022 走看看