1. 程式人生 > >NAND FLASH學習筆記之MTD下nand flash驅動(五)

NAND FLASH學習筆記之MTD下nand flash驅動(五)

驅動中probe函式的分析

其中探測函式程式碼執行流程為:


probe函式中主要是完成了NAND晶片級的初始化,主要有以下幾個作用:

-分配nand_chip記憶體,根據目標板及NAND控制器初始化nand_chip中成員函式(若未初始化則使用nand_base.c中的預設函式),將mtd_info中的priv指向nand_chip(或板相關私有結構),設定ecc模式及處理函式

-mtd_info為引數呼叫nand_scan_ident()

nand_scan_tail()函式,探測NAND Flash並進行初始化

nand_scan_ident()會讀取nand晶片ID

,並根據mtd->privnand_chip中成員初始化mtd_info

nand_scan_tail()進行了ECC的設定和剩下的MTD驅動函式的初始化。

-若有分割槽,則以mtd_infomtd_partition為引數呼叫add_mtd_partitions()新增分割槽資訊

nand_scan_ident()

首先初始化mtd_info中一個重要的指標priv,使這個指標指向nand_chip變數;

然後呼叫了同文件下的nand_set_defaults()和nand_get_flash_type(),nand_set_defaults()函式對struct nand_chip結構體的函式指標進行了賦值。在此函式中cmdfunc對映到了nand_command,nand_get_flash_type

()讀取了廠商和裝置ID進行匹配,並對struct nand_chip結構體的變數進行初始化操作。 

nand_scan_tail()

1、初始化oob區、ecc校驗相關引數和函式指標。

2初始化MTD驅動介面函式。

3呼叫nand_bbt()建立壞塊表。

probe程式程式碼分析如下:

NAND驅動的程式碼分析(二)
----------probe程式的分析
Joe

