diff options
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 157 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 14 |
2 files changed, 167 insertions, 4 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1a6310573ab4..f39775b05779 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -948,6 +948,148 @@ 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 * @@ -956,9 +1098,18 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) 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; } @@ -4172,6 +4323,10 @@ 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 */ @@ -4631,6 +4786,8 @@ void nand_release(struct mtd_info *mtd) mtd_device_unregister(mtd); + nand_release_data_interface(chip); + /* Free bad block table memory */ kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS)) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0c9412c2d80b..d3e3f8d03336 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -751,10 +751,9 @@ nand_get_sdr_timings(const struct nand_data_interface *conf) * also from the datasheet. It is the recommended ECC step * size, if known; if unknown, set to zero. * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is - * either deduced from the datasheet if the NAND - * chip is not ONFI compliant or set to 0 if it is - * (an ONFI chip is always configured in mode 0 - * after a NAND reset) + * set to the actually used ONFI mode if the chip is + * ONFI compliant or deduced from the datasheet if + * the NAND chip is not ONFI compliant. * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 @@ -774,6 +773,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf) * @read_retries: [INTERN] the number of read retry modes supported * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand + * @setup_data_interface: [OPTIONAL] setup the data interface and timing * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -820,6 +820,10 @@ struct nand_chip { int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); + int (*setup_data_interface)(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only); + int chip_delay; unsigned int options; @@ -849,6 +853,8 @@ struct nand_chip { struct nand_jedec_params jedec_params; }; + struct nand_data_interface *data_interface; + int read_retries; flstate_t state; |