Hi Daniel, On Mon, 16 Feb 2026 at 14:23, Daniel Golle <[email protected]> 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 <[email protected]> > --- > 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 <[email protected]> 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 <[email protected]> > + */ > + > +#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