static int jz4780_nand_probe(struct platform_device *pdev)
{

        int ret = 0;
        int bank = 0;
        int i = 0, j = 0, k = 0, m = 0;
        int eccpos_start;
        struct nand_chip *chip;
        struct mtd_info *mtd;
        struct jz4780_nand *nand;
        struct jz4780_nand_platform_data *pdata;
        nand_flash_if_t *nand_if;
《1》
        /*
         * sanity check主要是獲取平臺裝置資訊裝置的相關引數
         */
        pdata = dev_get_platdata(&pdev->dev);//獲取平臺裝置的資料
        if (!pdata) {
                dev_err(&pdev->dev, "Failed to get platform_data.\n");
                return -ENXIO;
        }

        nand = kzalloc(sizeof(struct jz4780_nand), GFP_KERNEL);//為裝置分配記憶體空間
        if (!nand) {
                dev_err(&pdev->dev,
                        "Failed to allocate jz4780_nand.\n");
                return -ENOMEM;
        }

        nand->pdev = pdev;
        nand->pdata = pdata;
        platform_set_drvdata(pdev, nand);//將nand儲存為平臺匯流排的私有資料,將nand裝置的資料資訊傳遞到系統平臺裝置中去

        nand->num_nand_flash_if = pdata->num_nand_flash_if;//所用nand flash的片數
        nand->xfer_type = pdata->xfer_type;
        nand->ecc_type = pdata->ecc_type;



《2》
        /*
         * request GPEMC banks獲取每一個bank的相關引數(busy,protect,timeout..)申請cs_gpio引腳
         */
        for (i = 0; i < nand->num_nand_flash_if; i++, j = i) {

                nand_if = &pdata->nand_flash_if_table[i];
                nand->nand_flash_if_table[i] = nand_if;
                bank = nand_if->bank;

                ret = gpemc_request_cs(&pdev->dev, &nand_if->cs, bank);

                if (ret) {
                        dev_err(&pdev->dev,
                                        "Failed to request busy"
                                        " gpio irq for bank%d\n", bank);
                                goto err_free_busy_irq;
                        }
                }
《3》
  	 /*
         * request busy GPIO interrupt申請busy_gpio引腳
         */
   switch (nand->xfer_type) {

   case NAND_XFER_CPU_IRQ:

   case NAND_XFER_DMA_IRQ://中斷方式讀取資料

                for (i = 0; i < nand->num_nand_flash_if; i++, k = i) {
                        nand_if = &pdata->nand_flash_if_table[i];

                        if (nand_if->busy_gpio < 0)
                                continue;

                        ret = request_busy_irq(nand_if);
                        if (ret) {
                                dev_err(&pdev->dev,
                                        "Failed to request busy"

                                        " gpio irq for bank%d\n", bank);
                                goto err_free_busy_irq;
                        }
                }

                break;

        case NAND_XFER_CPU_POLL:
        case NAND_XFER_DMA_POLL://poll機制輪詢讀取資料(類似與底半部方式)

                for (i = 0; i < nand->num_nand_flash_if; i++, k = i) {

                        nand_if = &pdata->nand_flash_if_table[i];
                        if (nand_if->busy_gpio < 0)
                        continue;

                        ret = request_busy_poll(nand_if);

                        if (ret) {
                                dev_err(&pdev->dev,
                                        "Failed to request busy"
                                        " gpio irq for bank%d\n", bank);
                       		         goto err_free_busy_irq;
                        }
                }

                nand->busy_poll = 1;
                break;

        default:

                WARN(1, "Unsupport transfer type.\n");
                BUG();
                break;

        }
《4》
        /*
         * request WP GPIO//申請防寫引腳
         */
        for (i = 0; i < nand->num_nand_flash_if; i++, m = i) {

                nand_if = &pdata->nand_flash_if_table[i];
                if (nand_if->wp_gpio < 0)
                        continue;
                if (!gpio_is_valid(nand_if->wp_gpio)) {
                        dev_err(&pdev->dev,
                                "Invalid wp GPIO:%d\n", nand_if->wp_gpio);
                        ret = -EINVAL;
                        goto err_free_wp_gpio;

                }

                bank = nand_if->bank;
                ret = gpio_request(nand_if->wp_gpio, label_wp_gpio[bank]);
                if (ret) {
                        dev_err(&pdev->dev,
                                "Failed to request wp GPIO:%d\n", nand_if->wp_gpio);
                        goto err_free_wp_gpio;

                }

                gpio_direction_output(nand_if->wp_gpio, 0);//設定gipo為輸出模式

                /* Write protect disabled by default */
                jz4780_nand_enable_wp(nand_if, 0);           //disable 防寫

        }

《5》
        /*
         * NAND flash devices support list override
         */
        nand->nand_flash_table = pdata->nand_flash_table ?
                pdata->nand_flash_table : builtin_nand_flash_table;
        nand->num_nand_flash = pdata->nand_flash_table ?
                pdata->num_nand_flash :

                        ARRAY_SIZE(builtin_nand_flash_table);
        /*
         * attach to MTD subsystem 
             連結MTD的子系統-----涉及到如何傳輸資料
         * struct nand_chip是一個與NAND晶片密切相關的結構體,主要包含三方面內 容:
         *指向一些操作NAND晶片的函式的指標;
         *表示NAND晶片特性的成員變數,主要有:
         *與ecc,oob和bbt (bad block table)相關的一些結構體,對於壞塊及壞塊管理
         */
        chip              = &nand->chip;
        chip->chip_delay  = MAX_RB_DELAY_US;
        chip->cmdfunc     = jz4780_nand_command;
        chip->dev_ready   = jz4780_nand_dev_is_ready;
        chip->select_chip = jz4780_nand_select_chip;
        chip->cmd_ctrl    = jz4780_nand_cmd_ctrl;
        chip->onfi_get_features = jz4780_nand_onfi_get_features;
        chip->onfi_set_features = jz4780_nand_onfi_set_features;

        switch (nand->xfer_type) {
        case NAND_XFER_DMA_IRQ:
        case NAND_XFER_DMA_POLL:
                /*
                 * DMA transfer DMA方式傳輸資料
                 */

                ret = jz4780_nand_request_dma(nand);
                if (ret) {
                        dev_err(&pdev->dev, "Failed to request DMA channel.\n");
                        goto err_free_wp_gpio;
                }

                chip->read_buf  = jz4780_nand_read_buf;//將晶片中的資料讀到緩衝區中
                chip->write_buf = jz4780_nand_write_buf;//將緩衝區中的資料寫入晶片
                nand->use_dma = 1;
                break;

        case NAND_XFER_CPU_IRQ:
        case NAND_XFER_CPU_POLL:
                /*
                 * CPU transfer CPU方式傳輸資料
                 */
  
                chip->read_buf  = jz4780_nand_cpu_read_buf;
                chip->write_buf = jz4780_nand_cpu_write_buf;
jz4780_nand_write_buf 和 jz4780_nand_read_buf:這是兩個最基本的操作函式,其功能,就是往你的Nand Flash的控制器中的FIFO讀寫資料。一般情況下,是MTD上層的操作,比如要讀取一頁的資料,那麼在傳送完相關的讀命令和等待時間之後,就會呼叫到你底層的read_buf,去Nand Flash的FIFO中,一點點把我們要的資料,讀取出來,放到我們制定的記憶體的快取中去。寫操作也是類似,將我們記憶體中的資料,寫到Nand Flash的FIFO中去。
                break;

        default:

                WARN(1, "Unsupport transfer type.\n");
                BUG();
                break;

        }
        mtd              = &nand->mtd;//填充mtd_info結構體相關資訊
        mtd->priv        = chip;//把指向struct nand_chip結構體的指標賦給struct mtd_info的priv成員變數,
           //因為MTD Core中很多函式之間的呼叫都只傳遞struct mtd_info,它需要通過priv成員變數得到struct nand_chip。
        mtd->name        = dev_name(&pdev->dev);
        mtd->owner       = THIS_MODULE;

        /*
         * if you use u-boot BBT creation code,specifying
         * this flag will let the kernel fish out the BBT
         * from the NAND, and also skip the full NAND scan
         * that can take 1/2s or so. little things...
         */

        if (pdata->flash_bbt) {//當flashbbt=1的時候系統在啟動的時候將跳過對bbt(bad block table)的掃描

                chip->bbt_options |= NAND_BBT_USE_FLASH;
                chip->options |= NAND_SKIP_BBTSCAN;

        }

        /*
         * nand_base handle subpage write by fill space
         * where are outside of the subpage with 0xff,
         * that make things totally wrong, so disable it.
         */

        chip->options |= NAND_NO_SUBPAGE_WRITE;

        /*
         * for relocation
         */

        nand->gpemc_enable_nand_flash = gpemc_enable_nand_flash;
        nand->nand_wait_ready = nand_wait_ready;
        nand->gpio_get_value = gpio_get_value;
        nand->wait_for_completion_timeout = wait_for_completion_timeout;
        nand->msecs_to_jiffies = msecs_to_jiffies;
        nand->printk = printk;
        nand->udelay = __udelay;
        nand->ndelay = __ndelay;
《6》
        /*
         * Detect NAND flash chips  偵測NAND flash chips
         */
  
      /* step1. relax bank timings to scan 定時掃描空閒的bank*/

        for (bank = 0; bank < nand->num_nand_flash_if; bank++) {

                nand_if = nand->nand_flash_if_table[bank];
                gpemc_relax_bank_timing(&nand_if->cs);

        }

        if (nand_scan_ident(mtd, nand->num_nand_flash_if,
                        nand->nand_flash_table)) {
                ret = -ENXIO;
                dev_err(&pdev->dev, "Failed to detect NAND flash.\n");
                goto err_dma_release_channel;

        }

        /*
         * post configure bank timing by detected NAND device  通過偵測的NAND裝置配置bank時序暫存器
         */

        /* step1. match NAND chip information */

        nand->curr_nand_flash_info = jz4780_nand_match_nand_chip_info(nand);

        if (!nand->curr_nand_flash_info) {
                ret = -ENODEV;
                goto err_dma_release_channel;
        }

        /*
         * step2. preinitialize NAND flash 預初始化NAND快閃記憶體
         */

        ret = jz4780_nand_pre_init(nand);
        if (ret) {
                dev_err(&nand->pdev->dev, "Failed to"
                                " preinitialize NAND chip.\n");
                goto err_dma_release_channel;
        }

       /* step3. replace NAND command function with large page version */// 用對頁面的操作命令函式替代NAND的功能命令函式

        if (mtd->writesize > 512)
                chip->cmdfunc = jz4780_nand_command_lp;

        /* step4. configure bank timings */ //配置BANK的時序

        switch (nand->curr_nand_flash_info->type) {
        case BANK_TYPE_NAND:
                for (bank = 0; bank < nand->num_nand_flash_if; bank++) {
                        nand_if = nand->nand_flash_if_table[bank];
                        gpemc_fill_timing_from_nand(&nand_if->cs,
                                &nand->curr_nand_flash_info->
                                nand_timing.common_nand_timing);//對bank時序進行賦值操作

                        ret = gpemc_config_bank_timing(&nand_if->cs);//對bank時序暫存器進行配置

                        if (ret) {
                                dev_err(&pdev->dev,
                                        "Failed to configure timings for bank%d\n"
                                                , nand_if->bank);
                                goto err_dma_release_channel;

                        }
                }
                break;

        case BANK_TYPE_TOGGLE:

                for (bank = 0; bank < nand->num_nand_flash_if; bank++) {
                        nand_if = nand->nand_flash_if_table[bank];
                        gpemc_fill_timing_from_toggle(&nand_if->cs,
                               &nand->curr_nand_flash_info->
                                nand_timing.toggle_nand_timing);

                        ret = gpemc_config_bank_timing(&nand_if->cs);

                        if (ret) {
                                dev_err(&pdev->dev,
                                        "Failed to configure timings for bank%d\n"
                                                , nand_if->bank);
                                goto err_dma_release_channel;
                        }

                }

                break;

        default:
                WARN(1, "Unsupported NAND type.\n");
                BUG();
                break;

        }
《7》
        /*
         * initialize ECC control//初始化NAND ECC控制器
         */
        /* step1. configure ECC step */

        switch (nand->ecc_type) {

        case NAND_ECC_TYPE_SW:         //軟體實現ECC檢測

                /*
                 * valid ECC configuration ?
                 */

                if (nand->curr_nand_flash_info->
                                ecc_step.data_size % 8
                                || nand->curr_nand_flash_info->
                                        ecc_step.ecc_bits % 8) {

                        ret = -EINVAL;

                        dev_err(&nand->pdev->dev, "Failed when configure ECC,"
                                        " ECC size, and ECC bits must be a multiple of 8.\n");//ECC的大小和ECC位大小必須是8位
                        goto err_dma_release_channel;

                }



                chip->ecc.mode  = NAND_ECC_SOFT_BCH;//ECC演算法的模式為軟體BCH模式
                chip->ecc.size  =
                        nand->curr_nand_flash_info->ecc_step.data_size;
                chip->ecc.bytes = (fls(8 * chip->ecc.size) *
                        (nand->curr_nand_flash_info->ecc_step.ecc_bits) + 7) / 8;

                break;

        case NAND_ECC_TYPE_HW:         //硬體實現ECC的檢測,選取的演算法同樣是BCH
                nand->bch_req.dev       = &nand->pdev->dev;
                nand->bch_req.complete  = jz4780_nand_bch_req_complete;
                nand->bch_req.ecc_level =
                                nand->curr_nand_flash_info->ecc_step.ecc_bits;
                nand->bch_req.blksz     =
                                nand->curr_nand_flash_info->ecc_step.data_size;
                nand->bch_req.errrept_data = kzalloc(MAX_ERRREPT_DATA_SIZE,
                                GFP_KERNEL);

                if (!nand->bch_req.errrept_data) {
                        dev_err(&pdev->dev,
                                "Failed to allocate ECC errrept_data buffer\n");
                        ret = -ENOMEM;
                        goto err_dma_release_channel;

                }

                init_completion(&nand->bch_req_done);


                chip->ecc.mode      = NAND_ECC_HW;
                chip->ecc.calculate = jz4780_nand_ecc_calculate_bch;
                chip->ecc.correct   = jz4780_nand_ecc_correct_bch;
                chip->ecc.hwctl     = jz4780_nand_ecc_hwctl;
                chip->ecc.size  =
                        nand->curr_nand_flash_info->ecc_step.data_size;
                chip->ecc.bytes = bch_ecc_bits_to_bytes(
                        nand->curr_nand_flash_info->ecc_step.ecc_bits);
                chip->ecc.strength = nand->bch_req.ecc_level;

                break;

        default :

                WARN(1, "Unsupported ECC type.\n");
                BUG();
                break;

        }
        /*
        /* step2. generate ECC layout *///產生出ECC的佈局
        /*
         * eccbytes = eccsteps * eccbytes_prestep;//計算ECC的位元組數
         */

        nand->ecclayout.eccbytes =
                mtd->writesize / chip->ecc.size * chip->ecc.bytes;
        //判斷是否ECC位元組數超出OOB的空間大小
        if (mtd->oobsize < (nand->ecclayout.eccbytes +
                        chip->badblockpos + 2)) {
                WARN(1, "ECC codes are out of OOB area.\n");
                BUG();

        }
       /*
         * ECC codes are right aligned  ECC碼為右對齊
         * start position = oobsize - eccbytes  起始位置的計算
         */
        eccpos_start = mtd->oobsize - nand->ecclayout.eccbytes;  //ECC碼的起始位置

        for (bank = 0; bank < nand->ecclayout.eccbytes; bank++)
                nand->ecclayout.eccpos[bank] = eccpos_start + bank;

        nand->ecclayout.oobfree->offset = chip->badblockpos + 2;
        nand->ecclayout.oobfree->length =
                mtd->oobsize - (nand->ecclayout.eccbytes
                        + chip->badblockpos + 2);
        chip->ecc.layout = &nand->ecclayout;
《8》
        /*
         * second phase NAND scan            //第二階段NAND掃描
         */
        if (nand_scan_tail(mtd)) {
                ret = -ENXIO;
                goto err_free_ecc;
        }

#ifdef CONFIG_DEBUG_FS
        nand->debugfs_entry = jz4780_nand_debugfs_init(nand);
        if (IS_ERR(nand->debugfs_entry)) {
                dev_err(&pdev->dev, "Failed to register debugfs entry.\n");
                ret = PTR_ERR(nand->debugfs_entry);
                goto err_free_ecc;
        }
#endif
        /*
         * relocate hot functions to TCSM
         */
        if (pdata->try_to_reloc_hot) {
                ret = jz4780_nand_reloc_hot_to_tcsm(nand);
                if (ret) {
                        dev_err(&pdev->dev, "Failed to relocate hot functions.\n");
                        goto err_debugfs_remove;
                }
        }



《9》
        /*
         * MTD register
         */
        ret = mtd_device_parse_register(mtd, NULL, NULL,
                        pdata->part_table, pdata->num_part);
        if (ret) {
                dev_err(&pdev->dev, "Failed to add MTD device\n");
                goto err_unreloc_hot;
        }

        dev_info(&pdev->dev,
                "Successfully registered JZ4780 SoC NAND controller driver.\n");

        return 0;

err_unreloc_hot:
        if (pdata->try_to_reloc_hot)
                jz4780_nand_unreloc_hot_from_tcsm(nand);

err_debugfs_remove:
#ifdef CONFIG_DEBUG_FS
        debugfs_remove_recursive(nand->debugfs_entry);
#endif
err_free_ecc:

        if (pdata->ecc_type == NAND_ECC_TYPE_HW)
                kfree(nand->bch_req.errrept_data);

err_dma_release_channel:

        if (nand->xfer_type == NAND_XFER_DMA_IRQ ||
                        nand->xfer_type == NAND_XFER_DMA_POLL)
                dma_release_channel(nand->dma_pipe_nand.chan);

err_free_wp_gpio:

        for (bank = 0; bank < m; bank++) {
                nand_if = &pdata->nand_flash_if_table[bank];
                if (nand_if->wp_gpio < 0)
                        continue;
                gpio_free(nand_if->wp_gpio);
        }

err_free_busy_irq:

        for (bank = 0; bank < k; bank++) {
                nand_if = &pdata->nand_flash_if_table[bank];
                if (nand_if->busy_gpio < 0)
                        continue;
                if (pdata->xfer_type == NAND_XFER_CPU_IRQ ||
                                pdata->xfer_type ==NAND_XFER_DMA_IRQ)
                        free_irq(nand_if->busy_irq, nand_if);
                gpio_free(nand_if->busy_gpio);

        }
err_release_cs:
        for (bank = 0; bank < j; bank++) {
                nand_if = &pdata->nand_flash_if_table[bank];
                gpemc_release_cs(&nand_if->cs);
        }
        kfree(nand);
        return ret;
}

