[U-Boot] [PATCH v13 5/7] cmd: mtd: add 'mtd' command

Adam Ford aford173 at gmail.com
Wed Oct 3 12:35:15 UTC 2018


On Mon, Oct 1, 2018 at 8:48 AM Miquel Raynal <miquel.raynal at bootlin.com> wrote:
>
> 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 <miquel.raynal at bootlin.com>
> Acked-by: Jagan Teki <jagan at openedev.com>
> Reviewed-by: Stefan Roese <sr at denx.de>
> Reviewed-by: Boris Brezillon <boris.brezillon at bootlin.com>
> ---
>  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 sparse
>           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 the
>           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 command
>           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) += misc.o
>  obj-$(CONFIG_CMD_MMC) += mmc.o
>  obj-$(CONFIG_CMD_MMC_SPI) += mmc_spi.o
>  obj-$(CONFIG_MP) += mp.o
> +obj-$(CONFIG_CMD_MTD) += mtd.o
>  obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
>  obj-$(CONFIG_CMD_NAND) += nand.o
>  obj-$(CONFIG_CMD_NET) += 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èl Raynal <miquel.raynal at bootlin.com>
> + */
> +
> +#include <command.h>
> +#include <common.h>
> +#include <console.h>
> +#include <malloc.h>
> +#include <mapmem.h>
> +#include <mtd.h>
> +
> +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 size)
> +{
> +       return !do_div(size, mtd->writesize);
> +}
> +
> +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
> +{
> +       return !do_div(size, mtd->erasesize);
> +}
> +
> +static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
> +{
> +       int i, j;
> +
> +       for (i = 0; i < len; ) {
> +               printf("0x%08x:\t", offset + i);
> +               for (j = 0; j < 8; j++)
> +                       printf("%02x ", buf[i + j]);
> +               printf(" ");
> +               i += 8;
> +               for (j = 0; j < 8; j++)
> +                       printf("%02x ", buf[i + j]);
> +               printf("\n");
> +               i += 8;
> +       }
> +}
> +
> +static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
> +                               const u8 *buf, u64 len, bool woob)
> +{
> +       bool has_pages = mtd->type == MTD_NANDFLASH ||
> +               mtd->type == MTD_MLCNANDFLASH;
> +       int npages = mtd_len_to_pages(mtd, len);
> +       uint page;
> +
> +       if (has_pages) {
> +               for (page = 0; page < npages; page++) {
> +                       u64 data_off = 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 = 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 = 0; i < level; i++)
> +                       printf("\t");
> +               printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
> +                      part->offset, part->offset + part->size, part->name);
> +
> +               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_size);
> +               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 = 0; i < op->len; i++)
> +               if (op->datbuf[i] != 0xff)
> +                       return false;
> +
> +       for (i = 0; i < op->ooblen; i++)
> +               if (op->oobbuf[i] != 0xff)
> +                       return false;
> +
> +       return true;
> +}
> +
> +static int do_mtd_list(void)
> +{
> +       struct mtd_info *mtd;
> +       int dev_nb = 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 = 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 = mtd->writesize;
> +               io_op->oobretlen = woob ? mtd->oobsize : 0;
> +       } else {
> +               ret = mtd_write_oob(mtd, off, io_op);
> +       }
> +
> +       return ret;
> +}
> +
> +static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> +{
> +       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 = argv[1];
> +
> +       /* List the MTD devices if that is what the user wants */
> +       if (strcmp(cmd, "list") == 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 = argv[2];
> +       mtd_probe_devices();
> +       mtd = 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 -= 3;
> +       argv += 3;
> +
> +       /* Do the parsing */
> +       if (!strncmp(cmd, "read", 4) || !strncmp(cmd, "dump", 4) ||
> +           !strncmp(cmd, "write", 5)) {
> +               bool has_pages = mtd->type == MTD_NANDFLASH ||
> +                                mtd->type == MTD_MLCNANDFLASH;
> +               bool dump, read, raw, woob, write_empty_pages;
> +               struct mtd_oob_ops io_op = {};
> +               uint user_addr = 0, npages;
> +               u64 start_off, off, len, remaining, default_len;
> +               u32 oob_len;
> +               u8 *buf;
> +               int ret;
> +
> +               dump = !strncmp(cmd, "dump", 4);
> +               read = dump || !strncmp(cmd, "read", 4);
> +               raw = strstr(cmd, ".raw");
> +               woob = strstr(cmd, ".oob");
> +               write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
> +
> +               if (!dump) {
> +                       if (!argc)
> +                               return CMD_RET_USAGE;
> +
> +                       user_addr = simple_strtoul(argv[0], NULL, 16);
> +                       argc--;
> +                       argv++;
> +               }
> +
> +               start_off = 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 = dump ? mtd->writesize : mtd->size;
> +               len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) :
> +                                default_len;
> +               if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
> +                       len = round_up(len, mtd->writesize);
> +                       printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
> +                              mtd->writesize, len);
> +               }
> +
> +               remaining = len;
> +               npages = mtd_len_to_pages(mtd, len);
> +               oob_len = woob ? npages * mtd->oobsize : 0;
> +
> +               if (dump)
> +                       buf = kmalloc(len + oob_len, GFP_KERNEL);
> +               else
> +                       buf = 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_off);
> +
> +               io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
> +               io_op.len = has_pages ? mtd->writesize : len;
> +               io_op.ooblen = woob ? mtd->oobsize : 0;
> +               io_op.datbuf = buf;
> +               io_op.oobbuf = woob ? &buf[len] : NULL;
> +
> +               /* Search for the first good block after the given offset */
> +               off = start_off;
> +               while (mtd_block_isbad(mtd, off))
> +                       off += 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 += mtd->erasesize;
> +                               continue;
> +                       }
> +
> +                       if (read)
> +                               ret = mtd_read_oob(mtd, off, &io_op);
> +                       else
> +                               ret = mtd_special_write_oob(mtd, off, &io_op,
> +                                                           write_empty_pages,
> +                                                           woob);
> +
> +                       if (ret) {
> +                               printf("Failure while %s at offset 0x%llx\n",
> +                                      read ? "reading" : "writing", off);
> +                               return CMD_RET_FAILURE;
> +                       }
> +
> +                       off += io_op.retlen;
> +                       remaining -= io_op.retlen;
> +                       io_op.datbuf += io_op.retlen;
> +                       io_op.oobbuf += io_op.oobretlen;
> +               }
> +
> +               if (!ret && dump)
> +                       mtd_dump_device_buf(mtd, start_off, buf, len, woob);
> +
> +               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 = strstr(cmd, ".dontskipbad");
> +               struct erase_info erase_op = {};
> +               u64 off, len;
> +               int ret;
> +
> +               off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
> +               len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->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 = mtd;
> +               erase_op.addr = off;
> +               erase_op.len = len;
> +               erase_op.scrub = scrub;
> +
> +               while (erase_op.len) {
> +                       ret = mtd_erase(mtd, &erase_op);
> +
> +                       /* Abort if its not a bad block error */
> +                       if (ret != -EIO)
> +                               break;
> +
> +                       printf("Skipping bad block at 0x%08llx\n",
> +                              erase_op.fail_addr);
> +
> +                       /* Skip bad block and continue behind it */
> +                       erase_op.len -= erase_op.fail_addr - erase_op.addr;
> +                       erase_op.len -= mtd->erasesize;
> +                       erase_op.addr = erase_op.fail_addr + mtd->erasesize;
> +               }
> +
> +               if (ret && ret != -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 blocks\n");
> +                       return CMD_RET_SUCCESS;
> +               }
> +
> +               printf("MTD device %s bad blocks list:\n", mtd->name);
> +               for (off = 0; off < mtd->size; off += 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[] =
> +#ifdef CONFIG_SYS_LONGHELP
> +       "- generic operations on memory technology devices\n\n"
> +       "mtd list\n"
> +       "mtd read[.raw][.oob]                  <name> <addr> [<off> [<size>]]\n"
> +       "mtd dump[.raw][.oob]                  <name>        [<off> [<size>]]\n"
> +       "mtd write[.raw][.oob][.dontskipff]    <name> <addr> [<off> [<size>]]\n"
> +       "mtd erase[.dontskipbad]               <name>        [<off> [<size>]]\n"
> +       "\n"
> +       "Specific functions:\n"
> +       "mtd bad                               <name>\n"
> +       "\n"
> +       "With:\n"
> +       "\t<name>: NAND partition/chip name\n"
> +       "\t<addr>: user address from/to which data will be retrieved/stored\n"
> +       "\t<off>: offset in <name> 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<size>: 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: default 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_CMD_ONENAND)$(CONFIG_CMD_SF)))
> +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF)$(CONFIG_CMD_MTD)))
>  obj-y += mtdcore.o mtd_uboot.o
>  endif
>  obj-$(CONFIG_MTD) += 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 <common.h>
> +#include <dm/device.h>
> +#include <dm/uclass-internal.h>
> +#include <jffs2/jffs2.h> /* LEGACY */
>  #include <linux/mtd/mtd.h>
> -#include <jffs2/jffs2.h> /* Legacy */
> +#include <linux/mtd/partitions.h>
> +#include <mtd.h>
> +
> +#define MTD_NAME_MAX_LEN 20
> +
>
>  /**
>   * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to
> @@ -68,6 +75,158 @@ int mtd_search_alternate_name(const char *mtdname, char *altname,
>         return -EINVAL;
>  }
>
> +#if IS_ENABLED(CONFIG_MTD)
> +static void mtd_probe_uclass_mtd_devs(void)
> +{
> +       struct udevice *dev;
> +       int idx = 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 = env_get("mtdparts");
> +       const char *mtdids = env_get("mtdids");
> +       bool remaining_partitions = true;
> +       struct mtd_info *mtd;
> +
> +       mtd_probe_uclass_mtd_devs();
> +
> +       /* Check if mtdparts/mtdids changed since last call, otherwise: exit */
> +       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 = 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;
> +               }
> +       }
> +
> +       /*
> +        * 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.
> +        */
> +       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;
> +                       }
> +               }
> +       }
> +
> +       /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */
> +       if (strstr(mtdparts, "mtdparts="))
> +               mtdparts += 9;
> +
> +       /* For each MTD device in mtdparts */
> +       while (mtdparts[0] != '\0') {
> +               char mtd_name[MTD_NAME_MAX_LEN], *colon;
> +               struct mtd_partition *parts;
> +               int mtd_name_len, nparts;
> +               int ret;
> +
> +               colon = strchr(mtdparts, ':');
> +               if (!colon) {
> +                       printf("Wrong mtdparts: %s\n", mtdparts);
> +                       return -EINVAL;
> +               }
> +
> +               mtd_name_len = colon - mtdparts;
> +               strncpy(mtd_name, mtdparts, mtd_name_len);
> +               mtd_name[mtd_name_len] = '\0';
> +               /* Move the pointer forward (including the ':') */
> +               mtdparts += mtd_name_len + 1;
> +               mtd = 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 having
> +                        * the same type and number as defined in the mtdids.
> +                        */
> +                       debug("No device named %s\n", mtd_name);
> +                       ret = mtd_search_alternate_name(mtd_name, linux_name,
> +                                                       MTD_NAME_MAX_LEN);
> +                       if (!ret)
> +                               mtd = get_mtd_device_nm(linux_name);
> +
> +                       /*
> +                        * If no device could be found, move the mtdparts
> +                        * pointer forward until the next set of partitions.
> +                        */
> +                       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++;
> +
> +                               continue;
> +                       }
> +               }
> +
> +               /*
> +                * Parse the MTD device partitions. It will update the mtdparts
> +                * pointer, create an array of parts (that must be freed), and
> +                * return the number of partition structures in the array.
> +                */
> +               ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts);
> +               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 <linux/mtd/mtd.h>
>
> +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 udevice *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


More information about the U-Boot mailing list