NAND驅動分析--(二)

在上一篇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_FIReLBC_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設備層。

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