summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2020-05-29 13:13:22 +0200
committerMiquel Raynal <miquel.raynal@bootlin.com>2020-06-26 08:35:08 +0200
commit35b6bcc970f759d4a86d6221d09ca28ea20467c8 (patch)
treee4fbcafb8ba76f30656658248fcf31097b469cd4 /drivers/mtd/nand
parenta69ad11168dca68b3f0adc6882422f4a2e2cb050 (diff)
mtd: rawnand: Allocate the interface configurations dynamically
Instead of manipulating the statically allocated structure and copy timings around, allocate one at identification time and save it in the nand_chip structure once it has been initialized. All NAND chips using the same interface configuration during reset and startup, we define a helper to retrieve a single reset interface configuration object, shared across all NAND chips. We use a second pointer to always have a reference on the currently applied interface configuration, which may either point to the "best interface configuration" or to the "default reset interface configuration". Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> Link: https://lore.kernel.org/linux-mtd/20200529111322.7184-29-miquel.raynal@bootlin.com
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/raw/internals.h1
-rw-r--r--drivers/mtd/nand/raw/nand_base.c84
-rw-r--r--drivers/mtd/nand/raw/nand_timings.c6
3 files changed, 59 insertions, 32 deletions
diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h
index 5ebfbb89e572..012876e14317 100644
--- a/drivers/mtd/nand/raw/internals.h
+++ b/drivers/mtd/nand/raw/internals.h
@@ -93,6 +93,7 @@ onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings);
int nand_choose_best_sdr_timings(struct nand_chip *chip,
struct nand_interface_config *iface,
struct nand_sdr_timings *spec_timings);
+const struct nand_interface_config *nand_get_reset_interface_config(void);
int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param);
int nand_read_page_raw_notsupp(struct nand_chip *chip, u8 *buf,
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 753328f106c1..0c768cb88f96 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -928,9 +928,9 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
* timings to timing mode 0.
*/
- onfi_fill_interface_config(chip, &chip->interface_config,
- NAND_SDR_IFACE, 0);
- ret = ops->setup_interface(chip, chipnr, &chip->interface_config);
+ chip->current_interface_config = nand_get_reset_interface_config();
+ ret = ops->setup_interface(chip, chipnr,
+ chip->current_interface_config);
if (ret)
pr_err("Failed to configure data interface to SDR timing mode 0\n");
@@ -949,13 +949,25 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr)
*/
static int nand_setup_interface(struct nand_chip *chip, int chipnr)
{
- u8 mode = chip->interface_config.timings.mode;
- u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { mode, };
+ const struct nand_controller_ops *ops = chip->controller->ops;
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { };
int ret;
if (!nand_controller_can_setup_interface(chip))
return 0;
+ /*
+ * A nand_reset_interface() put both the NAND chip and the NAND
+ * controller in timings mode 0. If the default mode for this chip is
+ * also 0, no need to proceed to the change again. Plus, at probe time,
+ * nand_setup_interface() uses ->set/get_features() which would
+ * fail anyway as the parameter page is not available yet.
+ */
+ if (!chip->best_interface_config)
+ return 0;
+
+ tmode_param[0] = chip->best_interface_config->timings.mode;
+
/* Change the mode on the chip side (if supported by the NAND chip) */
if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
nand_select_target(chip, chipnr);
@@ -967,14 +979,13 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
}
/* Change the mode on the controller side */
- ret = chip->controller->ops->setup_interface(chip, chipnr,
- &chip->interface_config);
+ ret = ops->setup_interface(chip, chipnr, chip->best_interface_config);
if (ret)
return ret;
/* Check the mode has been accepted by the chip, if supported */
if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE))
- return 0;
+ goto update_interface_config;
memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
nand_select_target(chip, chipnr);
@@ -984,12 +995,15 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr)
if (ret)
goto err_reset_chip;
- if (tmode_param[0] != mode) {
+ if (tmode_param[0] != chip->best_interface_config->timings.mode) {
pr_warn("timing mode %d not acknowledged by the NAND chip\n",
- mode);
+ chip->best_interface_config->timings.mode);
goto err_reset_chip;
}
+update_interface_config:
+ chip->current_interface_config = chip->best_interface_config;
+
return 0;
err_reset_chip:
@@ -1031,8 +1045,10 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
/* Verify the controller supports the requested interface */
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
iface);
- if (!ret)
+ if (!ret) {
+ chip->best_interface_config = iface;
return ret;
+ }
/* Fallback to slower modes */
best_mode = iface->timings.mode;
@@ -1046,9 +1062,11 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
iface);
if (!ret)
- return 0;
+ break;
}
+ chip->best_interface_config = iface;
+
return 0;
}
@@ -1067,15 +1085,25 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip,
*/
static int nand_choose_interface_config(struct nand_chip *chip)
{
+ struct nand_interface_config *iface;
+ int ret;
+
if (!nand_controller_can_setup_interface(chip))
return 0;
+ iface = kzalloc(sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return -ENOMEM;
+
if (chip->ops.choose_interface_config)
- return chip->ops.choose_interface_config(chip,
- &chip->interface_config);
+ ret = chip->ops.choose_interface_config(chip, iface);
+ else
+ ret = nand_choose_best_sdr_timings(chip, iface, NULL);
- return nand_choose_best_sdr_timings(chip, &chip->interface_config,
- NULL);
+ if (ret)
+ kfree(iface);
+
+ return ret;
}
/**
@@ -2501,7 +2529,6 @@ EXPORT_SYMBOL_GPL(nand_subop_get_data_len);
*/
int nand_reset(struct nand_chip *chip, int chipnr)
{
- struct nand_interface_config saved_intf_config = chip->interface_config;
int ret;
ret = nand_reset_interface(chip, chipnr);
@@ -2519,18 +2546,6 @@ int nand_reset(struct nand_chip *chip, int chipnr)
if (ret)
return ret;
- /*
- * A nand_reset_interface() put both the NAND chip and the NAND
- * controller in timings mode 0. If the default mode for this chip is
- * also 0, no need to proceed to the change again. Plus, at probe time,
- * nand_setup_interface() uses ->set/get_features() which would
- * fail anyway as the parameter page is not available yet.
- */
- if (!memcmp(&chip->interface_config, &saved_intf_config,
- sizeof(saved_intf_config)))
- return 0;
-
- chip->interface_config = saved_intf_config;
ret = nand_setup_interface(chip, chipnr);
if (ret)
return ret;
@@ -5198,7 +5213,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
mutex_init(&chip->lock);
/* Enforce the right timings for reset/detection */
- onfi_fill_interface_config(chip, &chip->interface_config, NAND_SDR_IFACE, 0);
+ chip->current_interface_config = nand_get_reset_interface_config();
ret = nand_dt_init(chip);
if (ret)
@@ -5994,7 +6009,7 @@ static int nand_scan_tail(struct nand_chip *chip)
for (i = 0; i < nanddev_ntargets(&chip->base); i++) {
ret = nand_setup_interface(chip, i);
if (ret)
- goto err_nanddev_cleanup;
+ goto err_free_interface_config;
}
/* Check, if we should skip the bad block table scan */
@@ -6004,10 +6019,12 @@ static int nand_scan_tail(struct nand_chip *chip)
/* Build bad block table */
ret = nand_create_bbt(chip);
if (ret)
- goto err_nanddev_cleanup;
+ goto err_free_interface_config;
return 0;
+err_free_interface_config:
+ kfree(chip->best_interface_config);
err_nanddev_cleanup:
nanddev_cleanup(&chip->base);
@@ -6101,6 +6118,9 @@ void nand_cleanup(struct nand_chip *chip)
& NAND_BBT_DYNAMICSTRUCT)
kfree(chip->badblock_pattern);
+ /* Free the data interface */
+ kfree(chip->best_interface_config);
+
/* Free manufacturer priv data. */
nand_manufacturer_cleanup(chip);
diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
index 1e22006c79ba..94d832646487 100644
--- a/drivers/mtd/nand/raw/nand_timings.c
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -292,6 +292,12 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
},
};
+/* All NAND chips share the same reset data interface: SDR mode 0 */
+const struct nand_interface_config *nand_get_reset_interface_config(void)
+{
+ return &onfi_sdr_timings[0];
+}
+
/**
* onfi_find_closest_sdr_mode - Derive the closest ONFI SDR timing mode given a
* set of timings