uboot之nand flash相關(2)

下面分析nand_scan函數(在文件driver/mtd/nand/nand_base.c中,此文件提供一些默認的nand flash操作函數) ,這個函數主要 用默認函數填充一些未初始化的函數指針,讀取flash id 使用默認數據填充mtd,chip相關結構體(如果板級文件沒有提供相關支持)。此函數的分析直接寫在代碼註釋中。
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
*
* This fills out all the not initialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values. Buffers are allocated if
* they are not provided by the board driver
*
*/
int nand_scan (struct mtd_info *mtd, int maxchips)
{
int i, j, nand_maf_id, nand_dev_id, busw;
struct nand_chip *this = mtd->priv; //在調用函數nand_init_chip中mtd->priv = nand;

/* Get buswidth to select the correct functions*/
busw = this->options & NAND_BUSWIDTH_16; //=0

/* check for proper chip_delay setup, set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20; //延時默認20毫秒

/* check, if a user supplied command function given */
if (this->cmdfunc == NULL) //在board_nand_init中設置爲 sep4020_nand_command
this->cmdfunc = nand_command;

/* check, if a user supplied wait function given */
if (this->waitfunc == NULL)
this->waitfunc = nand_wait; //默認的flash 擦除 燒寫 等待函數 //wc
if (!this->select_chip) //在board_nand_init中設置爲 sep4020_nand_select_chip;
this->select_chip = nand_select_chip;
if (!this->write_byte) //在board_nand_init中設置爲 sep4020_nand_write_byte;
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
if (!this->read_byte) //在board_nand_init中設置爲 sep4020_nand_read_byte
this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!this->write_word)
this->write_word = nand_write_word; //
if (!this->read_word)
this->read_word = nand_read_word; //
if (!this->block_bad)
this->block_bad = nand_block_bad; //默認壞塊檢查函數 //wc
if (!this->block_markbad)
this->block_markbad = nand_default_block_markbad; //默認標記壞塊函數 //wc
if (!this->write_buf) //在board_nand_init中設置爲 sep4020_nand_write_buf
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!this->read_buf) //在board_nand_init中設置爲 sep4020_nand_read_buf
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!this->verify_buf) //
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!this->scan_bbt)
this->scan_bbt = nand_default_bbt;
/* Select the device */
this->select_chip(mtd, 0); //選擇flash 因爲只有一個flash,sep4020_nand_select_chip函數是空的
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); //flash命令發送函數,讀flash ID命令

/* Read manufacturer and device IDs */
#ifdef CONFIG_MTD_NAND_SEP4020 //這裏是sep4020專用的讀id
/* Read manufacturer and device IDs for sep4020 */
nand_maf_id = this->read_byte(mtd); //讀flash 一個字節
nand_dev_id = (EMI_NAND_ID)>>8; //EMI_NAND_ID是sep4020 flashID寄存器,8:15位是device code
//printf("read id! id is 0x%x\n",nand_dev_id);
#else
nand_maf_id = this->read_byte(mtd);
nand_dev_id = this->read_byte(mtd);
#endif

/* Print and store flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
//在driver/mtd/nand/nand_ids.c 中定義nand_flash_ids數組,類型是nand_flash_dev,包含信息有name id 頁大小 整片容量 塊大小和option
if (nand_dev_id != nand_flash_ids[i].id) //如果不相等id跳過下面代碼繼續在表中找,相等繼續往下執行
continue;
if (!mtd->name) mtd->name = nand_flash_ids[i].name; //複製table中的名字給 name
this->chipsize = nand_flash_ids[i].chipsize << 20;

/* New devices have all the information in additional id bytes */
if (!nand_flash_ids[i].pagesize) {
int extid;
/* The 3rd id byte contains non relevant data ATM */
extid = this->read_byte(mtd);
/* The 4th id byte is the important one */
extid = this->read_byte(mtd); //此週期包含附加信息
#ifdef CONFIG_MTD_NAND_SEP4020
extid = 0x15; //如果是sep4020 根本不管讀出的信息,直接賦值0x15 = 0--0--01--0--1--01
//訪問間隔50ns--8位結構--塊大小128K--XX--每512字節對應的oob大小,16--頁大小2K
#endif
/* Calc pagesize */
mtd->oobblock = 1024 << (extid & 0x3); //2K
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->oobblock / 512); //64
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03); //128K
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; //8位數據寬度

} else {
/* Old devices have this data hardcoded in the //小頁的flash設置
* device id table */
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->oobblock = nand_flash_ids[i].pagesize;
mtd->oobsize = mtd->oobblock / 32;
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
}

