]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/cmd_mtdparts.c
cmd_mtdparts.c: report ECC status along with the partition map
[karo-tx-uboot.git] / common / cmd_mtdparts.c
index c8bf2c6392e8e9e99551455186c02ce80b60ef0b..a2289df4f64c60228d76c05eb56a1956502bebbf 100644 (file)
  *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
  *   kernel tree.
  *
+ * (C) Copyright 2008
+ * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
+ *
  *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
  *   Copyright 2002 SYSGO Real-Time Solutions GmbH
  *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 /*
  *
  */
 
-/*
- * JFFS2/CRAMFS support
- */
 #include <common.h>
 #include <command.h>
 #include <malloc.h>
-#include <jffs2/jffs2.h>
+#include <jffs2/load_kernel.h>
 #include <linux/list.h>
 #include <linux/ctype.h>
-#include <cramfs/cramfs_fs.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
 
 #if defined(CONFIG_CMD_NAND)
-#ifdef CONFIG_NAND_LEGACY
-#include <linux/mtd/nand_legacy.h>
-#else /* !CONFIG_NAND_LEGACY */
 #include <linux/mtd/nand.h>
 #include <nand.h>
-#endif /* !CONFIG_NAND_LEGACY */
 #endif
 
 #if defined(CONFIG_CMD_ONENAND)
-#include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
 #include <onenand_uboot.h>
 #endif
 
-/* enable/disable debugging messages */
-#define        DEBUG_MTDPARTS
-#undef DEBUG_MTDPARTS
-
-#ifdef  DEBUG_MTDPARTS
-# define DEBUGF(fmt, args...)  printf(fmt ,##args)
-#else
-# define DEBUGF(fmt, args...)
-#endif
+DECLARE_GLOBAL_DATA_PTR;
 
 /* special size referring to all the remaining space in a partition */
 #define SIZE_REMAINING         0xFFFFFFFF
 #if defined(MTDIDS_DEFAULT)
 static const char *const mtdids_default = MTDIDS_DEFAULT;
 #else
-#warning "MTDIDS_DEFAULT not defined!"
 static const char *const mtdids_default = NULL;
 #endif
 
 #if defined(MTDPARTS_DEFAULT)
 static const char *const mtdparts_default = MTDPARTS_DEFAULT;
 #else
-#warning "MTDPARTS_DEFAULT not defined!"
 static const char *const mtdparts_default = NULL;
 #endif
 
@@ -163,14 +133,14 @@ static char last_partition[PARTITION_MAXLEN];
 extern void jffs2_free_cache(struct part_info *part);
 
 /* mtdids mapping list, filled by parse_ids() */
-struct list_head mtdids;
+static struct list_head mtdids;
 
 /* device/partition list, parse_cmdline() parses into here */
-struct list_head devices;
+static struct list_head devices;
 
 /* current active device and partition number */
-static struct mtd_device *current_dev = NULL;
-static u8 current_partnum = 0;
+struct mtd_device *current_mtd_dev = NULL;
+u8 current_mtd_partnum = 0;
 
 static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
 
