linux完整备份nand,24.Linux-Nand Flash驅動(分析MTD層並制作NAND驅動)

1.本節使用的nand flash型號為K9F2G08U0M,它的命令如下:

6f5c939c1fe6ed73ef9f32eea26a3834.png

1.1我們以上圖的read id(讀ID)為例,它的時序圖如下:

fc5b0fa6df862138c8e972510ed16e6f.png

首先需要使能CE片選

1)使能CLE

2)發送0X90命令,並發出WE寫脈沖

3)復位CLE,然后使能ALE

4)發送0X00地址,並發出WE寫脈沖

5)設CLE和ALE為低電平

6)while判斷nRE(讀使能)是否為低電平

7)讀出8個I/O的數據,並發出RE上升沿脈沖

(我們的nand flash為8個I/O口,所以型號為K9F2G08U0M)

1.2 nand flash 控制器介紹

在2440中有個nand flash 控制器,它會自動控制CLE,ALE那些控制引腳,我們只需要配置控制器,就可以直接寫命令,寫地址,讀寫數據到它的寄存器中便能完成(讀寫數據之前需要判斷RnB腳),如下圖所示:

4df2d6a513bd3006a93138c12cd70022.png

若在nand flash 控制器下,我們讀ID就只需要如下幾步(非常方便):

1)將寄存器NFCONT(0x4E000004)的bit1=0,來使能片選

2)寫入寄存器NFCMMD(0x4E000008)=0X90,發送命令

3)寫入寄存器NFADDR(0x4E00000C)=0X00,發送地址

4)while判斷nRE(讀使能)是否為低電平

5)讀取寄存器NFDATA(0x4E000010),來讀取數據

1.3 我們在uboot中測試,通過md和mw命令來實現讀id(x要小寫)

如下圖所示,最終讀取出0XEC  0XDA  0X10  0X95

0ce88f654fa49bb3ffd23dd6ac601cc9.png

剛好對應了我們nand flash手冊里的數據(其中0XEC表示廠家ID, 0XDA表示設備ID):

64a44f8bcea140bf4640aa0f1dab99c9.png

若我們要退出讀ID命令時,只需要reset就行,同樣地,要退出讀數據/寫數據時,也是reset

1.4 reset的命令為0xff,它的時序圖如下所示:

a0667f90a73d4342d6ea411418241281.png

1.5 同樣地,我們再參考讀地址時序圖來看看:

db87e7113b766a4a09f19b031b6c758f.png

其中column Address對應列地址,表示某頁里的2k地址

row Address對應行地址,表示具體的哪一頁

5個地址的周期的圖,如下所示:

78f7fe8ec3fef6548abdf262ea5341a4.png

因為我們的nand flash=256MB=(2k*128M)b

所以row Address=128M=2^17(A27~A11)

所以column Address=2k=2^11( A10~A0)

2.接下來我們來參考自帶的nand flash驅動,位於drivers/mtd/nand/s3c2410.c中

2.1 為什么nand在mtd目錄下?

因為mtd(memory technology device 存儲 技術設備 ) 是用於訪問 memory 設備( ROM 、 flash )的Linux 的子系統。 MTD 的主要目的是為了使新的 memory 設備的驅動更加簡單,為此它在硬件和上層之間提供了一個抽象的接口

2.2首先來看s3c2410.c的入口函數:

static int __init s3c2410_nand_init(void)

{

printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

platform_driver_register(&s3c2412_nand_driver);

platform_driver_register(&s3c2440_nand_driver);return platform_driver_register(&s3c2410_nand_driver);

}

在入口函數中,注冊了一個platform平台設備驅動,也是說當與nandflash設備匹配時,就會調用s3c2440_nand_driver ->probe來初始化

我們進入probe函數中,看看是如何初始化

static int s3c24xx_nand_probe(struct platform_device *pdev,enums3c_cpu_type cpu_type)

{

... ...

err=s3c2410_nand_inithw(info, pdev);//初始化硬件hardware,設置TACLS 、TWRPH0、TWRPH1通信時序等

s3c2410_nand_init_chip(info, nmtd, sets);//初始化芯片

nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); //3.掃描nandflash... ...

s3c2410_nand_add_partition(info, nmtd, sets);//4.調用add_mtd_partitions()來添加mtd分區

