summaryrefslogtreecommitdiff
path: root/common/cmd_mtdparts.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/cmd_mtdparts.c')
-rw-r--r--common/cmd_mtdparts.c336
1 files changed, 316 insertions, 20 deletions
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c
index 32d6c899d14..a32762f5f21 100644
--- a/common/cmd_mtdparts.c
+++ b/common/cmd_mtdparts.c
@@ -132,6 +132,12 @@ static const char *const mtdids_default = MTDIDS_DEFAULT;
static const char *const mtdids_default = NULL;
#endif
+#if defined(MTDFLAGS_DEFAULT)
+staitc const char *const mtdflags_default = MTDFLAGS_DEFAULT;
+#else
+staitc const char *const mtdflags_default = NULL;
+#endif
+
#if defined(MTDPARTS_DEFAULT)
static const char *const mtdparts_default = MTDPARTS_DEFAULT;
#else
@@ -145,6 +151,10 @@ char *get_mtdids_default(void)
{
return (char *)mtdids_default;
}
+char *get_mtdflags_default(void)
+{
+ return (char *)mtdflags_default;
+}
#endif
/* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
@@ -153,7 +163,9 @@ char *get_mtdids_default(void)
#define PARTITION_MAXLEN 16
static char last_ids[MTDIDS_MAXLEN];
static char last_parts[MTDPARTS_MAXLEN];
+static char last_flags[MTDPARTS_MAXLEN];
static char last_partition[PARTITION_MAXLEN];
+static int mtdflags_defaultmask;
/* low level jffs2 cache cleaning routine */
extern void jffs2_free_cache(struct part_info *part);
@@ -173,6 +185,7 @@ static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part
/* command line only routines */
static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
static int device_del(struct mtd_device *dev);
+static char *mtdflags_tostring(int flags, int parenth);
/**
* Parses a string into a number. The number stored at ptr is
@@ -1233,6 +1246,79 @@ static int generate_mtdparts_save(char *buf, u32 buflen)
return ret;
}
+/**
+ * Process all devices and generate corresponding mtdparts string describing
+ * all partitions on all devices.
+ *
+ * @param buf output buffer holding generated mtdparts string (output)
+ * @param buflen buffer size
+ * @return 0 on success, 1 otherwise
+ */
+static int generate_mtdflags(char *buf, u32 buflen)
+{
+ struct list_head *pentry, *dentry;
+ struct mtd_device *dev;
+ struct part_info *part;
+ char *p = buf;
+ u32 maxlen = buflen - 1;
+ int needs_semicolon=0;
+
+ debug("--- generate_mtdflags ---\n");
+
+ if (list_empty(&devices)) {
+ buf[0] = '\0';
+ return 0;
+ }
+
+ buf[0] = '\0';
+
+ if(mtdflags_defaultmask)
+ {
+ p = stpncpy(p, "default=", maxlen - (p - buf));
+ p = stpncpy(p, mtdflags_tostring(mtdflags_defaultmask, 0), maxlen - (p - buf));
+ needs_semicolon = 1;
+ }
+
+ list_for_each(dentry, &devices) {
+ dev = list_entry(dentry, struct mtd_device, link);
+ list_for_each(pentry, &dev->parts) {
+ part = list_entry(pentry, struct part_info, link);
+
+ if(part->mtdflags_mask)
+ {
+ // Append the name
+ if(needs_semicolon)
+ p = stpncpy(p, ";", maxlen - (p - buf));
+ p = stpncpy(p, part->name, maxlen - (p - buf));
+ p = stpncpy(p, "=", maxlen - (p - buf));
+ p = stpncpy(p, mtdflags_tostring(part->mtdflags_mask, 0), maxlen - (p - buf));
+ needs_semicolon=1;
+ }
+ }
+ }
+
+ /* we still have at least one char left, as we decremented maxlen at
+ * the begining */
+ *p = '\0';
+
+ return 0;
+}
+
+
+static int generate_mtdflags_save(char *buf, u32 buflen)
+{
+ int ret;
+
+ ret = generate_mtdflags(buf, buflen);
+
+ if ((buf[0] != '\0') && (ret == 0))
+ setenv("mtdflags", buf);
+ else
+ setenv("mtdflags", NULL);
+
+ return ret;
+}
+
#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
/**
* Get the net size (w/o bad blocks) of the given partition.
@@ -1277,7 +1363,7 @@ static void print_partition_table(void)
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\tnet size\toffset\t\tmask_flags\n");
+ printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\tnand_flags\n");
list_for_each(pentry, &dev->parts) {
u32 net_size;
@@ -1286,21 +1372,24 @@ static void print_partition_table(void)
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",
+ printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\t\t%s\n",
part_num, part->name, part->size,
net_size, size_note, part->offset,
- part->mask_flags);
+ part->mask_flags,
+ mtdflags_tostring(part->mtdflags_mask, 1)
+ );
#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");
+ printf(" #: name\t\tsize\t\toffset\t\tmask_flags\tnand_flags\n");
list_for_each(pentry, &dev->parts) {
part = list_entry(pentry, struct part_info, link);
- printf("%2d: %-20s0x%08x\t0x%08x\t%d\n",
+ printf("%2d: %-20s0x%08x\t0x%08x\t%d\t\t%s\n",
part_num, part->name, part->size,
- part->offset, part->mask_flags);
+ part->offset, part->mask_flags,
+ mtdflags_tostring(part->mtdflags_mask, 1));
#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
part_num++;
}
@@ -1317,7 +1406,7 @@ static void print_partition_table(void)
static void list_partitions(void)
{
struct part_info *part;
- char *mtdids_default, *mtdparts_default;
+ char *mtdids_default, *mtdparts_default, *mtdflags_default;
debug("\n---list_partitions---\n");
print_partition_table();
@@ -1345,9 +1434,12 @@ static void list_partitions(void)
* printbuffer. Use puts() to prevent system crashes.
*/
mtdparts_default = get_mtdparts_default();
- puts("mtdparts: ");
- puts(mtdparts_default ? mtdparts_default : "none");
- puts("\n");
+ printf("mtdparts: %s\n",
+ mtdparts_default ? mtdparts_default : "none");
+
+ mtdflags_default = get_mtdflags_default();
+ printf("mtdflags: %s\n",
+ mtdflags_default ? mtdflags_default : "none");
}
/**
@@ -1432,6 +1524,7 @@ static int delete_partition(const char *id)
u8 pnum;
struct mtd_device *dev;
struct part_info *part;
+ int ret;
if (find_dev_and_part(id, &dev, &pnum, &part, 0) == 0) {
@@ -1443,10 +1536,14 @@ static int delete_partition(const char *id)
return 1;
if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ ret = 1;
printf("generated mtdparts too long, reseting to null\n");
- return 1;
}
- return 0;
+ if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) {
+ ret = 1;
+ printf("generated mtdflags too long, resetting to null\n");
+ }
+ return ret;
}
printf("partition %s not found\n", id);
@@ -1511,6 +1608,7 @@ static int spread_partitions(void)
struct mtd_info *mtd;
int part_num;
uint64_t cur_offs;
+ int ret = 0;
list_for_each(dentry, &devices) {
dev = list_entry(dentry, struct mtd_device, link);
@@ -1541,10 +1639,14 @@ static int spread_partitions(void)
index_partitions();
if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ ret = 1;
printf("generated mtdparts too long, reseting to null\n");
- return 1;
}
- return 0;
+ if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) {
+ ret = 1;
+ printf("generated mtdflags too long, resetting to null\n");
+ }
+ return ret;
}
#endif /* CONFIG_CMD_MTDPARTS_SPREAD */
@@ -1604,6 +1706,160 @@ static int parse_mtdparts(const char *const mtdparts)
return 0;
}
+/*
+ * mtdflags - partitionid1=flag1,flag2,flag3;partitionid2=flag1,flag2,flag3;...
+
+
+setenv mtdflags 'default=ecc_chip;x-loader=ecc_hw,repeat;system=yaffs;userdata=yaffs;cache=yaffs'
+mtdparts ecc x-loader
+
+ */
+const char *const mtdflags_strings[] = {
+ [MTDFLAGS_ECC_SW] = "ecc_sw",
+ [MTDFLAGS_ECC_HW] = "ecc_hw",
+ [MTDFLAGS_ECC_CHIP] = "ecc_chip",
+ [MTDFLAGS_ECC_BCH] = "ecc_bch",
+ [MTDFLAGS_YAFFS] = "yaffs",
+ [MTDFLAGS_REPEAT] = "repeat",
+};
+
+static int validate_mtdflags(int mask)
+{
+ int ecccnt = 0;
+ if((1 << MTDFLAGS_ECC_SW) & mask)
+ ecccnt++;
+ if((1 << MTDFLAGS_ECC_HW) & mask)
+ ecccnt++;
+ if((1 << MTDFLAGS_ECC_CHIP) & mask)
+ ecccnt++;
+ if((1 << MTDFLAGS_ECC_BCH) & mask)
+ ecccnt++;
+
+ return ecccnt <= 1;
+}
+
+static int parse_mtdflags_findmask(const char *const flags, int len)
+{
+ int i;
+
+ for(i=0;i<ARRAY_SIZE(mtdflags_strings);++i)
+ {
+ if(mtdflags_strings[i] &&
+ strlen(mtdflags_strings[i]) == len &&
+ strncmp(mtdflags_strings[i], flags, len) == 0)
+ {
+ return 1 << i;
+ }
+ }
+ return 0;
+}
+
+static char *mtdflags_tostring(int flags, int include_defaults)
+{
+ static char str[64];
+ char *cur = str;
+ int i;
+ int comma = 0;
+ int from_common = mtdflags_defaultmask;
+
+ if(flags & MTDFLAGS_DEFAULT_PERMITTED)
+ {
+ from_common = 0;
+ }
+
+ str[0] = '\0';
+
+ for(i=0;i < ARRAY_SIZE(mtdflags_strings);++i)
+ {
+ if(include_defaults && ((1 << i) & from_common))
+ {
+ if(comma)
+ cur = stpncpy(cur, ",", ARRAY_SIZE(str) - (cur - str));
+ cur = stpncpy(cur, "(", ARRAY_SIZE(str) - (cur - str));
+ cur = stpncpy(cur, mtdflags_strings[i], ARRAY_SIZE(str) - (cur - str));
+ cur = stpncpy(cur, ")", ARRAY_SIZE(str) - (cur - str));
+ comma=1;
+ }
+ if((1 << i) & flags)
+ {
+ if(comma)
+ cur = stpncpy(cur, ",", ARRAY_SIZE(str) - (cur - str));
+ cur = stpncpy(cur, mtdflags_strings[i], ARRAY_SIZE(str) - (cur - str));
+ comma=1;
+ }
+ }
+ str[ARRAY_SIZE(str) - 1] = '\0';
+ return str;
+}
+
+static int parse_mtdflags(const char *const mtdflags)
+{
+ const char *part, *part_end;
+ const char *flags, *flags_end;
+
+ if(!mtdflags)
+ return 1;
+
+ part = flags_end = mtdflags;
+
+ while(*part != '\0')
+ {
+ // Bring the pointer "part" past all blank items
+ for(part=flags_end;*part && (*part == ';' || *part == ' ');++part);
+ // Bring the pointer "flags" past the equals sign
+ for(part_end=part;*part_end && *part_end != '=';++part_end);
+ for(flags=part_end;*flags && *flags == '=';++flags);
+ // Bring the pointer "p" to the next partition
+ for(flags_end=flags;*flags_end && *flags_end != ';';++flags_end);
+
+ // Now, make sure it's sane
+ if(part != part_end && flags != flags_end && part != flags_end)
+ {
+ struct list_head *dentry, *pentry;
+ struct mtd_device *dev;
+ struct part_info *parti;
+ int mask = 0;
+
+ while(flags < flags_end)
+ {
+ const char *cur = flags;
+
+ for(;flags < flags_end && *flags != ',';++flags);
+ mask |= parse_mtdflags_findmask(cur, flags - cur);
+ for(;flags < flags_end && *flags == ',';++flags);
+ }
+ if(!validate_mtdflags(mask))
+ {
+ return 1;
+ }
+ if(strncmp(part, "default", 7) == 0)
+ {
+ mtdflags_defaultmask = mask & MTDFLAGS_DEFAULT_PERMITTED;
+ continue;
+ }
+
+ list_for_each(dentry, &devices)
+ {
+ dev = list_entry(dentry, struct mtd_device, link);
+ list_for_each(pentry, &dev->parts)
+ {
+ parti = list_entry(pentry, struct part_info, link);
+ if(strlen(parti->name) == part_end - part &&
+ strncmp(parti->name, part, part_end - part) == 0)
+ {
+ parti->mtdflags_mask = mask;
+ goto cont;
+ }
+ }
+ }
+ return 1;
+ cont:
+ continue;
+ }
+ }
+ return 0;
+}
+
/**
* Parse provided string describing mtdids mapping (see file header for mtdids
* variable format). Allocate memory for each entry and add all found entries
@@ -1724,11 +1980,11 @@ static int parse_mtdids(const char *const ids)
int mtdparts_init(void)
{
static int initialized = 0;
- const char *ids, *parts;
+ const char *ids, *parts, *flags;
const char *current_partition;
int ids_changed;
char tmp_ep[PARTITION_MAXLEN];
- char *mtdids_default, *mtdparts_default;
+ char *mtdids_default;
debug("\n---mtdparts_init---\n");
if (!initialized) {
@@ -1743,6 +1999,7 @@ int mtdparts_init(void)
/* get variables */
ids = getenv("mtdids");
parts = getenv("mtdparts");
+ flags = getenv("mtdflags");
current_partition = getenv("partition");
/* save it for later parsing, cannot rely on current partition pointer
@@ -1802,10 +2059,19 @@ int mtdparts_init(void)
}
/* parse partitions if either mtdparts or mtdids were updated */
- if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
+ if (parts && (last_parts[0] == '\0' ||
+ strcmp(last_parts, parts) != 0 ||
+ ids_changed ||
+ (flags && strcmp(last_flags, flags) != 0))) {
if (parse_mtdparts(parts) != 0)
return 1;
+ if(flags && parse_mtdflags(flags))
+ {
+ printf("Bad mtdflags.\n");
+ return 1;
+ }
+
if (list_empty(&devices)) {
printf("mtdparts_init: no valid partitions\n");
return 1;
@@ -1813,6 +2079,10 @@ int mtdparts_init(void)
/* ok it's good, save new parts */
strncpy(last_parts, parts, MTDPARTS_MAXLEN);
+ if(flags)
+ strncpy(last_flags, flags, MTDPARTS_MAXLEN);
+ else
+ last_flags[0] = '\0';
/* reset first partition from first dev from the list as current */
current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
@@ -1853,6 +2123,14 @@ int mtdparts_init(void)
return 0;
}
+int mtd_part_getmask(struct part_info *part)
+{
+ if(part->mtdflags_mask & MTDFLAGS_DEFAULT_PERMITTED)
+ return part->mtdflags_mask;
+ else
+ return part->mtdflags_mask | mtdflags_defaultmask;
+}
+
/**
* Return pointer to the partition of a requested number from a requested
* device.
@@ -1948,14 +2226,17 @@ int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
*/
int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
- char *mtdids_default, *mtdparts_default;
+ char *mtdids_default, *mtdparts_default, *mtdflags_default;
+ int ret = 0;
if (argc == 2) {
if (strcmp(argv[1], "default") == 0) {
mtdids_default = get_mtdids_default();
mtdparts_default = get_mtdparts_default();
+ mtdflags_default = get_mtdflags_default();
setenv("mtdids", (char *)mtdids_default);
setenv("mtdparts", (char *)mtdparts_default);
+ setenv("mtdflags", (char *)mtdflags_default);
setenv("partition", NULL);
mtdparts_init();
@@ -2044,11 +2325,15 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
+ ret = 1;
printf("generated mtdparts too long, reseting to null\n");
- return 1;
+ }
+ if (generate_mtdflags_save(last_flags, ARRAY_SIZE(last_flags)) != 0) {
+ ret = 1;
+ printf("generated mtdflags too long, resetting to null\n");
}
- return 0;
+ return ret;
}
/* mtdparts del part-id */
@@ -2119,6 +2404,17 @@ U_BOOT_CMD(
"<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)"
+ "mtdflags=<flag-def>[;<flag-def>...]\n\n"
+ "<flag-def> := <part-def>=<flag>[,<flag>...]\n"
+ "Flags affecting ECC modes (only applicable when \"nandecc auto\" is selected):\n"
+ " ecc_sw = Select SW ECC for NAND writes\n"
+ " ecc_hw = Select HW ECC for NAND writes\n"
+ " ecc_chip = Select ON-Chip ECC for NAND writes\n"
+ " ecc_bch = Select SW BCH ECC for NAND writes\n"
+ "Flags affecting nand write.auto\n"
+ " <blank> = Use nand write\n"
+ " yaffs = Use nand write.yaffs\n"
+ " repeat = Use nand.write.repeat\n"
);
/***************************************************/