diff options
author | Brian Norris <computersforpeace@gmail.com> | 2016-09-28 13:34:58 -0700 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2016-10-03 09:51:35 -0700 |
commit | 1e2da4ad2e9b23556630bc774d3b1aaa61c0a69e (patch) | |
tree | c886868b46dac770418459eaaa0988504011f2e8 /drivers/mtd/nand/nand_base.c | |
parent | 5e149073ba427f99c5e751e558670d034a278f77 (diff) | |
parent | d44154f969a44269a9288c274c1c2fd9e85df8a5 (diff) | |
download | linux-1e2da4ad2e9b23556630bc774d3b1aaa61c0a69e.tar.gz linux-1e2da4ad2e9b23556630bc774d3b1aaa61c0a69e.tar.bz2 linux-1e2da4ad2e9b23556630bc774d3b1aaa61c0a69e.zip |
Merge tag 'for-4.9' of github.com:linux-nand/linux
"
Notable core changes:
- add the infrastructure to automate NAND timings configuration
- provide a generic DT property to maximize ECC strength
The rest is just a bunch of minor drivers and core fixes/cleanup
patches.
"
Also not noted: some refactoring in the core bad block table handling,
to help with improving some of the logic in error cases.
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'drivers/mtd/nand/nand_base.c')
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 260 |
1 files changed, 234 insertions, 26 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 77533f7f2429..7a00245ebada 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 ouput 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; + } + } + + return 0; +} + +static void nand_release_data_interface(struct nand_chip *chip) +{ + kfree(chip->data_interface); +} + +/** + * nand_reset - Reset and initialize a NAND device + * @chip: The NAND chip + * + * Returns 0 for success or negative error code otherwise + */ +int nand_reset(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = nand_reset_data_interface(chip); + if (ret) + return ret; + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + ret = nand_setup_data_interface(chip); + if (ret) + return ret; + + return 0; +} + +/** * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks * @mtd: mtd info * @ofs: offset to start unlock from @@ -1025,7 +1194,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) * some operation can also clear the bit 7 of status register * eg. erase/program a locked block */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset(chip); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { @@ -1084,7 +1253,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) * some operation can also clear the bit 7 of status register * eg. erase/program a locked block */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset(chip); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { @@ -2162,7 +2331,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int ret = -ENOTSUPP; + int ret; ops->retlen = 0; @@ -2173,24 +2342,18 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - nand_get_device(mtd, FL_READING); - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - case MTD_OPS_RAW: - break; + if (ops->mode != MTD_OPS_PLACE_OOB && + ops->mode != MTD_OPS_AUTO_OOB && + ops->mode != MTD_OPS_RAW) + return -ENOTSUPP; - default: - goto out; - } + nand_get_device(mtd, FL_READING); if (!ops->datbuf) ret = nand_do_read_oob(mtd, from, ops); else ret = nand_do_read_ops(mtd, from, ops); -out: nand_release_device(mtd); return ret; } @@ -2788,7 +2951,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, * if we don't do this. I have no clue why, but I seem to have 'fixed' * it in the doc2000 driver in August 1999. dwmw2. */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset(chip); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { @@ -3191,8 +3354,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->controller) { chip->controller = &chip->hwcontrol; - spin_lock_init(&chip->controller->lock); - init_waitqueue_head(&chip->controller->wq); + nand_hw_control_init(chip->controller); } } @@ -3829,7 +3991,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) * after power-up. */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset(chip); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); @@ -4113,6 +4275,9 @@ static int nand_dt_init(struct nand_chip *chip) if (ecc_step > 0) chip->ecc.size = ecc_step; + if (of_property_read_bool(dn, "nand-ecc-maximize")) + chip->ecc.options |= NAND_ECC_MAXIMIZE; + return 0; } @@ -4141,6 +4306,15 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, if (!mtd->name && mtd->dev.parent) mtd->name = dev_name(mtd->dev.parent); + if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { + /* + * Default functions assigned for chip_select() and + * cmdfunc() both expect cmd_ctrl() to be populated, + * so we need to check that that's the case + */ + pr_err("chip.cmd_ctrl() callback is not provided"); + return -EINVAL; + } /* Set the default functions */ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); @@ -4155,13 +4329,17 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, return PTR_ERR(type); } + ret = nand_init_data_interface(chip); + if (ret) + return ret; + 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); + nand_reset(chip); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ @@ -4221,6 +4399,7 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) ecc->write_page_raw = nand_write_page_raw; ecc->read_oob = nand_read_oob_std; ecc->write_oob = nand_write_oob_std; + /* * Board driver should supply ecc.size and ecc.strength * values to select how many bits are correctable. @@ -4243,6 +4422,25 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) } mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + + } + + /* + * We can only maximize ECC config when the default layout is + * used, otherwise we don't know how many bytes can really be + * used. + */ + if (mtd->ooblayout == &nand_ooblayout_lp_ops && + ecc->options & NAND_ECC_MAXIMIZE) { + int steps, bytes; + + /* Always prefer 1k blocks over 512bytes ones */ + ecc->size = 1024; + steps = mtd->writesize / ecc->size; + + /* Reserve 2 bytes for the BBM */ + bytes = (mtd->oobsize - 2) / steps; + ecc->strength = bytes * 8 / fls(8 * ecc->size); } /* See nand_bch_init() for details. */ @@ -4601,18 +4799,16 @@ int nand_scan(struct mtd_info *mtd, int maxchips) EXPORT_SYMBOL(nand_scan); /** - * nand_release - [NAND Interface] Free resources held by the NAND device - * @mtd: MTD device structure + * nand_cleanup - [NAND Interface] Free resources held by the NAND device + * @chip: NAND chip object */ -void nand_release(struct mtd_info *mtd) +void nand_cleanup(struct nand_chip *chip) { - struct nand_chip *chip = mtd_to_nand(mtd); - if (chip->ecc.mode == NAND_ECC_SOFT && chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); - mtd_device_unregister(mtd); + nand_release_data_interface(chip); /* Free bad block table memory */ kfree(chip->bbt); @@ -4624,6 +4820,18 @@ void nand_release(struct mtd_info *mtd) & NAND_BBT_DYNAMICSTRUCT) kfree(chip->badblock_pattern); } +EXPORT_SYMBOL_GPL(nand_cleanup); + +/** + * nand_release - [NAND Interface] Unregister the MTD device and free resources + * held by the NAND device + * @mtd: MTD device structure + */ +void nand_release(struct mtd_info *mtd) +{ + mtd_device_unregister(mtd); + nand_cleanup(mtd_to_nand(mtd)); +} EXPORT_SYMBOL_GPL(nand_release); MODULE_LICENSE("GPL"); |