溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

u-boot內(nèi)nand初始化過(guò)程是怎樣的

發(fā)布時(shí)間:2022-01-12 16:30:12 來(lái)源:億速云 閱讀:157 作者:iii 欄目:互聯(lián)網(wǎng)科技

這篇文章主要講解了“u-boot內(nèi)nand初始化過(guò)程是怎樣的”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“u-boot內(nèi)nand初始化過(guò)程是怎樣的”吧!

針對(duì)soc片上nand控制器而言,硬件初始化應(yīng)該包含一下幾個(gè)方面:
1、nand模塊使用的時(shí)鐘設(shè)置
2、既然接了一顆芯片,那么相關(guān)幾個(gè)引腳需要初始化成支持nand的功能
3、nand控制寄存器的配置,例如中斷、DMA模式等等
這是簡(jiǎn)單的一些想法,下面看看u-boot內(nèi)的代碼流程
一、流程分析
假設(shè)定義了nand驅(qū)動(dòng),上電時(shí)就會(huì)調(diào)用下面一段代碼
...........
#if defined(CONFIG_CMD_NAND)
puts("NAND:  ");
nand_init(); /* go init the NAND */
#endif
............
這段代碼在文件\u-boot-sunxi-sunxi\arch\arm\lib\board.c內(nèi)
可見(jiàn)想要使用nand驅(qū)動(dòng),得定義宏 CONFIG_CMD_NAND
調(diào)用了一個(gè)函數(shù) nand_init(),在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand.c內(nèi):
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
}
這個(gè)函數(shù)用了一個(gè)宏CONFIG_SYS_MAX_NAND_DEVICE,很明顯,表示板子上有幾顆nand芯片,
cubieboard當(dāng)然是一顆,這個(gè)宏需要自己定義,值為1
一般來(lái)說(shuō),這個(gè)函數(shù)都將調(diào)用nand_init_chip(),在來(lái)看看這個(gè)函數(shù),也在這個(gè)文件內(nèi):
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)   // 防止錯(cuò)誤的數(shù)據(jù)
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))  // 接口函數(shù)初始化
return;

nand_register(i);   //設(shè)備注冊(cè)
}
這個(gè)函數(shù)調(diào)用了三個(gè)函數(shù),完成初始化工作
board_nand_init()函數(shù)就是跟板子相關(guān)的初始化,就是自己需要實(shí)現(xiàn)的代碼。
nand_scan()函數(shù),我的理解就是接口的初始化,就是將MTD模塊使用的部分函數(shù)初始化到用戶自己實(shí)現(xiàn)的代碼上
nand_register()注冊(cè)驅(qū)動(dòng),這個(gè)代碼不需要用戶實(shí)現(xiàn)
這里有一個(gè)代碼細(xì)節(jié)
mtd->priv = nand; 
mtd模塊的私有數(shù)據(jù)就是nand這個(gè)結(jié)構(gòu)體,初始化時(shí)實(shí)現(xiàn)nand結(jié)構(gòu)體的初始化就能把操作傳遞到mtd層去了
這個(gè)后面在分析一下。
再來(lái)看看這三個(gè)函數(shù),board_nand_init()不存在,就是需要用戶自己實(shí)現(xiàn)
例如,在\u-boot-sunxi-sunxi\drivers\mtd\nand\創(chuàng)建自己的驅(qū)動(dòng)代碼文件sunxi_nand.c
里面就需要定義這個(gè)函數(shù)
int  board_nand_init(struct nand_chip *nand)
這個(gè)函數(shù)有兩部分:
1、就是開始提到的三個(gè)方面
     nand模塊使用的時(shí)鐘設(shè)置
     相關(guān)幾個(gè)引腳需要初始化成支持nand的功能
    nand控制寄存器的配置,例如中斷、DMA模式等等
