单片机
返回首页

S3C2440 nand_flash驱动程序

2024-08-05 来源:cnblogs

一、一个简单的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.31driversmtdnands3c2410.c   atmel_nand.c 

  3  */

  4 

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

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


二、完善前面的程序


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.31driversmtdnands3c2410.c   atmel_nand.c 

  3  */

  4 

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

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


三、增加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.31driversmtdnands3c2410.c   atmel_nand.c 

  3  */

  4 

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

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


四、增加分区挂接


要增加分区的话,首先要定义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.31driversmtdnands3c2410.c   atmel_nand.c 

  3  */

  4 

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 15 #include

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


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章