... ...

}

通過上面代碼和注釋,得出:驅動主要調用內核的nand_scan()函數,add_mtd_partitions()函數,來完成注冊nandflash

3.上面probe()里的 nand_scan()掃描函數 位於/drivers/mtd/nand/nand_base.c

它會調用nand_scan()->nand_scan_ident()->nand_get_flash_type()來獲取flash存儲器的類型

以及nand_scan()->nand_scan_ident()->nand_scan_tail()來構造mtd設備的成員(實現對nandflash的讀,寫,擦除等)

3.1其中nand_get_flash_type()函數如下所示:

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,struct nand_chip *chip,int busw, int *maf_id)

{struct nand_flash_dev *type =NULL;inti, dev_id, maf_idx;chip->select_chip(mtd, 0); //調用nand_chip結構體的成員select_chip使能flash片選

chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//3.2調用nand_chip結構體的成員cmdfunc,發送讀id命令,最后數據保存在mtd結構體里

*maf_id = chip->read_byte(mtd); //獲取廠家ID,dev_id= chip->read_byte(mtd); //獲取設備ID

/*3.3for循環匹配nand_flash_ids[]數組,找到對應的nandflash信息*/

for (i = 0; nand_flash_ids[i].name != NULL; i++)

{if (dev_id ==nand_flash_ids[i].id)     //匹配設備ID

{type= &nand_flash_ids[i];break;}

} ... .../*3.4 匹配成功,便打印nandflash參數*/printk(KERN_INFO"NAND device: Manufacturer ID:"

"0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,

dev_id, nand_manuf_ids[maf_idx].name, mtd->name);

... ...

}

從上面代碼和注釋得出, nand_chip結構體就是保存與硬件相關的函數(后面會講這個結構體)

3.2 其中NAND_CMD_READID定義為0x90,也就是發送0X90命令,和0x00地址來讀id,最后放到mtd中

3.3 nand_flash_ids[]數組是個全局變量,這里通過匹配設備ID,來確定我們的nand flash是個多大的存儲器

如下圖所示,在芯片手冊中,看到nand flash的設備ID=0XDA

42440184e20d171675bdcf0c15736f88.png

所以就匹配到nand_flash_ids[]里的0XDA:

035d5892da433feece2384b58f647987.png

3.4 然后打印出nand flash參數,我們啟動內核就可以看到:

2153be357b63c0899c24f13273d73e42.png

4. probe()里的s3c2410_nand_add_partition()函數主要是注冊mtd設備的nand flash

最終它調用了s3c2410_nand_add_partition()->add_mtd_partitions() -> add_mtd_device()

其中add_mtd_partitions()函數主要實現多個分區創建,也就是多次調用add_mtd_device()

當只設置nand_flash為一個分區時,就直接調用add_mtd_device()即可.

4.1 add_mtd_partitions()函數原型如下:

int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,intnbparts);  //創建多個分區mtd設備//函 數 成 員 介 紹 ://master:就是要創建的mtd設備//parts:分區信息的數組,它的結構體是mtd_partition,該結構體如下所示:/*struct mtd_partition {

char *name; //分區名,比如bootloader、params、kernel、root

u_int32_t size; //分區大小

u_int32_t offset; //分區所在的偏移值

u_int32_t mask_flags; //掩碼標志

struct nand_ecclayout *ecclayout; //OOB布局

struct mtd_info **mtdp; //MTD的指針,不常用

};*///nbparts:等於分區信息的數組個數,表示要創建分區的個數

比如我們啟動內核時,也能找到內核自帶的nandflash的分區信息:

9a55a8a43650dd15d2bc8d2ff6bcf03e.png

4.2 其中add_mtd_device()函數如下所示:

int add_mtd_device(struct mtd_info *mtd) //創建一個mtd設備{struct list_head *this;

... ...

list_for_each(this, &mtd_notifiers) //4.3找mtd_notifiers鏈表里的list_head結構體 { struct mtd_notifier *not = list_entry(this, structmtd_notifier, list);//通過list_head找到struct mtd_notifier *not

not->add(mtd);         //最后調用mtd_notifier 的add()函數

}

... ...

}

