原文:http://blog.csdn.net/andy_wsj/article/details/9335755
分析一下u-boot內nand初始化過程,都是我所看所想的一些東西
之前從未寫過nand相關代碼,也沒了解過nand的工做方式,理解可能有所誤差,各位請不吝指正。
針對soc片上nand控制器而言,硬件初始化應該包含一下幾個方面:
一、nand模塊使用的時鐘設置
二、既然接了一顆芯片,那麼相關幾個引腳須要初始化成支持nand的功能
三、nand控制寄存器的配置,例如中斷、DMA模式等等
這是簡單的一些想法,下面看看u-boot內的代碼流程
1、流程分析
假設定義了nand驅動,上電時就會調用下面一段代碼
...........
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif
............
這段代碼在文件\u-boot-sunxi-sunxi\arch\arm\lib\board.c內
可見想要使用nand驅動,得定義宏 CONFIG_CMD_NAND
調用了一個函數 nand_init(),在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand.c內:
void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i);
#endif
printf("%lu MiB\n", total_nand_size / 1024);
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*
* Select the chip in the board/cpu specific driver
*/
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
這個函數用了一個宏CONFIG_SYS_MAX_NAND_DEVICE,很明顯,表示板子上有幾顆nand芯片,
cubieboard固然是一顆,這個宏須要本身定義,值爲1
通常來講,這個函數都將調用nand_init_chip(),在來看看這個函數,也在這個文件內:
static void nand_init_chip(int i)
{
struct mtd_info *mtd = &nand_info;
struct nand_chip *nand = &nand_chip;
ulong base_addr = base_address;
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1) // 防止錯誤的數據
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand)) // 硬件初始化
return;
if (nand_scan(mtd, maxchips)) // 接口函數初始化
return;
nand_register(i); //設備註冊
}
這個函數調用了三個函數,完成初始化工做
board_nand_init()函數就是跟板子相關的初始化,就是本身須要實現的代碼。
nand_scan()函數,個人理解就是接口的初始化,就是將MTD模塊使用的部分函數初始化到用戶本身實現的代碼上
nand_register()註冊驅動,這個代碼不須要用戶實現
這裏有一個代碼細節
mtd->priv = nand;
mtd模塊的私有數據就是nand這個結構體,初始化時實現nand結構體的初始化就能把操做傳遞到mtd層去了
這個後面在分析一下。
再來看看這三個函數,board_nand_init()不存在,就是須要用戶本身實現
例如,在\u-boot-sunxi-sunxi\drivers\mtd\nand\建立本身的驅動代碼文件sunxi_nand.c
裏面就須要定義這個函數
int board_nand_init(struct nand_chip *nand)
這個函數有兩部分:
一、就是開始提到的三個方面
nand模塊使用的時鐘設置
相關幾個引腳須要初始化成支持nand的功能
nand控制寄存器的配置,例如中斷、DMA模式等等
二、相關結構體數據的初始化
sunxi_nand.c內還須要實現其餘部分代碼,這個要根據實際的CPU來肯定
到這裏就須要說說編程模式了,nand驅動的編寫也是採用面向對象的方式,而board_nand_init的做用就是將一個nand類實例化
這裏操做了一個結構體
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
u8 *id_data);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
int page_addr);
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);
int chip_delay;
unsigned int options;
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
uint64_t chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
int badblockbits;
int onfi_version;
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
struct nand_onfi_params onfi_params;
#endif
int state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
struct mtd_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;
};
這個結構體就是一個「類」,全部的nand均可以這樣實現,用戶只要實現其中的一些「方法」就能夠了
這些「方法「都已經有了默認的實現,在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c內都已經寫好了
例如比較老的芯片s3c2440,s3c6410均可以直接使用這些庫函數,不須要在另外寫,很遺憾,cubieboard的A10不在此列。
針對A10,在board_nand_init函數內,以下初始化是必要的:
......
/* initialize nand_chip data structure */
nand->IO_ADDR_R = (void *)&nand_reg->io_data; // 讀寫寄存器,這個初始化還須要斟酌,沒有資料就是苦B啊
nand->IO_ADDR_W = (void *)&nand_reg->io_data;
nand->select_chip = sunxi_select_chip; // 片選函數,任何一款CPU都要實現這個功能
/* hwcontrol always must be implemented */
nand->dev_ready = sunxi_dev_ready; // 讀取芯片就緒狀態
nand->cmdfunc = sunxi_nand_command; //命令操做函數,有了這個就不須要 nand->cmd_ctrl 啦
#ifdef CONFIG_SUNXI_NAND_HWECC
nand->ecc.hwctl = sunxi_nand_enable_hwecc; //這些差錯控制都沒實現,先留下接口,等進一步改進,使用默認的實現
nand->ecc.calculate = sunxi_nand_calculate_ecc;
nand->ecc.correct = sunxi_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_SUNXI_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0; /* 8bit */ // flash的操做位寬,看看芯片資料8位的
#endif
nand->priv = nand_cs + chip_n++; // 這個至關因而C++的私有數據了,我存了芯片的編號,只有一片nand,就是0
....
這裏nand指針就是前面說的細節
mtd->priv = nand;
這是後至關部分mtd內容也已經初始化了。
再來看看nand_scan()函數,\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c內
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
ret = nand_scan_ident(mtd, maxchips, NULL);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
這個函數的調用關係分析下去,能夠看出,根據前面的初始化,讀取nand的信息,就是id,廠商,名字,頁大小,容量等等;
初始化用戶未實現的部分,這些代碼使用以下形式,舉例以下:
/* check for proper chip_delay setup, set 20us if not */
if (!chip->chip_delay)
chip->chip_delay = 20; //若是沒有定義芯片延時,則默認20ns,這個值要看具體的芯片手冊了
/* check, if a user supplied command function given */
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command; // 若是沒有實現chip->cmdfunc,那麼使用默認的命令操做函數
// 而默認的不合適A10,board_nand_init函數定義了nand->cmdfunc = sunxi_nand_command
// 因此這個不會被再次初始化
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait; //這個沒有被定義,將使用默認函數nand_wait
諸如此類的一大堆調用,固然,用戶也能夠徹底將全部函數都本身實現一遍,但若是有合適的就沒有必要啦,站在別人肩膀上,才能看得更遠
2、總結
經過分析,假設本身須要實現初始化工做,從代碼角度來看,須要實現一下幾項:
一、定義相關的宏,例如
CONFIG_CMD_NAND
CONFIG_SYS_MAX_NAND_DEVICE
這不是所有宏,請參考其餘芯片定義,如s3c2440
二、實現本身的int board_nand_init(struct nand_chip *nand)函數
這個函數內初始化cpu的寄存器,IO腳功能,設置模塊時鐘
設置用戶本身的硬件操做函數
片選操做、命令操做或硬件控制操做、芯片就緒等等,具體狀況根據不一樣CPU而不一樣
有的芯片還要本身實現讀寫操做。
三、沒有詳細CPU用戶手冊狀況下,寫驅動就是一苦B事php
原文做者:andy
原文連接:http://forum.cubietech.com/forum.php?mod=viewthread&tid=745&extra=page%3D1編程