Add a new helper: given a data interface with a specific set of timings, check with the controller if this interface can be supported. If not, fallback to the closest slower ONFI mode. Extract this logic from nand_choose_data_interface() and use the new helper instead, so that this code can be reused later. Signed-off-by: Miquel Raynal <miquel.raynal@xxxxxxxxxxx> --- drivers/mtd/nand/raw/nand_base.c | 74 ++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 776f2d119bad..15e10f045c9f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1004,6 +1004,42 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return ret; } +/** + * nand_choose_best_sdr_iface - given a data interface, find the closest + * mode/timings set for this interface supported + * by both the NAND controller and the NAND chip + * @chip: the NAND chip + * @best_iface: the best data interface (can eventually be updated) + */ +static int nand_choose_best_sdr_iface(struct nand_chip *chip, + struct nand_data_interface *best_iface) +{ + const struct nand_controller_ops *ops = chip->controller->ops; + int mode, ret; + + /* Verify the controller supports the requested interface */ + ret = ops->setup_data_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, + best_iface); + if (!ret) + return ret; + + /* Fallback to slower modes */ + for (mode = best_iface->timings.mode - 1; mode >= 0; mode--) { + ret = onfi_fill_data_interface(chip, best_iface, + NAND_SDR_IFACE, mode); + if (ret) + continue; + + ret = ops->setup_data_interface(chip, + NAND_DATA_IFACE_CHECK_ONLY, + best_iface); + if (!ret) + break; + } + + return 0; +} + /** * nand_choose_data_interface - find the best data interface and timings * @chip: The NAND chip @@ -1019,7 +1055,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) */ static int nand_choose_data_interface(struct nand_chip *chip) { - int modes, mode, ret; + int best_mode, ret; if (!nand_controller_has_setup_data_iface(chip)) return 0; @@ -1029,35 +1065,17 @@ static int nand_choose_data_interface(struct nand_chip *chip) * if the NAND does not support ONFI, fallback to the default ONFI * timing mode. */ - if (chip->parameters.onfi) { - modes = chip->parameters.onfi->async_timing_mode; - } else { - if (!chip->default_timing_mode) - return 0; + if (chip->parameters.onfi) + best_mode = fls(chip->parameters.onfi->async_timing_mode) - 1; + else + best_mode = chip->default_timing_mode; - modes = GENMASK(chip->default_timing_mode, 0); - } + ret = onfi_fill_data_interface(chip, &chip->data_interface, + NAND_SDR_IFACE, best_mode); + if (ret) + return ret; - for (mode = fls(modes) - 1; mode >= 0; mode--) { - ret = onfi_fill_data_interface(chip, &chip->data_interface, - NAND_SDR_IFACE, mode); - if (ret) - continue; - - /* - * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the - * controller supports the requested timings. - */ - ret = chip->controller->ops->setup_data_interface(chip, - NAND_DATA_IFACE_CHECK_ONLY, - &chip->data_interface); - if (!ret) { - chip->default_timing_mode = mode; - break; - } - } - - return 0; + return nand_choose_best_sdr_iface(chip, &chip->data_interface); } /** -- 2.20.1 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/