mtd: add support for parsing partitions defined in OF
authorMarek Behún <marek.behun@nic.cz>
Wed, 26 May 2021 12:08:19 +0000 (14:08 +0200)
committerJagan Teki <jagan@amarulasolutions.com>
Thu, 24 Jun 2021 06:23:15 +0000 (11:53 +0530)
Add support for parsing partitions defined in device-trees via the
`partitions` node with `fixed-partitions` compatible.

The `mtdparts`/`mtdids` mechanism takes precedence. If some partitions
are defined for a MTD device via this mechanism, the code won't register
partitions for that MTD device from OF, even if they are defined.

Signed-off-by: Marek Behún <marek.behun@nic.cz>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Patrice Chotard <patrice.chotard@foss.st.com>
Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
Cc: Simon Glass <sjg@chromium.org>
Cc: Heiko Schocher <hs@denx.de>
Cc: Patrick Delaunay <patrick.delaunay@st.com>
drivers/mtd/mtd_uboot.c
drivers/mtd/mtdpart.c
include/linux/mtd/mtd.h

index c53ec65..4843cf1 100644 (file)
@@ -198,53 +198,11 @@ static void mtd_del_all_parts(void)
        } while (ret > 0);
 }
 
-int mtd_probe_devices(void)
+static int parse_mtdparts(const char *mtdparts, const char *mtdids)
 {
-       static char *old_mtdparts;
-       static char *old_mtdids;
-       const char *mtdparts = get_mtdparts();
-       const char *mtdids = get_mtdids();
-       const char *mtdparts_next = mtdparts;
+       const char *mtdparts_next;
        struct mtd_info *mtd;
 
-       mtd_probe_uclass_mtd_devs();
-
-       /*
-        * 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;
-
-       /* Update the local copy of mtdparts */
-       free(old_mtdparts);
-       free(old_mtdids);
-       old_mtdparts = strdup(mtdparts);
-       old_mtdids = strdup(mtdids);
-
-       /*
-        * 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();
-
-       /*
-        * Call mtd_dev_list_updated() to clear updates generated by our own
-        * parts removal loop.
-        */
-       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 (!strncmp(mtdparts, "mtdparts=", sizeof("mtdparts=") - 1))
                mtdparts += 9;
@@ -343,6 +301,66 @@ int mtd_probe_devices(void)
                put_mtd_device(mtd);
        }
 
+       return 0;
+}
+
+int mtd_probe_devices(void)
+{
+       static char *old_mtdparts;
+       static char *old_mtdids;
+       const char *mtdparts = get_mtdparts();
+       const char *mtdids = get_mtdids();
+       struct mtd_info *mtd;
+
+       mtd_probe_uclass_mtd_devs();
+
+       /*
+        * 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;
+
+       /* Update the local copy of mtdparts */
+       free(old_mtdparts);
+       free(old_mtdids);
+       old_mtdparts = strdup(mtdparts);
+       old_mtdids = strdup(mtdids);
+
+       /*
+        * 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();
+
+       /*
+        * Call mtd_dev_list_updated() to clear updates generated by our own
+        * parts removal loop.
+        */
+       mtd_dev_list_updated();
+
+       /* If both mtdparts and mtdids are non-empty, parse */
+       if (mtdparts && mtdids) {
+               if (parse_mtdparts(mtdparts, mtdids) < 0)
+                       printf("Failed parsing MTD partitions from mtdparts!\n");
+       }
+
+       /* Fallback to OF partitions */
+       mtd_for_each_device(mtd) {
+               if (list_empty(&mtd->partitions)) {
+                       if (add_mtd_partitions_of(mtd) < 0)
+                               printf("Failed parsing MTD %s OF partitions!\n",
+                                       mtd->name);
+               }
+       }
+
        /*
         * Call mtd_dev_list_updated() to clear updates generated by our own
         * parts registration loop.
index d064ac3..aa58f72 100644 (file)
@@ -892,6 +892,69 @@ int add_mtd_partitions(struct mtd_info *master,
        return 0;
 }
 
+#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(OF_CONTROL)
+int add_mtd_partitions_of(struct mtd_info *master)
+{
+       ofnode parts, child;
+       int i = 0;
+
+       if (!master->dev)
+               return 0;
+
+       parts = ofnode_find_subnode(mtd_get_ofnode(master), "partitions");
+       if (!ofnode_valid(parts) || !ofnode_is_available(parts) ||
+           !ofnode_device_is_compatible(parts, "fixed-partitions"))
+               return 0;
+
+       ofnode_for_each_subnode(child, parts) {
+               struct mtd_partition part = { 0 };
+               struct mtd_info *slave;
+               fdt_addr_t offset, size;
+
+               if (!ofnode_is_available(child))
+                       continue;
+
+               offset = ofnode_get_addr_size_index_notrans(child, 0, &size);
+               if (offset == FDT_ADDR_T_NONE || !size) {
+                       debug("Missing partition offset/size on \"%s\" partition\n",
+                             master->name);
+                       continue;
+               }
+
+               part.name = ofnode_read_string(child, "label");
+               if (!part.name)
+                       part.name = ofnode_read_string(child, "name");
+
+               /*
+                * .mask_flags is used to remove flags in allocate_partition(),
+                * so when "read-only" is present, we add MTD_WRITABLE to the
+                * mask, and so MTD_WRITABLE will be removed on partition
+                * allocation
+                */
+               if (ofnode_read_bool(child, "read-only"))
+                       part.mask_flags |= MTD_WRITEABLE;
+               if (ofnode_read_bool(child, "lock"))
+                       part.mask_flags |= MTD_POWERUP_LOCK;
+
+               part.offset = offset;
+               part.size = size;
+               part.ecclayout = master->ecclayout;
+
+               slave = allocate_partition(master, &part, i++, 0);
+               if (IS_ERR(slave))
+                       return PTR_ERR(slave);
+
+               mutex_lock(&mtd_partitions_mutex);
+               list_add_tail(&slave->node, &master->partitions);
+               mutex_unlock(&mtd_partitions_mutex);
+
+               add_mtd_device(slave);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(OF_CONTROL) */
+
 #ifndef __UBOOT__
 static DEFINE_SPINLOCK(part_parser_lock);
 static LIST_HEAD(part_parsers);
index 9278549..3b302fb 100644 (file)
@@ -581,6 +581,16 @@ static inline int del_mtd_partitions(struct mtd_info *mtd)
 }
 #endif
 
+#if defined(CONFIG_MTD_PARTITIONS) && CONFIG_IS_ENABLED(DM) && \
+    CONFIG_IS_ENABLED(OF_CONTROL)
+int add_mtd_partitions_of(struct mtd_info *master);
+#else
+static inline int add_mtd_partitions_of(struct mtd_info *master)
+{
+       return 0;
+}
+#endif
+
 struct mtd_info *__mtd_next_device(int i);
 #define mtd_for_each_device(mtd)                       \
        for ((mtd) = __mtd_next_device(0);              \