在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
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.31\drivers\mtd\nand\s3c2410.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 */ 二、完善前面的程序 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.31\drivers\mtd\nand\s3c2410.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 发出 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论