NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A" 问1. 原理图上NAND FLASH和S3C2440之间只有数据线, 怎么传输地址? 答1.在DATA0~DATA7上既传输数据,又传输地址 当ALE为高电平时传输的是地址, 问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令 怎么传入命令? 答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令 当ALE为高电平时传输的是地址, 当CLE为高电平时传输的是命令 当ALE和CLE都为低电平时传输的是数据 问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等 那么怎么避免干扰? 答3. 这些设备,要访问之必须"选中", 没有选中的芯片不会工作,相当于没接一样 问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后, NAND FLASH肯定不可能瞬间完成烧写的, 怎么判断烧写完成? 答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙 问5. 怎么操作NAND FLASH呢? 答5. 根据NAND FLASH的芯片手册,一般的过程是: 发出命令 发出地址 发出数据/读数据 NAND FLASH S3C2440 发命令 选中芯片 CLE设为高电平 NFCMMD=命令值 在DATA0~DATA7上输出命令值 发出一个写脉冲 发地址 选中芯片 NFADDR=地址值 ALE设为高电平 在DATA0~DATA7上输出地址值 发出一个写脉冲 发数据 选中芯片 NFDATA=数据值 ALE,CLE设为低电平 在DATA0~DATA7上输出数据值 发出一个写脉冲 读数据 选中芯片 val=NFDATA 发出读脉冲 读DATA0~DATA7的数据 用UBOOT来体验NAND FLASH的操作: 1. 读ID S3C2440 u-boot 选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1 发出命令0x90 NFCMMD=0x90 mw.b 0x4E000008 0x90 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 读数据得到0xEC val=NFDATA md.b 0x4E000010 1 读数据得到device code val=NFDATA md.b 0x4E000010 1 0xda 退出读ID的状态 NFCMMD=0xff mw.b 0x4E000008 0xff 2. 读内容: 读0地址的数据 使用UBOOT命令: nand dump 0 Page 00000000 dump: 17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 S3C2440 u-boot 选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1 发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30 读数据得到0x17 val=NFDATA md.b 0x4E000010 1 读数据得到0x00 val=NFDATA md.b 0x4E000010 1 读数据得到0x00 val=NFDATA md.b 0x4E000010 1 读数据得到0xea val=NFDATA md.b 0x4E000010 1 退出读状态 NFCMMD=0xff mw.b 0x4E000008 0xff NAND FLASH驱动程序层次 看内核启动信息 S3C24XX NAND Driver, (c) 2004 Simtec Electronics s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit) Scanning device for bad blocks Bad eraseblock 256 at 0x02000000 Bad eraseblock 257 at 0x02020000 Bad eraseblock 319 at 0x027e0000 Bad eraseblock 606 at 0x04bc0000 Bad eraseblock 608 at 0x04c00000 Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit": 0x00000000-0x00040000 : "bootloader" 0x00040000-0x00060000 : "params" 0x00060000-0x00260000 : "kernel" 0x00260000-0x10000000 : "root" 搜"S3C24XX NAND Driver" S3c2410.c (driversmtd and) s3c2410_nand_inithw s3c2410_nand_init_chip nand_scan // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info nand_scan_ident nand_set_defaults if (!chip->select_chip) chip->select_chip = nand_select_chip; // 默认值不适用 if (chip->cmdfunc == NULL) chip->cmdfunc = nand_command; chip->cmd_ctrl(mtd, command, ctrl); if (!chip->read_byte) chip->read_byte = nand_read_byte; readb(chip->IO_ADDR_R); if (chip->waitfunc == NULL) chip->waitfunc = nand_wait; chip->dev_ready nand_get_flash_type chip->select_chip(mtd, 0); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); nand_scan_tail mtd->erase = nand_erase; mtd->read = nand_read; mtd->write = nand_write; s3c2410_nand_add_partition add_mtd_partitions add_mtd_device list_for_each(this, &mtd_notifiers) { // 问. mtd_notifiers在哪设置 // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); not->add(mtd); // mtd_notify_add 和 blktrans_notify_add 先看字符设备的mtd_notify_add class_device_create class_device_create 再看块设备的blktrans_notify_add list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置 // 答. driversmtdmdblock.c或mtdblock_ro.c register_mtd_blktrans struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); tr->add_mtd(tr, mtd); mtdblock_add_mtd (driversmtdmdblock.c) add_mtd_blktrans_dev alloc_disk gd->queue = tr->blkcore_priv->rq; // tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); add_disk 测试4th: 1. make menuconfig去掉内核自带的NAND FLASH驱动 -> Device Drivers -> Memory Technology Device (MTD) support -> NAND Device Support < > NAND Flash support for S3C2410/S3C2440 SoC 2. make uImage 使用新内核启动, 并且使用NFS作为根文件系统 3. insmod s3c_nand.ko 4. 格式化 (参考下面编译工具) flash_eraseall /dev/mtd3 // yaffs 5. 挂接 mount -t yaffs /dev/mtdblock3 /mnt 6. 在/mnt目录下建文件 编译工具: 1. tar xjf mtd-utils-05.07.23.tar.bz2 2. cd mtd-utils-05.07.23/util 修改Makefile: #CROSS=arm-linux- 改为 CROSS=arm-linux- 3. make 4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/
struct nand_chip { /*8 位NAND 芯片的读写地址*/ void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); /*读取数据并验证*/ int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); /*选定芯片*/ void (*select_chip)(struct mtd_info *mtd, int chip); /*检查坏块*/ int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); /*mark(标记)bad block*/ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); /*发送命令/地址*/ void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl); /*读取芯片状态*/ int (*dev_ready)(struct mtd_info *mtd); /*发送命令*/ void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); /*等待就绪*/ int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); /*擦除命令函数*/ void (*erase_cmd)(struct mtd_info *mtd, int page); /*扫描坏块表*/ int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); /*高级页面写功能*/ int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw); int chip_delay; struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; ................... }; struct nand_ecc_ctrl { nand_ecc_modes_t mode; int steps; int size; int bytes; int total; int prepad; int postpad; struct nand_ecclayout *layout; /*hwctl函数: *这个函数用来控制硬件产生ecc *其实它主要的工作就是控制NAND controller 向 NAND 芯片发出NAND_ECC_READ 、NAND_ECC_WRITE 和NAND_ECC_READSYN *等命令,与struct nand_chip 结构体中的cmdfunc 类似 */ void (*hwctl)(struct mtd_info *mtd, int mode); /*根据data 计算ecc 值*/ int (*calculate)(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code); /*根据ecc 值,判断读写数据时是否有错误发生,若有错,则立即试着纠正,纠正失败则返回错误*/ int (*correct)(struct mtd_info *mtd, uint8_t *dat,uint8_t *read_ecc,uint8_t *calc_ecc); /*read_page_raw write_page_raw函数 *从NAND 芯片中读取一个page 的原始数据和向NAND 芯片写入一个page 的原始数据,所谓的原始数据,即不对读写的数据做ecc处理 *该读写什么值就读写什么值。另外,这两个函数会读写整个page 中的所有内容,即不但会读写一个page 中MAIN部分,还会读写OOB 部分。 */ int (*read_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf); void (*write_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf); /* *read_page 和write_page 在读写过程中会加入ecc 的计算,校验,和纠正等处理。 */ int (*read_page)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf); void (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf); /* *读写oob 中的内容,不包括MAIN 部分。 */ int (*read_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page,int sndcmd); int (*write_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page); }; 其实,以上提到的这几个read_xxx 和write_xxx 函数,最终都会调用struct nand_chip 中的read_buf 和write_buf 这两个函数,所以如果没有特殊需求的话,我认为不必自己实现,使用MTD 提供的default 的函数即可。
1 #include <linux/module.h> 2 #include <linux/types.h> 3 #include <linux/init.h> 4 #include <linux/kernel.h> 5 #include <linux/string.h> 6 #include <linux/ioport.h> 7 #include <linux/platform_device.h> 8 #include <linux/delay.h> 9 #include <linux/err.h> 10 #include <linux/slab.h> 11 #include <linux/clk.h> 12 13 #include <linux/mtd/mtd.h> 14 #include <linux/mtd/nand.h> 15 #include <linux/mtd/nand_ecc.h> 16 #include <linux/mtd/partitions.h> 17 18 #include <asm/io.h> 19 20 #include <asm/arch/regs-nand.h> 21 #include <asm/arch/nand.h> 22 23 struct s3c_nand_regs { 24 unsigned long nfconf ; 25 unsigned long nfcont ; 26 unsigned long nfcmd ; 27 unsigned long nfaddr ; 28 unsigned long nfdata ; 29 unsigned long nfeccd0 ; 30 unsigned long nfeccd1 ; 31 unsigned long nfeccd ; 32 unsigned long nfstat ; 33 unsigned long nfestat0; 34 unsigned long nfestat1; 35 unsigned long nfmecc0 ; 36 unsigned long nfmecc1 ; 37 unsigned long nfsecc ; 38 unsigned long nfsblk ; 39 unsigned long nfeblk ; 40 }; 41 42 static struct nand_chip *s3c_nand_chip; 43 static struct mtd_info *s3c_mtd; 44 static struct s3c_nand_regs *s3c_nand_regs; 45 46 static struct mtd_partition s3c_nand_parts[] = { 47 [0] = { 48 .name = "bootloader", 49 .size = 0x00040000, 50 .offset = 0, 51 }, 52 [1] = { 53 .name = "params", 54 .offset = MTDPART_OFS_APPEND, 55 .size = 0x00020000, 56 }, 57 [2] = { 58 .name = "kernel", 59 .offset = MTDPART_OFS_APPEND, 60 .size = 0x00200000, 61 }, 62 [3] = { 63 .name = "root", 64 .offset = MTDPART_OFS_APPEND, 65 .size = MTDPART_SIZ_FULL, 66 } 67 }; 68 69 70 /*判断忙*/ 71 static int s3c_dev_ready(struct mtd_info *mtd) 72 { 73 /*返回"NFSTAT的bit[0]";*/ 74 return (s3c_nand_regs->nfstat & (1<<0)); 75 } 76 77 static void s3c_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl) 78 { 79 80 if (ctrl & NAND_CLE) 81 { 82 /* 发命令: NFCMMD=dat */ 83 s3c_nand_regs->nfcmd = dat; 84 //writeb(cmd, host->io_base + (1 << host->board->cle));/*命令*/ 85 } 86 else 87 { 88 /* 发地址: NFADDR=dat */ 89 s3c_nand_regs->nfaddr = dat; 90 //writeb(cmd, host->io_base + (1 << host->board->ale));/*地址*/ 91 } 92 } 93 94 95 static void s3c_select_chip(struct mtd_info *mtd, int chip) 96 { 97 if(chip ==-1) 98 { 99 /*表示取消选中 NFCONT[1]设为1 */ 100 s3c_nand_regs->nfcont |=(1<<1); 101 } 102 else 103 { 104 s3c_nand_regs->nfcont &=~(1<<1); 105 /*选中芯片 NFCONT[1]设为0 */ 106 } 107 } 108 109 110 static int s3c_nand_init(void) 111 { 112 113 struct clk *clk; 114 115 /*1.分配一个nand_chip结构体*/ 116 s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); 117 118 s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs)); 119 120 /*2.设置nandc_chip结构体*/ 121 s3c_nand_chip->IO_ADDR_R = &s3c_nand_regs->nfdata; 122 s3c_nand_chip->IO_ADDR_W = &s3c_nand_regs->nfdata; 123 s3c_nand_chip->cmd_ctrl = s3c_cmd_ctrl; 124 s3c_nand_chip->dev_ready = s3c_dev_ready; 125 s3c_nand_chip->select_chip = s3c_select_chip; 126 s3c_nand_chip->ecc.mode = NAND_ECC_SOFT; 127 //s3c_nand_chip->chip_delay = 20; 128 129 /* 使能NAND FLASH控制器的时钟 */ 130 clk = clk_get(NULL, "nand"); 131 clk_enable(clk); /* CLKCON'bit[4] */ 132 133 134 /*初始化nand控制器 设置寄存器*/ 135 #define TACLS 0 136 #define TWRPH0 3 137 #define TWRPH1 0 138 /* HCLK=100MHz 139 * TACLS: 发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0 140 * TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1 141 * TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0 142 */ 143 s3c_nand_regs->nfconf=(TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); 144 145 /* NFCONT: 146 * BIT1-设为1, 取消片选 147 * BIT0-设为1, 使能NAND FLASH控制器 148 * BIT4-设为0, 未初始化hardware ECC 149 */ 150 s3c_nand_regs->nfcont = (1<<1) | (1<<0); 151 152 /*分配一个mtd_info结构体*/ 153 s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); 154 155 /*设置s3c_mtd结构体*/ 156 s3c_mtd->owner = THIS_MODULE; 157 s3c_mtd->priv = s3c_nand_chip; 158 159 /* 识别NAND FLASH, 构造mtd_info */ 160 nand_scan(s3c_mtd, 1); 161 162 /*设置分区表*/ 163 add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4); 164 165 add_mtd_device(s3c_mtd); 166 return 0; 167 } 168 169 static void s3c_nand_exit(void) 170 { 171 kfree(s3c_mtd); 172 kfree(s3c_nand_chip); 173 iounmap(s3c_nand_regs); 174 } 175 176 module_init(s3c_nand_init); 177 module_exit(s3c_nand_exit); 178 MODULE_LICENSE("GPL");