天天看點

s3c2410移植nand支援到uboot, 使用nand.c ,linux mtd 架構 【2】

轉載位址:http://blog.csdn.net/ying_seven/article/details/6888923

u-boot-1.1.6與1.1.4相比,兩者有較大的不同,1.1.6 更像是複制了 kernel 的方法來實作。

下面對nand flash的初始化代碼nand_init()進行分析:

1.如果定義(CONFIG_COMMANDS & CFG_CMD_NAND)沒定義(CFG_NAND_LEGACY) 則start_armboot()調用driver/nand/nand.c中的nand_init(),否則如果定義(CONFIG_COMMANDS & CFG_CMD_NAND)并且有定義了CFG_NAND_LEGACY,則調用自己定義的nand_init()。現在使用 driver/nand/nand.c中的nand_init()。

2.nand_init()調用本檔案中的nand_init_chip()對nand進行初始化。

3.nand_init_chip()首先調用board_nand_init()。

4.board_nand_init()是需要自己添加的函數,這個函數的主要功能是對struct nand_chip結構體的函數指針指派,讓它們指向自己為nand驅動編寫的一些函數,對未指派的指針,uboot會在後面為其賦上通用nand驅動函數指針。此函數可放到自己闆子目錄的檔案下。

5.nand_init_chip()接着調用nand_scan().

6.nand_scan()定義在drivers/nand/nand_base.c檔案中。它首先對struct nand_chip結構體中在board_nand_init()函數中未指派的指針賦上通用nand驅動函數指針。

7.nand_scan()->select_chip = nand_select_chip;

此函數用于打開或關閉nand晶片,0為打開,1為關閉。在這個函數中會調用nand_chip結構體中的 hwcontrol函數指針。 hwcontrol在board_nand_init()函數中被指派。主要作用是向 nand flash發送一些nand flash開啟與關閉指令。

8.nand_scan()剩餘部分初始化nand_chip和mtd_info結構體。

9.nand_scan()最後在傳回時調用drivers/nand/nand_bbt.c檔案中的nand_default_bbt()。

nand_default_bbt()選擇一個壞塊描述表,傳回時調用本檔案中的nand_scan_bbt()(尋找建立一個壞塊描述表)

10.最後傳回到nand_init(),這樣nand驅動的初始化完成了。

通過上述步驟我們可以知道,移植nand主要按如下步驟:

1、在board/xxx下建立c檔案

2、在此檔案上添加函數board_nand_init(),實作nand_chip的初始化功能

3、添加初始化的函數

4、在include/configs/xxx.h中定義相關宏,比如#define CFG_MAX_CHIPS

完成上述移植後,實際上啟動後的uboot中的nand指令是通過include/nand.h實作的,例如nand erase調用了:

static inline int nand_erase(nand_info_t *info, ulong off, ulong size)

看看人家怎麼做的:

//----------------------------------------------------------------------------------------------------

為了讓uboot支援自己QT闆子的nand flash而進行修改的部分 

1.前面的移植請參考我寫的一篇《U-Boot的編譯與移植到QT-S3C44B0X開發闆上》,現在在board/51EDA/QT/目錄下建立nand.c檔案。

2.在nand.c中添加自己的board_nand_init()函數。設定nand_chip結構中的hwcontrol和dev_ready指針指向自己的函數QT_hwcontrol和QT_device_ready。并建立自己的QT_hwcontrol和QT_device_ready函數。

3.由于自己闆子的nand flash的指令發送方式與uboot提供的通用nand flash指令發送方式不同,是以在nand.c檔案中建立自己的指令發送函數QT_nand_command(),并在 board_nand_init()函數中将nand_chip結構中的cmdfunc指針指向QT_nand_command()函數,使其使用自己定義的發送指令函數。

4.在include/configs/QT.h中定義CFG_NAND_BASE用于指定自己闆子nand flash的I/O位址。

5.在CONFIG_COMMANDS中打開CFG_CMD_NAND選項。

6.在include/configs/QT.h中定義NAND_MAX_CHIPS指定自己闆子的nand flash晶片數。

7.在include/configs/QT.h中定義CFG_MAX_NAND_DEVICE指定想要支援的nand flash裝置數。

//這是比較深入的分析:

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

U-BOOT Nand指令支援

u-boot1.1.6 nand_legacy驅動提供了u-boot對nand相關指令的一個輕量級的實作,但好象可擴充性不足。本文主要分析u-boot 1.16/drivers/nand文檔夾下的源程式。

一.關鍵資料結構

1.struct mtd_info

該結構在include\linux\mtd\Mtd.h中定義,字段比較多,有很多還是函數指針,他是MTD裝置操作的通用接口,這個結構中有一個比較重要的成員 void *priv,priv被聲明成void指針,在下文的分析中會知道priv實際上指向了nand_chip結構。

2.struct nand_chip

   該結構在include\linux\mtd\Nand.h中定義,從名字上看就知道u-boot用他來描述Nand Flash晶片的結構,比如他定義了頁位址的偏移,頁位址的位掩碼等。struct nand_chip不用我們手動的初始化,而是由另外一個結構,struct nand_flash_dev在程式中動态的初始化。

