NAND FLASH是一個存儲晶元 那麼: 這樣的操作很合理"讀地址A的數據,把數據B寫到地址A" 問1. 原理圖上NAND FLASH和S3C2440之間只有數據線, 怎麼傳輸地址? 答1.在DATA0~DATA7上既傳輸數據,又傳輸地址 當ALE為高電平時傳輸的是地址, ... ...
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 (drivers\mtd\nand) 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在哪設置 // 答. drivers\mtd\mdblock.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 (drivers\mtd\mdblock.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");