2、相關(guān)結(jié)構(gòu)體數(shù)據(jù)的初始化
sunxi_nand.c內(nèi)還需要實(shí)現(xiàn)其他部分代碼,這個(gè)要根據(jù)實(shí)際的CPU來(lái)確定
到這里就需要說(shuō)說(shuō)編程模式了,nand驅(qū)動(dòng)的編寫也是采用面向?qū)ο蟮姆绞剑鴅oard_nand_init的作用就是將一個(gè)nand類實(shí)例化
這里操作了一個(gè)結(jié)構(gòu)體
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;
};
這個(gè)結(jié)構(gòu)體就是一個(gè)“類”,所有的nand都可以這樣實(shí)現(xiàn),用戶只要實(shí)現(xiàn)其中的一些“方法”就可以了
這些“方法“都已經(jīng)有了默認(rèn)的實(shí)現(xiàn),在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c內(nèi)都已經(jīng)寫好了
例如比較老的芯片s3c2440,s3c6410都可以直接使用這些庫(kù)函數(shù),不需要在另外寫,很遺憾,cubieboard的A10不在此列。
針對(duì)A10,在board_nand_init函數(shù)內(nèi),如下初始化是必要的:
......
/* initialize nand_chip data structure */
nand->IO_ADDR_R = (void *)&nand_reg->io_data;  // 讀寫寄存器,這個(gè)初始化還需要斟酌,沒(méi)有資料就是苦B啊
nand->IO_ADDR_W = (void *)&nand_reg->io_data;
nand->select_chip = sunxi_select_chip;     //  片選函數(shù),任何一款CPU都要實(shí)現(xiàn)這個(gè)功能
/* hwcontrol always must be implemented */
nand->dev_ready = sunxi_dev_ready;        // 讀取芯片就緒狀態(tài)
nand->cmdfunc    = sunxi_nand_command; //命令操作函數(shù),有了這個(gè)就不需要 nand->cmd_ctrl 啦

#ifdef CONFIG_SUNXI_NAND_HWECC
nand->ecc.hwctl      = sunxi_nand_enable_hwecc;  //這些差錯(cuò)控制都沒(méi)實(shí)現(xiàn),先留下接口,等進(jìn)一步改進(jìn),使用默認(rèn)的實(shí)現(xiàn)
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++;  // 這個(gè)相當(dāng)于是C++的私有數(shù)據(jù)了,我存了芯片的編號(hào),只有一片nand,就是0
....
這里nand指針就是前面說(shuō)的細(xì)節(jié)
mtd->priv = nand; 
這是后相當(dāng)部分mtd內(nèi)容也已經(jīng)初始化了。
再來(lái)看看nand_scan()函數(shù),\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c內(nèi)
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;
}
這個(gè)函數(shù)的調(diào)用關(guān)系分析下去,可以看出,根據(jù)前面的初始化,讀取nand的信息,就是id,廠商,名字,頁(yè)大小,容量等等;
初始化用戶未實(shí)現(xiàn)的部分,這些代碼使用如下形式,舉例如下:
/* check for proper chip_delay setup, set 20us if not */
if (!chip->chip_delay)  
chip->chip_delay = 20;    //如果沒(méi)有定義芯片延時(shí),則默認(rèn)20ns,這個(gè)值要看具體的芯片手冊(cè)了
/* check, if a user supplied command function given */
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;    // 如果沒(méi)有實(shí)現(xiàn)chip->cmdfunc,那么使用默認(rèn)的命令操作函數(shù)
                                                                                  // 而默認(rèn)的不合適A10,board_nand_init函數(shù)定義了nand->cmdfunc    = sunxi_nand_command
                                                                                  // 所以這個(gè)不會(huì)被再次初始化
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL)                        
chip->waitfunc = nand_wait;               //這個(gè)沒(méi)有被定義,將使用默認(rèn)函數(shù)nand_wait
諸如此類的一大堆調(diào)用,當(dāng)然,用戶也可以完全將所有函數(shù)都自己實(shí)現(xiàn)一遍,但如果有合適的就沒(méi)有必要啦,站在別人肩膀上,才能看得更遠(yuǎn)

感謝各位的閱讀,以上就是“u-boot內(nèi)nand初始化過(guò)程是怎樣的”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)u-boot內(nèi)nand初始化過(guò)程是怎樣的這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI