From mboxrd@z Thu Jan 1 00:00:00 1970 From: Adam Ford Date: Wed, 3 Oct 2018 07:35:15 -0500 Subject: [U-Boot] [PATCH v13 5/7] cmd: mtd: add 'mtd' command In-Reply-To: <20181001134331.9756-6-miquel.raynal@bootlin.com> References: <20181001134331.9756-1-miquel.raynal@bootlin.com> <20181001134331.9756-6-miquel.raynal@bootlin.com> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable To: u-boot@lists.denx.de On Mon, Oct 1, 2018 at 8:48 AM Miquel Raynal wr= ote: > > There should not be a 'nand' command, a 'sf' command and certainly not > a new 'spi-nand' command. Write a 'mtd' command instead to manage all > MTD devices/partitions at once. This should be the preferred way to > access any MTD device. What is the expected behavior when I type 'mtd list' on my omap37 board, it just hangs. I can use the nand read/write functions and mtdparts lists the partitions, so I know nand works. My defconfig lists the partitions, so if we're not supposed to use mtdparts, where I do store the partition information? I intentionally removed it from the device tree a while ago, because U-Boot was passing the partition info to Linux. adam > > Signed-off-by: Miquel Raynal > Acked-by: Jagan Teki > Reviewed-by: Stefan Roese > Reviewed-by: Boris Brezillon > --- > cmd/Kconfig | 10 +- > cmd/Makefile | 1 + > cmd/mtd.c | 473 ++++++++++++++++++++++++++++++++++++++++ > drivers/mtd/Makefile | 2 +- > drivers/mtd/mtd_uboot.c | 161 +++++++++++++- > include/mtd.h | 16 ++ > 6 files changed, 659 insertions(+), 4 deletions(-) > create mode 100644 cmd/mtd.c > > diff --git a/cmd/Kconfig b/cmd/Kconfig > index 13d4c991bf..c9be85989c 100644 > --- a/cmd/Kconfig > +++ b/cmd/Kconfig > @@ -864,6 +864,12 @@ config CMD_MMC_SWRITE > Enable support for the "mmc swrite" command to write Android sp= arse > images to eMMC. > > +config CMD_MTD > + bool "mtd" > + select MTD_PARTITIONS > + help > + MTD commands support. > + > config CMD_NAND > bool "nand" > default y if NAND_SUNXI > @@ -1697,14 +1703,14 @@ config CMD_MTDPARTS > > config MTDIDS_DEFAULT > string "Default MTD IDs" > - depends on CMD_MTDPARTS || CMD_NAND || CMD_FLASH > + depends on CMD_MTD || CMD_MTDPARTS || CMD_NAND || CMD_FLASH > help > Defines a default MTD IDs list for use with MTD partitions in t= he > Linux MTD command line partitions format. > > config MTDPARTS_DEFAULT > string "Default MTD partition scheme" > - depends on CMD_MTDPARTS || CMD_NAND || CMD_FLASH > + depends on CMD_MTD || CMD_MTDPARTS || CMD_NAND || CMD_FLASH > help > Defines a default MTD partitioning scheme in the Linux MTD comm= and > line partitions format > diff --git a/cmd/Makefile b/cmd/Makefile > index a61fab6583..3ba65d4d93 100644 > --- a/cmd/Makefile > +++ b/cmd/Makefile > @@ -92,6 +92,7 @@ obj-$(CONFIG_CMD_MISC) +=3D misc.o > obj-$(CONFIG_CMD_MMC) +=3D mmc.o > obj-$(CONFIG_CMD_MMC_SPI) +=3D mmc_spi.o > obj-$(CONFIG_MP) +=3D mp.o > +obj-$(CONFIG_CMD_MTD) +=3D mtd.o > obj-$(CONFIG_CMD_MTDPARTS) +=3D mtdparts.o > obj-$(CONFIG_CMD_NAND) +=3D nand.o > obj-$(CONFIG_CMD_NET) +=3D net.o > diff --git a/cmd/mtd.c b/cmd/mtd.c > new file mode 100644 > index 0000000000..6142223984 > --- /dev/null > +++ b/cmd/mtd.c > @@ -0,0 +1,473 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * mtd.c > + * > + * Generic command to handle basic operations on any memory device. > + * > + * Copyright: Bootlin, 2018 > + * Author: Miqu=C3=A8l Raynal > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len) > +{ > + do_div(len, mtd->writesize); > + > + return len; > +} > + > +static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 si= ze) > +{ > + return !do_div(size, mtd->writesize); > +} > + > +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 siz= e) > +{ > + return !do_div(size, mtd->erasesize); > +} > + > +static void mtd_dump_buf(const u8 *buf, uint len, uint offset) > +{ > + int i, j; > + > + for (i =3D 0; i < len; ) { > + printf("0x%08x:\t", offset + i); > + for (j =3D 0; j < 8; j++) > + printf("%02x ", buf[i + j]); > + printf(" "); > + i +=3D 8; > + for (j =3D 0; j < 8; j++) > + printf("%02x ", buf[i + j]); > + printf("\n"); > + i +=3D 8; > + } > +} > + > +static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off, > + const u8 *buf, u64 len, bool woob) > +{ > + bool has_pages =3D mtd->type =3D=3D MTD_NANDFLASH || > + mtd->type =3D=3D MTD_MLCNANDFLASH; > + int npages =3D mtd_len_to_pages(mtd, len); > + uint page; > + > + if (has_pages) { > + for (page =3D 0; page < npages; page++) { > + u64 data_off =3D page * mtd->writesize; > + > + printf("\nDump %d data bytes from 0x%08llx:\n", > + mtd->writesize, start_off + data_off); > + mtd_dump_buf(&buf[data_off], > + mtd->writesize, start_off + data_off= ); > + > + if (woob) { > + u64 oob_off =3D page * mtd->oobsize; > + > + printf("Dump %d OOB bytes from page at 0x= %08llx:\n", > + mtd->oobsize, start_off + data_off= ); > + mtd_dump_buf(&buf[len + oob_off], > + mtd->oobsize, 0); > + } > + } > + } else { > + printf("\nDump %lld data bytes from 0x%llx:\n", > + len, start_off); > + mtd_dump_buf(buf, len, start_off); > + } > +} > + > +static void mtd_show_parts(struct mtd_info *mtd, int level) > +{ > + struct mtd_info *part; > + int i; > + > + list_for_each_entry(part, &mtd->partitions, node) { > + for (i =3D 0; i < level; i++) > + printf("\t"); > + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", > + part->offset, part->offset + part->size, part->nam= e); > + > + mtd_show_parts(part, level + 1); > + } > +} > + > +static void mtd_show_device(struct mtd_info *mtd) > +{ > + /* Device */ > + printf("* %s\n", mtd->name); > +#if defined(CONFIG_DM) > + if (mtd->dev) { > + printf(" - device: %s\n", mtd->dev->name); > + printf(" - parent: %s\n", mtd->dev->parent->name); > + printf(" - driver: %s\n", mtd->dev->driver->name); > + } > +#endif > + > + /* MTD device information */ > + printf(" - type: "); > + switch (mtd->type) { > + case MTD_RAM: > + printf("RAM\n"); > + break; > + case MTD_ROM: > + printf("ROM\n"); > + break; > + case MTD_NORFLASH: > + printf("NOR flash\n"); > + break; > + case MTD_NANDFLASH: > + printf("NAND flash\n"); > + break; > + case MTD_DATAFLASH: > + printf("Data flash\n"); > + break; > + case MTD_UBIVOLUME: > + printf("UBI volume\n"); > + break; > + case MTD_MLCNANDFLASH: > + printf("MLC NAND flash\n"); > + break; > + case MTD_ABSENT: > + default: > + printf("Unknown\n"); > + break; > + } > + > + printf(" - block size: 0x%x bytes\n", mtd->erasesize); > + printf(" - min I/O: 0x%x bytes\n", mtd->writesize); > + > + if (mtd->oobsize) { > + printf(" - OOB size: %u bytes\n", mtd->oobsize); > + printf(" - OOB available: %u bytes\n", mtd->oobavail); > + } > + > + if (mtd->ecc_strength) { > + printf(" - ECC strength: %u bits\n", mtd->ecc_strength); > + printf(" - ECC step size: %u bytes\n", mtd->ecc_step_siz= e); > + printf(" - bitflip threshold: %u bits\n", > + mtd->bitflip_threshold); > + } > + > + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", > + mtd->offset, mtd->offset + mtd->size, mtd->name); > + > + /* MTD partitions, if any */ > + mtd_show_parts(mtd, 1); > +} > + > +/* Logic taken from fs/ubifs/recovery.c:is_empty() */ > +static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) > +{ > + int i; > + > + for (i =3D 0; i < op->len; i++) > + if (op->datbuf[i] !=3D 0xff) > + return false; > + > + for (i =3D 0; i < op->ooblen; i++) > + if (op->oobbuf[i] !=3D 0xff) > + return false; > + > + return true; > +} > + > +static int do_mtd_list(void) > +{ > + struct mtd_info *mtd; > + int dev_nb =3D 0; > + > + /* Ensure all devices (and their partitions) are probed */ > + mtd_probe_devices(); > + > + printf("List of MTD devices:\n"); > + mtd_for_each_device(mtd) { > + if (!mtd_is_partition(mtd)) > + mtd_show_device(mtd); > + > + dev_nb++; > + } > + > + if (!dev_nb) { > + printf("No MTD device found\n"); > + return CMD_RET_FAILURE; > + } > + > + return CMD_RET_SUCCESS; > +} > + > +static int mtd_special_write_oob(struct mtd_info *mtd, u64 off, > + struct mtd_oob_ops *io_op, > + bool write_empty_pages, bool woob) > +{ > + int ret =3D 0; > + > + /* > + * By default, do not write an empty page. > + * Skip it by simulating a successful write. > + */ > + if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) { > + io_op->retlen =3D mtd->writesize; > + io_op->oobretlen =3D woob ? mtd->oobsize : 0; > + } else { > + ret =3D mtd_write_oob(mtd, off, io_op); > + } > + > + return ret; > +} > + > +static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const arg= v[]) > +{ > + struct mtd_info *mtd; > + const char *cmd; > + char *mtd_name; > + > + /* All MTD commands need at least two arguments */ > + if (argc < 2) > + return CMD_RET_USAGE; > + > + /* Parse the command name and its optional suffixes */ > + cmd =3D argv[1]; > + > + /* List the MTD devices if that is what the user wants */ > + if (strcmp(cmd, "list") =3D=3D 0) > + return do_mtd_list(); > + > + /* > + * The remaining commands require also at least a device ID. > + * Check the selected device is valid. Ensure it is probed. > + */ > + if (argc < 3) > + return CMD_RET_USAGE; > + > + mtd_name =3D argv[2]; > + mtd_probe_devices(); > + mtd =3D get_mtd_device_nm(mtd_name); > + if (IS_ERR_OR_NULL(mtd)) { > + printf("MTD device %s not found, ret %ld\n", > + mtd_name, PTR_ERR(mtd)); > + return CMD_RET_FAILURE; > + } > + put_mtd_device(mtd); > + > + argc -=3D 3; > + argv +=3D 3; > + > + /* Do the parsing */ > + if (!strncmp(cmd, "read", 4) || !strncmp(cmd, "dump", 4) || > + !strncmp(cmd, "write", 5)) { > + bool has_pages =3D mtd->type =3D=3D MTD_NANDFLASH || > + mtd->type =3D=3D MTD_MLCNANDFLASH; > + bool dump, read, raw, woob, write_empty_pages; > + struct mtd_oob_ops io_op =3D {}; > + uint user_addr =3D 0, npages; > + u64 start_off, off, len, remaining, default_len; > + u32 oob_len; > + u8 *buf; > + int ret; > + > + dump =3D !strncmp(cmd, "dump", 4); > + read =3D dump || !strncmp(cmd, "read", 4); > + raw =3D strstr(cmd, ".raw"); > + woob =3D strstr(cmd, ".oob"); > + write_empty_pages =3D !has_pages || strstr(cmd, ".dontski= pff"); > + > + if (!dump) { > + if (!argc) > + return CMD_RET_USAGE; > + > + user_addr =3D simple_strtoul(argv[0], NULL, 16); > + argc--; > + argv++; > + } > + > + start_off =3D argc > 0 ? simple_strtoul(argv[0], NULL, 16= ) : 0; > + if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) { > + printf("Offset not aligned with a page (0x%x)\n", > + mtd->writesize); > + return CMD_RET_FAILURE; > + } > + > + default_len =3D dump ? mtd->writesize : mtd->size; > + len =3D argc > 1 ? simple_strtoul(argv[1], NULL, 16) : > + default_len; > + if (!mtd_is_aligned_with_min_io_size(mtd, len)) { > + len =3D round_up(len, mtd->writesize); > + printf("Size not on a page boundary (0x%x), round= ing to 0x%llx\n", > + mtd->writesize, len); > + } > + > + remaining =3D len; > + npages =3D mtd_len_to_pages(mtd, len); > + oob_len =3D woob ? npages * mtd->oobsize : 0; > + > + if (dump) > + buf =3D kmalloc(len + oob_len, GFP_KERNEL); > + else > + buf =3D map_sysmem(user_addr, 0); > + > + if (!buf) { > + printf("Could not map/allocate the user buffer\n"= ); > + return CMD_RET_FAILURE; > + } > + > + if (has_pages) > + printf("%s %lld byte(s) (%d page(s)) at offset 0x= %08llx%s%s%s\n", > + read ? "Reading" : "Writing", len, npages,= start_off, > + raw ? " [raw]" : "", woob ? " [oob]" : "", > + !read && write_empty_pages ? " [dontskipff= ]" : ""); > + else > + printf("%s %lld byte(s) at offset 0x%08llx\n", > + read ? "Reading" : "Writing", len, start_o= ff); > + > + io_op.mode =3D raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB; > + io_op.len =3D has_pages ? mtd->writesize : len; > + io_op.ooblen =3D woob ? mtd->oobsize : 0; > + io_op.datbuf =3D buf; > + io_op.oobbuf =3D woob ? &buf[len] : NULL; > + > + /* Search for the first good block after the given offset= */ > + off =3D start_off; > + while (mtd_block_isbad(mtd, off)) > + off +=3D mtd->erasesize; > + > + /* Loop over the pages to do the actual read/write */ > + while (remaining) { > + /* Skip the block if it is bad */ > + if (mtd_is_aligned_with_block_size(mtd, off) && > + mtd_block_isbad(mtd, off)) { > + off +=3D mtd->erasesize; > + continue; > + } > + > + if (read) > + ret =3D mtd_read_oob(mtd, off, &io_op); > + else > + ret =3D mtd_special_write_oob(mtd, off, &= io_op, > + write_empty_p= ages, > + woob); > + > + if (ret) { > + printf("Failure while %s at offset 0x%llx= \n", > + read ? "reading" : "writing", off); > + return CMD_RET_FAILURE; > + } > + > + off +=3D io_op.retlen; > + remaining -=3D io_op.retlen; > + io_op.datbuf +=3D io_op.retlen; > + io_op.oobbuf +=3D io_op.oobretlen; > + } > + > + if (!ret && dump) > + mtd_dump_device_buf(mtd, start_off, buf, len, woo= b); > + > + if (dump) > + kfree(buf); > + else > + unmap_sysmem(buf); > + > + if (ret) { > + printf("%s on %s failed with error %d\n", > + read ? "Read" : "Write", mtd->name, ret); > + return CMD_RET_FAILURE; > + } > + > + } else if (!strcmp(cmd, "erase")) { > + bool scrub =3D strstr(cmd, ".dontskipbad"); > + struct erase_info erase_op =3D {}; > + u64 off, len; > + int ret; > + > + off =3D argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; > + len =3D argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mt= d->size; > + > + if (!mtd_is_aligned_with_block_size(mtd, off)) { > + printf("Offset not aligned with a block (0x%x)\n", > + mtd->erasesize); > + return CMD_RET_FAILURE; > + } > + > + if (!mtd_is_aligned_with_block_size(mtd, len)) { > + printf("Size not a multiple of a block (0x%x)\n", > + mtd->erasesize); > + return CMD_RET_FAILURE; > + } > + > + printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\= n", > + off, off + len - 1, mtd_div_by_eb(len, mtd)); > + > + erase_op.mtd =3D mtd; > + erase_op.addr =3D off; > + erase_op.len =3D len; > + erase_op.scrub =3D scrub; > + > + while (erase_op.len) { > + ret =3D mtd_erase(mtd, &erase_op); > + > + /* Abort if its not a bad block error */ > + if (ret !=3D -EIO) > + break; > + > + printf("Skipping bad block at 0x%08llx\n", > + erase_op.fail_addr); > + > + /* Skip bad block and continue behind it */ > + erase_op.len -=3D erase_op.fail_addr - erase_op.a= ddr; > + erase_op.len -=3D mtd->erasesize; > + erase_op.addr =3D erase_op.fail_addr + mtd->erase= size; > + } > + > + if (ret && ret !=3D -EIO) > + return CMD_RET_FAILURE; > + } else if (!strcmp(cmd, "bad")) { > + loff_t off; > + > + if (!mtd_can_have_bb(mtd)) { > + printf("Only NAND-based devices can have bad bloc= ks\n"); > + return CMD_RET_SUCCESS; > + } > + > + printf("MTD device %s bad blocks list:\n", mtd->name); > + for (off =3D 0; off < mtd->size; off +=3D mtd->erasesize) > + if (mtd_block_isbad(mtd, off)) > + printf("\t0x%08llx\n", off); > + } else { > + return CMD_RET_USAGE; > + } > + > + return CMD_RET_SUCCESS; > +} > + > +static char mtd_help_text[] =3D > +#ifdef CONFIG_SYS_LONGHELP > + "- generic operations on memory technology devices\n\n" > + "mtd list\n" > + "mtd read[.raw][.oob] [ []]\n" > + "mtd dump[.raw][.oob] [ []]\n" > + "mtd write[.raw][.oob][.dontskipff] [ []]\n" > + "mtd erase[.dontskipbad] [ []]\n" > + "\n" > + "Specific functions:\n" > + "mtd bad \n" > + "\n" > + "With:\n" > + "\t: NAND partition/chip name\n" > + "\t: user address from/to which data will be retrieved/stor= ed\n" > + "\t: offset in in bytes (default: start of the part)\= n" > + "\t\t* must be block-aligned for erase\n" > + "\t\t* must be page-aligned otherwise\n" > + "\t: length of the operation in bytes (default: the entire = device)\n" > + "\t\t* must be a multiple of a block for erase\n" > + "\t\t* must be a multiple of a page otherwise (special case: defa= ult is a page with dump)\n" > + "\n" > + "The .dontskipff option forces writing empty pages, don't use it = if unsure.\n" > +#endif > + ""; > + > +U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text); > diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile > index cdf515256a..22ceda93c0 100644 > --- a/drivers/mtd/Makefile > +++ b/drivers/mtd/Makefile > @@ -3,7 +3,7 @@ > # (C) Copyright 2000-2007 > # Wolfgang Denk, DENX Software Engineering, wd at denx.de. > > -ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CM= D_ONENAND)$(CONFIG_CMD_SF))) > +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CM= D_ONENAND)$(CONFIG_CMD_SF)$(CONFIG_CMD_MTD))) > obj-y +=3D mtdcore.o mtd_uboot.o > endif > obj-$(CONFIG_MTD) +=3D mtd-uclass.o > diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c > index 8d7e7890b7..6a211d52ff 100644 > --- a/drivers/mtd/mtd_uboot.c > +++ b/drivers/mtd/mtd_uboot.c > @@ -4,8 +4,15 @@ > * Heiko Schocher, DENX Software Engineering, hs at denx.de. > */ > #include > +#include > +#include > +#include /* LEGACY */ > #include > -#include /* Legacy */ > +#include > +#include > + > +#define MTD_NAME_MAX_LEN 20 > + > > /** > * mtd_search_alternate_name - Search an alternate name for @mtdname tha= nks to > @@ -68,6 +75,158 @@ int mtd_search_alternate_name(const char *mtdname, ch= ar *altname, > return -EINVAL; > } > > +#if IS_ENABLED(CONFIG_MTD) > +static void mtd_probe_uclass_mtd_devs(void) > +{ > + struct udevice *dev; > + int idx =3D 0; > + > + /* Probe devices with DM compliant drivers */ > + while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { > + mtd_probe(dev); > + idx++; > + } > +} > +#else > +static void mtd_probe_uclass_mtd_devs(void) { } > +#endif > + > +#if defined(CONFIG_MTD_PARTITIONS) > +int mtd_probe_devices(void) > +{ > + static char *old_mtdparts; > + static char *old_mtdids; > + const char *mtdparts =3D env_get("mtdparts"); > + const char *mtdids =3D env_get("mtdids"); > + bool remaining_partitions =3D true; > + struct mtd_info *mtd; > + > + mtd_probe_uclass_mtd_devs(); > + > + /* Check if mtdparts/mtdids changed since last call, otherwise: e= xit */ > + if (!strcmp(mtdparts, old_mtdparts) && !strcmp(mtdids, old_mtdids= )) > + return 0; > + > + /* Update the local copy of mtdparts */ > + free(old_mtdparts); > + free(old_mtdids); > + old_mtdparts =3D strdup(mtdparts); > + old_mtdids =3D strdup(mtdids); > + > + /* If at least one partition is still in use, do not delete anyth= ing */ > + 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 partit= ions of > + * one device at the same time), so break and iterate from start = each > + * time a new partition is found and deleted. > + */ > + while (remaining_partitions) { > + remaining_partitions =3D false; > + mtd_for_each_device(mtd) { > + if (!mtd_is_partition(mtd) && mtd_has_partitions(= mtd)) { > + del_mtd_partitions(mtd); > + remaining_partitions =3D true; > + break; > + } > + } > + } > + > + /* Start the parsing by ignoring the extra 'mtdparts=3D' prefix, = if any */ > + if (strstr(mtdparts, "mtdparts=3D")) > + mtdparts +=3D 9; > + > + /* For each MTD device in mtdparts */ > + while (mtdparts[0] !=3D '\0') { > + char mtd_name[MTD_NAME_MAX_LEN], *colon; > + struct mtd_partition *parts; > + int mtd_name_len, nparts; > + int ret; > + > + colon =3D strchr(mtdparts, ':'); > + if (!colon) { > + printf("Wrong mtdparts: %s\n", mtdparts); > + return -EINVAL; > + } > + > + mtd_name_len =3D colon - mtdparts; > + strncpy(mtd_name, mtdparts, mtd_name_len); > + mtd_name[mtd_name_len] =3D '\0'; > + /* Move the pointer forward (including the ':') */ > + mtdparts +=3D mtd_name_len + 1; > + mtd =3D get_mtd_device_nm(mtd_name); > + if (IS_ERR_OR_NULL(mtd)) { > + char linux_name[MTD_NAME_MAX_LEN]; > + > + /* > + * The MTD device named "mtd_name" does not exist= . Try > + * to find a correspondance with an MTD device ha= ving > + * the same type and number as defined in the mtd= ids. > + */ > + debug("No device named %s\n", mtd_name); > + ret =3D mtd_search_alternate_name(mtd_name, linux= _name, > + MTD_NAME_MAX_LEN); > + if (!ret) > + mtd =3D get_mtd_device_nm(linux_name); > + > + /* > + * If no device could be found, move the mtdparts > + * pointer forward until the next set of partitio= ns. > + */ > + if (ret || IS_ERR_OR_NULL(mtd)) { > + printf("Could not find a valid device for= %s\n", > + mtd_name); > + mtdparts =3D strchr(mtdparts, ';'); > + if (mtdparts) > + mtdparts++; > + > + continue; > + } > + } > + > + /* > + * Parse the MTD device partitions. It will update the mt= dparts > + * pointer, create an array of parts (that must be freed)= , and > + * return the number of partition structures in the array. > + */ > + ret =3D mtd_parse_partitions(mtd, &mtdparts, &parts, &npa= rts); > + if (ret) { > + printf("Could not parse device %s\n", mtd->name); > + put_mtd_device(mtd); > + return -EINVAL; > + } > + > + if (!nparts) > + continue; > + > + /* Create the new MTD partitions */ > + add_mtd_partitions(mtd, parts, nparts); > + > + /* Free the structures allocated during the parsing */ > + mtd_free_parsed_partitions(parts, nparts); > + > + put_mtd_device(mtd); > + } > + > + return 0; > +} > +#else > +int mtd_probe_devices(void) > +{ > + mtd_probe_uclass_mtd_devs(); > + > + return 0; > +} > +#endif /* defined(CONFIG_MTD_PARTITIONS) */ > + > /* Legacy */ > > static int get_part(const char *partname, int *idx, loff_t *off, loff_t = *size, > diff --git a/include/mtd.h b/include/mtd.h > index 6e6da3002f..011f26b3e1 100644 > --- a/include/mtd.h > +++ b/include/mtd.h > @@ -8,6 +8,9 @@ > > #include > > +struct udevice; > + > +#if defined(CONFIG_DM) > /* > * Get mtd_info structure of the dev, which is stored as uclass private. > * > @@ -20,5 +23,18 @@ static inline struct mtd_info *mtd_get_info(struct ude= vice *dev) > } > > int mtd_probe(struct udevice *dev); > +#else > +static inline struct mtd_info *mtd_get_info(struct udevice *dev) > +{ > + return NULL; > +} > + > +static inline int mtd_probe(struct udevice *dev) > +{ > + return 0; > +} > +#endif > + > +int mtd_probe_devices(void); > > #endif /* _MTD_H_ */ > -- > 2.17.1 > > _______________________________________________ > U-Boot mailing list > U-Boot at lists.denx.de > https://lists.denx.de/listinfo/u-boot