mtd: rawnand: Allocate the interface configurations dynamically
authorMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 29 May 2020 11:13:22 +0000 (13:13 +0200)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 26 Jun 2020 06:35:08 +0000 (08:35 +0200)
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
drivers/mtd/nand/raw/internals.h
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_timings.c
include/linux/mtd/rawnand.h

index 5ebfbb8..012876e 100644 (file)
@@ -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,
index 753328f..0c768cb 100644 (file)
@@ -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);
 
index 1e22006..94d8326 100644 (file)
@@ -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
index a2427c6..a725b62 100644 (file)
@@ -1069,7 +1069,11 @@ struct nand_manufacturer {
  * @options: Various chip options. They can partly be set to inform nand_scan
  *           about special functionality. See the defines for further
  *           explanation.
- * @interface_config: NAND interface timing information
+ * @current_interface_config: The currently used NAND interface configuration
+ * @best_interface_config: The best NAND interface configuration which fits both
+ *                         the NAND chip and NAND controller constraints. If
+ *                         unset, the default reset interface configuration must
+ *                         be used.
  * @bbt_erase_shift: Number of address bits in a bbt entry
  * @bbt_options: Bad block table specific options. All options used here must
  *               come from bbm.h. By default, these options will be copied to
@@ -1116,7 +1120,8 @@ struct nand_chip {
        unsigned int options;
 
        /* Data interface */
-       struct nand_interface_config interface_config;
+       const struct nand_interface_config *current_interface_config;
+       struct nand_interface_config *best_interface_config;
 
        /* Bad block information */
        unsigned int bbt_erase_shift;
@@ -1209,7 +1214,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
 static inline const struct nand_interface_config *
 nand_get_interface_config(struct nand_chip *chip)
 {
-       return &chip->interface_config;
+       return chip->current_interface_config;
 }
 
 /*