1. 程式人生 > >nandflash驅動程式之完善識別過程

nandflash驅動程式之完善識別過程

我們發出了那些訊號,

如上圖,發出一些訊號我們應該會維持一段時間啊,太短了 nandflash可能反應不過來,所以我們還要設定一些事件引數

根據nandflash的手冊 設定時間引數

我們看一看那開發板的晶片手冊


它的時間引數就在TACLS TWRPH0 TWRPH1 ,我們來看看這個時間引數是什麼意思


在這張圖可以看出TACLS是說你發出CLE與ALE之後過多長時間才能發出寫訊號,TWRPH0表示這個寫訊號的脈衝寬度,TWRPH1表示你寫訊號變為高電平之後還要過多長事件你這個ALE和CLE才能變為低電平


在這張圖上我們一眼就可以看出twp這個脈衝寬度,可以看出tclh是寫訊號結束之後CLE維持的時間  然後TACLS就是tcls-twp

由這裡可以看出,twp最小是12ns。tclh最小值5ns, 我們可以看到tcls最小值是12ns,twp最小值也是12ns,顯然我們的tacls可以是0

現在還不需要把原理的nandflash驅動去掉,現在還只做了識別

我們把晶片手冊開啟,會發現我們的nandflash挺有意思的,


這裡面是一頁一頁的東西,一頁是2k,除了2k之外還有64位元組的空間  這64位元組稱為oob區 oob是out of bank 在bank之外的東西

為什麼引入oob呢,因為nandflash有個缺點,就是位反轉,就是說我讀一頁資料,讀出來有很大的機率某一位發生發轉,我本來想讀的是1,但是讀出來是0

寫進去的時候一樣的。怎麼辦呢,就引入了ECC校驗,我除了寫這一頁之外,還會用這一頁資料生成ECC碼 校驗碼,這個校驗碼把ECC寫入OOB

讀的操作就是把這整頁資料,讀OOB,然後根據資料重新算校驗碼,然後比較,可以判斷是否發生位反轉,而且可以知道哪一位發生了錯誤。這個校驗碼可以硬體生成也可以軟體生成,我們用軟體生成

在atmel_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/cpufreq.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 <plat/regs-nand.h>
#include <plat/nand.h>


static struct nand_chip *nand;
static struct mtd_info *nand_mtd;
struct s5p_nand_regs{
unsigned long nfconf;
unsigned long nfcont;
unsigned long nfcmmd;
unsigned long nfaddr;
unsigned long nfdata;
unsigned long nfmeccd0;
unsigned long nfmeccd1;
unsigned long nfseccd;
unsigned long nfsblk;
unsigned long nfeblk;
unsigned long nfstat;
unsigned long nfeccerr0;
unsigned long nfeccerr1;
};
static struct s5p_nand_regs *s5p_nand_regs;




static void gh_select_chip(struct mtd_info *mtd, int chipnr)
{
if(chipnr == -1)
{
//取消選中 將NFCONT[1] 設定為1
s5p_nand_regs->nfcont |=(1<<1);
}
else
{
//選中 NFCONT【1】設定為0
s5p_nand_regs->nfcont &= ~(1<<1);

}


}


static void gh_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{


if (ctrl & NAND_CLE)
{
//發命令 NFCMD暫存器 = cmd的值
s5p_nand_regs->nfcmmd = cmd;
}
else
{
//發地址 NFADDR暫存器=cmd的值
s5p_nand_regs->nfaddr = cmd;

}

}


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


static int nand_init(void)
{
struct clk *nand_clk;
/*1.分配一個nand_chip結構體*/
nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/*2.設定*/
s5p_nand_regs = ioremap(0xb0e00000,sizeof(struct s5p_nand_regs));
//設定nand_chip是給nand_scan_ident函式用的,如果不知道怎麼設定,先看nand_scan_ident怎麼使用*/
//它應該提供發命令,發地址,發資料,讀資料,判斷狀態的功能
nand->select_chip = gh_select_chip; 
nand->cmd_ctrl =gh_cmd_ctrl;
nand->dev_ready = gh_dev_ready;
nand->IO_ADDR_R = &s5p_nand_regs->nfdata;
nand->IO_ADDR_W = &s5p_nand_regs->nfdata;
nand->ecc.mode      = NAND_ECC_SOFT;
/*硬體相關的操作*/

/*3.硬體相關*/
/*使能時鐘*/
nand_clk = clk_get(NULL, "nand");
clk_enable(nand_clk);






/*設定時鐘*/
//hclk是166.75Mhz,就是16.675ns
#define TWRPH1    1
#define TWRPH0    1
#define TACLS     1
s5p_nand_regs->nfconf |= (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

/*
* AddrCycle[1]:1 = 傳送地址需要5個週期
*/
s5p_nand_regs->nfconf |= 1<<1;
/*
* MODE[0]:1     = 使能Nand Flash控制器
* Reg_nCE0[1]:1 = 取消片選
*/
s5p_nand_regs->nfcont |= (1<<1)|(1<<0);

/*3.使用*/
nand_mtd=kzalloc(sizeof(struct mtd_info),GFP_KERNEL);

//將mtd_info與nandchip相聯絡起來


nand_mtd->owner = THIS_MODULE;
nand_mtd->priv = nand;




//nand_scan_ident(nand_mtd,1,NULL);//第二個引數是最大晶片個數
//nand_scan_tail(nand_mtd);
nand_scan(nand_mtd, 1);
return 0;
}


static void nand_exit(void)
{
kfree(nand);
iounmap(s5p_nand_regs);
kfree(nand_mtd);
}


module_init(nand_init);
module_exit(nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EIGHT");
 

測試效果如圖