mtd: nand: raw: Move CONFIG_SYS_NAND_USE_FLASH_BBT to Kconfig
[oweals/u-boot.git] / drivers / mtd / mtd_uboot.c
index 0eda36278309da616c167955015f71105875abc0..557422759831b1215726318f665670308628f545 100644 (file)
@@ -4,6 +4,7 @@
  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
  */
 #include <common.h>
+#include <env.h>
 #include <dm/device.h>
 #include <dm/uclass-internal.h>
 #include <jffs2/jffs2.h> /* LEGACY */
@@ -122,7 +123,6 @@ static const char *get_mtdparts(void)
 {
        __maybe_unused const char *mtdids = NULL;
        static char tmp_parts[MTDPARTS_MAXLEN];
-       static bool use_defaults = true;
        const char *mtdparts = NULL;
 
        if (gd->flags & GD_FLG_ENV_READY)
@@ -130,7 +130,7 @@ static const char *get_mtdparts(void)
        else if (env_get_f("mtdparts", tmp_parts, sizeof(tmp_parts)) != -1)
                mtdparts = tmp_parts;
 
-       if (mtdparts || !use_defaults)
+       if (mtdparts)
                return mtdparts;
 
 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
@@ -144,29 +144,77 @@ static const char *get_mtdparts(void)
        if (mtdparts)
                env_set("mtdparts", mtdparts);
 
-       use_defaults = false;
-
        return mtdparts;
 }
 
+static int mtd_del_parts(struct mtd_info *mtd, bool quiet)
+{
+       int ret;
+
+       if (!mtd_has_partitions(mtd))
+               return 0;
+
+       /* do not delete partitions if they are in use. */
+       if (mtd_partitions_used(mtd)) {
+               if (!quiet)
+                       printf("\"%s\" partitions still in use, can't delete them\n",
+                              mtd->name);
+               return -EACCES;
+       }
+
+       ret = del_mtd_partitions(mtd);
+       if (ret)
+               return ret;
+
+       return 1;
+}
+
+static bool mtd_del_all_parts_failed;
+
+static void mtd_del_all_parts(void)
+{
+       struct mtd_info *mtd;
+       int ret = 0;
+
+       mtd_del_all_parts_failed = false;
+
+       /*
+        * It is not safe to remove entries from the mtd_for_each_device loop
+        * as it uses idr indexes and the partitions removal is done in bulk
+        * (all partitions of one device at the same time), so break and
+        * iterate from start each time a new partition is found and deleted.
+        */
+       do {
+               mtd_for_each_device(mtd) {
+                       ret = mtd_del_parts(mtd, false);
+                       if (ret > 0)
+                               break;
+                       else if (ret < 0)
+                               mtd_del_all_parts_failed = true;
+               }
+       } while (ret > 0);
+}
+
 int mtd_probe_devices(void)
 {
        static char *old_mtdparts;
        static char *old_mtdids;
        const char *mtdparts = get_mtdparts();
        const char *mtdids = get_mtdids();
-       bool remaining_partitions = true;
+       const char *mtdparts_next = mtdparts;
        struct mtd_info *mtd;
 
        mtd_probe_uclass_mtd_devs();
 
        /*
-        * Check if mtdparts/mtdids changed or if the MTD dev list was updated
-        * since last call, otherwise: exit
+        * Check if mtdparts/mtdids changed, if the MTD dev list was updated
+        * or if our previous attempt to delete existing partititions failed.
+        * In any of these cases we want to update the partitions, otherwise,
+        * everything is up-to-date and we can return 0 directly.
         */
        if ((!mtdparts && !old_mtdparts && !mtdids && !old_mtdids) ||
            (mtdparts && old_mtdparts && mtdids && old_mtdids &&
-            !mtd_dev_list_updated() &&
+            !mtd_dev_list_updated() && !mtd_del_all_parts_failed &&
             !strcmp(mtdparts, old_mtdparts) &&
             !strcmp(mtdids, old_mtdids)))
                return 0;
@@ -177,32 +225,12 @@ int mtd_probe_devices(void)
        old_mtdparts = strdup(mtdparts);
        old_mtdids = strdup(mtdids);
 
-       /* If at least one partition is still in use, do not delete anything */
-       mtd_for_each_device(mtd) {
-               if (mtd->usecount) {
-                       printf("Partition \"%s\" already in use, aborting\n",
-                              mtd->name);
-                       return -EACCES;
-               }
-       }
-
        /*
-        * Everything looks clear, remove all partitions. It is not safe to
-        * remove entries from the mtd_for_each_device loop as it uses idr
-        * indexes and the partitions removal is done in bulk (all partitions of
-        * one device at the same time), so break and iterate from start each
-        * time a new partition is found and deleted.
+        * Remove all old parts. Note that partition removal can fail in case
+        * one of the partition is still being used by an MTD user, so this
+        * does not guarantee that all old partitions are gone.
         */
-       while (remaining_partitions) {
-               remaining_partitions = false;
-               mtd_for_each_device(mtd) {
-                       if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) {
-                               del_mtd_partitions(mtd);
-                               remaining_partitions = true;
-                               break;
-                       }
-               }
-       }
+       mtd_del_all_parts();
 
        /*
         * Call mtd_dev_list_updated() to clear updates generated by our own
@@ -219,13 +247,22 @@ int mtd_probe_devices(void)
                mtdparts += 9;
 
        /* For each MTD device in mtdparts */
-       while (mtdparts[0] != '\0') {
+       for (; mtdparts[0] != '\0'; mtdparts = mtdparts_next) {
                char mtd_name[MTD_NAME_MAX_LEN], *colon;
                struct mtd_partition *parts;
                unsigned int mtd_name_len;
                int nparts, ret;
 
+               mtdparts_next = strchr(mtdparts, ';');
+               if (!mtdparts_next)
+                       mtdparts_next = mtdparts + strlen(mtdparts);
+               else
+                       mtdparts_next++;
+
                colon = strchr(mtdparts, ':');
+               if (colon > mtdparts_next)
+                       colon = NULL;
+
                if (!colon) {
                        printf("Wrong mtdparts: %s\n", mtdparts);
                        return -EINVAL;
@@ -263,14 +300,22 @@ int mtd_probe_devices(void)
                        if (ret || IS_ERR_OR_NULL(mtd)) {
                                printf("Could not find a valid device for %s\n",
                                       mtd_name);
-                               mtdparts = strchr(mtdparts, ';');
-                               if (mtdparts)
-                                       mtdparts++;
-
+                               mtdparts = mtdparts_next;
                                continue;
                        }
                }
 
+               /*
+                * Call mtd_del_parts() again, even if it's already been called
+                * in mtd_del_all_parts(). We need to know if old partitions are
+                * still around (because they are still being used by someone),
+                * and if they are, we shouldn't create new partitions, so just
+                * skip this MTD device and try the next one.
+                */
+               ret = mtd_del_parts(mtd, true);
+               if (ret < 0)
+                       continue;
+
                /*
                 * Parse the MTD device partitions. It will update the mtdparts
                 * pointer, create an array of parts (that must be freed), and