/* Check, if buswidth is correct. Hardware drivers should set
* this correct ! */ //檢查讀出的數據總線和table中儲存的是否一致,不一致就打印錯誤,並函數返回
if (busw != (this->options & NAND_BUSWIDTH_16)) {
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[i].name , mtd->name);
printk (KERN_WARNING
"NAND bus width %d instead %d bit\n",
(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
this->select_chip(mtd, -1);
return 1;
}

/* Calculate the address shift from the page size *///address shift :地址偏移位 ,
this->page_shift = ffs(mtd->oobblock) - 1; //ffs測試第一個位爲真的位置,2K的第12位爲1 減1 則爲11
this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
this->chip_shift = ffs(this->chipsize) - 1;

/* Set the bad block position */
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
//壞塊位位置表示,512頁大小的在oob區的第6個位,大頁的在oob區域的第1位

/* Get chip options, preserve non chip based options */
this->options &= ~NAND_CHIPOPTIONS_MSK;
this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
/* Set this as a default. Board drivers can override it, if neccecary */
this->options |= NAND_NO_AUTOINCR;
/* Check if this is a not a samsung device. Do not clear the options
* for chips which are not having an extended id.
*/
if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
this->options &= ~NAND_SAMSUNG_LP_OPTIONS;

/* Check for AND chips with 4 page planes */
//this will change if the chip is sep4020 //如果定義了sep4020則不要這段
//其實修改nand_base.c文件 理論上是不應該的 這個文件中定義的操作都是默認的標準,有差異的板級修改都在可以在board_nand_init中等函數修改。
#ifndef CONFIG_MTD_NAND_SEP4020
if (this->options & NAND_4PAGE_ARRAY)
this->erase_cmd = multi_erase_cmd;
else
this->erase_cmd = single_erase_cmd;
#endif

/* Do not replace user supplied command function ! */
//如果是大頁,並且用戶沒有指定cmdfunc,則cmdfunc被替
//換成nand_command_lp,大頁專用
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;

/* Try to identify manufacturer */
//留了個框架 啥都沒幹。(處理廠商id)
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
if (nand_manuf_ids[j].id == nand_maf_id)
break;
}
break; //在找到對應芯片id後,設置完了,終於break出去繼續別的活了。
}
//-------------------------
//nand_flash_ids[i].name爲空說明循環到了最後出去了還沒找到配對的id,
//打印出錯信息返回
if (!nand_flash_ids[i].name) {
#ifndef CFG_NAND_QUIET_TEST
printk (KERN_WARNING "No NAND device found!!!\n");
#endif
this->select_chip(mtd, -1);
return 1;
}

//怎麼又讀id,又留個框架,什麼都不幹。
for (i=1; i < maxchips; i++) {
this->select_chip(mtd, i);

/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

/* Read manufacturer and device IDs */
if (nand_maf_id != this->read_byte(mtd) ||
nand_dev_id != this->read_byte(mtd))
break;
}
if (i > 1) //flash多於一個的話
printk(KERN_INFO "%d NAND chips detected\n", i);

/* Allocate buffers, if neccecary */
if (!this->oob_buf) {
size_t len;
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
//this->phys_erase_shift - this->page_shift 一塊內多少個頁的位置偏移。oobsize左移6位即乘64.
//一個塊有64個頁 (2K大頁 舉例)
this->oob_buf = kmalloc (len, GFP_KERNEL);
if (!this->oob_buf) {
printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
return -ENOMEM;
}
this->options |= NAND_OOBBUF_ALLOC;
}

if (!this->data_buf) { //分配一個頁的內存當緩衝區
size_t len;
len = mtd->oobblock + mtd->oobsize;
this->data_buf = kmalloc (len, GFP_KERNEL);
if (!this->data_buf) {
if (this->options & NAND_OOBBUF_ALLOC)
kfree (this->oob_buf);
printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
return -ENOMEM;
}
this->options |= NAND_DATABUF_ALLOC;
}

/* Store the number of chips and calc total size for mtd */
this->numchips = i;
mtd->size = i * this->chipsize;
/* Convert chipsize to number of pages per chip -1. */
this->pagemask = (this->chipsize >> this->page_shift) - 1; //pagemask 頁號的掩碼
/* Preset the internal oob buffer */
memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));

