1. 程式人生 > >linux之NAND FLASH驅動程式

linux之NAND FLASH驅動程式

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/

驅動程式原始碼:

/* 參考 
 * 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 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 nfmecc0 ;
	unsigned long nfmecc1 ;
	unsigned long nfsecc  ;
	unsigned long nfsblk  ;
	unsigned long nfeblk  ;
};


static struct nand_chip *s3c_nand;
static struct mtd_info *s3c_mtd;
static struct s3c_nand_regs *s3c_nand_regs;

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 void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
	if (chipnr == -1)
	{
		/* 取消選中: NFCONT[1]設為1 */
		s3c_nand_regs->nfcont |= (1<<1);		
	}
	else
	{
		/* 選中: NFCONT[1]設為0 */
		s3c_nand_regs->nfcont &= ~(1<<1);
	}
}

static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	if (ctrl & NAND_CLE)
	{
		/* 發命令: NFCMMD=dat */
		s3c_nand_regs->nfcmd = dat;
	}
	else
	{
		/* 發地址: NFADDR=dat */
		s3c_nand_regs->nfaddr = dat;
	}
}

static int s3c2440_dev_ready(struct mtd_info *mtd)
{
	return (s3c_nand_regs->nfstat & (1<<0));
}


static int s3c_nand_init(void)
{
	struct clk *clk;
	
	/* 1. 分配一個nand_chip結構體 */
	s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);

	s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
	
	/* 2. 設定nand_chip */
	/* 設定nand_chip是給nand_scan函式使用的, 如果不知道怎麼設定, 先看nand_scan怎麼使用 
	 * 它應該提供:選中,發命令,發地址,發資料,讀資料,判斷狀態的功能
	 */
	s3c_nand->select_chip = s3c2440_select_chip;
	s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;
	s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;
	s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;
	s3c_nand->dev_ready   = s3c2440_dev_ready;
	s3c_nand->ecc.mode    = NAND_ECC_SOFT;
	
	/* 3. 硬體相關的設定: 根據NAND FLASH的手冊設定時間引數 */
	/* 使能NAND FLASH控制器的時鐘 */
	clk = clk_get(NULL, "nand");
	clk_enable(clk);              /* CLKCON'bit[4] */
	
	/* HCLK=100MHz
	 * TACLS:  發出CLE/ALE之後多長時間才發出nWE訊號, 從NAND手冊可知CLE/ALE與nWE可以同時發出,所以TACLS=0
	 * TWRPH0: nWE的脈衝寬度, HCLK x ( TWRPH0 + 1 ), 從NAND手冊可知它要>=12ns, 所以TWRPH0>=1
	 * TWRPH1: nWE變為高電平後多長時間CLE/ALE才能變為低電平, 從NAND手冊可知它要>=5ns, 所以TWRPH1>=0
	 */
#define TACLS    0
#define TWRPH0   1
#define TWRPH1   0
	s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

	/* NFCONT: 
	 * BIT1-設為1, 取消片選 
	 * BIT0-設為1, 使能NAND FLASH控制器
	 */
	s3c_nand_regs->nfcont = (1<<1) | (1<<0);
	
	/* 4. 使用: 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);  /* 識別NAND FLASH, 構造mtd_info */
	
	/* 5. add_mtd_partitions */
	add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
	
	//add_mtd_device(s3c_mtd);
	return 0;
}

static void s3c_nand_exit(void)
{
	del_mtd_partitions(s3c_mtd);
	kfree(s3c_mtd);
	iounmap(s3c_nand_regs);
	kfree(s3c_nand);
}

module_init(s3c_nand_init);
module_exit(s3c_nand_exit);

MODULE_LICENSE("GPL");