@@ -246,36 +216,34 @@ static void memsize_format(char *buf, u32 size)
  */
 static void index_partitions(void)
 {
-       char buf[16];
        u16 mtddevnum;
        struct part_info *part;
        struct list_head *dentry;
        struct mtd_device *dev;
 
-       DEBUGF("--- index partitions ---\n");
+       debug("--- index partitions ---\n");
 
-       if (current_dev) {
+       if (current_mtd_dev) {
                mtddevnum = 0;
                list_for_each(dentry, &devices) {
                        dev = list_entry(dentry, struct mtd_device, link);
-                       if (dev == current_dev) {
-                               mtddevnum += current_partnum;
-                               sprintf(buf, "%d", mtddevnum);
-                               setenv("mtddevnum", buf);
+                       if (dev == current_mtd_dev) {
+                               mtddevnum += current_mtd_partnum;
+                               setenv_ulong("mtddevnum", mtddevnum);
                                break;
                        }
                        mtddevnum += dev->num_parts;
                }
 
-               part = mtd_part_info(current_dev, current_partnum);
+               part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
                setenv("mtddevname", part->name);
 
-               DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
+               debug("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
        } else {
                setenv("mtddevnum", NULL);
                setenv("mtddevname", NULL);
 
-               DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n");
+               debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
        }
 }
 
@@ -286,157 +254,128 @@ static void current_save(void)
 {
        char buf[16];
 
-       DEBUGF("--- current_save ---\n");
+       debug("--- current_save ---\n");
 
-       if (current_dev) {
-               sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type),
-                                       current_dev->id->num, current_partnum);
+       if (current_mtd_dev) {
+               sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
+                                       current_mtd_dev->id->num, current_mtd_partnum);
 
                setenv("partition", buf);
                strncpy(last_partition, buf, 16);
 
-               DEBUGF("=> partition %s\n", buf);
+               debug("=> partition %s\n", buf);
        } else {
                setenv("partition", NULL);
                last_partition[0] = '\0';
 
-               DEBUGF("=> partition NULL\n");
+               debug("=> partition NULL\n");
        }
        index_partitions();
 }
 
-/**
- * Performs sanity check for supplied NOR flash partition. Table of existing
- * NOR flash devices is searched and partition device is located. Alignment
- * with the granularity of NOR flash sectors is verified.
- *
- * @param id of the parent device
- * @param part partition to validate
- * @return 0 if partition is valid, 1 otherwise
- */
-static int part_validate_nor(struct mtdids *id, struct part_info *part)
-{
-#if defined(CONFIG_CMD_FLASH)
-       /* info for FLASH chips */
-       extern flash_info_t flash_info[];
-       flash_info_t *flash;
-       int offset_aligned;
-       u32 end_offset, sector_size = 0;
-       int i;
-
-       flash = &flash_info[id->num];
-
-       /* size of last sector */
-       part->sector_size = flash->size -
-               (flash->start[flash->sector_count-1] - flash->start[0]);
-
-       offset_aligned = 0;
-       for (i = 0; i < flash->sector_count; i++) {
-               if ((flash->start[i] - flash->start[0]) == part->offset) {
-                       offset_aligned = 1;
-                       break;
-               }
-       }
-       if (offset_aligned == 0) {
-               printf("%s%d: partition (%s) start offset alignment incorrect\n",
-                               MTD_DEV_TYPE(id->type), id->num, part->name);
-               return 1;
-       }
-
-       end_offset = part->offset + part->size;
-       offset_aligned = 0;
-       for (i = 0; i < flash->sector_count; i++) {
-               if (i) {
-                       sector_size = flash->start[i] - flash->start[i-1];
-                       if (part->sector_size < sector_size)
-                               part->sector_size = sector_size;
-               }
-               if ((flash->start[i] - flash->start[0]) == end_offset)
-                       offset_aligned = 1;
-       }
-
-       if (offset_aligned || flash->size == end_offset)
-               return 0;
-
-       printf("%s%d: partition (%s) size alignment incorrect\n",
-                       MTD_DEV_TYPE(id->type), id->num, part->name);
-#endif
-       return 1;
-}
 
 /**
- * Performs sanity check for supplied NAND flash partition. Table of existing
- * NAND flash devices is searched and partition device is located. Alignment
- * with the granularity of nand erasesize is verified.
+ * Produce a mtd_info given a type and num.
  *
- * @param id of the parent device
- * @param part partition to validate
- * @return 0 if partition is valid, 1 otherwise
+ * @param type mtd type
+ * @param num mtd number
+ * @param mtd a pointer to an mtd_info instance (output)
+ * @return 0 if device is valid, 1 otherwise
  */
-static int part_validate_nand(struct mtdids *id, struct part_info *part)
+static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
 {
-#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
-       /* info for NAND chips */
-       nand_info_t *nand;
-
-       nand = &nand_info[id->num];
-
-       part->sector_size = nand->erasesize;
-
-       if ((unsigned long)(part->offset) % nand->erasesize) {
-               printf("%s%d: partition (%s) start offset alignment incorrect\n",
-                               MTD_DEV_TYPE(id->type), id->num, part->name);
-               return 1;
-       }
+       char mtd_dev[16];
 
-       if (part->size % nand->erasesize) {
-               printf("%s%d: partition (%s) size alignment incorrect\n",
-                               MTD_DEV_TYPE(id->type), id->num, part->name);
+       sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
+       *mtd = get_mtd_device_nm(mtd_dev);
+       if (IS_ERR(*mtd)) {
+               printf("Device %s not found!\n", mtd_dev);
                return 1;
        }
 
        return 0;
-#else
-       return 1;
-#endif
 }
 
 /**
- * Performs sanity check for supplied OneNAND flash partition.
- * Table of existing OneNAND flash devices is searched and partition device
+ * Performs sanity check for supplied flash partition.
+ * Table of existing MTD flash devices is searched and partition device
  * is located. Alignment with the granularity of nand erasesize is verified.
  *
  * @param id of the parent device
  * @param part partition to validate
  * @return 0 if partition is valid, 1 otherwise
  */
-static int part_validate_onenand(struct mtdids *id, struct part_info *part)
+static int part_validate_eraseblock(struct mtdids *id, struct part_info *part)
 {
-#if defined(CONFIG_CMD_ONENAND)
-       /* info for OneNAND chips */
-       struct mtd_info *mtd;
+       struct mtd_info *mtd = NULL;
+       int i, j;
+       ulong start;
 
-       mtd = &onenand_mtd;
+       if (get_mtd_info(id->type, id->num, &mtd))
+               return 1;
 
        part->sector_size = mtd->erasesize;
 
-       if ((unsigned long)(part->offset) % mtd->erasesize) {
-               printf("%s%d: partition (%s) start offset"
-                       "alignment incorrect\n",
-                               MTD_DEV_TYPE(id->type), id->num, part->name);
+       if (!mtd->numeraseregions) {
+               /*
+                * Only one eraseregion (NAND, OneNAND or uniform NOR),
+                * checking for alignment is easy here
+                */
+               if ((unsigned long)part->offset % mtd->erasesize) {
+                       printf("%s%d: partition (%s) start offset alignment incorrect\n",
+                              MTD_DEV_TYPE(id->type), id->num, part->name);
+                       return 1;
+               }
+
+               if (part->size % mtd->erasesize) {
+                       printf("%s%d: partition (%s) size alignment incorrect\n",
+                              MTD_DEV_TYPE(id->type), id->num, part->name);
+                       return 1;
+               }
+       } else {
+               /*
+                * Multiple eraseregions (non-uniform NOR),
+                * checking for alignment is more complex here
+                */
+
+               /* Check start alignment */
+               for (i = 0; i < mtd->numeraseregions; i++) {
+                       start = mtd->eraseregions[i].offset;
+                       for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
+                               if (part->offset == start)
+                                       goto start_ok;
+                               start += mtd->eraseregions[i].erasesize;
+                       }
+               }
+
+               printf("%s%d: partition (%s) start offset alignment incorrect\n",
+                      MTD_DEV_TYPE(id->type), id->num, part->name);
                return 1;
-       }
 
-       if (part->size % mtd->erasesize) {
+       start_ok:
+
+               /* Check end/size alignment */
+               for (i = 0; i < mtd->numeraseregions; i++) {
+                       start = mtd->eraseregions[i].offset;
+                       for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
+                               if ((part->offset + part->size) == start)
+                                       goto end_ok;
+                               start += mtd->eraseregions[i].erasesize;
+                       }
+               }
+               /* Check last sector alignment */
+               if ((part->offset + part->size) == start)
+                       goto end_ok;
+
                printf("%s%d: partition (%s) size alignment incorrect\n",
-                               MTD_DEV_TYPE(id->type), id->num, part->name);
+                      MTD_DEV_TYPE(id->type), id->num, part->name);
                return 1;
+
+       end_ok:
+               return 0;
        }
 
        return 0;
-#else
-       return 1;
-#endif
 }
 
 
