diff mbox series

[18/22] mtd: rawnand: Choose the best timings, NV-DDR included

Message ID 20210505213750.257417-19-miquel.raynal@bootlin.com
State Accepted
Delegated to: Miquel Raynal
Headers show
Series mtd: rawnand: NV-DDR support | expand

Commit Message

Miquel Raynal May 5, 2021, 9:37 p.m. UTC
Now that the necessary peaces to support the NV-DDR interface type have
been contributed, let's add the relevant logic to make use of it. In
particular, the core does not choose the best SDR timings anymore but
calls a more generic helper instead.

This helper checks if NV-DDR is supported by trying to find the best
NV-DDR supported mode through a logic very close to what is being done
for SDR timings. If no NV-DDR mode in common between the NAND controller
and the NAND chip is found, the core will fallback to SDR.

Side note: theoretically, the data clock speed in NV-DDR mode 0 is
slower than in SDR mode 5. In the situation where we would get a working
NV-DDR mode 0, we could also try if SDR mode 5 is supported and
eventually fallback to it in order to get the fastest possible
throughput. However, in the field, it looks like most of the devices
supporting NV-DDR avoid implementing the fastest SDR modes (like 4 and 5
EDO modes, which are a bit more complicated to handle than the other SDR
modes). So, we will stick to the simplest logic: try NV-DDR otherwise
fallback to SDR. If someone else experiences strong differences because
of that we may still implement the logic defined above.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/internals.h |  3 ++
 drivers/mtd/nand/raw/nand_base.c | 76 +++++++++++++++++++++++++++++++-
 2 files changed, 78 insertions(+), 1 deletion(-)

Comments

Miquel Raynal May 26, 2021, 9:04 a.m. UTC | #1
On Wed, 2021-05-05 at 21:37:46 UTC, Miquel Raynal wrote:
> Now that the necessary peaces to support the NV-DDR interface type have
> been contributed, let's add the relevant logic to make use of it. In
> particular, the core does not choose the best SDR timings anymore but
> calls a more generic helper instead.
> 
> This helper checks if NV-DDR is supported by trying to find the best
> NV-DDR supported mode through a logic very close to what is being done
> for SDR timings. If no NV-DDR mode in common between the NAND controller
> and the NAND chip is found, the core will fallback to SDR.
> 
> Side note: theoretically, the data clock speed in NV-DDR mode 0 is
> slower than in SDR mode 5. In the situation where we would get a working
> NV-DDR mode 0, we could also try if SDR mode 5 is supported and
> eventually fallback to it in order to get the fastest possible
> throughput. However, in the field, it looks like most of the devices
> supporting NV-DDR avoid implementing the fastest SDR modes (like 4 and 5
> EDO modes, which are a bit more complicated to handle than the other SDR
> modes). So, we will stick to the simplest logic: try NV-DDR otherwise
> fallback to SDR. If someone else experiences strong differences because
> of that we may still implement the logic defined above.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h
index c06bc2f1aaa2..7016e0f38398 100644
--- a/drivers/mtd/nand/raw/internals.h
+++ b/drivers/mtd/nand/raw/internals.h
@@ -95,6 +95,9 @@  onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings);
 int nand_choose_best_sdr_timings(struct nand_chip *chip,
 				 struct nand_interface_config *iface,
 				 struct nand_sdr_timings *spec_timings);
+int nand_choose_best_nvddr_timings(struct nand_chip *chip,
+				   struct nand_interface_config *iface,
+				   struct nand_nvddr_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);
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index e20551cb3ce5..22974a53077f 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -961,6 +961,80 @@  int nand_choose_best_sdr_timings(struct nand_chip *chip,
 	return ret;
 }
 
+/**
+ * nand_choose_best_nvddr_timings - Pick up the best NVDDR timings that both the
+ *                                  NAND controller and the NAND chip support
+ * @chip: the NAND chip
+ * @iface: the interface configuration (can eventually be updated)
+ * @spec_timings: specific timings, when not fitting the ONFI specification
+ *
+ * If specific timings are provided, use them. Otherwise, retrieve supported
+ * timing modes from ONFI information.
+ */
+int nand_choose_best_nvddr_timings(struct nand_chip *chip,
+				   struct nand_interface_config *iface,
+				   struct nand_nvddr_timings *spec_timings)
+{
+	const struct nand_controller_ops *ops = chip->controller->ops;
+	int best_mode = 0, mode, ret;
+
+	iface->type = NAND_NVDDR_IFACE;
+
+	if (spec_timings) {
+		iface->timings.nvddr = *spec_timings;
+		iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings);
+
+		/* Verify the controller supports the requested interface */
+		ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+					   iface);
+		if (!ret) {
+			chip->best_interface_config = iface;
+			return ret;
+		}
+
+		/* Fallback to slower modes */
+		best_mode = iface->timings.mode;
+	} else if (chip->parameters.onfi) {
+		best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1;
+	}
+
+	for (mode = best_mode; mode >= 0; mode--) {
+		onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode);
+
+		ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY,
+					   iface);
+		if (!ret) {
+			chip->best_interface_config = iface;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both
+ *                            NAND controller and the NAND chip support
+ * @chip: the NAND chip
+ * @iface: the interface configuration (can eventually be updated)
+ *
+ * If specific timings are provided, use them. Otherwise, retrieve supported
+ * timing modes from ONFI information.
+ */
+static int nand_choose_best_timings(struct nand_chip *chip,
+				    struct nand_interface_config *iface)
+{
+	int ret;
+
+	/* Try the fastest timings: NV-DDR */
+	ret = nand_choose_best_nvddr_timings(chip, iface, NULL);
+	if (!ret)
+		return 0;
+
+	/* Fallback to SDR timings otherwise */
+	return nand_choose_best_sdr_timings(chip, iface, NULL);
+}
+
 /**
  * nand_choose_interface_config - find the best data interface and timings
  * @chip: The NAND chip
@@ -989,7 +1063,7 @@  static int nand_choose_interface_config(struct nand_chip *chip)
 	if (chip->ops.choose_interface_config)
 		ret = chip->ops.choose_interface_config(chip, iface);
 	else
-		ret = nand_choose_best_sdr_timings(chip, iface, NULL);
+		ret = nand_choose_best_timings(chip, iface);
 
 	if (ret)
 		kfree(iface);