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
nand_scan_tail()進行了ECC的設定和剩下的MTD驅動函式的初始化。
-若有分割槽,則以mtd_info和mtd_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
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);