4.3 我們搜索上面函數里的mtd_notifiers鏈表

看看里面的list_head結構體,在哪里放入的,就能找到執行的add()是什么了。

4.4 如下圖,發現list_head在register_mtd_user()里放到mtd_notifiers鏈表中

670505097dc8abbf68dc1384120451bc.png

4.5 繼續搜索register_mtd_user(),被哪個調用

be2cfdfe185db65425323b7039529f20.png

如上圖,找到被drivers/mtd/mtdchar.c、drivers/mtd/mtd_blkdevs.c調用(4.6節和4.7節會分析)

是因為mtd層既提供了字符設備的操作接口(mtdchar.c), 也實現了塊設備的操作接口(mtd_blkdevs.c)

我們在控制台輸入ls -l /dev/mtd*,也能找到塊MTD設備節點和字符MTD設備節點,如下圖所示:

72abb3561fb91d67970ca9027a3da46d.png

上圖中,可以看到共創了4個分區的設備,每個分區都包含了兩個字符設備(mtd%d,mtd%dro)、一個塊設備(mtdblock0).

其中MTD的塊設備的主設備號為31,MTD的字符設備的主設備號為90 (后面會講到在哪被創建)

4.6 我們進入上面搜到的drivers/mtd/mtdchar.c, 找到它的入口函數是init_mtdchar():

static int __init init_mtdchar(void)

{/*創建字符設備mtd,主設備號為90 ,cat /proc/devices 可以看到*/

if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {

printk(KERN_NOTICE"Can't allocate major number %d for Memory Technology Devices.\n",MTD_CHAR_MAJOR);return -EAGAIN;

}

mtd_class= class_create(THIS_MODULE, "mtd"); //創建類

if(IS_ERR(mtd_class)) {

printk(KERN_ERR"Error creating mtd class.\n");

unregister_chrdev(MTD_CHAR_MAJOR,"mtd");returnPTR_ERR(mtd_class);

}

register_mtd_user(&notifier); //調用register_mtd_user(),將notifier添加到mtd_notifiers鏈表中

return 0;

}

之所以上面沒有創建設備節點,是因為此時沒有nand flash驅動.

4.6.1發現上面的notifiers是 mtd_notifier結構體的:

de0d5b7e1b372cf8c204be931f65a00c.png

4.6.2如上圖,我們進入notifie的mtd_notify_add ()函數看看:

static void mtd_notify_add(struct mtd_info*mtd)

{if (!mtd)return;/*其中MTD_CHAR_MAJOR主設備定義為90*/class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),NULL,"mtd%d", mtd->index);//創建mtd%d字符設備節點

class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL,"mtd%dro", mtd->index);//創建mtd%dro字符設備節點

}

該函數創建了兩個字符設備(mtd%d, mtd%dro ),其中ro的字符設備表示為只讀

總結出:

mtdchar.c的入口函數 將notifie添加到mtd_notifiers鏈表中,

然后在add_mtd_device()函數中當查找到mtd字符設備的list_head時,就調用mtd_notifiers->add()來創建兩個字符設備(mtd%d,mtd%dro)

4.7 同樣,我們也進入mtd_blkdevs.c (MTD塊設備)中,找到注冊到mtd_notifiers鏈表的是blktrans_notifier變量:

20a19b06c7fe22a166bb6606b49b0ded.png

4.7.1然后進入blktrans_notifier變量的blktrans_notify_add ()函數:

static void blktrans_notify_add(struct mtd_info *mtd)

{struct list_head *this;if (mtd->type ==MTD_ABSENT)return;

list_for_each(this, &blktrans_majors) //找blktrans_majors鏈表里的list_head結構體

{

struct mtd_blktrans_ops *tr = list_entry(this, structmtd_blktrans_ops, list);

tr->add_mtd(tr, mtd); //執行mtd_blktrans_ops結構體的add_mtd()}

}

從上面的代碼和注釋得出:塊設備的add()是查找blktrans_majors鏈表,然后執行mtd_blktrans_ops結構體的add_mtd()

4.7.2我們搜索blktrans_majors鏈表,看看mtd_blktrans_ops結構體在哪里添加進去的

找到該鏈表在register_mtd_blktrans()函數中:

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)