3.struct nand_flash_dev

    該結構的定義有兩處地方分别是

①include/linux/mtd/nand_legacy.h 由nand_legacy子產品使用

②include/linux/mtd/nand.h       由u-boot通用nand架構使用

特别是在移植的時候要小心把兩者混淆。我們先來看看改結構的定義

struct nand_flash_dev {

       char *name;            

       int id;

       unsigned long pagesize;

       unsigned long chipsize;

       unsigned long erasesize;

       unsigned long options;

};

name : Nand Flash名稱

id : u-boot内部id編号???

chipsize : 以MB為機關的晶片大小,比如64(M)

erasesize : 擦除塊的大小,比如0x4000(16K)

options : 一些選項,比較重要的是Flash的資料位寬,假如您的Nand Flash是16位寬的,則必須包含NAND_BUSWIDTH_16選項。我們必須根據所使用的Nand Flash來填充裡面的字段。

4.關鍵資料結構在程式中的使用

struct nand_info_t nand_info[ CFG_MAX_NAND_DEVICE ];

在 drivers\nand\nand.c中定義。CFG_MAX_NAND_DEVICE是闆子的Nand Flash晶片的數量必須在闆子的配置文檔中定義(比如 include\configs\smdk2410.h)。

static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];

在drivers\nand\nand.c中定義。CFG_MAX_NAND_DEVICE的定義同上。

struct nand_flash_dev nand_flash_ids[] = { … };

在drivers\nand\nand_ids.c中定義。這裡要注意一點,在include\linux\mtd\nand_ids.h裡面也 nand_flash_ids[]的定義,那是由nand legacy驅動子產品使用的。兩者不能混淆!!!。在nand_flash_ids的定義中我找到了适合我的Nand Flash的結構描述:

{"NAND 64MiB 3,3V 8-bit",        0x76, 512, 64, 0x4000, 0}

裝置ID為0x76,頁大小為512Byte,總的容量為64M,擦除塊為0x4000(16K),資料位寬8Bit。假如您的Nand Flash沒有合适的描述,需要自己在該數組中添加相應的定義。

二.Nand Flash初始化

1.nand_init( drivers\nand\nand.c )

nand_init函數在lib_xxx/Board.c的start_armboot中調用。是u-boot Nand的主函數。nand_init的主要功能是對CFG_MAX_NAND_DEVICE個Nand裝置進行初始化(調用 nand_init_chip),累加Nand Flash的總大小。在nand_init結束時,能夠配置是否執行board_nand_select_device,選擇Nand晶片。

2.nand_init_chip( drivers\nand\nand.c )

static void nand_init_chip( struct mtd_info *mtd, struct nand_chip *nand, ulong base_addr )調用各個研發闆提供的 board_nand_init 函數( board\\.c )讓研發闆獲得初始化Nand Flash晶片的機會。調用 nand_scan。

3.nand_scan ( drivers\nand\nand_base.c )

int nand_scan( struct mtd_info *mtd, int maxchips )

這是u-boot初始化nand裝置的核心函數。他主要完成以下工作

1)初始化nand_chip的函數指針,這些函數一般在 board\\.c中定義。

struct nand_chip *this = mtd->priv

....

if( this-> cmdfunc == NULL )

       this->cmdfunc = nand_command;

上面是初始化nand_chip中cmdfunc指針的代碼,假如在board_init_nand中研發闆沒有提供自己的nand_command函數,u-boot 将使用預設的nand_command函數(我覺得u-boot提供的這些預設的函數都不适合特定的硬體,是以很多都要自己重新寫)。

2)使用上面注冊的函數指針,讀取Nand Flash的裝置,并且在上文提到的nand_flash_ids[]中找是否有比對項,若找到比對的項,則初始化 nand_chip 和 mtd_info,他們的初始化代碼老長的一段,一般沒什麼問題。

三. Nand Flash 操作

1. Read 

以common/env_nand.c裡面讀取Nand Flash中的環境變量為例

common/env_nand.c

ret = nand_read( &nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr );

nand_info[]就是我們在1.4講到的nand_info_t(mtd_info的别名)數組。此處的nand_read是個inline函數,下面是他的實作:

include/nand.h

static inline int nand_read(nand_info_t *info, ulong ofs, ulong *len, u_char *buf)

{

       return info->read(info, ofs, *len, (size_t*)len, buf);

}

能夠看出nand_read實際上調用的是nand_info的read方法。nand_info的read方法是在2.3中講到的nand_scan中初始化

drivers/nand/nand_base.c

int nand_scan(struct mtd_info *mtd, int machips)

{

       …

       mtd->read = nand_read;

       …

}

此處又一個nand_read!!!

drivers/nand/nand_base.c

static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)

{

       return nand_read_ecc( mtd, from, len, retlen, buf, NULL, NULL );

}

又一層包裝!!!

drivers/nand/nand_base.c

static int nand_read_ecc(…)

{

       …

}

終于到達最後一層了,nand_read_ecc通過調用nand_chip裡面提供的函數對nand flash完成讀的操作。具體能夠看看代碼,老長的一段。