mtd: rawnand: Choose the best timings, NV-DDR included
authorMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 5 May 2021 21:37:46 +0000 (23:37 +0200)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 26 May 2021 08:52:43 +0000 (10:52 +0200)
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>
Link: https://lore.kernel.org/linux-mtd/20210505213750.257417-19-miquel.raynal@bootlin.com
drivers/mtd/nand/raw/internals.h
drivers/mtd/nand/raw/nand_base.c

index c06bc2f1aaa27eb249f0f2b08f39d10ebe5cccb8..7016e0f38398e201f4b489ad541934876f5debbe 100644 (file)
@@ -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);
index e20551cb3ce59201d8bd5b90d45d5b554ab6bb6a..22974a53077f08a08d09fd04917f8dbbcaa4522d 100644 (file)
@@ -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);