NAND FLASH是一個存儲芯片。
在芯片上的DATA0~DATA7上既能傳輸數據也能傳輸地址。
當ALE為高電平時傳輸的是地址。
當CLE為高電平時傳輸的是命令。
當ALE和CLE都為低電平時傳輸的是數據。
將數據發給nand Flash后,在發送第二次數據之前還要判斷芯片是否處於空閑狀態。一般是通過引腳RnB來判斷,一般是高電平代表就緒,低電平代表正忙。
操作Nand Flash的一般步驟是:
1. 發命令
選中芯片
CLE設置為高電平
在DATA0~DATA7上輸出命令值
發出一個寫脈沖
2. 發地址
選中芯片
ALE為高電平
在DATA0~DATA7上傳輸數據
發出一個寫脈沖
3. 發數據
選中芯片
發出讀脈沖
讀取DATA0~DATA7上的數據。
使用UBOOT來體驗NAND FLASH的操作:
讀ID
選中 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
退出讀ID的狀態 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
驅動程序代碼:
/* * drivers\mtd\nand\s3c2410.c * drivers\mtd\nand\at91_nand.c */ #include <linux/module.h> #include <linux/types.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/clk.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <asm/io.h> #include <asm/arch/regs-nand.h> #include <asm/arch/nand.h> struct nand_chip *s3c_nand; struct mtd_info *s3c_mtd; struct clk *clk; struct s3c_nand_regs{ unsigned long nfconf; unsigned long nfcont; unsigned long nfcmd; unsigned long nfaddr; unsigned long nfdata; unsigned long nfeccd0; unsigned long nfeccd1; unsigned long nfeccd; unsigned long nfstat; unsigned long nfestat0; unsigned long nfestat1; unsigned long nfeecc0; unsigned long nfeecc1; unsigned long nfsecc; unsigned long nfsblk; unsigned long nfeblk; }; static struct mtd_partition s3c_nand_parts[] = { [0] = { .name = "bootloader", .size = 0x00040000, .offset= 0, }, [1] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = 0x00020000, }, [2] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = 0x00200000, }, [3] = { .name = "root", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; static volatile struct s3c_nand_regs *nand_regs; static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr ) { if(chipnr == -1 ) { /* 取消選中 */ nand_regs->nfcont |= (1<<1); } else { /* 選中 NFCONT^1 設置為1 */ nand_regs->nfcont &= ~(1<<1); } } static void s3c2440_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { if (cmd == NAND_CMD_NONE) return; if (ctrl & NAND_CLE) { /* 發命令 */ nand_regs->nfcmd = cmd; } else { /* 發地址 */ nand_regs->nfaddr = cmd; } } static int s3c2440_dev_ready(struct mtd_info *mtd) { /* */ return (nand_regs->nfstat & (1<<0)); } static int s3c_nand_init(void) { /* 分配一個nand_chip結構體 */ s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); nand_regs = ioremap(0x4e000000,sizeof(struct s3c_nand_regs)); clk = clk_get(NULL,"nand"); clk_enable(clk); /* 設置 */ s3c_nand->select_chip = s3c2440_select_chip; s3c_nand->cmd_ctrl = s3c2440_nand_cmd_ctrl; s3c_nand->IO_ADDR_R = (void *)&nand_regs->nfdata; s3c_nand->IO_ADDR_W = (void *)&nand_regs->nfdata; s3c_nand->dev_ready = s3c2440_dev_ready; s3c_nand->ecc.mode = NAND_ECC_SOFT; /* 硬件相關的操作:根據nand Flash的手冊設置時間參數 */ /* HCLK = 100MHz */ /* TACLS: 發出cle/ale之后多長時間才發出nWE信號 */ /* TWRPHO: nWE的脈沖寬度 */ nand_regs->nfconf = (1<<8); nand_regs->nfcont = 0x03; /* 使用:nand_scan */ s3c_mtd = kzalloc( sizeof(struct mtd_info), GFP_KERNEL ); s3c_mtd->owner = THIS_MODULE; s3c_mtd->priv = s3c_nand; nand_scan(s3c_mtd,1); /* 掃描識別 */ /* add_mtd_partitions */ add_mtd_partitions(s3c_mtd,s3c_nand_parts,4); //add_mtd_device(s3c_mtd); //整個flash只有一個分區的話可以用這個 return 0; } static void s3c_nand_exit(void) { del_mtd_partitions(s3c_mtd); kfree(s3c_mtd); iounmap(nand_regs); kfree(s3c_nand); } module_init(s3c_nand_init); module_exit(s3c_nand_exit); MODULE_LICENSE("GPL");
在添加這個內核模塊的時候,首先卸載內核中的nand Flash驅動。
->Device Drivers
->Memory Technology Device (MTD) support
->NAND Device Support
sd
