在上一篇nand驅動分析中,大概描述了nand flash驅動加載時的初始化流程,接下來對其調用的一些函數進行進一步的闡述。
首先,上一篇說到調用了fsl_elbc_chip_init()函數,此函數還是在drivers\mtd\nand\fsl_elbc_nand.c文件中被定義,其代碼如下所示:
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
{
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; //將寄存器基址賦值給lbc
struct nand_chip *chip = &priv->chip; //將priv結構的chip成員地址傳遞給此函數中的chip指針,這樣就可以對priv結構中的chip成員進行一系列初始化
printk("eLBC Set Information for bank %d\n", priv->bank);
priv->mtd.priv = chip; //將chip指針傳遞給主分區mtd中的私有數據,其類型爲nand_chip
priv->mtd.owner = THIS_MODULE;
priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM;
/* 初始化nand_chip中的一些基本操作函數 */
chip->read_byte = fsl_elbc_read_byte; //讀數據函數
chip->write_buf = fsl_elbc_write_buf;
chip->read_buf = fsl_elbc_read_buf;
chip->verify_buf = fsl_elbc_verify_buf;
chip->select_chip = fsl_elbc_select_chip;
chip->cmdfunc = fsl_elbc_cmdfunc; //寫命令函數
chip->waitfunc = fsl_elbc_wait; //等待命令執行完畢函數
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
NAND_USE_FLASH_BBT;
chip->controller = &elbc_fcm_ctrl->controller;
chip->priv = priv;
chip->ecc.read_page = fsl_elbc_read_page;
chip->ecc.write_page = fsl_elbc_write_page;
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
BR_DECC_CHK_GEN) {
chip->ecc.mode = NAND_ECC_HW;
/* put in small page settings and adjust later if needed */
#if 0
chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
#else
chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
&fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0;
#endif
chip->ecc.size = 512;
chip->ecc.bytes = 3;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}
chip->ecc.mode = NAND_ECC_NONE;
return 0;
}
以上代碼的主要作用有兩點:1、將主分區的mtd_info結構中的priv指針,指向nand_chip;2、初始化nand_chip結構體中的一些基本操作函數。
而在nand_chip結構體中最重要的一個操作函數是chip->cmdfunc,所以接下來以此函數進行分析,而其他的函數就不再進行詳細分析。代碼如下所示:
/* cmdfunc send commands to the FCM */
static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
elbc_fcm_ctrl->use_mdr = 0;
/* clear the read buffer */
elbc_fcm_ctrl->read_bytes = 0;
if (command != NAND_CMD_PAGEPROG)
elbc_fcm_ctrl->index = 0;
switch (command) //根據發送的命令來執行相應的操作
{
/* READ0 and READ1 read the entire buffer to use hardware ECC. */
case NAND_CMD_READ1:
column += 256;
/* fall-through */
case NAND_CMD_READ0: //讀數據命令
out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
set_addr(mtd, 0, page_addr, 0); //設置要讀取數據的首地址,即向elbc的塊、頁和列地址寄存器寫值
elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; //計算出每次讀取字節的總數
elbc_fcm_ctrl->index += column;
fsl_elbc_do_read(chip, 0); //向FCM模式的本地總線發送讀取nand flash數據的指令
fsl_elbc_run_command(mtd); //使cpu運行剛纔向elbc發送的指令
return;
/* READOOB reads only the OOB because no ECC is performed. */
case NAND_CMD_READOOB:
out_be32(&lbc->fbcr, mtd->oobsize - column);
set_addr(mtd, column, page_addr, 1);
elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
fsl_elbc_do_read(chip, 1);
fsl_elbc_run_command(mtd);
return;
/* READID must read all 5 possible bytes while CEB is active */
case NAND_CMD_READID: //讀取芯片ID
/* 向fir寄存器寫入想要執行指令的步驟和順序 */
out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | //執行fcr中第一條指令
(FIR_OP_UA << FIR_OP1_SHIFT) | //使用用戶默認地址
(FIR_OP_RBW << FIR_OP2_SHIFT)); //等待並讀取fbcr寄存器的值
out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); //寫入讀取設備ID指令到fcr寄存器中的第一條指令的位置
/* 5 bytes for manuf, device and exts */
out_be32(&lbc->fbcr, 5); //向數據計數寄存器中寫入要讀取數據總數的值
elbc_fcm_ctrl->read_bytes = 5;
elbc_fcm_ctrl->use_mdr = 1;
elbc_fcm_ctrl->mdr = 0; //將FCM數據寄存器的值先清零
set_addr(mtd, 0, 0, 0); //設置地址,此時是讀設備ID,所以地址爲0
fsl_elbc_run_command(mtd); //開始運行fir以及fcr寄存器中的指令
return;
/* 從此處可知,擦除命令分爲兩個步驟,NAND_CMD_ERASE1命令用於設置想要擦除內容的首地址,NAND_CMD_ERASE2命令用於擦除步驟1中設置的地址所指向的內容 */
/* ERASE1 stores the block and page address */
case NAND_CMD_ERASE1: //擦除命令1
set_addr(mtd, 0, page_addr, 0); //設置要擦除nand塊的首地址
return;
/* ERASE2 uses the block and page address from ERASE1 */
case NAND_CMD_ERASE2: //擦除命令2
out_be32(&lbc->fir,
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
(FIR_OP_PA << FIR_OP1_SHIFT) |
(FIR_OP_CM2 << FIR_OP2_SHIFT) |
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
(FIR_OP_RS << FIR_OP4_SHIFT));
out_be32(&lbc->fcr,
(NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
(NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
(NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
out_be32(&lbc->fbcr, 0);
elbc_fcm_ctrl->read_bytes = 0;
elbc_fcm_ctrl->use_mdr = 1;
fsl_elbc_run_command(mtd);
return;
/* RESET without waiting for the ready line */
case NAND_CMD_RESET: //nand flash芯片復位操作
out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
fsl_elbc_run_command(mtd);
return;
default:
dev_err(priv->dev,
"fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
command);
}
}
從上述代碼可以看出,elbc是如何通過數據、地址總線向nand flash發送命令、地址等數據的。
操作nand flash的方法,如下所示:
eLBC_FIR和eLBC_FCR這兩個寄存器是操作nand flash的關鍵。
1、eLBC_FIR寄存器,向此寄存器中寫入一些操作碼,即可實現相應的功能,其功能碼如下所示:
/* FIR寄存器一共有8個可編程指令序列,而此指令爲4bit位的操作碼 */
#define FIR_OP0 0xF0000000
#define FIR_OP0_SHIFT 28
#define FIR_OP1 0x0F000000
#define FIR_OP1_SHIFT 24
#define FIR_OP2 0x00F00000
#define FIR_OP2_SHIFT 20
#define FIR_OP3 0x000F0000
#define FIR_OP3_SHIFT 16
#define FIR_OP4 0x0000F000
#define FIR_OP4_SHIFT 12
#define FIR_OP5 0x00000F00
#define FIR_OP5_SHIFT 8
#define FIR_OP6 0x000000F0
#define FIR_OP6_SHIFT 4
#define FIR_OP7 0x0000000F
#define FIR_OP7_SHIFT 0
/* No operation and end of sequence */
#define FIR_OP_NOP 0x0
/* Issue current column address */
#define FIR_OP_CA 0x1
/* Issue current block+page address */
#define FIR_OP_PA 0x2
/* Issue user defined address */
#define FIR_OP_UA 0x3
/* Issue command from FCR[CMD0] */
#define FIR_OP_CM0 0x4
/* Issue command from FCR[CMD1] */
#define FIR_OP_CM1 0x5
/* Issue command from FCR[CMD2] */
#define FIR_OP_CM2 0x6
/* Issue command from FCR[CMD3] */
#define FIR_OP_CM3 0x7
/* Write FBCR bytes from FCM buffer */
#define FIR_OP_WB 0x8
/* Write 1 or 2 bytes from MDR[AS] */
#define FIR_OP_WS 0x9
/* Read FBCR bytes to FCM buffer */
#define FIR_OP_RB 0xA
/* Read 1 or 2 bytes to MDR[AS] */
#define FIR_OP_RS 0xB
/* Wait then issue FCR[CMD0] */
#define FIR_OP_CW0 0xC
/* Wait then issue FCR[CMD1] */
#define FIR_OP_CW1 0xD
/* Wait then read FBCR bytes */
#define FIR_OP_RBW 0xE
/* Wait then read 1 or 2 bytes */
#define FIR_OP_RSW 0xE
2、eLBC_FCR寄存器,而此寄存器中則主要根據nand芯片手冊的一些操作命令來寫入nand flash芯片的一些指令,如0x90表示讀取芯片ID等。
最後再運行fsl_elbc_run_command(mtd);函數即可根據上面的兩個寄存器的值來進行nand操作。
接下來就會執行到nand_scan (&priv->mtd, 1)函數,此函數用於掃描連接到elbc的外部nand flash,具體代碼如下所示:
int nand_scan (struct mtd_info *mtd, int maxchips)
{
int ret;
int i, j, nand_maf_id, nand_dev_id, busw;
struct nand_chip *this = mtd->priv;
/* Get buswidth to select the correct functions*/
busw = this->options & NAND_BUSWIDTH_16;
/* check for proper chip_delay setup, set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20;
/* check, if a user supplied command function given */
if (this->cmdfunc == NULL) //此函數已經初始化,並且上文中對此函數有詳細的分析
this->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (this->waitfunc == NULL)
this->waitfunc = nand_wait;
if (!this->select_chip)
this->select_chip = nand_select_chip;
if (!this->write_byte)
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
if (!this->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;
if (!this->block_markbad)
this->block_markbad = nand_default_block_markbad;
if (!this->write_buf)
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!this->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);
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); //讀取nand設備ID號
/* Read manufacturer and device IDs */
nand_maf_id = this->read_byte(mtd); //將讀出的ID號賦值給nand_maf_id
nand_dev_id = this->read_byte(mtd); //將讀出的ID號賦值給nand_dev_id
/* Print and store flash device information */
/* 輪詢定義的芯片信息有沒有與讀出的設備ID一致的芯片 */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (nand_dev_id != nand_flash_ids[i].id)
continue;
/* 查找到對應的芯片信息後,根據芯片信息將nand_chip結構體補充完整 */
if (!mtd->name) mtd->name = nand_flash_ids[i].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);
/* Calc pagesize */
mtd->oobblock = 1024 << (extid & 0x3);
mtd->writesize = 1024 << (extid & 0x3);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
} else {
/* Old devices have this data hardcoded in the
* 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;
mtd->writesize = nand_flash_ids[i].pagesize;
}
/* Check, if buswidth is correct. Hardware drivers should set
* this correct ! */
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 */
this->page_shift = ffs(mtd->oobblock) - 1;
/* Convert chipsize to number of pages per chip -1. */
this->pagemask = (this->chipsize >> this->page_shift) - 1;
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;
/* 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 */
if (this->options & NAND_4PAGE_ARRAY)
this->erase_cmd = multi_erase_cmd;
else
this->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
/* Try to identify manufacturer */
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
if (nand_manuf_ids[j].id == nand_maf_id)
break;
}
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[j].name , nand_flash_ids[i].name);
break;
}
if (!nand_flash_ids[i].name) {
printk (KERN_WARNING "No NAND device found!!!\n");
this->select_chip(mtd, -1);
return 1;
}
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)
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->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;
/* 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_fsl;
break;
default:
printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
BUG();
}
}
/* The number of bytes available for the filesystem to place fs dependend
* oob data */
if (this->options & NAND_BUSWIDTH_16) {
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
if (this->autooob->eccbytes & 0x01)
mtd->oobavail--;
} else
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 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 */
this->eccbytes = 3;
switch (this->eccmode) {
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;
this->correct_data = nand_correct_data;
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) {
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;
}
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
/* De-select the device */
this->select_chip(mtd, -1);
/* Invalidate the pagebuffer reference */
this->pagebuf = -1;
/* Fill in remaining MTD driver data */
/* 填充主分區的mtd_info結構體,每一個分區都包含了一個mtd_info結構體,所以每一個分區都可以調用mtd_info結構體中的一些功能函數,對分區進行操作 */
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;
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = NULL;
mtd->resume = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
/* and make the autooob the default one */
memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
mtd->owner = THIS_MODULE;
fsl_elbc_chip_init_tail(mtd);
/* Build bad block table */
return this->scan_bbt (mtd);
}
nand_scan函數的主要作用就是用於掃描外部nand flash,並與代碼中已有的nand flash設備ID號進行匹配,如果匹配成功,則利用對應的nand設備信息將nand_chip結構體和mtd_info結構體填充完整。
到程序的最後,將調用add_mtd_partitions(&priv->mtd, p1020_partition_info, 3)函數來進行nand flash的分區,並將priv->mtd作爲主分區,來將其它分區的mtd_info結構補充完整。其代碼分析如下:
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
u_int32_t cur_offset = 0;
int i;
printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
/* 根據nbparts參數來確定nand存儲器將劃分爲幾個分區 */
for (i = 0; i < nbparts; i++) {
/* allocate the partition structure */
slave = kmalloc (sizeof(*slave), GFP_KERNEL);
if (!slave) {
printk ("memory allocation error while creating partitions for \"%s\"\n",
master->name);
del_mtd_partitions(master);
return -ENOMEM;
}
memset(slave, 0, sizeof(*slave));
list_add(&slave->list, &mtd_partitions);
/* set up the MTD object for this partition */
/* 根據主分區的mtd_info結構信息來初始化各個分區的mtd_info信息 */
slave->mtd.type = master->type;
slave->mtd.flags = master->flags & ~parts[i].mask_flags;
slave->mtd.size = parts[i].size;
slave->mtd.oobblock = master->oobblock;
slave->mtd.oobsize = master->oobsize;
slave->mtd.ecctype = master->ecctype;
slave->mtd.eccsize = master->eccsize;
slave->mtd.name = parts[i].name;
slave->mtd.bank_size = master->bank_size;
slave->mtd.owner = master->owner;
slave->mtd.read = part_read;
slave->mtd.write = part_write;
if(master->point && master->unpoint){
slave->mtd.point = part_point;
slave->mtd.unpoint = part_unpoint;
}
if (master->read_ecc)
slave->mtd.read_ecc = part_read_ecc;
if (master->write_ecc)
slave->mtd.write_ecc = part_write_ecc;
if (master->read_oob)
slave->mtd.read_oob = part_read_oob;
if (master->write_oob)
slave->mtd.write_oob = part_write_oob;
if(master->read_user_prot_reg)
slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
if(master->read_fact_prot_reg)
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
if(master->write_user_prot_reg)
slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
if (master->sync)
slave->mtd.sync = part_sync;
if (!i && master->suspend && master->resume) {
slave->mtd.suspend = part_suspend;
slave->mtd.resume = part_resume;
}
if (master->writev)
slave->mtd.writev = part_writev;
if (master->readv)
slave->mtd.readv = part_readv;
if (master->writev_ecc)
slave->mtd.writev_ecc = part_writev_ecc;
if (master->readv_ecc)
slave->mtd.readv_ecc = part_readv_ecc;
if (master->lock)
slave->mtd.lock = part_lock;
if (master->unlock)
slave->mtd.unlock = part_unlock;
if (master->block_isbad)
slave->mtd.block_isbad = part_block_isbad;
if (master->block_markbad)
slave->mtd.block_markbad = part_block_markbad;
slave->mtd.erase = part_erase;
slave->master = master;
slave->offset = parts[i].offset;
slave->index = i;
if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset;
if (slave->offset == MTDPART_OFS_NXTBLK) {
u_int32_t emask = master->erasesize-1;
slave->offset = (cur_offset + emask) & ~emask;
if (slave->offset != cur_offset) {
printk(KERN_NOTICE "Moving partition %d: "
"0x%08x -> 0x%08x\n", i,
cur_offset, slave->offset);
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
cur_offset = slave->offset + slave->mtd.size;
printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
slave->offset + slave->mtd.size, slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
parts[i].name);
}
if (slave->offset + slave->mtd.size > master->size) {
slave->mtd.size = master->size - slave->offset;
printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
parts[i].name, master->name, slave->mtd.size);
}
if (master->numeraseregions>1) {
/* Deal with variable erase size stuff */
int i;
struct mtd_erase_region_info *regions = master->eraseregions;
/* Find the first erase regions which is part of this partition. */
for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
;
for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
if (slave->mtd.erasesize < regions[i].erasesize) {
slave->mtd.erasesize = regions[i].erasesize;
}
}
} else {
/* Single erase size */
slave->mtd.erasesize = master->erasesize;
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
(slave->offset % slave->mtd.erasesize)) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
parts[i].name);
}
if ((slave->mtd.flags & MTD_WRITEABLE) &&
(slave->mtd.size % slave->mtd.erasesize)) {
slave->mtd.flags &= ~MTD_WRITEABLE;
printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
parts[i].name);
}
/* copy oobinfo from master */
memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));
if(parts[i].mtdp)
{ /* store the object pointer (caller may or may not register it */
*parts[i].mtdp = &slave->mtd;
slave->registered = 0;
}
else
{
/* register our partition */
/* 最後再根據從分區的mtd_info結構來註冊分區 */
add_mtd_device(&slave->mtd);
slave->registered = 1;
}
}
return 0;
}
所以此函數的主要作用就是向系統註冊nand flash的分區。
此時,nand flash的硬件驅動層就分析完畢。下一篇文章將講述mtd設備層。