[RFC PATCH 16/20] boot: bootdev: add MTD boot device
Simon Glass
sjg at chromium.org
Thu Feb 19 14:09:30 CET 2026
Hi Daniel,
On Mon, 16 Feb 2026 at 14:23, Daniel Golle <daniel at makrotopia.org> wrote:
>
> Add a boot device driver for MTD (Memory Technology Devices) that
> enables bootstd to scan MTD partitions for FIT images.
>
> Add a mtd_bootdev_hunt() callback that:
> - Calls mtd_probe_devices() once to probe all MTD devices (both
> UCLASS_MTD and UCLASS_SPI_FLASH) and parse their partitions
> - Iterates the MTD subsystem device list and binds an mtd_bootdev for
> any top-level MTD device that has partitions but is not in UCLASS_MTD
> (those already get their bootdev from mtd_post_bind)
> - Uses bootdev_bind() directly since sf_bootdev may already occupy the
> standard bootdev child slot
>
> The MTD bootdev calls bootmeth_check() at the start of get_bootflow()
> so that only compatible bootmeths (those that accept MTD bootdevs)
> produce bootflows. Until bootmeth_openwrt's check() is extended to
> accept MTD bootdevs, this driver is inert — each commit compiles and
> introduces no regressions independently.
>
> Signed-off-by: Daniel Golle <daniel at makrotopia.org>
> ---
> boot/Kconfig | 9 +++
> boot/Makefile | 1 +
> boot/mtd_bootdev.c | 150 +++++++++++++++++++++++++++++++++++++++
> drivers/mtd/mtd-uclass.c | 15 ++++
> 4 files changed, 175 insertions(+)
> create mode 100644 boot/mtd_bootdev.c
>
Reviewed-by: Simon Glass <simon.glass at canonical.com>
nits below
> diff --git a/boot/Kconfig b/boot/Kconfig
> index d8c7b8360ac..63e373cc62d 100644
> --- a/boot/Kconfig
> +++ b/boot/Kconfig
> @@ -585,6 +585,15 @@ config BOOTMETH_CROS
>
> Note that only x86 devices are supported at present.
>
> +config BOOTDEV_MTD
> + bool "MTD bootdev support"
> + depends on DM_MTD
> + depends on BOOTSTD
> + help
> + Enable a boot device for MTD (Memory Technology Devices).
> + This scans MTD partitions for uImage.FIT firmware images,
> + enabling raw-flash boot via the OpenWrt boot method.
> +
> config BOOTMETH_OPENWRT
> bool "Bootdev support for OpenWrt"
> depends on FIT
> diff --git a/boot/Makefile b/boot/Makefile
> index 7b42358eb0c..feeed4924dd 100644
> --- a/boot/Makefile
> +++ b/boot/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_FW) += vbe_simple_fw.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_SIMPLE_OS) += vbe_simple_os.o
>
> obj-$(CONFIG_$(PHASE_)BOOTMETH_ANDROID) += bootmeth_android.o
> +obj-$(CONFIG_$(PHASE_)BOOTDEV_MTD) += mtd_bootdev.o
> obj-$(CONFIG_$(PHASE_)BOOTMETH_OPENWRT) += bootmeth_openwrt.o
>
> obj-$(CONFIG_IMAGE_LOADER) += image-loader.o
> diff --git a/boot/mtd_bootdev.c b/boot/mtd_bootdev.c
> new file mode 100644
> index 00000000000..8a3304be988
> --- /dev/null
> +++ b/boot/mtd_bootdev.c
> @@ -0,0 +1,150 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * MTD boot device
> + *
> + * Copyright (C) 2026 Daniel Golle <daniel at makrotopia.org>
> + */
> +
> +#define LOG_CATEGORY UCLASS_BOOTSTD
> +
> +#include <bootdev.h>
> +#include <bootflow.h>
> +#include <bootmeth.h>
> +#include <dm.h>
> +#include <malloc.h>
> +#include <mtd.h>
> +#include <linux/libfdt.h>
> +
> +static int mtd_bootdev_get_bootflow(struct udevice *dev,
> + struct bootflow_iter *iter,
> + struct bootflow *bflow)
> +{
> + struct udevice *parent = dev_get_parent(dev);
> + struct mtd_info *mtd, *part;
> + u8 buf[40];
> + size_t retlen;
> + char dname[60];
> + int n = 0;
> + int ret;
> +
> + ret = bootmeth_check(bflow->method, iter);
> + if (ret)
> + return log_msg_ret("chk", ret);
> +
> + /* Find the top-level MTD device matching our parent */
> + mtd_for_each_device(mtd) {
> + if (!mtd_is_partition(mtd) && mtd->dev == parent)
> + break;
> + }
> + if (!mtd || mtd->dev != parent)
> + return log_msg_ret("mtd", -ESHUTDOWN);
> +
> + /* Count partitions so the scanning framework knows the bound */
> + list_for_each_entry(part, &mtd->partitions, node)
> + n++;
> + if (n)
> + iter->max_part = n - 1;
> +
> + n = 0;
> +
> + /* Walk to the iter->part'th sub-partition */
> + list_for_each_entry(part, &mtd->partitions, node) {
> + if (n == iter->part)
> + goto found;
> + n++;
> + }
blank line here
> + return -ESHUTDOWN;
> +
> +found:
> + ret = mtd_read(part, 0, sizeof(buf), &retlen, buf);
> + if (ret || retlen < sizeof(buf))
> + return log_msg_ret("rd", -EIO);
> +
> + if (fdt_check_header(buf))
> + return log_msg_ret("fdt", -ENOENT);
> +
> + /* Device-style name and partition index for bootflow list display */
> + snprintf(dname, sizeof(dname), "%s.part_%x", dev->name, iter->part);
> + bflow->name = strdup(dname);
> + bflow->part = iter->part;
> + bflow->fname = strdup(part->name);
> + bflow->bootmeth_priv = strdup(part->name);
> + bflow->state = BOOTFLOWST_MEDIA;
> +
> + return bootmeth_read_bootflow(bflow->method, bflow);
> +}
> +
> +static int mtd_bootdev_bind(struct udevice *dev)
> +{
> + struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
> +
> + ucp->prio = BOOTDEVP_4_SCAN_FAST;
> +
> + return 0;
> +}
> +
> +struct bootdev_ops mtd_bootdev_ops = {
> + .get_bootflow = mtd_bootdev_get_bootflow,
> +};
> +
> +static const struct udevice_id mtd_bootdev_ids[] = {
> + { .compatible = "u-boot,bootdev-mtd" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(mtd_bootdev) = {
> + .name = "mtd_bootdev",
> + .id = UCLASS_BOOTDEV,
> + .ops = &mtd_bootdev_ops,
> + .bind = mtd_bootdev_bind,
> + .of_match = mtd_bootdev_ids,
> +};
> +
> +/**
> + * mtd_bootdev_hunt() - probe MTD devices and bind bootdevs
> + *
> + * Call mtd_probe_devices() to ensure all MTD devices (including SPI NOR
> + * flash via CONFIG_SPI_FLASH_MTD) are probed and their partitions parsed.
> + *
> + * UCLASS_MTD devices already get an mtd_bootdev via mtd_post_bind().
> + * This creates mtd_bootdev instances for other MTD devices (e.g. SPI NOR
> + * in UCLASS_SPI_FLASH) that have partitions but would otherwise lack one.
> + * bootdev_bind() is used directly because sf_bootdev may already occupy
> + * the standard bootdev child slot.
> + */
> +static int mtd_bootdev_hunt(struct bootdev_hunter *info, bool show)
> +{
> + struct mtd_info *mtd;
> + struct udevice *bdev;
> +
> + mtd_probe_devices();
> +
> + mtd_for_each_device(mtd) {
> + /* Only top-level MTD devices, not partitions */
> + if (mtd_is_partition(mtd))
> + continue;
> +
> + /* Must have a DM device */
> + if (!mtd->dev)
> + continue;
> +
> + /* UCLASS_MTD devices already have mtd_bootdev from post_bind */
> + if (device_get_uclass_id(mtd->dev) == UCLASS_MTD)
> + continue;
> +
> + /* Only interested in MTD devices that have partitions */
> + if (list_empty(&mtd->partitions))
> + continue;
> +
> + bootdev_bind(mtd->dev, "mtd_bootdev", "mtdbootdev", &bdev);
Check error
> + }
> +
> + return 0;
> +}
> +
> +BOOTDEV_HUNTER(mtd_bootdev_hunter) = {
> + .prio = BOOTDEVP_4_SCAN_FAST,
> + .uclass = UCLASS_MTD,
> + .drv = DM_DRIVER_REF(mtd_bootdev),
> + .hunt = mtd_bootdev_hunt,
> +};
> diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c
> index 720bd824c4d..0c637d3f5ec 100644
> --- a/drivers/mtd/mtd-uclass.c
> +++ b/drivers/mtd/mtd-uclass.c
> @@ -5,11 +5,25 @@
>
> #define LOG_CATEGORY UCLASS_MTD
>
> +#include <bootdev.h>
> #include <dm.h>
> #include <dm/device-internal.h>
> #include <errno.h>
> #include <mtd.h>
>
> +static int mtd_post_bind(struct udevice *dev)
> +{
> + if (CONFIG_IS_ENABLED(BOOTDEV_MTD)) {
> + int ret;
> +
> + ret = bootdev_setup_for_dev(dev, "mtd_bootdev");
> + if (ret)
> + return log_msg_ret("bd", ret);
> + }
> +
> + return 0;
> +}
> +
> /*
> * Implement a MTD uclass which should include most flash drivers.
> * The uclass private is pointed to mtd_info.
> @@ -18,5 +32,6 @@
> UCLASS_DRIVER(mtd) = {
> .id = UCLASS_MTD,
> .name = "mtd",
> + .post_bind = mtd_post_bind,
> .per_device_auto = sizeof(struct mtd_info),
> };
> --
> 2.53.0
Regards,
Simon
More information about the U-Boot
mailing list