nand_scan_ident()程式程式碼分析:

int nand_scan_ident(struct mtd_info *mtd, int maxchips,struct nand_flash_dev *table)
{
        int i, busw, nand_maf_id, nand_dev_id;
        struct nand_chip *chip = mtd->priv;
// mtd->priv在probe函式中被初始化為資料結構nand_chip的變數nand_chip[i],所以這裡的this指標指向的就是變數nand_chip[i]。
        struct nand_flash_dev *type;

        /* Get buswidth to select the correct functions */
        busw = chip->options & NAND_BUSWIDTH_16;
//位寬設定,options bit1設定為0是busw為0,表示位寬為8。options會在該函式後續被初始化為nand_flash_ids[i].options。如果使用者需要配置擴充套件功能只能在nand_flash_ids[i].options配置。
        /* Set the default functions */
        nand_set_defaults(chip, busw);
//nand_set_defaults()函式對struct nand_chip結構體的函式指標進行了賦值。在此函式中cmdfunc對映到了nand_command,
/* Set default functions */
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
        /* check for proper chip_delay setup, set 20us if not */
        if (!chip->chip_delay)
                chip->chip_delay = 20

        /* check, if a user supplied command function given */
        if (chip->cmdfunc == NULL)
                chip->cmdfunc = nand_command;

        /* check, if a user supplied wait function given */
        if (chip->waitfunc == NULL)
                chip->waitfunc = nand_wait;

        if (!chip->select_chip)
                chip->select_chip = nand_select_chip;

        if (!chip->read_byte)
                chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;

        if (!chip->read_word)
                chip->read_word = nand_read_word;

        if (!chip->block_bad)
                chip->block_bad = nand_block_bad;

        if (!chip->block_markbad)
                chip->block_markbad = nand_default_block_markbad;

        if (!chip->write_buf)
                chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;

        if (!chip->read_buf)
                chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;

        if (!chip->scan_bbt)
                chip->scan_bbt = nand_default_bbt;

        if (!chip->controller) {
                chip->controller = &chip->hwcontrol;
                spin_lock_init(&chip->controller->lock);
                init_waitqueue_head(&chip->controller->wq);
        }
}

        /* Read the flash type */
        type = nand_get_flash_type(mtd, chip, busw,
                                &nand_maf_id, &nand_dev_id, table);

