/* * Nominally, the purpose of this function is to look for or create the bad * block table. In fact, since the we call this function at the very end of * the initialization process started by nand_scan(), and we doesn't have a * more formal mechanism, we "hook" this function to continue init process. * * At this point, the physical NAND Flash chips have been identified and * counted, so we know the physical geometry. This enables us to make some * important configuration decisions. * * The return value of this function propogates directly back to this driver's * call to nand_scan(). Anything other than zero will cause this driver to * tear everything down and declare failure. */ static int mxs_nand_scan_bbt(struct mtd_info *mtd) { struct nand_chip *nand = mtd->priv; struct mxs_nand_info *nand_info = nand->priv; struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; uint32_t tmp; /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); /* Configure layout 0 */ tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; writel(tmp, &bch_regs->hw_bch_flash0layout0); tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; writel(tmp, &bch_regs->hw_bch_flash0layout1); /* Set *all* chip selects to use layout 0 */ writel(0, &bch_regs->hw_bch_layoutselect); /* Enable BCH complete interrupt */ writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); /* Hook some operations at the MTD level. */ if (mtd->_read_oob != mxs_nand_hook_read_oob) { nand_info->hooked_read_oob = mtd->_read_oob; mtd->_read_oob = mxs_nand_hook_read_oob; } if (mtd->_write_oob != mxs_nand_hook_write_oob) { nand_info->hooked_write_oob = mtd->_write_oob; mtd->_write_oob = mxs_nand_hook_write_oob; } if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { nand_info->hooked_block_markbad = mtd->_block_markbad; mtd->_block_markbad = mxs_nand_hook_block_markbad; } /* We use the reference implementation for bad block management. */ return nand_default_bbt(mtd); }
/* * Probe for NAND controller */ static int __init lpc32xx_nand_probe(struct platform_device *pdev) { struct lpc32xx_nand_host *host; struct mtd_info *mtd; struct nand_chip *chip; struct resource *rc; int res; rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (rc == NULL) { dev_err(&pdev->dev,"No memory resource found for" " device\n"); return -ENXIO; } /* Allocate memory for the device structure (and zero it) */ host = kzalloc(sizeof(struct lpc32xx_nand_host), GFP_KERNEL); if (!host) { dev_err(&pdev->dev,"failed to allocate device structure\n"); return -ENOMEM; } host->io_base_dma = (dma_addr_t) rc->start; host->io_base = ioremap(rc->start, rc->end - rc->start + 1); if (host->io_base == NULL) { dev_err(&pdev->dev,"ioremap failed\n"); res = -EIO; goto err_exit1; } host->ncfg = pdev->dev.platform_data; if (!host->ncfg) { dev_err(&pdev->dev,"Missing platform data\n"); res = -ENOENT; goto err_exit1; } mtd = &host->mtd; chip = &host->nand_chip; chip->priv = host; mtd->priv = chip; mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; /* Get NAND clock */ host->clk = clk_get(&pdev->dev, "nand_ck"); if (IS_ERR(host->clk)) { dev_err(&pdev->dev,"Clock failure\n"); res = -ENOENT; goto err_exit2; } clk_enable(host->clk); /* Set NAND IO addresses and command/ready functions */ chip->IO_ADDR_R = SLC_DATA(host->io_base); chip->IO_ADDR_W = SLC_DATA(host->io_base); chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; chip->dev_ready = lpc32xx_nand_device_ready; chip->chip_delay = 20; /* 20us command delay time */ /* Init NAND controller */ lpc32xx_nand_setup(host); lpc32xx_wp_disable(host); platform_set_drvdata(pdev, host); /* NAND callbacks for LPC32xx SLC hardware */ chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->read_byte = lpc32xx_nand_read_byte; chip->read_buf = lpc32xx_nand_read_buf; chip->write_buf = lpc32xx_nand_write_buf; chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; chip->ecc.calculate = lpc32xx_nand_ecc_calculate; chip->ecc.correct = nand_correct_data; chip->ecc.hwctl = lpc32xx_nand_ecc_enable; chip->verify_buf = lpc32xx_verify_buf; /* * Allocate a large enough buffer for a single huge page plus * extra space for the spare area and ECC storage area */ host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; host->data_buf = dma_alloc_coherent(&pdev->dev, host->dma_buf_len, &host->data_buf_dma, GFP_KERNEL); if (host->data_buf == NULL) { dev_err(&pdev->dev, "Error allocating memory\n"); res = -ENOMEM; goto err_exit3; } /* Get free DMA channel and alloc DMA descriptor link list */ res = lpc32xx_nand_dma_setup(host, LPC32XX_MAX_DMA_DESCRIPTORS); if(res) { res = -EIO; goto err_exit4; } init_waitqueue_head(&host->dma_waitq); /* Find NAND device */ if (nand_scan_ident(mtd, 1)) { res = -ENXIO; goto err_exit5; } /* OOB and ECC CPU and DMA work areas */ host->ecc_buf_dma = host->data_buf_dma + LPC32XX_DMA_DATA_SIZE; host->ecc_buf = (uint32_t *) (host->data_buf + LPC32XX_DMA_DATA_SIZE); /* * Small page FLASH has a unique OOB layout, but large and huge * page FLASH use the standard layout. Small page FLASH uses a * custom BBT marker layout. */ if (mtd->writesize <= 512) chip->ecc.layout = &lpc32xx_nand_oob_16; /* These sizes remain the same regardless of page size */ chip->ecc.size = 256; chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; chip->ecc.prepad = chip->ecc.postpad = 0; /* Avoid extra scan if using BBT, setup BBT support */ if (host->ncfg->use_bbt) { chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; /* * Use a custom BBT marker setup for small page FLASH that * won't interfere with the ECC layout. Large and huge page * FLASH use the standard layout. */ if (mtd->writesize <= 512) { chip->bbt_td = &bbt_smallpage_main_descr; chip->bbt_md = &bbt_smallpage_mirror_descr; } } /* * Fills out all the uninitialized function pointers with the defaults */ if (nand_scan_tail(mtd)) { res = -ENXIO; goto err_exit5; } /* Standard layout in FLASH for bad block tables */ if (host->ncfg->use_bbt) { if (nand_default_bbt(mtd) < 0) dev_err(&pdev->dev, "Error initializing default bad" " block tables\n"); } res = lpc32xx_add_partitions(host); if (!res) return res; nand_release(mtd); err_exit5: /* Free the DMA channel used by us */ lpc32xx_dma_ch_disable(host->dmach); lpc32xx_dma_dealloc_llist(host->dmach); lpc32xx_dma_ch_put(host->dmach); host->dmach = -1; err_exit4: dma_free_coherent(&pdev->dev, host->dma_buf_len, host->data_buf, host->data_buf_dma); err_exit3: clk_disable(host->clk); clk_put(host->clk); platform_set_drvdata(pdev, NULL); err_exit2: lpc32xx_wp_enable(host); iounmap(host->io_base); err_exit1: kfree(host); return res; }
/* * Nominally, the purpose of this function is to look for or create the bad * block table. In fact, since the we call this function at the very end of * the initialization process started by nand_scan(), and we don't have a * more formal mechanism, we "hook" this function to continue init process. * * At this point, the physical NAND Flash chips have been identified and * counted, so we know the physical geometry. This enables us to make some * important configuration decisions. * * The return value of this function propogates directly back to this driver's * call to nand_scan(). Anything other than zero will cause this driver to * tear everything down and declare failure. */ static int mxs_nand_scan_bbt(struct mtd_info *mtd) { struct nand_chip *nand = mtd->priv; struct mxs_nand_info *nand_info = nand->priv; uint32_t tmp; /* Configure BCH and set NFC geometry */ if (readl(&bch_regs->hw_bch_ctrl_reg) & (BCH_CTRL_SFTRST | BCH_CTRL_CLKGATE)) /* When booting from NAND the BCH engine will already * be operational and obviously does not like being reset here. * There will be occasional read errors upon boot when this * reset is done. */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); readl(&bch_regs->hw_bch_ctrl_reg); debug("mtd->writesize=%d\n", mtd->writesize); debug("mtd->oobsize=%d\n", mtd->oobsize); debug("ecc_strength=%d\n", mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize)); /* Configure layout 0 */ tmp = (mxs_nand_ecc_chunk_cnt(mtd) - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; writel(tmp, &bch_regs->hw_bch_flash0layout0); tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; writel(tmp, &bch_regs->hw_bch_flash0layout1); /* Set *all* chip selects to use layout 0 */ writel(0, &bch_regs->hw_bch_layoutselect); /* Enable BCH complete interrupt */ writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); /* Hook some operations at the MTD level. */ if (mtd->read_oob != mxs_nand_hook_read_oob) { nand_info->hooked_read_oob = mtd->read_oob; mtd->read_oob = mxs_nand_hook_read_oob; } if (mtd->write_oob != mxs_nand_hook_write_oob) { nand_info->hooked_write_oob = mtd->write_oob; mtd->write_oob = mxs_nand_hook_write_oob; } if (mtd->block_markbad != mxs_nand_hook_block_markbad) { nand_info->hooked_block_markbad = mtd->block_markbad; mtd->block_markbad = mxs_nand_hook_block_markbad; } /* We use the reference implementation for bad block management. */ return nand_default_bbt(mtd); }
/* * Nominally, the purpose of this function is to look for or create the bad * block table. In fact, since the we call this function at the very end of * the initialization process started by nand_scan(), and we doesn't have a * more formal mechanism, we "hook" this function to continue init process. * * At this point, the physical NAND Flash chips have been identified and * counted, so we know the physical geometry. This enables us to make some * important configuration decisions. * * The return value of this function propagates directly back to this driver's * call to nand_scan(). Anything other than zero will cause this driver to * tear everything down and declare failure. */ static int mxs_nand_scan_bbt(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(nand); struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; uint32_t tmp; if (mtd->oobsize > MXS_NAND_CHUNK_DATA_CHUNK_SIZE) { galois_field = 14; chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 2; } if (mtd->oobsize > chunk_data_size) { printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", chunk_data_size); return -EINVAL; } /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); /* Configure layout 0 */ tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; tmp |= (14 == galois_field ? 1 : 0) << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout0); tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; tmp |= (14 == galois_field ? 1 : 0) << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout1); /* Set *all* chip selects to use layout 0 */ writel(0, &bch_regs->hw_bch_layoutselect); /* Enable BCH complete interrupt */ writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); /* Hook some operations at the MTD level. */ if (mtd->_read_oob != mxs_nand_hook_read_oob) { nand_info->hooked_read_oob = mtd->_read_oob; mtd->_read_oob = mxs_nand_hook_read_oob; } if (mtd->_write_oob != mxs_nand_hook_write_oob) { nand_info->hooked_write_oob = mtd->_write_oob; mtd->_write_oob = mxs_nand_hook_write_oob; } if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { nand_info->hooked_block_markbad = mtd->_block_markbad; mtd->_block_markbad = mxs_nand_hook_block_markbad; } /* We use the reference implementation for bad block management. */ return nand_default_bbt(mtd); }