{

... ...

ret= register_blkdev(tr->major, tr->name); //注冊塊設備tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);//分配一個請求隊列... ...

list_add(&tr->list, &blktrans_majors); //將tr->list 添加到blktrans_majors鏈表}

繼續搜索register_mtd_blktrans(),如下圖,找到被drivers/mtd/Mtdblock.c、Mtdblock_ro.c調用

63381879942f2ab6f215ecc2ac0ab929.png

4.7.3 我們進入drivers/mtd/Mtdblock.c函數中,如下圖所示:

e26b0469ea73da10467c3a09e93db063.png

找到執行mtd_blktrans_ops結構體的add_mtd()函數,就是上圖的mtdblock_add_mtd()函數

在mtdblock_add_mtd()函數中最終會調用add_mtd_blktrans_dev()

4.7.4 add_mtd_blktrans_dev()函數如下所示:

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)

{

... ...

gd= alloc_disk(1 << tr->part_bits); //分配一個gendisk結構體

gd->major = tr->major; //設置gendisk的主設備號

gd->first_minor = (new->devnum) << tr->part_bits; //設置gendisk的起始此設備號

gd->fops = &mtd_blktrans_ops; //設置操作函數... ...

gd->queue = tr->blkcore_priv->rq; //設置請求隊列

add_disk(gd);//向內核注冊gendisk結構體}

總結出:

mtd_blkdevs()塊設備的入口函數 將blktrans_notifier添加到mtd_notifiers鏈表中,並創建塊設備,請求隊列.

然后在add_mtd_device()函數中,當查找到有blktrans_notifier時,就調用blktrans_notifier->add()來分配設置注冊gendisk結構體

5.顯然在內核中,mtd已經幫我們做了整個框架,而我們的nand flash驅動只需要以下幾步即可:

1)設置mtd_info結構體成員

2)設置nand_chip結構體成員

3)設置硬件相關(設置nand控制器時序等)

4)通過nand_scan()來掃描nandflash

5)通過add_mtd_partitions()來添加分區,創建MTD字符/塊設備

5.1 mtd_info結構體介紹:

主要是實現對nandflash的read()、write()、read_oob()、write_oob()、erase()等操作,屬於軟件的部分,它會通過它的成員priv來找到對應的nand_chip結構體,來調用與硬件相關的操作.

5.2 nand_chip結構體介紹:

它是mtd_info結構體的priv成員,主要是對MTD設備中的nandflash硬件相關的描述.

當我們不設置nand_chip的成員時,以下的成員就會被mtd自動設為默認值,代碼位於: nand_scan()->nand_scan_ident()->nand_set_defaults()

structnand_chip {void __iomem *IO_ADDR_R; /*需要讀出數據的nandflash地址*/

void __iomem *IO_ADDR_W; /*需要寫入數據的nandflash地址*/

/*從芯片中讀一個字節*/uint8_t (*read_byte)(struct mtd_info *mtd);/*從芯片中讀一個字*/u16 (*read_word)(struct mtd_info *mtd);/*將緩沖區內容寫入nandflash地址, len:數據長度*/

void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, intlen);/*讀nandflash地址至緩沖區, len:數據長度*/

void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, intlen);/*驗證芯片和寫入緩沖區中的數據*/

int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, intlen);/*選中芯片,當chip==0表示選中,chip==-1時表示取消選中*/

void (*select_chip)(struct mtd_info *mtd, intchip);/*檢測是否有壞塊*/

int (*block_bad)(struct mtd_info *mtd, loff_t ofs, intgetchip);/*標記壞塊*/

int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);/*命令、地址控制函數 , dat :要傳輸的命令/地址*/

/*當ctrl的bit[1]==1: 表示要發送的dat是命令

bit[2]==1: 表示要發送的dat是地址bit[0]==1:表示使能nand , ==0:表示禁止nand具體可以參考內核的nand_command_lp()函數,它會調用這個cmd_crtl函數實現功能*/

void(*cmd_ctrl)(structmtd_info *mtd,intdat,unsignedintctrl);

/*設備是否就緒,當該函數返回的RnB引腳的數據等於1,表示nandflash已就緒*/

int (*dev_ready)(struct mtd_info *mtd);/*實現命令發送,最終調用nand_chip -> cmd_ctrl來實現*/