//nand_get_flash_type()讀取了廠商和裝置ID,並對struct nand_chip結構體的變數進行初始化操作

        if (IS_ERR(type)) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
                        pr_warn("No NAND device found\n");
                chip->select_chip(mtd, -1);
                return PTR_ERR(type);
        }

        chip->select_chip(mtd, -1);

        /* Check for a chip array */
        for (i = 1; i < maxchips; i++) {
                chip->select_chip(mtd, i);

                /* See comment in nand_get_flash_type for reset */
                chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
                /* Send the command for reading device ID */
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                /* Read manufacturer and device IDs */
                if (nand_maf_id != chip->read_byte(mtd) ||
                    nand_dev_id != chip->read_byte(mtd)) {
                        chip->select_chip(mtd, -1);
                        break;
                }
                chip->select_chip(mtd, -1);
 }

        if (i > 1)
                pr_info("%d NAND chips detected\n", i);

        /* Store the number of chips and calc total size for mtd */
        chip->numchips = i;
        mtd->size = i * chip->chipsize;
        return 0;
}
EXPORT_SYMBOL(nand_scan_ident);


nand_scan_tail()程式碼分析:
int nand_scan_tail(struct mtd_info *mtd)
{
        int i;
        struct nand_chip *chip = mtd->priv;

        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
        BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
                        !(chip->bbt_options & NAND_BBT_USE_FLASH));

        if (!(chip->options & NAND_OWN_BUFFERS))
                chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);

        if (!chip->buffers)
                return -ENOMEM;

        /* Set the internal oob buffer location, just after the page data */
        chip->oob_poi = chip->buffers->databuf + mtd->writesize;//設定快取位置,僅僅需要在資料頁後面就行
         * If no default placement scheme is given, select an appropriate one.如果沒有預設的方案,就選擇合適的一個
         */

        if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
                switch (mtd->oobsize) {
                case 8:
                        chip->ecc.layout = &nand_oob_8;
                        break;

                case 16:
                        chip->ecc.layout = &nand_oob_16;
                        break;

                case 64:
                        chip->ecc.layout = &nand_oob_64;
                        break;

                case 128:
                        chip->ecc.layout = &nand_oob_128;
                        break;

                default:
                        pr_warn("No oob scheme defined for oobsize %d\n",
                                   mtd->oobsize);
                        BUG();
                }
        }

        if (!chip->write_page)
                chip->write_page = nand_write_page;

        /* set for ONFI nand *///設定ONFI NAND的功能
        if (!chip->onfi_set_features)
                chip->onfi_set_features = nand_onfi_set_features;
        if (!chip->onfi_get_features)
                chip->onfi_get_features = nand_onfi_get_features;