@@ -472,16 +411,11 @@ static int part_validate(struct mtdids *id, struct part_info *part)
                return 1;
        }
 
-       if (id->type == MTD_DEV_TYPE_NAND)
-               return part_validate_nand(id, part);
-       else if (id->type == MTD_DEV_TYPE_NOR)
-               return part_validate_nor(id, part);
-       else if (id->type == MTD_DEV_TYPE_ONENAND)
-               return part_validate_onenand(id, part);
-       else
-               DEBUGF("part_validate: invalid dev type\n");
-
-       return 1;
+       /*
+        * Now we need to check if the partition starts and ends on
+        * sector (eraseblock) regions
+        */
+       return part_validate_eraseblock(id, part);
 }
 
 /**
@@ -501,26 +435,23 @@ static int part_del(struct mtd_device *dev, struct part_info *part)
 
        /* otherwise just delete this partition */
 
-       if (dev == current_dev) {
+       if (dev == current_mtd_dev) {
                /* we are modyfing partitions for the current device,
                 * update current */
                struct part_info *curr_pi;
-               curr_pi = mtd_part_info(current_dev, current_partnum);
+               curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
 
                if (curr_pi) {
                        if (curr_pi == part) {
                                printf("current partition deleted, resetting current to 0\n");
-                               current_partnum = 0;
+                               current_mtd_partnum = 0;
                        } else if (part->offset <= curr_pi->offset) {
-                               current_partnum--;
+                               current_mtd_partnum--;
                        }
                        current_save_needed = 1;
                }
        }
 
-#ifdef CONFIG_NAND_LEGACY
-       jffs2_free_cache(part);
-#endif
        list_del(&part->link);
        free(part);
        dev->num_parts--;
@@ -547,9 +478,6 @@ static void part_delall(struct list_head *head)
        list_for_each_safe(entry, n, head) {
                part_tmp = list_entry(entry, struct part_info, link);
 
-#ifdef CONFIG_NAND_LEGACY
-               jffs2_free_cache(part_tmp);
-#endif
                list_del(entry);
                free(part_tmp);
        }
@@ -571,7 +499,7 @@ static int part_sort_add(struct mtd_device *dev, struct part_info *part)
        part->dev = dev;
 
        if (list_empty(&dev->parts)) {
-               DEBUGF("part_sort_add: list empty\n");
+               debug("part_sort_add: list empty\n");
                list_add(&part->link, &dev->parts);
                dev->num_parts++;
                index_partitions();
@@ -582,8 +510,8 @@ static int part_sort_add(struct mtd_device *dev, struct part_info *part)
 
        /* get current partition info if we are updating current device */
        curr_pi = NULL;
-       if (dev == current_dev)
-               curr_pi = mtd_part_info(current_dev, current_partnum);
+       if (dev == current_mtd_dev)
+               curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
 
        list_for_each(entry, &dev->parts) {
                struct part_info *pi;
@@ -603,7 +531,7 @@ static int part_sort_add(struct mtd_device *dev, struct part_info *part)
                        if (curr_pi && (pi->offset <= curr_pi->offset)) {
                                /* we are modyfing partitions for the current
                                 * device, update current */
-                               current_partnum++;
+                               current_mtd_partnum++;
                                current_save();
                        } else {
                                index_partitions();
@@ -664,7 +592,7 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i
        /* fetch the partition size */
        if (*p == '-') {
                /* assign all remaining space to this partition */
-               DEBUGF("'-': remaining size assigned\n");
+               debug("'-': remaining size assigned\n");
                size = SIZE_REMAINING;
                p++;
        } else {
@@ -749,7 +677,7 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i
        part->name[name_len - 1] = '\0';
        INIT_LIST_HEAD(&part->link);
 
-       DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n",
+       debug("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n",
                        part->name, part->size,
                        part->offset, part->mask_flags);
 
@@ -760,53 +688,21 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i
 /**
  * Check device number to be within valid range for given device type.
  *
- * @param dev device to validate
+ * @param type mtd type
+ * @param num mtd number
+ * @param size a pointer to the size of the mtd device (output)
  * @return 0 if device is valid, 1 otherwise
  */
-int mtd_device_validate(u8 type, u8 num, u32 *size)
+static int mtd_device_validate(u8 type, u8 num, u32 *size)
 {
-       if (type == MTD_DEV_TYPE_NOR) {
-#if defined(CONFIG_CMD_FLASH)
-               if (num < CONFIG_SYS_MAX_FLASH_BANKS) {
-                       extern flash_info_t flash_info[];
-                       *size = flash_info[num].size;
-
-                       return 0;
-               }
+       struct mtd_info *mtd = NULL;
 
-               printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
-                               MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_FLASH_BANKS - 1);
-#else
-               printf("support for FLASH devices not present\n");
-#endif
-       } else if (type == MTD_DEV_TYPE_NAND) {
-#if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
-               if (num < CONFIG_SYS_MAX_NAND_DEVICE) {
-#ifndef CONFIG_NAND_LEGACY
-                       *size = nand_info[num].size;
-#else
-                       extern struct nand_chip nand_dev_desc[CONFIG_SYS_MAX_NAND_DEVICE];
-                       *size = nand_dev_desc[num].totlen;
-#endif
-                       return 0;
-               }
+       if (get_mtd_info(type, num, &mtd))
+               return 1;
 
-               printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
-                               MTD_DEV_TYPE(type), num, CONFIG_SYS_MAX_NAND_DEVICE - 1);
-#else
-               printf("support for NAND devices not present\n");
-#endif
-       } else if (type == MTD_DEV_TYPE_ONENAND) {
-#if defined(CONFIG_CMD_ONENAND)
-               *size = onenand_mtd.size;
-               return 0;
-#else
-               printf("support for OneNAND devices not present\n");
-#endif
-       } else
-               printf("Unknown defice type %d\n", type);
+       *size = mtd->size;
 
-       return 1;
+       return 0;
 }
 
 /**
@@ -845,15 +741,15 @@ static int device_del(struct mtd_device *dev)
        list_del(&dev->link);
        free(dev);
 
-       if (dev == current_dev) {
+       if (dev == current_mtd_dev) {
                /* we just deleted current device */
                if (list_empty(&devices)) {
-                       current_dev = NULL;
+                       current_mtd_dev = NULL;
                } else {
                        /* reset first partition from first dev from the
                         * devices list as current */
-                       current_dev = list_entry(devices.next, struct mtd_device, link);
-                       current_partnum = 0;
+                       current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
+                       current_mtd_partnum = 0;
                }
                current_save();
                return 0;
@@ -871,7 +767,7 @@ static int device_del(struct mtd_device *dev)
  * @param num device number
  * @return NULL if requested device does not exist
  */
-static struct mtd_device* device_find(u8 type, u8 num)
+struct mtd_device *device_find(u8 type, u8 num)
 {
        struct list_head *entry;
        struct mtd_device *dev_tmp;
@@ -896,8 +792,8 @@ static void device_add(struct mtd_device *dev)
        u8 current_save_needed = 0;
 
        if (list_empty(&devices)) {
-               current_dev = dev;
-               current_partnum = 0;
+               current_mtd_dev = dev;
+               current_mtd_partnum = 0;
                current_save_needed = 1;
        }
 
@@ -925,21 +821,24 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_
        struct mtdids *id;
        const char *mtd_id;
        unsigned int mtd_id_len;
-       const char *p, *pend;
+       const char *p;
+       const char *pend;
        LIST_HEAD(tmp_list);
        struct list_head *entry, *n;
        u16 num_parts;
        u32 offset;
        int err = 1;
 
-       p = mtd_dev;
+       debug("===device_parse===\n");
+
+       assert(retdev);
        *retdev = NULL;
-       *ret = NULL;
 
-       DEBUGF("===device_parse===\n");
+       if (ret)
+               *ret = NULL;
 
        /* fetch <mtd-id> */
-       mtd_id = p;
+       mtd_id = p = mtd_dev;
        if (!(p = strchr(mtd_id, ':'))) {
                printf("no <mtd-id> identifier\n");
                return 1;
@@ -953,11 +852,13 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_
                return 1;
        }
 
-       DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
+#ifdef DEBUG
+       pend = strchr(p, ';');
+#endif
+       debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
                        id->type, MTD_DEV_TYPE(id->type),
                        id->num, id->mtd_id);
-       pend = strchr(p, ';');
-       DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
+       debug("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
 
 
        /* parse partitions */
@@ -1003,17 +904,20 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_
                return 1;
        }
 
-       DEBUGF("\ntotal partitions: %d\n", num_parts);
+       debug("\ntotal partitions: %d\n", num_parts);
 
        /* check for next device presence */
        if (p) {
                if (*p == ';') {
-                       *ret = ++p;
+                       if (ret)
+                               *ret = ++p;
                } else if (*p == '\0') {
-                       *ret = p;
+                       if (ret)
+                               *ret = p;
                } else {
                        printf("unexpected character '%c' at the end of device\n", *p);
-                       *ret = NULL;
+                       if (ret)
+                               *ret = NULL;
                        return 1;
                }
        }
@@ -1041,7 +945,7 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_
 
        *retdev = dev;
 
-       DEBUGF("===\n\n");
+       debug("===\n\n");
        return 0;
 }
 
@@ -1053,7 +957,7 @@ static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_
 static int mtd_devices_init(void)
 {
        last_parts[0] = '\0';
-       current_dev = NULL;
+       current_mtd_dev = NULL;
        current_save();
 
        return device_delall(&devices);
@@ -1093,13 +997,13 @@ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_
        struct list_head *entry;
        struct mtdids *id;
 
-       DEBUGF("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
+       debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
                        mtd_id_len, mtd_id, mtd_id_len);
 
        list_for_each(entry, &mtdids) {
                id = list_entry(entry, struct mtdids, link);
 
-               DEBUGF("entry: '%s' (len = %d)\n",
+               debug("entry: '%s' (len = %d)\n",
                                id->mtd_id, strlen(id->mtd_id));
 
                if (mtd_id_len != strlen(id->mtd_id))
@@ -1121,7 +1025,8 @@ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_
  * @param dev_num parsed device number (output)
  * @return 0 on success, 1 otherwise
  */
-int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
+int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
+                u8 *dev_num)
 {
        const char *p = id;
 
@@ -1169,7 +1074,7 @@ static int generate_mtdparts(char *buf, u32 buflen)
        u32 size, offset, len, part_cnt;
        u32 maxlen = buflen - 1;
 
-       DEBUGF("--- generate_mtdparts ---\n");
+       debug("--- generate_mtdparts ---\n");
 
        if (list_empty(&devices)) {
                buf[0] = '\0';
@@ -1300,46 +1205,114 @@ static int generate_mtdparts_save(char *buf, u32 buflen)
        return ret;
 }
 
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
 /**
- * Format and print out a partition list for each device from global device
- * list.
+ * Get the net size (w/o bad blocks) of the given partition.
+ *
+ * @param mtd the mtd info
+ * @param part the partition
+ * @return the calculated net size of this partition
  */
-static void list_partitions(void)
+static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
+{
+       uint64_t i, net_size = 0;
+
+       if (!mtd->block_isbad)
+               return part->size;
+
+       for (i = 0; i < part->size; i += mtd->erasesize) {
+               if (!mtd->block_isbad(mtd, part->offset + i))
+                       net_size += mtd->erasesize;
+       }
+
+       return net_size;
+}
+#endif
+
+static void show_ecc_stats(struct mtd_device *dev)
+{
+       struct mtd_info *mtd;
+
+       if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+               return;
+
+       printf("ECC stats for device %s:\n", mtd->name);
+       printf("  corrected bit flips:\t%4u\n", mtd->ecc_stats.corrected);
+       printf("  uncorrectable errors:\t%4u\n", mtd->ecc_stats.failed);
+}
+
+static void print_partition_table(void)
 {
        struct list_head *dentry, *pentry;
        struct part_info *part;
        struct mtd_device *dev;
        int part_num;
 
-       DEBUGF("\n---list_partitions---\n");
        list_for_each(dentry, &devices) {
                dev = list_entry(dentry, struct mtd_device, link);
+               /* list partitions for given device */
+               part_num = 0;
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
+               struct mtd_info *mtd;
+
+               if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+                       return;
+
                printf("\ndevice %s%d <%s>, # parts = %d\n",
                                MTD_DEV_TYPE(dev->id->type), dev->id->num,
                                dev->id->mtd_id, dev->num_parts);
-               printf(" #: name\t\t\tsize\t\toffset\t\tmask_flags\n");
+               printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
+
+               list_for_each(pentry, &dev->parts) {
+                       u32 net_size;
+                       char *size_note;
+
+                       part = list_entry(pentry, struct part_info, link);
+                       net_size = net_part_size(mtd, part);
+                       size_note = part->size == net_size ? " " : " (!)";
+                       printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
+                                       part_num, part->name, part->size,
+                                       net_size, size_note, part->offset,
+                                       part->mask_flags);
+#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
+               printf("\ndevice %s%d <%s>, # parts = %d\n",
+                               MTD_DEV_TYPE(dev->id->type), dev->id->num,
+                               dev->id->mtd_id, dev->num_parts);
+               printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
 
-               /* list partitions for given device */
-               part_num = 0;
                list_for_each(pentry, &dev->parts) {
                        part = list_entry(pentry, struct part_info, link);
                        printf("%2d: %-20s0x%08x\t0x%08x\t%d\n",
                                        part_num, part->name, part->size,
                                        part->offset, part->mask_flags);
-
+#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
                        part_num++;
                }
+               show_ecc_stats(dev);
        }
+
        if (list_empty(&devices))
                printf("no partitions defined\n");
+}
+
+/**
+ * Format and print out a partition list for each device from global device
+ * list.
+ */
+static void list_partitions(void)
+{
+       struct part_info *part;
+
+       debug("\n---list_partitions---\n");
+       print_partition_table();
 
-       /* current_dev is not NULL only when we have non empty device list */
-       if (current_dev) {
-               part = mtd_part_info(current_dev, current_partnum);
+       /* current_mtd_dev is not NULL only when we have non empty device list */
+       if (current_mtd_dev) {
+               part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
                if (part) {
                        printf("\nactive partition: %s%d,%d - (%s) 0x%08x @ 0x%08x\n",
-                                       MTD_DEV_TYPE(current_dev->id->type),
-                                       current_dev->id->num, current_partnum,
+                                       MTD_DEV_TYPE(current_mtd_dev->id->type),
+                                       current_mtd_dev->id->num, current_mtd_partnum,
                                        part->name, part->size, part->offset);
                } else {
                        printf("could not get current partition info\n\n");
@@ -1347,8 +1320,16 @@ static void list_partitions(void)
        }
 
        printf("\ndefaults:\n");
-       printf("mtdids  : %s\n", mtdids_default);
-       printf("mtdparts: %s\n", mtdparts_default);
+       printf("mtdids  : %s\n",
+               mtdids_default ? mtdids_default : "none");
+       /*
+        * Using printf() here results in printbuffer overflow
+        * if default mtdparts string is greater than console
+        * printbuffer. Use puts() to prevent system crashes.
+        */
+       puts("mtdparts: ");
+       puts(mtdparts_default ? mtdparts_default : "none");
+       puts("\n");
 }
 
 /**
@@ -1368,7 +1349,7 @@ int find_dev_and_part(const char *id, struct mtd_device **dev,
        u8 type, dnum, pnum;
        const char *p;
 
-       DEBUGF("--- find_dev_and_part ---\nid = %s\n", id);
+       debug("--- find_dev_and_part ---\nid = %s\n", id);
 
        list_for_each(dentry, &devices) {
                *part_num = 0;
@@ -1429,7 +1410,7 @@ static int delete_partition(const char *id)
 
        if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
 
-               DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\n",
+               debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08x@0x%08x\n",
                                MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
                                part->name, part->size, part->offset);
 
@@ -1437,7 +1418,7 @@ static int delete_partition(const char *id)
                        return 1;
 
                if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
-                       printf("generated mtdparts too long, reseting to null\n");
+                       printf("generated mtdparts too long, resetting to null\n");
                        return 1;
                }
                return 0;
@@ -1447,6 +1428,101 @@ static int delete_partition(const char *id)
        return 1;
 }
 
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+/**
+ * Increase the size of the given partition so that it's net size is at least
+ * as large as the size member and such that the next partition would start on a
+ * good block if it were adjacent to this partition.
+ *
+ * @param mtd the mtd device
+ * @param part the partition
+ * @param next_offset pointer to the offset of the next partition after this
+ *                    partition's size has been modified (output)
+ */
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
+                            uint64_t *next_offset)
+{
+       uint64_t net_size, padding_size = 0;
+       int truncated;
+
+       mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
+                            &truncated);
+
+       /*
+        * Absorb bad blocks immediately following this
+        * partition also into the partition, such that
+        * the next partition starts with a good block.
+        */
+       if (!truncated) {
+               mtd_get_len_incl_bad(mtd, part->offset + net_size,
+                                    mtd->erasesize, &padding_size, &truncated);
+               if (truncated)
+                       padding_size = 0;
+               else
+                       padding_size -= mtd->erasesize;
+       }
+
+       if (truncated) {
+               printf("truncated partition %s to %lld bytes\n", part->name,
+                      (uint64_t) net_size + padding_size);
+       }
+
+       part->size = net_size + padding_size;
+       *next_offset = part->offset + part->size;
+}
+
+/**
+ * Adjust all of the partition sizes, such that all partitions are at least
+ * as big as their mtdparts environment variable sizes and they each start
+ * on a good block.
+ *
+ * @return 0 on success, 1 otherwise
+ */
+static int spread_partitions(void)
+{
+       struct list_head *dentry, *pentry;
+       struct mtd_device *dev;
+       struct part_info *part;
+       struct mtd_info *mtd;
+       int part_num;
+       uint64_t cur_offs;
+
+       list_for_each(dentry, &devices) {
+               dev = list_entry(dentry, struct mtd_device, link);
+
+               if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+                       return 1;
+
+               part_num = 0;
+               cur_offs = 0;
+               list_for_each(pentry, &dev->parts) {
+                       part = list_entry(pentry, struct part_info, link);
+
+                       debug("spread_partitions: device = %s%d, partition %d ="
+                               " (%s) 0x%08x@0x%08x\n",
+                               MTD_DEV_TYPE(dev->id->type), dev->id->num,
+                               part_num, part->name, part->size,
+                               part->offset);
+
+                       if (cur_offs > part->offset)
+                               part->offset = cur_offs;
+
+                       spread_partition(mtd, part, &cur_offs);
+
+                       part_num++;
+               }
+       }
+
+       index_partitions();
+
+       if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+               printf("generated mtdparts too long, resetting to null\n");
+               return 1;
+       }
+       return 0;
+}
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
+
 /**
  * Accept character string describing mtd partitions and call device_parse()
  * for each entry. Add created devices to the global devices list.
@@ -1459,8 +1535,9 @@ static int parse_mtdparts(const char *const mtdparts)
        const char *p = mtdparts;
        struct mtd_device *dev;
        int err = 1;
+       char tmp_parts[MTDPARTS_MAXLEN];
 
-       DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
+       debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
 
        /* delete all devices and partitions */
        if (mtd_devices_init() != 0) {
@@ -1469,7 +1546,12 @@ static int parse_mtdparts(const char *const mtdparts)
        }
 
        /* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
-       p = getenv("mtdparts");
+       if (gd->flags & GD_FLG_ENV_READY) {
+               p = getenv("mtdparts");
+       } else {
+               p = tmp_parts;
+               getenv_f("mtdparts", tmp_parts, MTDPARTS_MAXLEN);
+       }
 
        if (strncmp(p, "mtdparts=", 9) != 0) {
                printf("mtdparts variable doesn't start with 'mtdparts='\n");
@@ -1482,7 +1564,7 @@ static int parse_mtdparts(const char *const mtdparts)
                if ((device_parse(p, &p, &dev) != 0) || (!dev))
                        break;
 
-               DEBUGF("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
+               debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
                                dev->id->num, dev->id->mtd_id);
 
                /* check if parsed device is already on the list */
@@ -1523,12 +1605,12 @@ static int parse_mtdids(const char *const ids)
        u32 size;
        int ret = 1;
 
-       DEBUGF("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
+       debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
 
        /* clean global mtdids list */
        list_for_each_safe(entry, n, &mtdids) {
                id_tmp = list_entry(entry, struct mtdids, link);
-               DEBUGF("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
+               debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
                list_del(entry);
                free(id_tmp);
        }
@@ -1594,7 +1676,7 @@ static int parse_mtdids(const char *const ids)
                id->mtd_id[mtd_id_len - 1] = '\0';
                INIT_LIST_HEAD(&id->link);
 
-               DEBUGF("+ id %s%d\t%16d bytes\t%s\n",
+               debug("+ id %s%d\t%16d bytes\t%s\n",
                                MTD_DEV_TYPE(id->type), id->num,
                                id->size, id->mtd_id);
 
@@ -1627,8 +1709,9 @@ int mtdparts_init(void)
        const char *current_partition;
        int ids_changed;
        char tmp_ep[PARTITION_MAXLEN];
+       char tmp_parts[MTDPARTS_MAXLEN];
 
-       DEBUGF("\n---mtdparts_init---\n");
+       debug("\n---mtdparts_init---\n");
        if (!initialized) {
                INIT_LIST_HEAD(&mtdids);
                INIT_LIST_HEAD(&devices);
@@ -1640,7 +1723,17 @@ int mtdparts_init(void)
 
        /* get variables */
        ids = getenv("mtdids");
-       parts = getenv("mtdparts");
+       /*
+        * The mtdparts variable tends to be long. If we need to access it
+        * before the env is relocated, then we need to use our own stack
+        * buffer.  gd->env_buf will be too small.
+        */
+       if (gd->flags & GD_FLG_ENV_READY) {
+               parts = getenv("mtdparts");
+       } else {
+               parts = tmp_parts;
+               getenv_f("mtdparts", tmp_parts, MTDPARTS_MAXLEN);
+       }
        current_partition = getenv("partition");
 
        /* save it for later parsing, cannot rely on current partition pointer
@@ -1649,18 +1742,18 @@ int mtdparts_init(void)
        if (current_partition)
                strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
 
-       DEBUGF("last_ids  : %s\n", last_ids);
-       DEBUGF("env_ids   : %s\n", ids);
-       DEBUGF("last_parts: %s\n", last_parts);
-       DEBUGF("env_parts : %s\n\n", parts);
+       debug("last_ids  : %s\n", last_ids);
+       debug("env_ids   : %s\n", ids);
+       debug("last_parts: %s\n", last_parts);
+       debug("env_parts : %s\n\n", parts);
 
-       DEBUGF("last_partition : %s\n", last_partition);
-       DEBUGF("env_partition  : %s\n", current_partition);
+       debug("last_partition : %s\n", last_partition);
+       debug("env_partition  : %s\n", current_partition);
 
        /* if mtdids varible is empty try to use defaults */
        if (!ids) {
                if (mtdids_default) {
-                       DEBUGF("mtdids variable not defined, using default\n");
+                       debug("mtdids variable not defined, using default\n");
                        ids = mtdids_default;
                        setenv("mtdids", (char *)ids);
                } else {
@@ -1712,13 +1805,13 @@ int mtdparts_init(void)
                strncpy(last_parts, parts, MTDPARTS_MAXLEN);
 
                /* reset first partition from first dev from the list as current */
-               current_dev = list_entry(devices.next, struct mtd_device, link);
-               current_partnum = 0;
+               current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
+               current_mtd_partnum = 0;
                current_save();
 
-               DEBUGF("mtdparts_init: current_dev  = %s%d, current_partnum = %d\n",
-                               MTD_DEV_TYPE(current_dev->id->type),
-                               current_dev->id->num, current_partnum);
+               debug("mtdparts_init: current_mtd_dev  = %s%d, current_mtd_partnum = %d\n",
+                               MTD_DEV_TYPE(current_mtd_dev->id->type),
+                               current_mtd_dev->id->num, current_mtd_partnum);
        }
 
        /* mtdparts variable was reset to NULL, delete all devices/partitions */
@@ -1735,15 +1828,15 @@ int mtdparts_init(void)
                struct mtd_device *cdev;
                u8 pnum;
 
-               DEBUGF("--- getting current partition: %s\n", tmp_ep);
+               debug("--- getting current partition: %s\n", tmp_ep);
 
                if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
-                       current_dev = cdev;
-                       current_partnum = pnum;
+                       current_mtd_dev = cdev;
+                       current_mtd_partnum = pnum;
                        current_save();
                }
        } else if (getenv("partition") == NULL) {
-               DEBUGF("no partition variable set, setting...\n");
+               debug("no partition variable set, setting...\n");
                current_save();
        }
 
@@ -1767,7 +1860,7 @@ static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part
        if (!dev)
                return NULL;
 
-       DEBUGF("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
+       debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
                        part_num, MTD_DEV_TYPE(dev->id->type),
                        dev->id->num, dev->id->mtd_id);
 
@@ -1805,7 +1898,7 @@ static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part
  * @param argv arguments list
  * @return 0 on success, 1 otherwise
  */
-int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 /* command line only */
        struct mtd_device *dev;
@@ -1823,8 +1916,8 @@ int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
                return 1;
 
-       current_dev = dev;
-       current_partnum = pnum;
+       current_mtd_dev = dev;
+       current_mtd_partnum = pnum;
        current_save();
 
        printf("partition changed to %s%d,%d\n",
@@ -1843,7 +1936,8 @@ int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  * @param argv arguments list
  * @return 0 on success, 1 otherwise
  */
-int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc,
+                      char * const argv[])
 {
        if (argc == 2) {
                if (strcmp(argv[1], "default") == 0) {
@@ -1874,9 +1968,13 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        }
 
        /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
-       if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) {
+       if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
 #define PART_ADD_DESC_MAXLEN 64
                char tmpbuf[PART_ADD_DESC_MAXLEN];
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+               struct mtd_info *mtd;
+               uint64_t next_offset;
+#endif
                u8 type, num, len;
                struct mtd_device *dev;
                struct mtd_device *dev_tmp;
@@ -1903,27 +2001,37 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
                }
                sprintf(tmpbuf, "%s:%s(%s)%s",
                                id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
-               DEBUGF("add tmpbuf: %s\n", tmpbuf);
+               debug("add tmpbuf: %s\n", tmpbuf);
 
                if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
                        return 1;
 
-               DEBUGF("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
+               debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
                                dev->id->num, dev->id->mtd_id);
 
-               if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) {
+               p = list_entry(dev->parts.next, struct part_info, link);
+
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+               if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
+                       return 1;
+
+               if (!strcmp(&argv[1][3], ".spread")) {
+                       spread_partition(mtd, p, &next_offset);
+                       debug("increased %s to %d bytes\n", p->name, p->size);
+               }
+#endif
+
+               dev_tmp = device_find(dev->id->type, dev->id->num);
+               if (dev_tmp == NULL) {
                        device_add(dev);
-               } else {
+               } else if (part_add(dev_tmp, p) != 0) {
                        /* merge new partition with existing ones*/
-                       p = list_entry(dev->parts.next, struct part_info, link);
-                       if (part_add(dev_tmp, p) != 0) {
-                               device_del(dev);
-                               return 1;
-                       }
+                       device_del(dev);
+                       return 1;
                }
 
                if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
-                       printf("generated mtdparts too long, reseting to null\n");
+                       printf("generated mtdparts too long, resetting to null\n");
                        return 1;
                }
 
@@ -1932,13 +2040,17 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 
        /* mtdparts del part-id */
        if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
-               DEBUGF("del: part-id = %s\n", argv[2]);
+               debug("del: part-id = %s\n", argv[2]);
 
                return delete_partition(argv[2]);
        }
 
-       cmd_usage(cmdtp);
-       return 1;
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+       if ((argc == 2) && (strcmp(argv[1], "spread") == 0))
+               return spread_partitions();
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
+
+       return CMD_RET_USAGE;
 }
 
 /***************************************************/
@@ -1946,12 +2058,11 @@ U_BOOT_CMD(
        chpart, 2,      0,      do_chpart,
        "change active partition",
        "part-id\n"
-       "    - change active partition (e.g. part-id = nand0,1)\n"
+       "    - change active partition (e.g. part-id = nand0,1)"
 );
 
-U_BOOT_CMD(
-       mtdparts,       6,      0,      do_mtdparts,
-       "define flash/nand partitions",
+#ifdef CONFIG_SYS_LONGHELP
+static char mtdparts_help_text[] =
        "\n"
        "    - list partition table\n"
        "mtdparts delall\n"
@@ -1960,8 +2071,20 @@ U_BOOT_CMD(
        "    - delete partition (e.g. part-id = nand0,1)\n"
        "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
        "    - add partition\n"
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+       "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
+       "    - add partition, padding size by skipping bad blocks\n"
+#endif
        "mtdparts default\n"
-       "    - reset partition table to defaults\n\n"
+       "    - reset partition table to defaults\n"
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
+       "mtdparts spread\n"
+       "    - adjust the sizes of the partitions so they are\n"
+       "      at least as big as the mtdparts variable specifies\n"
+       "      and they each start on a good block\n\n"
+#else
+       "\n"
+#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
        "-----\n\n"
        "this command uses three environment variables:\n\n"
        "'partition' - keeps current partition identifier\n\n"
@@ -1981,6 +2104,11 @@ U_BOOT_CMD(
        "<size>     := standard linux memsize OR '-' to denote all remaining space\n"
        "<offset>   := partition start offset within the device\n"
        "<name>     := '(' NAME ')'\n"
-       "<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)\n"
+       "<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)";
+#endif
+
+U_BOOT_CMD(
+       mtdparts,       6,      0,      do_mtdparts,
+       "define flash/nand partitions", mtdparts_help_text
 );
 /***************************************************/