void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, intpage_addr);/*等待函數,通過nand_chip ->dev_ready來等待nandflash是否就緒*/

int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);/*擦除命令的處理*/

void (*erase_cmd)(struct mtd_info *mtd, intpage);/*掃描壞塊*/

int (*scan_bbt)(struct mtd_info *mtd);int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, intpage);/*寫一頁*/

int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, intraw);int chip_delay; /*由板決定的延遲時間*/ /*與具體的NAND芯片相關的一些選項,默認為8位寬nand,

比如設置為NAND_BUSWIDTH_16,表示nand的總線寬為16*/unsignedintoptions;/*用位表示的NAND芯片的page大小,如某片NAND芯片

* 的一個page有512個字節,那么page_shift就是9*/

intpage_shift;/*用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可

* 擦除16K字節(通常就是一個block的大小),那么phys_erase_shift就是14*/

intphys_erase_shift;/*用位表示的bad block table的大小,通常一個bbt占用一個block,

* 所以bbt_erase_shift通常與phys_erase_shift相等*/

intbbt_erase_shift;/*用位表示的NAND芯片的容量*/

intchip_shift;/*NADN FLASH芯片的數量*/

intnumchips;/*NAND芯片的大小*/uint64_t chipsize;intpagemask;intpagebuf;intsubpagesize;

uint8_t cellinfo;intbadblockpos;

nand_state_t state;

uint8_t*oob_poi;struct nand_hw_control *controller;struct nand_ecclayout *ecclayout; /*ECC布局*/

/*ECC校驗結構體,若不設置, ecc.mode默認為NAND_ECC_NONE(無ECC校驗)*/

/*可以為硬件ECC和軟件ECC校驗,比如:設置ecc.mode=NAND_ECC_SOFT(軟件ECC校驗)*/

structnand_ecc_ctrl ecc;struct nand_buffers *buffers;structnand_hw_control hwcontrol;structmtd_oob_ops ops;

uint8_t*bbt;struct nand_bbt_descr *bbt_td;struct nand_bbt_descr *bbt_md;struct nand_bbt_descr *badblock_pattern;void *priv;

};

5.3本節驅動我們需要設置nand_chip的成員如下:

IO_ADDR_R(提供讀數據)

IO_ADDR_W(提供寫數據)

select_chip(提供片選使能/禁止)

cmd_ctrl(提供寫命令/地址)

dev_ready(提供nandflash的RnB腳,來判斷是否就緒)

ecc.mode(設置ECC為硬件校驗/軟件校驗)

其它成員會通過nand_scan()->nand_scan_ident()->nand_set_defaults()來設置為默認值.

6.接下來我們就來寫nand flash塊設備驅動

參考:  drivers/mtd/nand/at91_nand.c

drivers/mtd/nand/s3c2410.c

6.1本節需要用到的函數如下所示:

int nand_scan(struct mtd_info *mtd, int maxchips); //掃描nandflash,掃描成功返回0

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,intnbparts);//將nandflash分成nbparts個分區,會創建多個MTD字符/塊設備,成功返回0//master:就是要創建的mtd設備//parts:分區信息的數組,它的結構體是mtd_partition//nbparts:要創建分區的個數,比如上圖,那么就等於4

int del_mtd_partitions(struct mtd_info *master);//卸載分區,並會卸載MTD字符/塊設備

6.2 在init入口函數中

1)通過kzalloc()來分配結構體: mtd_info和nand_chip

2)通過ioremap()來分配獲取nand flash 寄存器虛擬地址

3)設置mtd_info結構體成員

4)設置nand_chip結構體成員

5)設置硬件相關

->5.1) 通過clk_get()和clk_enable()來使能nand flash 時鍾

->5.2)設置時序

->5.3)關閉片選,並開啟nand flash 控制器

6)通過nand_scan()來掃描nandflash

7)通過add_mtd_partitions()來添加分區,創建MTD字符/塊設備

6.3 在exit入口函數中

1)卸載分區,卸載字符/塊設備

2)釋放mtd

3)釋放nand flash寄存器

4)釋放nand_chip

驅動代碼如下:

#include #include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include