//對chip結構題的ecc相關的初始化
        /*
         * Check ECC mode, default to software if 3byte/512byte hardware ECC is
         * selected and we have 256 byte pagesize fallback to software ECC
         *///如果硬體檢測選擇的是3/512那麼我們預設軟體ECC模式,我們每頁有256bytes為ECC備用
        // /*以下都是對chip賦值,對應nand_chip中的函式*/
        /*Nand_scan是在初始化nand的時候對nand進行的一步非常好重要的操作,
        *在nand_scan中會對我們所寫的關於特定晶片的讀寫函式過載到nand_chip結構中去,
        *並會將mtd_info結構體中的函式用nand的函式來過載,實現了mtd到底層驅動的聯絡。
        *並且在nand_scan函式中會通過讀取nand晶片的裝置號和廠家號自動在晶片列表中尋找相應的型號和引數,並將其註冊進去。*/

        switch (chip->ecc.mode) {

        case NAND_ECC_HW_OOB_FIRST:
                /* Similar to NAND_ECC_HW, but a separate read_page handle */
                if (!chip->ecc.calculate || !chip->ecc.correct ||
                     !chip->ecc.hwctl) {
                        pr_warn("No ECC functions supplied; "
                                   "hardware ECC not possible\n");
                        BUG();
                }

                if (!chip->ecc.read_page)
                        chip->ecc.read_page = nand_read_page_hwecc_oob_first;

        case NAND_ECC_HW:
                /* Use standard hwecc read page function? */
                if (!chip->ecc.read_page)
                        chip->ecc.read_page = nand_read_page_hwecc;
                if (!chip->ecc.write_page)
                        chip->ecc.write_page = nand_write_page_hwecc;
                if (!chip->ecc.read_page_raw)
                        chip->ecc.read_page_raw = nand_read_page_raw;
                if (!chip->ecc.write_page_raw)
                        chip->ecc.write_page_raw = nand_write_page_raw;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_std;
                if (!chip->ecc.write_oob)
                        chip->ecc.write_oob = nand_write_oob_std;
        case NAND_ECC_HW_SYNDROME:
                if ((!chip->ecc.calculate || !chip->ecc.correct ||
                     !chip->ecc.hwctl) &&
                    (!chip->ecc.read_page ||
                     chip->ecc.read_page == nand_read_page_hwecc ||
                     !chip->ecc.write_page ||
                     chip->ecc.write_page == nand_write_page_hwecc)) {
                        pr_warn("No ECC functions supplied; "
                                   "hardware ECC not possible\n");
                        BUG();

                }

                /* Use standard syndrome read/write page function? */
                if (!chip->ecc.read_page)
                        chip->ecc.read_page = nand_read_page_syndrome;
                if (!chip->ecc.write_page)
                        chip->ecc.write_page = nand_write_page_syndrome;
                if (!chip->ecc.read_page_raw)
                        chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
                if (!chip->ecc.write_page_raw)
                        chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_syndrome;
                if (!chip->ecc.write_oob)
                        chip->ecc.write_oob = nand_write_oob_syndrome;
                if (mtd->writesize >= chip->ecc.size) {
                        if (!chip->ecc.strength) {
                                pr_warn("Driver must set ecc.strength when using hardware ECC\n");
                                BUG();
                        }
                        break;
                }
                pr_warn("%d byte HW ECC not possible on "
                           "%d byte page size, fallback to SW ECC\n",
                           chip->ecc.size, mtd->writesize);
                chip->ecc.mode = NAND_ECC_SOFT;

        case NAND_ECC_SOFT:
                chip->ecc.calculate = nand_calculate_ecc;
                chip->ecc.correct = nand_correct_data;
                chip->ecc.read_page = nand_read_page_swecc;
                chip->ecc.read_subpage = nand_read_subpage;
                chip->ecc.write_page = nand_write_page_swecc;
                chip->ecc.read_page_raw = nand_read_page_raw;
                chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
  chip->ecc.write_oob = nand_write_oob_std;
                if (!chip->ecc.size)
                        chip->ecc.size = 256;
                chip->ecc.bytes = 3;
                chip->ecc.strength = 1;
                break;

        case NAND_ECC_SOFT_BCH:
                if (!mtd_nand_has_bch()) {
                        pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
                        BUG();
                }

                chip->ecc.calculate = nand_bch_calculate_ecc;
                chip->ecc.correct = nand_bch_correct_data;
                chip->ecc.read_page = nand_read_page_swecc;
                chip->ecc.read_subpage = nand_read_subpage;
                chip->ecc.write_page = nand_write_page_swecc;
                chip->ecc.read_page_raw = nand_read_page_raw;
                chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.write_oob = nand_write_oob_std;
                /*

                 * Board driver should supply ecc.size and ecc.bytes values to
                 * select how many bits are correctable; see nand_bch_init()
                 * for details. Otherwise, default to 4 bits for large pag
                 * devices.
                 */

                if (!chip->ecc.size && (mtd->oobsize >= 64)) {
                        chip->ecc.size = 512;
                        chip->ecc.bytes = 7;
                }//初始化NAND BCH 糾錯
                chip->ecc.priv = nand_bch_init(mtd,
                                               chip->ecc.size,
                                               chip->ecc.bytes,
                                               &chip->ecc.layout);//建立壞塊表
                if (!chip->ecc.priv) {
                        pr_warn("BCH ECC initialization failed!\n");
                        BUG();
                }

               //Driver must set ecc.strength when using hardware ECC
                chip->ecc.strength =
                        chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
                break;

        case NAND_ECC_NONE:
                pr_warn("NAND_ECC_NONE selected by board driver. "
                           "This is not recommended!\n");
                chip->ecc.read_page = nand_read_page_raw;
                chip->ecc.write_page = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.read_page_raw = nand_read_page_raw;
                chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
                chip->ecc.strength = 0;
                break;



        default:
                pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode);
                BUG();

        }
        /* For many systems, the standard OOB write also works for raw */
        if (!chip->ecc.read_oob_raw)
                chip->ecc.read_oob_raw = chip->ecc.read_oob;
        if (!chip->ecc.write_oob_raw)
                chip->ecc.write_oob_raw = chip->ecc.write_oob;

        /*
         * The number of bytes available for a client to place data into
         * the out of band area.
         */
        chip->ecc.layout->oobavail = 0;
        for (i = 0; chip->ecc.layout->oobfree[i].length
                        && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
                chip->ecc.layout->oobavail +=
                        chip->ecc.layout->oobfree[i].length;
        mtd->oobavail = chip->ecc.layout->oobavail;



        /*
         * Set the number of read / write steps for one page depending on ECC
         * mode.
         */
        chip->ecc.steps = mtd->writesize / chip->ecc.size;
        if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
                pr_warn("Invalid ECC parameters\n");
                BUG();
        }
        chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;


