diff options
28 files changed, 1263 insertions, 459 deletions
diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index 3733300de8dd..b05601600083 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -35,6 +35,15 @@ Optional NAND chip properties: - nand-ecc-step-size: integer representing the number of data bytes that are covered by a single ECC step. +- nand-ecc-maximize: boolean used to specify that you want to maximize ECC + strength. The maximum ECC strength is both controller and + chip dependent. The controller side has to select the ECC + config providing the best strength and taking the OOB area + size constraint into account. + This is particularly useful when only the in-band area is + used by the upper layers, and you want to make your NAND + as reliable as possible. + The ECC strength and ECC step size properties define the correction capability of a controller. Together, they say a controller can correct "{strength} bit errors per {size} bytes". diff --git a/MAINTAINERS b/MAINTAINERS index a009e004f8f7..4347bce8ada6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6142,6 +6142,12 @@ M: Zubair Lutfullah Kakakhel <Zubair.Kakakhel@imgtec.com> S: Maintained F: drivers/dma/dma-jz4780.c +INGENIC JZ4780 NAND DRIVER +M: Harvey Hunt <harveyhuntnexus@gmail.com> +L: linux-mtd@lists.infradead.org +S: Maintained +F: drivers/mtd/nand/jz4780_* + INTEGRITY MEASUREMENT ARCHITECTURE (IMA) M: Mimi Zohar <zohar@linux.vnet.ibm.com> M: Dmitry Kasatkin <dmitry.kasatkin@gmail.com> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index e3936b847c6b..4f270482cfd0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -376,6 +376,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, } /** + * mtd_wunit_to_pairing_info - get pairing information of a wunit + * @mtd: pointer to new MTD device info structure + * @wunit: write unit we are interested in + * @info: returned pairing information + * + * Retrieve pairing information associated to the wunit. + * This is mainly useful when dealing with MLC/TLC NANDs where pages can be + * paired together, and where programming a page may influence the page it is + * paired with. + * The notion of page is replaced by the term wunit (write-unit) to stay + * consistent with the ->writesize field. + * + * The @wunit argument can be extracted from an absolute offset using + * mtd_offset_to_wunit(). @info is filled with the pairing information attached + * to @wunit. + * + * From the pairing info the MTD user can find all the wunits paired with + * @wunit using the following loop: + * + * for (i = 0; i < mtd_pairing_groups(mtd); i++) { + * info.pair = i; + * mtd_pairing_info_to_wunit(mtd, &info); + * ... + * } + */ +int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, + struct mtd_pairing_info *info) +{ + int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd); + + if (wunit < 0 || wunit >= npairs) + return -EINVAL; + + if (mtd->pairing && mtd->pairing->get_info) + return mtd->pairing->get_info(mtd, wunit, info); + + info->group = 0; + info->pair = wunit; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info); + +/** + * mtd_wunit_to_pairing_info - get wunit from pairing information + * @mtd: pointer to new MTD device info structure + * @info: pairing information struct + * + * Returns a positive number representing the wunit associated to the info + * struct, or a negative error code. + * + * This is the reverse of mtd_wunit_to_pairing_info(), and can help one to + * iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info() + * doc). + * + * It can also be used to only program the first page of each pair (i.e. + * page attached to group 0), which allows one to use an MLC NAND in + * software-emulated SLC mode: + * + * info.group = 0; + * npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd); + * for (info.pair = 0; info.pair < npairs; info.pair++) { + * wunit = mtd_pairing_info_to_wunit(mtd, &info); + * mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit), + * mtd->writesize, &retlen, buf + (i * mtd->writesize)); + * } + */ +int mtd_pairing_info_to_wunit(struct mtd_info *mtd, + const struct mtd_pairing_info *info) +{ + int ngroups = mtd_pairing_groups(mtd); + int npairs = mtd_wunit_per_eb(mtd) / ngroups; + + if (!info || info->pair < 0 || info->pair >= npairs || + info->group < 0 || info->group >= ngroups) + return -EINVAL; + + if (mtd->pairing && mtd->pairing->get_wunit) + return mtd->pairing->get_wunit(mtd, info); + + return info->pair; +} +EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit); + +/** + * mtd_pairing_groups - get the number of pairing groups + * @mtd: pointer to new MTD device info structure + * + * Returns the number of pairing groups. + * + * This number is usually equal to the number of bits exposed by a single + * cell, and can be used in conjunction with mtd_pairing_info_to_wunit() + * to iterate over all pages of a given pair. + */ +int mtd_pairing_groups(struct mtd_info *mtd) +{ + if (!mtd->pairing || !mtd->pairing->ngroups) + return 1; + + return mtd->pairing->ngroups; +} +EXPORT_SYMBOL_GPL(mtd_pairing_groups); + +/** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure * diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 1f13e32556f8..c1f34f04e338 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -317,6 +317,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) return res; } +static int part_get_device(struct mtd_info *mtd) +{ + struct mtd_part *part = mtd_to_part(mtd); + return part->master->_get_device(part->master); +} + +static void part_put_device(struct mtd_info *mtd) +{ + struct mtd_part *part = mtd_to_part(mtd); + part->master->_put_device(part->master); +} + static int part_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -397,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.oobsize = master->oobsize; slave->mtd.oobavail = master->oobavail; slave->mtd.subpage_sft = master->subpage_sft; + slave->mtd.pairing = master->pairing; slave->mtd.name = name; slave->mtd.owner = master->owner; @@ -463,6 +476,12 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd._block_isbad = part_block_isbad; if (master->_block_markbad) slave->mtd._block_markbad = part_block_markbad; + + if (master->_get_device) + slave->mtd._get_device = part_get_device; + if (master->_put_device) + slave->mtd._put_device = part_put_device; + slave->mtd._erase = part_erase; slave->master = master; slave->offset = part->offset; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 21ff58099f3b..7b7a887b4709 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -88,11 +88,11 @@ config MTD_NAND_AMS_DELTA Support for NAND flash on Amstrad E3 (Delta). config MTD_NAND_OMAP2 - tristate "NAND Flash device on OMAP2, OMAP3 and OMAP4" - depends on ARCH_OMAP2PLUS + tristate "NAND Flash device on OMAP2, OMAP3, OMAP4 and Keystone" + depends on (ARCH_OMAP2PLUS || ARCH_KEYSTONE) help - Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 - platforms. + Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4 + and Keystone platforms. config MTD_NAND_OMAP_BCH depends on MTD_NAND_OMAP2 @@ -428,7 +428,7 @@ config MTD_NAND_ORION config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" - depends on PPC + depends on FSL_SOC select FSL_LBC help Various Freescale chips, including the 8313, include a NAND Flash @@ -438,7 +438,7 @@ config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_IFC tristate "NAND support for Freescale IFC controller" - depends on MTD_NAND && (FSL_SOC || ARCH_LAYERSCAPE) + depends on FSL_SOC || ARCH_LAYERSCAPE select FSL_IFC select MEMORY help diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 37da4236ab90..3962f55bd034 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -761,8 +761,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); - spin_lock_init(&info->controller.lock); - init_waitqueue_head(&info->controller.wq); + nand_hw_control_init(&info->controller); info->device = &pdev->dev; info->platform = plat; diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 8eb2c64df38c..9d2424bfdbf5 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1336,7 +1336,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, u32 *flash_cache = (u32 *)ctrl->flash_cache; int i; - brcmnand_soc_data_bus_prepare(ctrl->soc); + brcmnand_soc_data_bus_prepare(ctrl->soc, true); /* * Must cache the FLASH_CACHE now, since changes in @@ -1349,7 +1349,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, */ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); - brcmnand_soc_data_bus_unprepare(ctrl->soc); + brcmnand_soc_data_bus_unprepare(ctrl->soc, true); /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ if (host->hwcfg.sector_size_1k) @@ -1565,12 +1565,12 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, brcmnand_waitfunc(mtd, chip); if (likely(buf)) { - brcmnand_soc_data_bus_prepare(ctrl->soc); + brcmnand_soc_data_bus_prepare(ctrl->soc, false); for (j = 0; j < FC_WORDS; j++, buf++) *buf = brcmnand_read_fc(ctrl, j); - brcmnand_soc_data_bus_unprepare(ctrl->soc); + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); } if (oob) @@ -1815,12 +1815,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); if (buf) { - brcmnand_soc_data_bus_prepare(ctrl->soc); + brcmnand_soc_data_bus_prepare(ctrl->soc, false); for (j = 0; j < FC_WORDS; j++, buf++) brcmnand_write_fc(ctrl, j, *buf); - brcmnand_soc_data_bus_unprepare(ctrl->soc); + brcmnand_soc_data_bus_unprepare(ctrl->soc, false); } else if (oob) { for (j = 0; j < FC_WORDS; j++) brcmnand_write_fc(ctrl, j, 0xffffffff); @@ -2370,8 +2370,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) init_completion(&ctrl->done); init_completion(&ctrl->dma_done); - spin_lock_init(&ctrl->controller.lock); - init_waitqueue_head(&ctrl->controller.wq); + nand_hw_control_init(&ctrl->controller); INIT_LIST_HEAD(&ctrl->host_list); /* NAND register range */ diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h index ef5eabba88e5..5c44cd4aba87 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/brcmnand/brcmnand.h @@ -23,19 +23,22 @@ struct dev_pm_ops; struct brcmnand_soc { bool (*ctlrdy_ack)(struct brcmnand_soc *soc); void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); - void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, + bool is_param); }; -static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc) +static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc, + bool is_param) { if (soc && soc->prepare_data_bus) - soc->prepare_data_bus(soc, true); + soc->prepare_data_bus(soc, true, is_param); } -static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc) +static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc, + bool is_param) { if (soc && soc->prepare_data_bus) - soc->prepare_data_bus(soc, false); + soc->prepare_data_bus(soc, false, is_param); } static inline u32 brcmnand_readl(void __iomem *addr) diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/brcmnand/iproc_nand.c index 585596c549b2..4c6ae113664d 100644 --- a/drivers/mtd/nand/brcmnand/iproc_nand.c +++ b/drivers/mtd/nand/brcmnand/iproc_nand.c @@ -74,7 +74,8 @@ static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en) spin_unlock_irqrestore(&priv->idm_lock, flags); } -static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) +static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare, + bool is_param) { struct iproc_nand_soc *priv = container_of(soc, struct iproc_nand_soc, soc); @@ -86,10 +87,19 @@ static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare) val = brcmnand_readl(mmio); - if (prepare) - val |= IPROC_NAND_APB_LE_MODE; - else + /* + * In the case of BE or when dealing with NAND data, alway configure + * the APB bus to LE mode before accessing the FIFO and back to BE mode + * after the access is done + */ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) { + if (prepare) + val |= IPROC_NAND_APB_LE_MODE; + else + val &= ~IPROC_NAND_APB_LE_MODE; + } else { /* when in LE accessing the parameter page, keep APB in BE */ val &= ~IPROC_NAND_APB_LE_MODE; + } brcmnand_writel(val, mmio); diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 47316998017f..7af2a3cd949e 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1249,8 +1249,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; nand->controller = &nand->hwcontrol; - spin_lock_init(&nand->controller->lock); - init_waitqueue_head(&nand->controller->wq); + nand_hw_control_init(nand->controller); /* methods */ nand->cmdfunc = docg4_command; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 60a88f24c6b3..113f76e59937 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -879,8 +879,7 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) } elbc_fcm_ctrl->counter++; - spin_lock_init(&elbc_fcm_ctrl->controller.lock); - init_waitqueue_head(&elbc_fcm_ctrl->controller.wq); + nand_hw_control_init(&elbc_fcm_ctrl->controller); fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; } else { elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 4e9e5fd8faf3..0a177b1bfe3e 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -987,8 +987,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) ifc_nand_ctrl->addr = NULL; fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; - spin_lock_init(&ifc_nand_ctrl->controller.lock); - init_waitqueue_head(&ifc_nand_ctrl->controller.wq); + nand_hw_control_init(&ifc_nand_ctrl->controller); } else { ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; } diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 6e461560c6a8..6c062b8251d2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -318,7 +318,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) return -EINVAL; } - geo->page_size = mtd->writesize + mtd->oobsize; + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; geo->payload_size = mtd->writesize; /* diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c index 175f67da25af..a39bb70175ee 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/jz4780_nand.c @@ -368,9 +368,8 @@ static int jz4780_nand_probe(struct platform_device *pdev) nfc->dev = dev; nfc->num_banks = num_banks; - spin_lock_init(&nfc->controller.lock); + nand_hw_control_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); - init_waitqueue_head(&nfc->controller.wq); ret = jz4780_nand_init_chips(nfc, pdev); if (ret) { diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 57cbe2b83849..d7f724b24fd7 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -152,6 +152,9 @@ struct mxc_nand_devtype_data { void (*select_chip)(struct mtd_info *mtd, int chip); int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + int (*setup_data_interface)(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only); /* * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked @@ -1012,6 +1015,82 @@ static void preset_v1(struct mtd_info *mtd) writew(0x4, NFC_V1_V2_WRPROT); } +static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int tRC_min_ns, tRC_ps, ret; + unsigned long rate, rate_round; + const struct nand_sdr_timings *timings; + u16 config1; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + config1 = readw(NFC_V1_V2_CONFIG1); + + tRC_min_ns = timings->tRC_min / 1000; + rate = 1000000000 / tRC_min_ns; + + /* + * For tRC < 30ns we have to use EDO mode. In this case the controller + * does one access per clock cycle. Otherwise the controller does one + * access in two clock cycles, thus we have to double the rate to the + * controller. + */ + if (tRC_min_ns < 30) { + rate_round = clk_round_rate(host->clk, rate); + config1 |= NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000); + } else { + rate *= 2; + rate_round = clk_round_rate(host->clk, rate); + config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000 / 2); + } + + /* + * The timing values compared against are from the i.MX25 Automotive + * datasheet, Table 50. NFC Timing Parameters + */ + if (timings->tCLS_min > tRC_ps - 1000 || + timings->tCLH_min > tRC_ps - 2000 || + timings->tCS_min > tRC_ps - 1000 || + timings->tCH_min > tRC_ps - 2000 || + timings->tWP_min > tRC_ps - 1500 || + timings->tALS_min > tRC_ps || + timings->tALH_min > tRC_ps - 3000 || + timings->tDS_min > tRC_ps || + timings->tDH_min > tRC_ps - 5000 || + timings->tWC_min > 2 * tRC_ps || + timings->tWH_min > tRC_ps - 2500 || + timings->tRR_min > 6 * tRC_ps || + timings->tRP_min > 3 * tRC_ps / 2 || + timings->tRC_min > 2 * tRC_ps || + timings->tREH_min > (tRC_ps / 2) - 2500) { + dev_dbg(host->dev, "Timing out of bounds\n"); + return -EINVAL; + } + + if (check_only) + return 0; + + ret = clk_set_rate(host->clk, rate); + if (ret) + return ret; + + writew(config1, NFC_V1_V2_CONFIG1); + + dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, + config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : + "normal"); + + return 0; +} + static void preset_v2(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd_to_nand(mtd); @@ -1239,6 +1318,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + host->buf_start = 0; + + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, subfeature_param[i]); + + memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); + host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false); + mxc_do_addr_cycle(mtd, addr, -1); + host->devtype_data->send_page(mtd, NFC_INPUT); + + return 0; +} + +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int i; + + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -EINVAL; + + host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); + mxc_do_addr_cycle(mtd, addr, -1); + host->devtype_data->send_page(mtd, NFC_OUTPUT); + memcpy32_fromio(host->data_buf, host->main_area0, 512); + host->buf_start = 0; + + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + *subfeature_param++ = chip->read_byte(mtd); + + return 0; +} + /* * The generic flash bbt decriptors overlap with our ecc * hardware, so define some i.MX specific ones. @@ -1327,6 +1457,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v2, .correct_data = mxc_nand_correct_data_v2_v3, + .setup_data_interface = mxc_nand_v2_setup_data_interface, .irqpending_quirk = 0, .needs_ip = 0, .regs_offset = 0x1e00, @@ -1434,7 +1565,7 @@ static const struct platform_device_id mxcnd_devtype[] = { }; MODULE_DEVICE_TABLE(platform, mxcnd_devtype); -#ifdef CONFIG_OF_MTD +#ifdef CONFIG_OF static const struct of_device_id mxcnd_dt_ids[] = { { .compatible = "fsl,imx21-nand", @@ -1513,6 +1644,8 @@ static int mxcnd_probe(struct platform_device *pdev) this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; + this->onfi_set_features = mxc_nand_onfi_set_features; + this->onfi_get_features = mxc_nand_onfi_get_features; host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) @@ -1533,6 +1666,8 @@ static int mxcnd_probe(struct platform_device *pdev) if (err < 0) return err; + this->setup_data_interface = host->devtype_data->setup_data_interface; + if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs_ip = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 77533f7f2429..e5718e5ecf92 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -745,7 +745,10 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; - chip->cmd_ctrl(mtd, column >> 8, ctrl); + + /* Only output a single addr cycle for 8bits opcodes. */ + if (!nand_opcode_8bits(command)) + chip->cmd_ctrl(mtd, column >> 8, ctrl); } if (page_addr != -1) { chip->cmd_ctrl(mtd, page_addr, ctrl); @@ -948,6 +951,172 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** + * nand_reset_data_interface - Reset data interface and timings + * @chip: The NAND chip + * + * Reset the Data interface and timings to ONFI mode 0. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_reset_data_interface(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_data_interface *conf; + int ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * The ONFI specification says: + * " + * To transition from NV-DDR or NV-DDR2 to the SDR data + * interface, the host shall use the Reset (FFh) command + * using SDR timing mode 0. A device in any timing mode is + * required to recognize Reset (FFh) command issued in SDR + * timing mode 0. + * " + * + * Configure the data interface in SDR mode and set the + * timings to timing mode 0. + */ + + conf = nand_get_default_data_interface(); + ret = chip->setup_data_interface(mtd, conf, false); + if (ret) + pr_err("Failed to configure data interface to SDR timing mode 0\n"); + + return ret; +} + +/** + * nand_setup_data_interface - Setup the best data interface and timings + * @chip: The NAND chip + * + * Find and configure the best data interface and NAND timings supported by + * the chip and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_setup_data_interface(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (!chip->setup_data_interface || !chip->data_interface) + return 0; + + /* + * Ensure the timing mode has been changed on the chip side + * before changing timings on the controller side. + */ + if (chip->onfi_version) { + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { + chip->onfi_timing_mode_default, + }; + + ret = chip->onfi_set_features(mtd, chip, + ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + if (ret) + goto err; + } + + ret = chip->setup_data_interface(mtd, chip->data_interface, false); +err: + return ret; +} + +/** + * nand_init_data_interface - find the best data interface and timings + * @chip: The NAND chip + * + * Find the best data interface and NAND timings supported by the chip + * and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. After this + * function nand_chip->data_interface is initialized with the best timing mode + * available. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_init_data_interface(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int modes, mode, ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * First try to identify the best timings from ONFI parameters and + * if the NAND does not support ONFI, fallback to the default ONFI + * timing mode. + */ + modes = onfi_get_async_timing_mode(chip); + if (modes == ONFI_TIMING_MODE_UNKNOWN) { + if (!chip->onfi_timing_mode_default) + return 0; + + modes = GENMASK(chip->onfi_timing_mode_default, 0); + } + + chip->data_interface = kzalloc(sizeof(*chip->data_interface), + GFP_KERNEL); + if (!chip->data_interface) + return -ENOMEM; + + for (mode = fls(modes) - 1; mode >= 0; mode--) { + ret = onfi_init_data_interface(chip, chip->data_interface, + NAND_SDR_IFACE, mode); + if (ret) + continue; + + ret = chip->setup_data_interface(mtd, chip->data_interface, + true); + if (!ret) { + chip->onfi_timing_mode_default = mode; + break; + } + } |