/* If no default placement scheme is given, select an
* appropriate one */
if (!this->autooob) {
/* Select the appropriate default oob placement scheme for
* placement agnostic filesystems */
switch (mtd->oobsize) {
case 8:
this->autooob = &nand_oob_8;
break;
case 16:
this->autooob = &nand_oob_16;
break;
case 64:
this->autooob = &nand_oob_64;
break;
default:
printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
/* BUG(); */
}
}
/****************************
* 這裏剛好看一下oob的分佈
* static struct nand_oobinfo nand_oob_64 = {
* .useecc = MTD_NANDECC_AUTOPLACE,
* .eccbytes = 24,
* .eccpos = {
* 40, 41, 42, 43, 44, 45, 46, 47,
* 48, 49, 50, 51, 52, 53, 54, 55,
* 56, 57, 58, 59, 60, 61, 62, 63},
* .oobfree = { {2, 38} }
* };
*
* 從上面的可以看出64字節的oob區域中,ECC中佔24字節,
* (256字節數據,生成ECC碼 3字節)[40:63]字節,
* {2,38}表示第2位開始有38個字節空閒
*
*
***************************/
/* The number of bytes available for the filesystem to place fs dependend
* oob data */
mtd->oobavail = 0;
for (i=0; this->autooob->oobfree[i][1]; i++) //統計空閒oob區域
mtd->oobavail += this->autooob->oobfree[i][1];

/*
* check ECC mode, default to software
* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
* fallback to software ECC
*/
this->eccsize = 256; /* set default eccsize *///256字節 3字節
this->eccbytes = 3;

switch (this->eccmode) { //在board_nand_init中 設置爲NAND_ECC_SOFT
case NAND_ECC_HW12_2048:
if (mtd->oobblock < 2048) {
printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
mtd->oobblock);
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
} else
this->eccsize = 2048;
break;

case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512:
if (mtd->oobblock == 256) {
printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
} else
this->eccsize = 512; /* set eccsize to 512 */
break;

case NAND_ECC_HW3_256:
break;

case NAND_ECC_NONE:
printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
this->eccmode = NAND_ECC_NONE;
break;

case NAND_ECC_SOFT:
this->calculate_ecc = nand_calculate_ecc; //軟件計算ECC
this->correct_data = nand_correct_data; //使用ECC發現和糾正位錯誤(發現2位錯誤,糾正1位的錯誤)
break;

default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
/* BUG(); */
}

/* Check hardware ecc function availability and adjust number of ecc bytes per
* calculation step
*/
switch (this->eccmode) { //是否支持硬件ECC
case NAND_ECC_HW12_2048:
this->eccbytes += 4;
case NAND_ECC_HW8_512:
this->eccbytes += 2;
case NAND_ECC_HW6_512:
this->eccbytes += 3;
case NAND_ECC_HW3_512:
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
/* BUG(); */
}

mtd->eccsize = this->eccsize;

/* Set the number of read / write steps for one page to ensure ECC generation */
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
this->eccsteps = mtd->oobblock / 2048;
break;
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512:
this->eccsteps = mtd->oobblock / 512;
break;
case NAND_ECC_HW3_256:
case NAND_ECC_SOFT:
this->eccsteps = mtd->oobblock / 256;
break;

case NAND_ECC_NONE:
this->eccsteps = 1;
break;
}

/* XXX U-BOOT XXX */
#if 0
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
#endif

/* De-select the device */
this->select_chip(mtd, -1);

/* Invalidate the pagebuffer reference */
this->pagebuf = -1;

/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
/* XXX U-BOOT XXX */
#if 0
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
#endif
mtd->sync = nand_sync;
/* XXX U-BOOT XXX */
#if 0
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = NULL;
mtd->resume = NULL;
#endif
mtd->block_isbad = nand_block_isbad; //從bbt中查出是否是壞塊
mtd->block_markbad = nand_block_markbad; //標記壞塊

/* and make the autooob the default one */
memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
//把nand_chip的nand_oobinfo信息拷貝給mtd
/* XXX U-BOOT XXX */
#if 0
mtd->owner = THIS_MODULE;
#endif
/* Build bad block table */
return this->scan_bbt (mtd); //掃描整個flash,建立bbt表
}

至此返回到nand_init中我們發現nand的初始化已經差不多完畢了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章