structmynand_regs {

unsignedlong nfconf ; //0x4E000000

unsigned longnfcont ;

unsignedlongnfcmd ;

unsignedlongnfaddr ;

unsignedlongnfdata ;

unsignedlongnfeccd0 ;

unsignedlongnfeccd1 ;

unsignedlongnfeccd ;

unsignedlongnfstat ;

unsignedlongnfestat0;

unsignedlongnfestat1;

unsignedlongnfmecc0 ;

unsignedlongnfmecc1 ;

unsignedlongnfsecc ;

unsignedlongnfsblk ;

unsignedlongnfeblk ;

};static struct mynand_regs *my_regs; //nand寄存器

static struct mtd_info *my_mtd;static struct nand_chip *mynand_chip;static struct mtd_partition mynand_part[] ={

[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,

}

};/*nand flash :CE*/

static void mynand_select_chip(struct mtd_info *mtd, intchipnr)

{if(chipnr==-1) //CE Disable

{

my_regs->nfcont|=(0x01<<1); //bit1置1

}else //CE Enable

{

my_regs->nfcont&=~(0x01<<1); //bit1置0}

}/*命令/地址控制函數*/

static void mynand__cmd_ctrl(struct mtd_info *mtd, int dat, unsigned intctrl)

{if (ctrl & NAND_CLE) //當前為command狀態 ,

my_regs->nfcmd=dat;else //當前為地址狀態 , if (ctrl & NAND_ALE)

my_regs->nfaddr=dat;

}/*nand flash 設備就緒函數(獲取RnB引腳狀態*/

static int mynand__device_ready(struct mtd_info *mtd)

{return (my_regs->nfstat&0x01); //獲取RnB狀態,0:busy 1:ready

}/*init入口函數*/

static int mynand_init(void)

{struct clk *nand_clk;intres;/*1.分配結構體: mtd_info和nand_chip*/my_mtd=kzalloc(sizeof(structmtd_info), GFP_KERNEL);

mynand_chip=kzalloc(sizeof(structnand_chip), GFP_KERNEL);/*2.獲取nand flash 寄存器虛擬地址*/my_regs=ioremap(0x4E000000, sizeof(structmynand_regs));/*3.設置mtd_info*/my_mtd->owner=THIS_MODULE;

my_mtd->priv=mynand_chip; //私有數據

/*4.設置nand_chip*/mynand_chip->IO_ADDR_R=&my_regs->nfdata; //設置讀data

mynand_chip->IO_ADDR_W=&my_regs->nfdata; //設置寫data

mynand_chip->select_chip=mynand_select_chip; //設置CE

mynand_chip->cmd_ctrl = mynand__cmd_ctrl; //設置寫command/address

mynand_chip->dev_ready = mynand__device_ready; //設置RnBmynand_chip->ecc.mode = NAND_ECC_SOFT;//設置軟件ECC

/*5.設置硬件相關*/

/*5.1使能nand flash 時鍾*/nand_clk=clk_get(NULL,"nand");

clk_enable(nand_clk);/*5.2設置時序*/

#define TACLS 0 //0nS

#define TWRPH0 1 //15nS

#define TWRPH1 0 //5nSmy_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);/*5.3 bit1:關閉片選, bit0:開啟nand flash 控制器*/my_regs->nfcont=(1<<1)|(1<<0);/*6.掃描NAND*/

if (nand_scan(my_mtd, 1)) { //1:表示只掃描一個nand flash 設備

res = -ENXIO;goto out;

}/*7.添加分區,創建字符/塊設備*/res= add_mtd_partitions(my_mtd, mynand_part, 4);

if(res)return 0;out:

del_mtd_partitions(my_mtd);//卸載分區,卸載字符/塊設備

kfree(my_mtd); //釋放mtd

iounmap(my_regs); //釋放nand flash寄存器

kfree(mynand_chip); //釋放nand_chip

return 0;

}/*exit出口函數*/

static void mynand_exit(void)

{

del_mtd_partitions(my_mtd);//卸載分區,卸載字符/塊設備

kfree(my_mtd); //釋放mtd

iounmap(my_regs); //釋放nand flash寄存器

kfree(mynand_chip); //釋放nand_chip}

module_init(mynand_init);

module_exit(mynand_exit);

MODULE_LICENSE("GPL");

7.編譯啟動內核

7.1 重新設置編譯內核(去掉默認的nand flash驅動)

make menuconfig ,進入menu菜單重新設置內核參數:

進入-> Device Drivers-> Memory Technology Device (MTD) support-> NAND Device Support

< > NAND Flash support for S3C2410/S3C2440 SoC //去掉默認的nandflash驅動

然后make uImage 編譯內核

將新的nandflash驅動模塊放入nfs文件系統目錄中

7.2然后燒寫內核,啟動內核

如下圖,發現內核啟動時,卡住了,是因為我們使用的文件系統是存在nand flash上

5616fd4e49d8ecae2beec1c91d9b0515.png

所以設置為nfs文件系統才行.

8.掛載nand flash 驅動

8.1如下圖,可以看到共添了4個分區: bootloader、params、kernel、root、

剛好對應了程序中的mynand_part數組里面的分區信息

cf9444602170b790e8f62db7b44b402c.png

8.2 如下圖,可以看到/dev下共創建了4個MTD塊設備(mtdblock%d),4個MTD字符設備(mtd%d、mtd%dro)

ad99a822f2ae91903ab8c3bf91d9d76c.png

8.3 如下圖,使用cat /proc/partitions ,可以看到分區信息

69ab818164a7cbb8b6d8fcd43d08417a.png

其中blocks表示分區的容量,每個blocks是1KB

9. 使用mount來掛載mtd塊設備

mount /dev/mtdblock3 /mnt/ //掛載, mount會自動獲取該設備的文件類型

進入mnt,可以看到里面就是我們之前存在nand flash上的文件系統

0e1628ba68d47bd0b8b83c4f05ea33d0.png

10. 使用mtd-util 工具擦除mtdblock3(使用nand之前最好擦除一次)

因為flash的特性如下:

寫入,只能把數據(bit)從1改為0;擦除,只能把所有數據(bit)從0改為1。

所以,要想寫入數據之前必須先擦除。因為flash只能寫0,寫1時其實是保持原來的狀態。

10.1 使用mtd-util工具步驟如下:

tar -xjf mtd-utils-05.07.23.tar.bz2     //解壓mtd-util工具cd mtd-utils-05.07.23/util /   //進入util目錄vi Makefile //修改交叉編譯改為: CROSS=arm-linux-make//編譯,生成flashcp 、flash_erase、flash_eraseall等命令cp flash_erase flash_eraseall/nfs文件系統目錄 //復制命令

10.2mtd-util工具的常用命令介紹

命令:flashcp

作用:copy數據到 flash 中

實例:

./flashcp fs.yaffs2 /dev/mtd0 //將文件系統yaffs2復制到mtd0中

命令:flash_erase

常用參數:

-j  使用jffs2來格式化分區

-q  不打印過程信息

作用:擦除某個分區的指定范圍 (其中指定位置必須以0x20000(128K)為倍數)

實例:

./flash_erase /dev/mtd0 0x20000 5 //擦除mtd0從0x20000開始的5塊數據 ,128K/塊

命令:flash_eraseall

常用參數:

-j  使用jffs2來格式化分區(對於norflash才加該參數)

-q  不打印過程信息

作用:擦除整個分區的內容

實例:

./flash_eraseall -q /dev/mtd0 //擦除mtd0,並不打印過程信息

10.3為什么這里的實例都是對mtd字符設備進行操作,而不是mtdblock塊設備?

因為每個分區的字符設備,其實就是對應着每個分區塊設備。即/dev/mtd3對應/dev/mtdblock3

flash_eraseall, flash_erase那些命令是以ioctl等基礎而實現, 而塊設備不支持ioctl, 只有字符設備支持

10.4 使用flash_eraseall來擦除分區3

步驟如下:

umount /mnt //擦除之前需要使用umount mnt來取消之前的掛載./flash_eraseall /dev/mtd3 //擦除mtd3mount-t yaffs /dev/mtdblock3 /mnt/ //使用yaffs類型來掛載mtdblock3塊設備//因為當前的mtdblock3為空,mount命令無法自動獲取mtdblock3的文件類型

如下圖,可以看到分區3已經為空了

7dc04279df208b03cd740c082fd99f25.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值