//subpage相關的初始化
        /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
        if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
            !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
                switch (chip->ecc.steps) {

                case 2:
                        mtd->subpage_sft = 1;
                        break;

                case 4:
                case 8:
                case 16:
                        mtd->subpage_sft = 2;
                        break;
                }
        }
        chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

        /* Initialize state */
        chip->state = FL_READY;

        /* Invalidate the pagebuffer reference */
        chip->pagebuf = -1;
//本開發板用不到
        /* Large page NAND with SOFT_ECC should support subpage reads */
        if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
                chip->options |= NAND_SUBPAGE_READ;


//初始化剩餘的mtd_info結構題
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
        mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
                                                MTD_CAP_NANDFLASH;

        mtd->_erase = nand_erase;
        mtd->_point = NULL;
        mtd->_unpoint = NULL;
        mtd->_read = nand_read;
        mtd->_write = nand_write;
        mtd->_panic_write = panic_nand_write;
        mtd->_read_oob = nand_read_oob;
        mtd->_write_oob = nand_write_oob;
        mtd->_sync = nand_sync;
        mtd->_lock = NULL;
        mtd->_unlock = NULL;
        mtd->_suspend = nand_suspend;
        mtd->_resume = nand_resume;
        mtd->_block_isbad = nand_block_isbad;
        mtd->_block_markbad = nand_block_markbad;
        mtd->writebufsize = mtd->writesize;
//把chip中的ecc資訊傳遞給mtd結構題,初始化mtd_info
        /* propagate ecc info to mtd_info */
        mtd->ecclayout = chip->ecc.layout;
        mtd->ecc_strength = chip->ecc.strength;
        /*
         * Initialize bitflip_threshold to its default prior scan_bbt() call.
         * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
         * properly set.
         */
        if (!mtd->bitflip_threshold)
                mtd->bitflip_threshold = mtd->ecc_strength;

        /* Check, if we should skip the bad block table scan */
	//判斷是否跳過壞塊表檢測
        if (chip->options & NAND_SKIP_BBTSCAN)
                return 0;
        /* Build bad block table *///掃描並建立壞塊表
        return chip->scan_bbt(mtd);

}

EXPORT_SYMBOL(nand_scan_tail);