diff options
author | Miquel Raynal <miquel.raynal@bootlin.com> | 2018-03-19 14:47:24 +0100 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@bootlin.com> | 2018-03-20 09:49:41 +0100 |
commit | 415ae78ffb5d97343ada8e4c2d4bda5f6416b5ef (patch) | |
tree | c96826801c39c69cf2c881ea3c8dda2c552226bd /drivers/mtd | |
parent | 29714d6bd72b2de163a89240a70550f23d25c324 (diff) |
mtd: rawnand: check ONFI timings have been acked by the chip
Choosing ONFI timings when ->set/get_features() calls are supported
by the NAND chip is a matter of reading the chip's ONFI parameter page
and telling the chip the chosen mode (between all of the supported ones)
with ->set_feature().
Add a check on whether the chip "acked" the timing mode or not.
This can be a problem for NAND chips that do not follow entirely the
ONFI specification. These chips actually support other modes than
"mode 0", but either:
1/ do not update the timing mode register once a timing mode has been
selected.
or
2/ do not support the TIMING_MODE featured and thus do not require users
to change the timing mode at all.
These issues will be addressed in another patch that will add the
feature to overwrite NAND chips features within the parameter page, from
the NAND chip driver.
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Han Xu <han.xu@nxp.com>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Diffstat (limited to 'drivers/mtd')
-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; } /** |