diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/raw/nand_base.c | 36 |
1 files changed, 35 insertions, 1 deletions
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 9d5c2739fe6d..837ea698af08 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1283,7 +1283,41 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) } /* Change the mode on the controller side */ - return chip->setup_data_interface(mtd, chipnr, &chip->data_interface); + ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); + if (ret) + return ret; + + /* Check the mode has been accepted by the chip, if supported */ + if (!nand_supports_set_get_features(chip)) + return 0; + + memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN); + chip->select_chip(mtd, chipnr); + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + chip->select_chip(mtd, -1); + if (ret) + goto err_reset_chip; + + if (tmode_param[0] != chip->onfi_timing_mode_default) { + pr_warn("timing mode %d not acknowledged by the NAND chip\n", + chip->onfi_timing_mode_default); + goto err_reset_chip; + } + + return 0; + +err_reset_chip: + /* + * Fallback to mode 0 if the chip explicitly did not ack the chosen + * timing mode. + */ + nand_reset_data_interface(chip, chipnr); + chip->select_chip(mtd, chipnr); + nand_reset_op(chip); + chip->select_chip(mtd, -1); + + return ret; } /** |