mtd: nand: raw: Move CONFIG_SYS_NAND_USE_FLASH_BBT to Kconfig
[oweals/u-boot.git] / drivers / mtd / mtd_uboot.c
index 5ca560c968794852384b5e8d4e5e024c5df20e01..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 */
 
 #define MTD_NAME_MAX_LEN 20
 
+void board_mtdparts_default(const char **mtdids, const char **mtdparts);
+
+static const char *get_mtdids(void)
+{
+       __maybe_unused const char *mtdparts = NULL;
+       const char *mtdids = env_get("mtdids");
+
+       if (mtdids)
+               return mtdids;
+
+#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
+       board_mtdparts_default(&mtdids, &mtdparts);
+#elif defined(MTDIDS_DEFAULT)
+       mtdids = MTDIDS_DEFAULT;
+#elif defined(CONFIG_MTDIDS_DEFAULT)
+       mtdids = CONFIG_MTDIDS_DEFAULT;
+#endif
+
+       if (mtdids)
+               env_set("mtdids", mtdids);
+
+       return mtdids;
+}
 
 /**
  * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
@@ -34,7 +58,7 @@ int mtd_search_alternate_name(const char *mtdname, char *altname,
        const char *mtdids, *equal, *comma, *dev_id, *mtd_id;
        int dev_id_len, mtd_id_len;
 
-       mtdids = env_get("mtdids");
+       mtdids = get_mtdids();
        if (!mtdids)
                return -EINVAL;
 
@@ -92,30 +116,6 @@ static void mtd_probe_uclass_mtd_devs(void) { }
 #endif
 
 #if defined(CONFIG_MTD_PARTITIONS)
-extern void board_mtdparts_default(const char **mtdids,
-                                  const char **mtdparts);
-
-static const char *get_mtdids(void)
-{
-       __maybe_unused const char *mtdparts = NULL;
-       const char *mtdids = env_get("mtdids");
-
-       if (mtdids)
-               return mtdids;
-
-#if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
-       board_mtdparts_default(&mtdids, &mtdparts);
-#elif defined(MTDIDS_DEFAULT)
-       mtdids = MTDIDS_DEFAULT;
-#elif defined(CONFIG_MTDIDS_DEFAULT)
-       mtdids = CONFIG_MTDIDS_DEFAULT;
-#endif
-
-       if (mtdids)
-               env_set("mtdids", mtdids);
-
-       return mtdids;
-}
 
 #define MTDPARTS_MAXLEN         512
 
@@ -123,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)
@@ -131,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)
@@ -145,25 +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 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_del_all_parts_failed &&
             !strcmp(mtdparts, old_mtdparts) &&
             !strcmp(mtdids, old_mtdids)))
                return 0;
@@ -174,55 +225,55 @@ 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;
-               }
-       }
+       /*
+        * 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.
+        */
+       mtd_del_all_parts();
 
        /*
-        * 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.
+        * Call mtd_dev_list_updated() to clear updates generated by our own
+        * parts removal loop.
         */
-       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_dev_list_updated();
 
        /* If either mtdparts or mtdids is empty, then exit */
        if (!mtdparts || !mtdids)
                return 0;
 
        /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
-       if (strstr(mtdparts, "mtdparts="))
+       if (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
                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;
-               int mtd_name_len, nparts;
-               int ret;
+               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;
                }
 
-               mtd_name_len = colon - mtdparts;
+               mtd_name_len = (unsigned int)(colon - mtdparts);
+               if (mtd_name_len + 1 > sizeof(mtd_name)) {
+                       printf("MTD name too long: %s\n", mtdparts);
+                       return -EINVAL;
+               }
+
                strncpy(mtd_name, mtdparts, mtd_name_len);
                mtd_name[mtd_name_len] = '\0';
                /* Move the pointer forward (including the ':') */
@@ -249,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
@@ -281,6 +340,12 @@ int mtd_probe_devices(void)
                put_mtd_device(mtd);
        }
 
+       /*
+        * Call mtd_dev_list_updated() to clear updates generated by our own
+        * parts registration loop.
+        */
+       mtd_dev_list_updated();
+
        return 0;
 }
 #else