]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_jffs2.c
Merge commit 'wd/master'
[karo-tx-uboot.git] / common / cmd_jffs2.c
1 /*
2  * (C) Copyright 2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2002
6  * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
7  *
8  * (C) Copyright 2003
9  * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
10  *
11  * (C) Copyright 2005
12  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
13  *
14  *   Added support for reading flash partition table from environment.
15  *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
16  *   kernel tree.
17  *
18  *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
19  *   Copyright 2002 SYSGO Real-Time Solutions GmbH
20  *
21  * See file CREDITS for list of people who contributed to this
22  * project.
23  *
24  * This program is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU General Public License as
26  * published by the Free Software Foundation; either version 2 of
27  * the License, or (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
37  * MA 02111-1307 USA
38  */
39
40 /*
41  * Three environment variables are used by the parsing routines:
42  *
43  * 'partition' - keeps current partition identifier
44  *
45  * partition  := <part-id>
46  * <part-id>  := <dev-id>,part_num
47  *
48  *
49  * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
50  *
51  * mtdids=<idmap>[,<idmap>,...]
52  *
53  * <idmap>    := <dev-id>=<mtd-id>
54  * <dev-id>   := 'nand'|'nor'<dev-num>
55  * <dev-num>  := mtd device number, 0...
56  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
57  *
58  *
59  * 'mtdparts' - partition list
60  *
61  * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
62  *
63  * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
64  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
65  * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
66  * <size>     := standard linux memsize OR '-' to denote all remaining space
67  * <offset>   := partition start offset within the device
68  * <name>     := '(' NAME ')'
69  * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
70  *
71  * Notes:
72  * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
73  * - if the above variables are not set defaults for a given target are used
74  *
75  * Examples:
76  *
77  * 1 NOR Flash, with 1 single writable partition:
78  * mtdids=nor0=edb7312-nor
79  * mtdparts=mtdparts=edb7312-nor:-
80  *
81  * 1 NOR Flash with 2 partitions, 1 NAND with one
82  * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
83  * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
84  *
85  */
86
87 /*
88  * JFFS2/CRAMFS support
89  */
90 #include <common.h>
91 #include <command.h>
92 #include <malloc.h>
93 #include <jffs2/jffs2.h>
94 #include <linux/list.h>
95 #include <linux/ctype.h>
96 #include <cramfs/cramfs_fs.h>
97
98 #if defined(CONFIG_CMD_NAND)
99 #ifdef CFG_NAND_LEGACY
100 #include <linux/mtd/nand_legacy.h>
101 #else /* !CFG_NAND_LEGACY */
102 #include <linux/mtd/nand.h>
103 #include <nand.h>
104 #endif /* !CFG_NAND_LEGACY */
105 #endif
106 /* enable/disable debugging messages */
107 #define DEBUG_JFFS
108 #undef  DEBUG_JFFS
109
110 #ifdef  DEBUG_JFFS
111 # define DEBUGF(fmt, args...)   printf(fmt ,##args)
112 #else
113 # define DEBUGF(fmt, args...)
114 #endif
115
116 /* special size referring to all the remaining space in a partition */
117 #define SIZE_REMAINING          0xFFFFFFFF
118
119 /* special offset value, it is used when not provided by user
120  *
121  * this value is used temporarily during parsing, later such offests
122  * are recalculated */
123 #define OFFSET_NOT_SPECIFIED    0xFFFFFFFF
124
125 /* minimum partition size */
126 #define MIN_PART_SIZE           4096
127
128 /* this flag needs to be set in part_info struct mask_flags
129  * field for read-only partitions */
130 #define MTD_WRITEABLE_CMD               1
131
132 #ifdef CONFIG_JFFS2_CMDLINE
133 /* default values for mtdids and mtdparts variables */
134 #if defined(MTDIDS_DEFAULT)
135 static const char *const mtdids_default = MTDIDS_DEFAULT;
136 #else
137 #warning "MTDIDS_DEFAULT not defined!"
138 static const char *const mtdids_default = NULL;
139 #endif
140
141 #if defined(MTDPARTS_DEFAULT)
142 static const char *const mtdparts_default = MTDPARTS_DEFAULT;
143 #else
144 #warning "MTDPARTS_DEFAULT not defined!"
145 static const char *const mtdparts_default = NULL;
146 #endif
147
148 /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
149 #define MTDIDS_MAXLEN           128
150 #define MTDPARTS_MAXLEN         512
151 #define PARTITION_MAXLEN        16
152 static char last_ids[MTDIDS_MAXLEN];
153 static char last_parts[MTDPARTS_MAXLEN];
154 static char last_partition[PARTITION_MAXLEN];
155
156 /* low level jffs2 cache cleaning routine */
157 extern void jffs2_free_cache(struct part_info *part);
158
159 /* mtdids mapping list, filled by parse_ids() */
160 struct list_head mtdids;
161
162 /* device/partition list, parse_cmdline() parses into here */
163 struct list_head devices;
164 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
165
166 /* current active device and partition number */
167 static struct mtd_device *current_dev = NULL;
168 static u8 current_partnum = 0;
169
170 extern int cramfs_check (struct part_info *info);
171 extern int cramfs_load (char *loadoffset, struct part_info *info, char *filename);
172 extern int cramfs_ls (struct part_info *info, char *filename);
173 extern int cramfs_info (struct part_info *info);
174
175 static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num);
176
177 /* command line only routines */
178 #ifdef CONFIG_JFFS2_CMDLINE
179
180 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
181 static int device_del(struct mtd_device *dev);
182
183 /**
184  * Parses a string into a number.  The number stored at ptr is
185  * potentially suffixed with K (for kilobytes, or 1024 bytes),
186  * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
187  * 1073741824).  If the number is suffixed with K, M, or G, then
188  * the return value is the number multiplied by one kilobyte, one
189  * megabyte, or one gigabyte, respectively.
190  *
191  * @param ptr where parse begins
192  * @param retptr output pointer to next char after parse completes (output)
193  * @return resulting unsigned int
194  */
195 static unsigned long memsize_parse (const char *const ptr, const char **retptr)
196 {
197         unsigned long ret = simple_strtoul(ptr, (char **)retptr, 0);
198
199         switch (**retptr) {
200                 case 'G':
201                 case 'g':
202                         ret <<= 10;
203                 case 'M':
204                 case 'm':
205                         ret <<= 10;
206                 case 'K':
207                 case 'k':
208                         ret <<= 10;
209                         (*retptr)++;
210                 default:
211                         break;
212         }
213
214         return ret;
215 }
216
217 /**
218  * Format string describing supplied size. This routine does the opposite job
219  * to memsize_parse(). Size in bytes is converted to string and if possible
220  * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
221  *
222  * Note, that this routine does not check for buffer overflow, it's the caller
223  * who must assure enough space.
224  *
225  * @param buf output buffer
226  * @param size size to be converted to string
227  */
228 static void memsize_format(char *buf, u32 size)
229 {
230 #define SIZE_GB ((u32)1024*1024*1024)
231 #define SIZE_MB ((u32)1024*1024)
232 #define SIZE_KB ((u32)1024)
233
234         if ((size % SIZE_GB) == 0)
235                 sprintf(buf, "%lug", size/SIZE_GB);
236         else if ((size % SIZE_MB) == 0)
237                 sprintf(buf, "%lum", size/SIZE_MB);
238         else if (size % SIZE_KB == 0)
239                 sprintf(buf, "%luk", size/SIZE_KB);
240         else
241                 sprintf(buf, "%lu", size);
242 }
243
244 /**
245  * This routine does global indexing of all partitions. Resulting index for
246  * current partition is saved in 'mtddevnum'. Current partition name in
247  * 'mtddevname'.
248  */
249 static void index_partitions(void)
250 {
251         char buf[16];
252         u16 mtddevnum;
253         struct part_info *part;
254         struct list_head *dentry;
255         struct mtd_device *dev;
256
257         DEBUGF("--- index partitions ---\n");
258
259         if (current_dev) {
260                 mtddevnum = 0;
261                 list_for_each(dentry, &devices) {
262                         dev = list_entry(dentry, struct mtd_device, link);
263                         if (dev == current_dev) {
264                                 mtddevnum += current_partnum;
265                                 sprintf(buf, "%d", mtddevnum);
266                                 setenv("mtddevnum", buf);
267                                 break;
268                         }
269                         mtddevnum += dev->num_parts;
270                 }
271
272                 part = jffs2_part_info(current_dev, current_partnum);
273                 setenv("mtddevname", part->name);
274
275                 DEBUGF("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
276         } else {
277                 setenv("mtddevnum", NULL);
278                 setenv("mtddevname", NULL);
279
280                 DEBUGF("=> mtddevnum NULL\n=> mtddevname NULL\n");
281         }
282 }
283
284 /**
285  * Save current device and partition in environment variable 'partition'.
286  */
287 static void current_save(void)
288 {
289         char buf[16];
290
291         DEBUGF("--- current_save ---\n");
292
293         if (current_dev) {
294                 sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_dev->id->type),
295                                         current_dev->id->num, current_partnum);
296
297                 setenv("partition", buf);
298                 strncpy(last_partition, buf, 16);
299
300                 DEBUGF("=> partition %s\n", buf);
301         } else {
302                 setenv("partition", NULL);
303                 last_partition[0] = '\0';
304
305                 DEBUGF("=> partition NULL\n");
306         }
307         index_partitions();
308 }
309
310 /**
311  * Performs sanity check for supplied NOR flash partition. Table of existing
312  * NOR flash devices is searched and partition device is located. Alignment
313  * with the granularity of NOR flash sectors is verified.
314  *
315  * @param id of the parent device
316  * @param part partition to validate
317  * @return 0 if partition is valid, 1 otherwise
318  */
319 static int part_validate_nor(struct mtdids *id, struct part_info *part)
320 {
321 #if defined(CONFIG_CMD_FLASH)
322         /* info for FLASH chips */
323         extern flash_info_t flash_info[];
324         flash_info_t *flash;
325         int offset_aligned;
326         u32 end_offset;
327         int i;
328
329         flash = &flash_info[id->num];
330
331         offset_aligned = 0;
332         for (i = 0; i < flash->sector_count; i++) {
333                 if ((flash->start[i] - flash->start[0]) == part->offset) {
334                         offset_aligned = 1;
335                         break;
336                 }
337         }
338         if (offset_aligned == 0) {
339                 printf("%s%d: partition (%s) start offset alignment incorrect\n",
340                                 MTD_DEV_TYPE(id->type), id->num, part->name);
341                 return 1;
342         }
343
344         end_offset = part->offset + part->size;
345         for (i = 0; i < flash->sector_count; i++) {
346                 if ((flash->start[i] - flash->start[0]) == end_offset)
347                         return 0;
348         }
349
350         if (flash->size == end_offset)
351                 return 0;
352
353         printf("%s%d: partition (%s) size alignment incorrect\n",
354                         MTD_DEV_TYPE(id->type), id->num, part->name);
355 #endif
356         return 1;
357 }
358
359 /**
360  * Performs sanity check for supplied NAND flash partition. Table of existing
361  * NAND flash devices is searched and partition device is located. Alignment
362  * with the granularity of nand erasesize is verified.
363  *
364  * @param id of the parent device
365  * @param part partition to validate
366  * @return 0 if partition is valid, 1 otherwise
367  */
368 static int part_validate_nand(struct mtdids *id, struct part_info *part)
369 {
370 #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
371         /* info for NAND chips */
372         nand_info_t *nand;
373
374         nand = &nand_info[id->num];
375
376         if ((unsigned long)(part->offset) % nand->erasesize) {
377                 printf("%s%d: partition (%s) start offset alignment incorrect\n",
378                                 MTD_DEV_TYPE(id->type), id->num, part->name);
379                 return 1;
380         }
381
382         if (part->size % nand->erasesize) {
383                 printf("%s%d: partition (%s) size alignment incorrect\n",
384                                 MTD_DEV_TYPE(id->type), id->num, part->name);
385                 return 1;
386         }
387
388         return 0;
389 #else
390         return 1;
391 #endif
392 }
393
394 /**
395  * Performs sanity check for supplied partition. Offset and size are verified
396  * to be within valid range. Partition type is checked and either
397  * parts_validate_nor() or parts_validate_nand() is called with the argument
398  * of part.
399  *
400  * @param id of the parent device
401  * @param part partition to validate
402  * @return 0 if partition is valid, 1 otherwise
403  */
404 static int part_validate(struct mtdids *id, struct part_info *part)
405 {
406         if (part->size == SIZE_REMAINING)
407                 part->size = id->size - part->offset;
408
409         if (part->offset > id->size) {
410                 printf("%s: offset %08lx beyond flash size %08lx\n",
411                                 id->mtd_id, part->offset, id->size);
412                 return 1;
413         }
414
415         if ((part->offset + part->size) <= part->offset) {
416                 printf("%s%d: partition (%s) size too big\n",
417                                 MTD_DEV_TYPE(id->type), id->num, part->name);
418                 return 1;
419         }
420
421         if (part->offset + part->size > id->size) {
422                 printf("%s: partitioning exceeds flash size\n", id->mtd_id);
423                 return 1;
424         }
425
426         if (id->type == MTD_DEV_TYPE_NAND)
427                 return part_validate_nand(id, part);
428         else if (id->type == MTD_DEV_TYPE_NOR)
429                 return part_validate_nor(id, part);
430         else
431                 DEBUGF("part_validate: invalid dev type\n");
432
433         return 1;
434 }
435
436 /**
437  * Delete selected partition from the partion list of the specified device.
438  *
439  * @param dev device to delete partition from
440  * @param part partition to delete
441  * @return 0 on success, 1 otherwise
442  */
443 static int part_del(struct mtd_device *dev, struct part_info *part)
444 {
445         u8 current_save_needed = 0;
446
447         /* if there is only one partition, remove whole device */
448         if (dev->num_parts == 1)
449                 return device_del(dev);
450
451         /* otherwise just delete this partition */
452
453         if (dev == current_dev) {
454                 /* we are modyfing partitions for the current device,
455                  * update current */
456                 struct part_info *curr_pi;
457                 curr_pi = jffs2_part_info(current_dev, current_partnum);
458
459                 if (curr_pi) {
460                         if (curr_pi == part) {
461                                 printf("current partition deleted, resetting current to 0\n");
462                                 current_partnum = 0;
463                         } else if (part->offset <= curr_pi->offset) {
464                                 current_partnum--;
465                         }
466                         current_save_needed = 1;
467                 }
468         }
469
470 #ifdef CFG_NAND_LEGACY
471         jffs2_free_cache(part);
472 #endif
473         list_del(&part->link);
474         free(part);
475         dev->num_parts--;
476
477         if (current_save_needed > 0)
478                 current_save();
479         else
480                 index_partitions();
481
482         return 0;
483 }
484
485 /**
486  * Delete all partitions from parts head list, free memory.
487  *
488  * @param head list of partitions to delete
489  */
490 static void part_delall(struct list_head *head)
491 {
492         struct list_head *entry, *n;
493         struct part_info *part_tmp;
494
495         /* clean tmp_list and free allocated memory */
496         list_for_each_safe(entry, n, head) {
497                 part_tmp = list_entry(entry, struct part_info, link);
498
499 #ifdef CFG_NAND_LEGACY
500                 jffs2_free_cache(part_tmp);
501 #endif
502                 list_del(entry);
503                 free(part_tmp);
504         }
505 }
506
507 /**
508  * Add new partition to the supplied partition list. Make sure partitions are
509  * sorted by offset in ascending order.
510  *
511  * @param head list this partition is to be added to
512  * @param new partition to be added
513  */
514 static int part_sort_add(struct mtd_device *dev, struct part_info *part)
515 {
516         struct list_head *entry;
517         struct part_info *new_pi, *curr_pi;
518
519         /* link partition to parrent dev */
520         part->dev = dev;
521
522         if (list_empty(&dev->parts)) {
523                 DEBUGF("part_sort_add: list empty\n");
524                 list_add(&part->link, &dev->parts);
525                 dev->num_parts++;
526                 index_partitions();
527                 return 0;
528         }
529
530         new_pi = list_entry(&part->link, struct part_info, link);
531
532         /* get current partition info if we are updating current device */
533         curr_pi = NULL;
534         if (dev == current_dev)
535                 curr_pi = jffs2_part_info(current_dev, current_partnum);
536
537         list_for_each(entry, &dev->parts) {
538                 struct part_info *pi;
539
540                 pi = list_entry(entry, struct part_info, link);
541
542                 /* be compliant with kernel cmdline, allow only one partition at offset zero */
543                 if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
544                         printf("cannot add second partition at offset 0\n");
545                         return 1;
546                 }
547
548                 if (new_pi->offset <= pi->offset) {
549                         list_add_tail(&part->link, entry);
550                         dev->num_parts++;
551
552                         if (curr_pi && (pi->offset <= curr_pi->offset)) {
553                                 /* we are modyfing partitions for the current
554                                  * device, update current */
555                                 current_partnum++;
556                                 current_save();
557                         } else {
558                                 index_partitions();
559                         }
560                         return 0;
561                 }
562         }
563
564         list_add_tail(&part->link, &dev->parts);
565         dev->num_parts++;
566         index_partitions();
567         return 0;
568 }
569
570 /**
571  * Add provided partition to the partition list of a given device.
572  *
573  * @param dev device to which partition is added
574  * @param part partition to be added
575  * @return 0 on success, 1 otherwise
576  */
577 static int part_add(struct mtd_device *dev, struct part_info *part)
578 {
579         /* verify alignment and size */
580         if (part_validate(dev->id, part) != 0)
581                 return 1;
582
583         /* partition is ok, add it to the list */
584         if (part_sort_add(dev, part) != 0)
585                 return 1;
586
587         return 0;
588 }
589
590 /**
591  * Parse one partition definition, allocate memory and return pointer to this
592  * location in retpart.
593  *
594  * @param partdef pointer to the partition definition string i.e. <part-def>
595  * @param ret output pointer to next char after parse completes (output)
596  * @param retpart pointer to the allocated partition (output)
597  * @return 0 on success, 1 otherwise
598  */
599 static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
600 {
601         struct part_info *part;
602         unsigned long size;
603         unsigned long offset;
604         const char *name;
605         int name_len;
606         unsigned int mask_flags;
607         const char *p;
608
609         p = partdef;
610         *retpart = NULL;
611         *ret = NULL;
612
613         /* fetch the partition size */
614         if (*p == '-') {
615                 /* assign all remaining space to this partition */
616                 DEBUGF("'-': remaining size assigned\n");
617                 size = SIZE_REMAINING;
618                 p++;
619         } else {
620                 size = memsize_parse(p, &p);
621                 if (size < MIN_PART_SIZE) {
622                         printf("partition size too small (%lx)\n", size);
623                         return 1;
624                 }
625         }
626
627         /* check for offset */
628         offset = OFFSET_NOT_SPECIFIED;
629         if (*p == '@') {
630                 p++;
631                 offset = memsize_parse(p, &p);
632         }
633
634         /* now look for the name */
635         if (*p == '(') {
636                 name = ++p;
637                 if ((p = strchr(name, ')')) == NULL) {
638                         printf("no closing ) found in partition name\n");
639                         return 1;
640                 }
641                 name_len = p - name + 1;
642                 if ((name_len - 1) == 0) {
643                         printf("empty partition name\n");
644                         return 1;
645                 }
646                 p++;
647         } else {
648                 /* 0x00000000@0x00000000 */
649                 name_len = 22;
650                 name = NULL;
651         }
652
653         /* test for options */
654         mask_flags = 0;
655         if (strncmp(p, "ro", 2) == 0) {
656                 mask_flags |= MTD_WRITEABLE_CMD;
657                 p += 2;
658         }
659
660         /* check for next partition definition */
661         if (*p == ',') {
662                 if (size == SIZE_REMAINING) {
663                         *ret = NULL;
664                         printf("no partitions allowed after a fill-up partition\n");
665                         return 1;
666                 }
667                 *ret = ++p;
668         } else if ((*p == ';') || (*p == '\0')) {
669                 *ret = p;
670         } else {
671                 printf("unexpected character '%c' at the end of partition\n", *p);
672                 *ret = NULL;
673                 return 1;
674         }
675
676         /*  allocate memory */
677         part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
678         if (!part) {
679                 printf("out of memory\n");
680                 return 1;
681         }
682         memset(part, 0, sizeof(struct part_info) + name_len);
683         part->size = size;
684         part->offset = offset;
685         part->mask_flags = mask_flags;
686         part->name = (char *)(part + 1);
687
688         if (name) {
689                 /* copy user provided name */
690                 strncpy(part->name, name, name_len - 1);
691                 part->auto_name = 0;
692         } else {
693                 /* auto generated name in form of size@offset */
694                 sprintf(part->name, "0x%08lx@0x%08lx", size, offset);
695                 part->auto_name = 1;
696         }
697
698         part->name[name_len - 1] = '\0';
699         INIT_LIST_HEAD(&part->link);
700
701         DEBUGF("+ partition: name %-22s size 0x%08x offset 0x%08x mask flags %d\n",
702                         part->name, part->size,
703                         part->offset, part->mask_flags);
704
705         *retpart = part;
706         return 0;
707 }
708 #endif/* #ifdef CONFIG_JFFS2_CMDLINE */
709
710 /**
711  * Check device number to be within valid range for given device type.
712  *
713  * @param dev device to validate
714  * @return 0 if device is valid, 1 otherwise
715  */
716 static int device_validate(u8 type, u8 num, u32 *size)
717 {
718         if (type == MTD_DEV_TYPE_NOR) {
719 #if defined(CONFIG_CMD_FLASH)
720                 if (num < CFG_MAX_FLASH_BANKS) {
721                         extern flash_info_t flash_info[];
722                         *size = flash_info[num].size;
723
724                         return 0;
725                 }
726
727                 printf("no such FLASH device: %s%d (valid range 0 ... %d\n",
728                                 MTD_DEV_TYPE(type), num, CFG_MAX_FLASH_BANKS - 1);
729 #else
730                 printf("support for FLASH devices not present\n");
731 #endif
732         } else if (type == MTD_DEV_TYPE_NAND) {
733 #if defined(CONFIG_JFFS2_NAND) && defined(CONFIG_CMD_NAND)
734                 if (num < CFG_MAX_NAND_DEVICE) {
735 #ifndef CFG_NAND_LEGACY
736                         *size = nand_info[num].size;
737 #else
738                         extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
739                         *size = nand_dev_desc[num].totlen;
740 #endif
741                         return 0;
742                 }
743
744                 printf("no such NAND device: %s%d (valid range 0 ... %d)\n",
745                                 MTD_DEV_TYPE(type), num, CFG_MAX_NAND_DEVICE - 1);
746 #else
747                 printf("support for NAND devices not present\n");
748 #endif
749         }
750
751         return 1;
752 }
753
754 #ifdef CONFIG_JFFS2_CMDLINE
755 /**
756  * Delete all mtd devices from a supplied devices list, free memory allocated for
757  * each device and delete all device partitions.
758  *
759  * @return 0 on success, 1 otherwise
760  */
761 static int device_delall(struct list_head *head)
762 {
763         struct list_head *entry, *n;
764         struct mtd_device *dev_tmp;
765
766         /* clean devices list */
767         list_for_each_safe(entry, n, head) {
768                 dev_tmp = list_entry(entry, struct mtd_device, link);
769                 list_del(entry);
770                 part_delall(&dev_tmp->parts);
771                 free(dev_tmp);
772         }
773         INIT_LIST_HEAD(&devices);
774
775         return 0;
776 }
777
778 /**
779  * If provided device exists it's partitions are deleted, device is removed
780  * from device list and device memory is freed.
781  *
782  * @param dev device to be deleted
783  * @return 0 on success, 1 otherwise
784  */
785 static int device_del(struct mtd_device *dev)
786 {
787         part_delall(&dev->parts);
788         list_del(&dev->link);
789         free(dev);
790
791         if (dev == current_dev) {
792                 /* we just deleted current device */
793                 if (list_empty(&devices)) {
794                         current_dev = NULL;
795                 } else {
796                         /* reset first partition from first dev from the
797                          * devices list as current */
798                         current_dev = list_entry(devices.next, struct mtd_device, link);
799                         current_partnum = 0;
800                 }
801                 current_save();
802                 return 0;
803         }
804
805         index_partitions();
806         return 0;
807 }
808
809 /**
810  * Search global device list and return pointer to the device of type and num
811  * specified.
812  *
813  * @param type device type
814  * @param num device number
815  * @return NULL if requested device does not exist
816  */
817 static struct mtd_device* device_find(u8 type, u8 num)
818 {
819         struct list_head *entry;
820         struct mtd_device *dev_tmp;
821
822         list_for_each(entry, &devices) {
823                 dev_tmp = list_entry(entry, struct mtd_device, link);
824
825                 if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
826                         return dev_tmp;
827         }
828
829         return NULL;
830 }
831
832 /**
833  * Add specified device to the global device list.
834  *
835  * @param dev device to be added
836  */
837 static void device_add(struct mtd_device *dev)
838 {
839         u8 current_save_needed = 0;
840
841         if (list_empty(&devices)) {
842                 current_dev = dev;
843                 current_partnum = 0;
844                 current_save_needed = 1;
845         }
846
847         list_add_tail(&dev->link, &devices);
848
849         if (current_save_needed > 0)
850                 current_save();
851         else
852                 index_partitions();
853 }
854
855 /**
856  * Parse device type, name and mtd-id. If syntax is ok allocate memory and
857  * return pointer to the device structure.
858  *
859  * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
860  * @param ret output pointer to next char after parse completes (output)
861  * @param retdev pointer to the allocated device (output)
862  * @return 0 on success, 1 otherwise
863  */
864 static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
865 {
866         struct mtd_device *dev;
867         struct part_info *part;
868         struct mtdids *id;
869         const char *mtd_id;
870         unsigned int mtd_id_len;
871         const char *p, *pend;
872         LIST_HEAD(tmp_list);
873         struct list_head *entry, *n;
874         u16 num_parts;
875         u32 offset;
876         int err = 1;
877
878         p = mtd_dev;
879         *retdev = NULL;
880         *ret = NULL;
881
882         DEBUGF("===device_parse===\n");
883
884         /* fetch <mtd-id> */
885         mtd_id = p;
886         if (!(p = strchr(mtd_id, ':'))) {
887                 printf("no <mtd-id> identifier\n");
888                 return 1;
889         }
890         mtd_id_len = p - mtd_id + 1;
891         p++;
892
893         /* verify if we have a valid device specified */
894         if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
895                 printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
896                 return 1;
897         }
898
899         DEBUGF("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
900                         id->type, MTD_DEV_TYPE(id->type),
901                         id->num, id->mtd_id);
902         pend = strchr(p, ';');
903         DEBUGF("parsing partitions %.*s\n", (pend ? pend - p : strlen(p)), p);
904
905
906         /* parse partitions */
907         num_parts = 0;
908
909         offset = 0;
910         if ((dev = device_find(id->type, id->num)) != NULL) {
911                 /* if device already exists start at the end of the last partition */
912                 part = list_entry(dev->parts.prev, struct part_info, link);
913                 offset = part->offset + part->size;
914         }
915
916         while (p && (*p != '\0') && (*p != ';')) {
917                 err = 1;
918                 if ((part_parse(p, &p, &part) != 0) || (!part))
919                         break;
920
921                 /* calculate offset when not specified */
922                 if (part->offset == OFFSET_NOT_SPECIFIED)
923                         part->offset = offset;
924                 else
925                         offset = part->offset;
926
927                 /* verify alignment and size */
928                 if (part_validate(id, part) != 0)
929                         break;
930
931                 offset += part->size;
932
933                 /* partition is ok, add it to the list */
934                 list_add_tail(&part->link, &tmp_list);
935                 num_parts++;
936                 err = 0;
937         }
938         if (err == 1) {
939                 part_delall(&tmp_list);
940                 return 1;
941         }
942
943         if (num_parts == 0) {
944                 printf("no partitions for device %s%d (%s)\n",
945                                 MTD_DEV_TYPE(id->type), id->num, id->mtd_id);
946                 return 1;
947         }
948
949         DEBUGF("\ntotal partitions: %d\n", num_parts);
950
951         /* check for next device presence */
952         if (p) {
953                 if (*p == ';') {
954                         *ret = ++p;
955                 } else if (*p == '\0') {
956                         *ret = p;
957                 } else {
958                         printf("unexpected character '%c' at the end of device\n", *p);
959                         *ret = NULL;
960                         return 1;
961                 }
962         }
963
964         /* allocate memory for mtd_device structure */
965         if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
966                 printf("out of memory\n");
967                 return 1;
968         }
969         memset(dev, 0, sizeof(struct mtd_device));
970         dev->id = id;
971         dev->num_parts = 0; /* part_sort_add increments num_parts */
972         INIT_LIST_HEAD(&dev->parts);
973         INIT_LIST_HEAD(&dev->link);
974
975         /* move partitions from tmp_list to dev->parts */
976         list_for_each_safe(entry, n, &tmp_list) {
977                 part = list_entry(entry, struct part_info, link);
978                 list_del(entry);
979                 if (part_sort_add(dev, part) != 0) {
980                         device_del(dev);
981                         return 1;
982                 }
983         }
984
985         *retdev = dev;
986
987         DEBUGF("===\n\n");
988         return 0;
989 }
990
991 /**
992  * Initialize global device list.
993  *
994  * @return 0 on success, 1 otherwise
995  */
996 static int devices_init(void)
997 {
998         last_parts[0] = '\0';
999         current_dev = NULL;
1000         current_save();
1001
1002         return device_delall(&devices);
1003 }
1004
1005 /*
1006  * Search global mtdids list and find id of requested type and number.
1007  *
1008  * @return pointer to the id if it exists, NULL otherwise
1009  */
1010 static struct mtdids* id_find(u8 type, u8 num)
1011 {
1012         struct list_head *entry;
1013         struct mtdids *id;
1014
1015         list_for_each(entry, &mtdids) {
1016                 id = list_entry(entry, struct mtdids, link);
1017
1018                 if ((id->type == type) && (id->num == num))
1019                         return id;
1020         }
1021
1022         return NULL;
1023 }
1024
1025 /**
1026  * Search global mtdids list and find id of a requested mtd_id.
1027  *
1028  * Note: first argument is not null terminated.
1029  *
1030  * @param mtd_id string containing requested mtd_id
1031  * @param mtd_id_len length of supplied mtd_id
1032  * @return pointer to the id if it exists, NULL otherwise
1033  */
1034 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
1035 {
1036         struct list_head *entry;
1037         struct mtdids *id;
1038
1039         DEBUGF("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
1040                         mtd_id_len, mtd_id, mtd_id_len);
1041
1042         list_for_each(entry, &mtdids) {
1043                 id = list_entry(entry, struct mtdids, link);
1044
1045                 DEBUGF("entry: '%s' (len = %d)\n",
1046                                 id->mtd_id, strlen(id->mtd_id));
1047
1048                 if (mtd_id_len != strlen(id->mtd_id))
1049                         continue;
1050                 if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
1051                         return id;
1052         }
1053
1054         return NULL;
1055 }
1056 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
1057
1058 /**
1059  * Parse device id string <dev-id> := 'nand'|'nor'<dev-num>, return device
1060  * type and number.
1061  *
1062  * @param id string describing device id
1063  * @param ret_id output pointer to next char after parse completes (output)
1064  * @param dev_type parsed device type (output)
1065  * @param dev_num parsed device number (output)
1066  * @return 0 on success, 1 otherwise
1067  */
1068 int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num)
1069 {
1070         const char *p = id;
1071
1072         *dev_type = 0;
1073         if (strncmp(p, "nand", 4) == 0) {
1074                 *dev_type = MTD_DEV_TYPE_NAND;
1075                 p += 4;
1076         } else if (strncmp(p, "nor", 3) == 0) {
1077                 *dev_type = MTD_DEV_TYPE_NOR;
1078                 p += 3;
1079         } else {
1080                 printf("incorrect device type in %s\n", id);
1081                 return 1;
1082         }
1083
1084         if (!isdigit(*p)) {
1085                 printf("incorrect device number in %s\n", id);
1086                 return 1;
1087         }
1088
1089         *dev_num = simple_strtoul(p, (char **)&p, 0);
1090         if (ret_id)
1091                 *ret_id = p;
1092         return 0;
1093 }
1094
1095 #ifdef CONFIG_JFFS2_CMDLINE
1096 /**
1097  * Process all devices and generate corresponding mtdparts string describing
1098  * all partitions on all devices.
1099  *
1100  * @param buf output buffer holding generated mtdparts string (output)
1101  * @param buflen buffer size
1102  * @return 0 on success, 1 otherwise
1103  */
1104 static int generate_mtdparts(char *buf, u32 buflen)
1105 {
1106         struct list_head *pentry, *dentry;
1107         struct mtd_device *dev;
1108         struct part_info *part, *prev_part;
1109         char *p = buf;
1110         char tmpbuf[32];
1111         u32 size, offset, len, part_cnt;
1112         u32 maxlen = buflen - 1;
1113
1114         DEBUGF("--- generate_mtdparts ---\n");
1115
1116         if (list_empty(&devices)) {
1117                 buf[0] = '\0';
1118                 return 0;
1119         }
1120
1121         sprintf(p, "mtdparts=");
1122         p += 9;
1123
1124         list_for_each(dentry, &devices) {
1125                 dev = list_entry(dentry, struct mtd_device, link);
1126
1127                 /* copy mtd_id */
1128                 len = strlen(dev->id->mtd_id) + 1;
1129                 if (len > maxlen)
1130                         goto cleanup;
1131                 memcpy(p, dev->id->mtd_id, len - 1);
1132                 p += len - 1;
1133                 *(p++) = ':';
1134                 maxlen -= len;
1135
1136                 /* format partitions */
1137                 prev_part = NULL;
1138                 part_cnt = 0;
1139                 list_for_each(pentry, &dev->parts) {
1140                         part = list_entry(pentry, struct part_info, link);
1141                         size = part->size;
1142                         offset = part->offset;
1143                         part_cnt++;
1144
1145                         /* partition size */
1146                         memsize_format(tmpbuf, size);
1147                         len = strlen(tmpbuf);
1148                         if (len > maxlen)
1149                                 goto cleanup;
1150                         memcpy(p, tmpbuf, len);
1151                         p += len;
1152                         maxlen -= len;
1153
1154
1155                         /* add offset only when there is a gap between
1156                          * partitions */
1157                         if ((!prev_part && (offset != 0)) ||
1158                                         (prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
1159
1160                                 memsize_format(tmpbuf, offset);
1161                                 len = strlen(tmpbuf) + 1;
1162                                 if (len > maxlen)
1163                                         goto cleanup;
1164                                 *(p++) = '@';
1165                                 memcpy(p, tmpbuf, len - 1);
1166                                 p += len - 1;
1167                                 maxlen -= len;
1168                         }
1169
1170                         /* copy name only if user supplied */
1171                         if(!part->auto_name) {
1172                                 len = strlen(part->name) + 2;
1173                                 if (len > maxlen)
1174                                         goto cleanup;
1175
1176                                 *(p++) = '(';
1177                                 memcpy(p, part->name, len - 2);
1178                                 p += len - 2;
1179                                 *(p++) = ')';
1180                                 maxlen -= len;
1181                         }
1182
1183                         /* ro mask flag */
1184                         if (part->mask_flags && MTD_WRITEABLE_CMD) {
1185                                 len = 2;
1186                                 if (len > maxlen)
1187                                         goto cleanup;
1188                                 *(p++) = 'r';
1189                                 *(p++) = 'o';
1190                                 maxlen -= 2;
1191                         }
1192
1193                         /* print ',' separator if there are other partitions
1194                          * following */
1195                         if (dev->num_parts > part_cnt) {
1196                                 if (1 > maxlen)
1197                                         goto cleanup;
1198                                 *(p++) = ',';
1199                                 maxlen--;
1200                         }
1201                         prev_part = part;
1202                 }
1203                 /* print ';' separator if there are other devices following */
1204                 if (dentry->next != &devices) {
1205                         if (1 > maxlen)
1206                                 goto cleanup;
1207                         *(p++) = ';';
1208                         maxlen--;
1209                 }
1210         }
1211
1212         /* we still have at least one char left, as we decremented maxlen at
1213          * the begining */
1214         *p = '\0';
1215
1216         return 0;
1217
1218 cleanup:
1219         last_parts[0] = '\0';
1220         return 1;
1221 }
1222
1223 /**
1224  * Call generate_mtdparts to process all devices and generate corresponding
1225  * mtdparts string, save it in mtdparts environment variable.
1226  *
1227  * @param buf output buffer holding generated mtdparts string (output)
1228  * @param buflen buffer size
1229  * @return 0 on success, 1 otherwise
1230  */
1231 static int generate_mtdparts_save(char *buf, u32 buflen)
1232 {
1233         int ret;
1234
1235         ret = generate_mtdparts(buf, buflen);
1236
1237         if ((buf[0] != '\0') && (ret == 0))
1238                 setenv("mtdparts", buf);
1239         else
1240                 setenv("mtdparts", NULL);
1241
1242         return ret;
1243 }
1244
1245 /**
1246  * Format and print out a partition list for each device from global device
1247  * list.
1248  */
1249 static void list_partitions(void)
1250 {
1251         struct list_head *dentry, *pentry;
1252         struct part_info *part;
1253         struct mtd_device *dev;
1254         int part_num;
1255
1256         DEBUGF("\n---list_partitions---\n");
1257         list_for_each(dentry, &devices) {
1258                 dev = list_entry(dentry, struct mtd_device, link);
1259                 printf("\ndevice %s%d <%s>, # parts = %d\n",
1260                                 MTD_DEV_TYPE(dev->id->type), dev->id->num,
1261                                 dev->id->mtd_id, dev->num_parts);
1262                 printf(" #: name\t\t\tsize\t\toffset\t\tmask_flags\n");
1263
1264                 /* list partitions for given device */
1265                 part_num = 0;
1266                 list_for_each(pentry, &dev->parts) {
1267                         part = list_entry(pentry, struct part_info, link);
1268                         printf("%2d: %-20s0x%08x\t0x%08x\t%d\n",
1269                                         part_num, part->name, part->size,
1270                                         part->offset, part->mask_flags);
1271
1272                         part_num++;
1273                 }
1274         }
1275         if (list_empty(&devices))
1276                 printf("no partitions defined\n");
1277
1278         /* current_dev is not NULL only when we have non empty device list */
1279         if (current_dev) {
1280                 part = jffs2_part_info(current_dev, current_partnum);
1281                 if (part) {
1282                         printf("\nactive partition: %s%d,%d - (%s) 0x%08lx @ 0x%08lx\n",
1283                                         MTD_DEV_TYPE(current_dev->id->type),
1284                                         current_dev->id->num, current_partnum,
1285                                         part->name, part->size, part->offset);
1286                 } else {
1287                         printf("could not get current partition info\n\n");
1288                 }
1289         }
1290
1291         printf("\ndefaults:\n");
1292         printf("mtdids  : %s\n", mtdids_default);
1293         printf("mtdparts: %s\n", mtdparts_default);
1294 }
1295
1296 /**
1297  * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
1298  * corresponding device and verify partition number.
1299  *
1300  * @param id string describing device and partition or partition name
1301  * @param dev pointer to the requested device (output)
1302  * @param part_num verified partition number (output)
1303  * @param part pointer to requested partition (output)
1304  * @return 0 on success, 1 otherwise
1305  */
1306 int find_dev_and_part(const char *id, struct mtd_device **dev,
1307                 u8 *part_num, struct part_info **part)
1308 {
1309         struct list_head *dentry, *pentry;
1310         u8 type, dnum, pnum;
1311         const char *p;
1312
1313         DEBUGF("--- find_dev_and_part ---\nid = %s\n", id);
1314
1315         list_for_each(dentry, &devices) {
1316                 *part_num = 0;
1317                 *dev = list_entry(dentry, struct mtd_device, link);
1318                 list_for_each(pentry, &(*dev)->parts) {
1319                         *part = list_entry(pentry, struct part_info, link);
1320                         if (strcmp((*part)->name, id) == 0)
1321                                 return 0;
1322                         (*part_num)++;
1323                 }
1324         }
1325
1326         p = id;
1327         *dev = NULL;
1328         *part = NULL;
1329         *part_num = 0;
1330
1331         if (id_parse(p, &p, &type, &dnum) != 0)
1332                 return 1;
1333
1334         if ((*p++ != ',') || (*p == '\0')) {
1335                 printf("no partition number specified\n");
1336                 return 1;
1337         }
1338         pnum = simple_strtoul(p, (char **)&p, 0);
1339         if (*p != '\0') {
1340                 printf("unexpected trailing character '%c'\n", *p);
1341                 return 1;
1342         }
1343
1344         if ((*dev = device_find(type, dnum)) == NULL) {
1345                 printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
1346                 return 1;
1347         }
1348
1349         if ((*part = jffs2_part_info(*dev, pnum)) == NULL) {
1350                 printf("no such partition\n");
1351                 *dev = NULL;
1352                 return 1;
1353         }
1354
1355         *part_num = pnum;
1356
1357         return 0;
1358 }
1359
1360 /**
1361  * Find and delete partition. For partition id format see find_dev_and_part().
1362  *
1363  * @param id string describing device and partition
1364  * @return 0 on success, 1 otherwise
1365  */
1366 static int delete_partition(const char *id)
1367 {
1368         u8 pnum;
1369         struct mtd_device *dev;
1370         struct part_info *part;
1371
1372         if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
1373
1374                 DEBUGF("delete_partition: device = %s%d, partition %d = (%s) 0x%08lx@0x%08lx\n",
1375                                 MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
1376                                 part->name, part->size, part->offset);
1377
1378                 if (part_del(dev, part) != 0)
1379                         return 1;
1380
1381                 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
1382                         printf("generated mtdparts too long, reseting to null\n");
1383                         return 1;
1384                 }
1385                 return 0;
1386         }
1387
1388         printf("partition %s not found\n", id);
1389         return 1;
1390 }
1391
1392 /**
1393  * Accept character string describing mtd partitions and call device_parse()
1394  * for each entry. Add created devices to the global devices list.
1395  *
1396  * @param mtdparts string specifing mtd partitions
1397  * @return 0 on success, 1 otherwise
1398  */
1399 static int parse_mtdparts(const char *const mtdparts)
1400 {
1401         const char *p = mtdparts;
1402         struct mtd_device *dev;
1403         int err = 1;
1404
1405         DEBUGF("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
1406
1407         /* delete all devices and partitions */
1408         if (devices_init() != 0) {
1409                 printf("could not initialise device list\n");
1410                 return err;
1411         }
1412
1413         /* re-read 'mtdparts' variable, devices_init may be updating env */
1414         p = getenv("mtdparts");
1415
1416         if (strncmp(p, "mtdparts=", 9) != 0) {
1417                 printf("mtdparts variable doesn't start with 'mtdparts='\n");
1418                 return err;
1419         }
1420         p += 9;
1421
1422         while (p && (*p != '\0')) {
1423                 err = 1;
1424                 if ((device_parse(p, &p, &dev) != 0) || (!dev))
1425                         break;
1426
1427                 DEBUGF("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
1428                                 dev->id->num, dev->id->mtd_id);
1429
1430                 /* check if parsed device is already on the list */
1431                 if (device_find(dev->id->type, dev->id->num) != NULL) {
1432                         printf("device %s%d redefined, please correct mtdparts variable\n",
1433                                         MTD_DEV_TYPE(dev->id->type), dev->id->num);
1434                         break;
1435                 }
1436
1437                 list_add_tail(&dev->link, &devices);
1438                 err = 0;
1439         }
1440         if (err == 1) {
1441                 device_delall(&devices);
1442                 return 1;
1443         }
1444
1445         return 0;
1446 }
1447
1448 /**
1449  * Parse provided string describing mtdids mapping (see file header for mtdids
1450  * variable format). Allocate memory for each entry and add all found entries
1451  * to the global mtdids list.
1452  *
1453  * @param ids mapping string
1454  * @return 0 on success, 1 otherwise
1455  */
1456 static int parse_mtdids(const char *const ids)
1457 {
1458         const char *p = ids;
1459         const char *mtd_id;
1460         int mtd_id_len;
1461         struct mtdids *id;
1462         struct list_head *entry, *n;
1463         struct mtdids *id_tmp;
1464         u8 type, num;
1465         u32 size;
1466         int ret = 1;
1467
1468         DEBUGF("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
1469
1470         /* clean global mtdids list */
1471         list_for_each_safe(entry, n, &mtdids) {
1472                 id_tmp = list_entry(entry, struct mtdids, link);
1473                 DEBUGF("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
1474                 list_del(entry);
1475                 free(id_tmp);
1476         }
1477         last_ids[0] = '\0';
1478         INIT_LIST_HEAD(&mtdids);
1479
1480         while(p && (*p != '\0')) {
1481
1482                 ret = 1;
1483                 /* parse 'nor'|'nand'<dev-num> */
1484                 if (id_parse(p, &p, &type, &num) != 0)
1485                         break;
1486
1487                 if (*p != '=') {
1488                         printf("mtdids: incorrect <dev-num>\n");
1489                         break;
1490                 }
1491                 p++;
1492
1493                 /* check if requested device exists */
1494                 if (device_validate(type, num, &size) != 0)
1495                         return 1;
1496
1497                 /* locate <mtd-id> */
1498                 mtd_id = p;
1499                 if ((p = strchr(mtd_id, ',')) != NULL) {
1500                         mtd_id_len = p - mtd_id + 1;
1501                         p++;
1502                 } else {
1503                         mtd_id_len = strlen(mtd_id) + 1;
1504                 }
1505                 if (mtd_id_len == 0) {
1506                         printf("mtdids: no <mtd-id> identifier\n");
1507                         break;
1508                 }
1509
1510                 /* check if this id is already on the list */
1511                 int double_entry = 0;
1512                 list_for_each(entry, &mtdids) {
1513                         id_tmp = list_entry(entry, struct mtdids, link);
1514                         if ((id_tmp->type == type) && (id_tmp->num == num)) {
1515                                 double_entry = 1;
1516                                 break;
1517                         }
1518                 }
1519                 if (double_entry) {
1520                         printf("device id %s%d redefined, please correct mtdids variable\n",
1521                                         MTD_DEV_TYPE(type), num);
1522                         break;
1523                 }
1524
1525                 /* allocate mtdids structure */
1526                 if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
1527                         printf("out of memory\n");
1528                         break;
1529                 }
1530                 memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
1531                 id->num = num;
1532                 id->type = type;
1533                 id->size = size;
1534                 id->mtd_id = (char *)(id + 1);
1535                 strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
1536                 id->mtd_id[mtd_id_len - 1] = '\0';
1537                 INIT_LIST_HEAD(&id->link);
1538
1539                 DEBUGF("+ id %s%d\t%16d bytes\t%s\n",
1540                                 MTD_DEV_TYPE(id->type), id->num,
1541                                 id->size, id->mtd_id);
1542
1543                 list_add_tail(&id->link, &mtdids);
1544                 ret = 0;
1545         }
1546         if (ret == 1) {
1547                 /* clean mtdids list and free allocated memory */
1548                 list_for_each_safe(entry, n, &mtdids) {
1549                         id_tmp = list_entry(entry, struct mtdids, link);
1550                         list_del(entry);
1551                         free(id_tmp);
1552                 }
1553                 return 1;
1554         }
1555
1556         return 0;
1557 }
1558
1559 /**
1560  * Parse and initialize global mtdids mapping and create global
1561  * device/partition list.
1562  *
1563  * @return 0 on success, 1 otherwise
1564  */
1565 int mtdparts_init(void)
1566 {
1567         static int initialized = 0;
1568         const char *ids, *parts;
1569         const char *current_partition;
1570         int ids_changed;
1571         char tmp_ep[PARTITION_MAXLEN];
1572
1573         DEBUGF("\n---mtdparts_init---\n");
1574         if (!initialized) {
1575                 INIT_LIST_HEAD(&mtdids);
1576                 INIT_LIST_HEAD(&devices);
1577                 memset(last_ids, 0, MTDIDS_MAXLEN);
1578                 memset(last_parts, 0, MTDPARTS_MAXLEN);
1579                 memset(last_partition, 0, PARTITION_MAXLEN);
1580                 initialized = 1;
1581         }
1582
1583         /* get variables */
1584         ids = getenv("mtdids");
1585         parts = getenv("mtdparts");
1586         current_partition = getenv("partition");
1587
1588         /* save it for later parsing, cannot rely on current partition pointer
1589          * as 'partition' variable may be updated during init */
1590         tmp_ep[0] = '\0';
1591         if (current_partition)
1592                 strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
1593
1594         DEBUGF("last_ids  : %s\n", last_ids);
1595         DEBUGF("env_ids   : %s\n", ids);
1596         DEBUGF("last_parts: %s\n", last_parts);
1597         DEBUGF("env_parts : %s\n\n", parts);
1598
1599         DEBUGF("last_partition : %s\n", last_partition);
1600         DEBUGF("env_partition  : %s\n", current_partition);
1601
1602         /* if mtdids varible is empty try to use defaults */
1603         if (!ids) {
1604                 if (mtdids_default) {
1605                         DEBUGF("mtdids variable not defined, using default\n");
1606                         ids = mtdids_default;
1607                         setenv("mtdids", (char *)ids);
1608                 } else {
1609                         printf("mtdids not defined, no default present\n");
1610                         return 1;
1611                 }
1612         }
1613         if (strlen(ids) > MTDIDS_MAXLEN - 1) {
1614                 printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
1615                 return 1;
1616         }
1617
1618         /* do no try to use defaults when mtdparts variable is not defined,
1619          * just check the length */
1620         if (!parts)
1621                 printf("mtdparts variable not set, see 'help mtdparts'\n");
1622
1623         if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
1624                 printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
1625                 return 1;
1626         }
1627
1628         /* check if we have already parsed those mtdids */
1629         if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
1630                 ids_changed = 0;
1631         } else {
1632                 ids_changed = 1;
1633
1634                 if (parse_mtdids(ids) != 0) {
1635                         devices_init();
1636                         return 1;
1637                 }
1638
1639                 /* ok it's good, save new ids */
1640                 strncpy(last_ids, ids, MTDIDS_MAXLEN);
1641         }
1642
1643         /* parse partitions if either mtdparts or mtdids were updated */
1644         if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
1645                 if (parse_mtdparts(parts) != 0)
1646                         return 1;
1647
1648                 if (list_empty(&devices)) {
1649                         printf("mtdparts_init: no valid partitions\n");
1650                         return 1;
1651                 }
1652
1653                 /* ok it's good, save new parts */
1654                 strncpy(last_parts, parts, MTDPARTS_MAXLEN);
1655
1656                 /* reset first partition from first dev from the list as current */
1657                 current_dev = list_entry(devices.next, struct mtd_device, link);
1658                 current_partnum = 0;
1659                 current_save();
1660
1661                 DEBUGF("mtdparts_init: current_dev  = %s%d, current_partnum = %d\n",
1662                                 MTD_DEV_TYPE(current_dev->id->type),
1663                                 current_dev->id->num, current_partnum);
1664         }
1665
1666         /* mtdparts variable was reset to NULL, delete all devices/partitions */
1667         if (!parts && (last_parts[0] != '\0'))
1668                 return devices_init();
1669
1670         /* do not process current partition if mtdparts variable is null */
1671         if (!parts)
1672                 return 0;
1673
1674         /* is current partition set in environment? if so, use it */
1675         if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
1676                 struct part_info *p;
1677                 struct mtd_device *cdev;
1678                 u8 pnum;
1679
1680                 DEBUGF("--- getting current partition: %s\n", tmp_ep);
1681
1682                 if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
1683                         current_dev = cdev;
1684                         current_partnum = pnum;
1685                         current_save();
1686                 }
1687         } else if (getenv("partition") == NULL) {
1688                 DEBUGF("no partition variable set, setting...\n");
1689                 current_save();
1690         }
1691
1692         return 0;
1693 }
1694 #else /* #ifdef CONFIG_JFFS2_CMDLINE */
1695 /*
1696  * 'Static' version of command line mtdparts_init() routine. Single partition on
1697  * a single device configuration.
1698  */
1699
1700 /**
1701  * Parse and initialize global mtdids mapping and create global
1702  * device/partition list.
1703  *
1704  * @return 0 on success, 1 otherwise
1705  */
1706 int mtdparts_init(void)
1707 {
1708         static int initialized = 0;
1709         u32 size;
1710         char *dev_name;
1711
1712         DEBUGF("\n---mtdparts_init---\n");
1713         if (!initialized) {
1714                 struct mtdids *id;
1715                 struct part_info *part;
1716
1717                 initialized = 1;
1718                 current_dev = (struct mtd_device *)
1719                         malloc(sizeof(struct mtd_device) +
1720                                         sizeof(struct part_info) +
1721                                         sizeof(struct mtdids));
1722                 if (!current_dev) {
1723                         printf("out of memory\n");
1724                         return 1;
1725                 }
1726                 memset(current_dev, 0, sizeof(struct mtd_device) +
1727                                         sizeof(struct part_info) + sizeof(struct mtdids));
1728
1729                 id = (struct mtdids *)(current_dev + 1);
1730                 part = (struct part_info *)(id + 1);
1731
1732                 /* id */
1733                 id->mtd_id = "single part";
1734
1735 #if defined(CONFIG_JFFS2_DEV)
1736                 dev_name = CONFIG_JFFS2_DEV;
1737 #else
1738                 dev_name = "nor0";
1739 #endif
1740
1741                 if ((id_parse(dev_name, NULL, &id->type, &id->num) != 0) ||
1742                                 (device_validate(id->type, id->num, &size) != 0)) {
1743                         printf("incorrect device: %s%d\n", MTD_DEV_TYPE(id->type), id->num);
1744                         free(current_dev);
1745                         return 1;
1746                 }
1747                 id->size = size;
1748                 INIT_LIST_HEAD(&id->link);
1749
1750                 DEBUGF("dev id: type = %d, num = %d, size = 0x%08lx, mtd_id = %s\n",
1751                                 id->type, id->num, id->size, id->mtd_id);
1752
1753                 /* partition */
1754                 part->name = "static";
1755                 part->auto_name = 0;
1756
1757 #if defined(CONFIG_JFFS2_PART_SIZE)
1758                 part->size = CONFIG_JFFS2_PART_SIZE;
1759 #else
1760                 part->size = SIZE_REMAINING;
1761 #endif
1762
1763 #if defined(CONFIG_JFFS2_PART_OFFSET)
1764                 part->offset = CONFIG_JFFS2_PART_OFFSET;
1765 #else
1766                 part->offset = 0x00000000;
1767 #endif
1768
1769                 part->dev = current_dev;
1770                 INIT_LIST_HEAD(&part->link);
1771
1772                 /* recalculate size if needed */
1773                 if (part->size == SIZE_REMAINING)
1774                         part->size = id->size - part->offset;
1775
1776                 DEBUGF("part  : name = %s, size = 0x%08lx, offset = 0x%08lx\n",
1777                                 part->name, part->size, part->offset);
1778
1779                 /* device */
1780                 current_dev->id = id;
1781                 INIT_LIST_HEAD(&current_dev->link);
1782                 current_dev->num_parts = 1;
1783                 INIT_LIST_HEAD(&current_dev->parts);
1784                 list_add(&part->link, &current_dev->parts);
1785         }
1786
1787         return 0;
1788 }
1789 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
1790
1791 /**
1792  * Return pointer to the partition of a requested number from a requested
1793  * device.
1794  *
1795  * @param dev device that is to be searched for a partition
1796  * @param part_num requested partition number
1797  * @return pointer to the part_info, NULL otherwise
1798  */
1799 static struct part_info* jffs2_part_info(struct mtd_device *dev, unsigned int part_num)
1800 {
1801         struct list_head *entry;
1802         struct part_info *part;
1803         int num;
1804
1805         if (!dev)
1806                 return NULL;
1807
1808         DEBUGF("\n--- jffs2_part_info: partition number %d for device %s%d (%s)\n",
1809                         part_num, MTD_DEV_TYPE(dev->id->type),
1810                         dev->id->num, dev->id->mtd_id);
1811
1812         if (part_num >= dev->num_parts) {
1813                 printf("invalid partition number %d for device %s%d (%s)\n",
1814                                 part_num, MTD_DEV_TYPE(dev->id->type),
1815                                 dev->id->num, dev->id->mtd_id);
1816                 return NULL;
1817         }
1818
1819         /* locate partition number, return it */
1820         num = 0;
1821         list_for_each(entry, &dev->parts) {
1822                 part = list_entry(entry, struct part_info, link);
1823
1824                 if (part_num == num++) {
1825                         return part;
1826                 }
1827         }
1828
1829         return NULL;
1830 }
1831
1832 /***************************************************/
1833 /* U-boot commands                                 */
1834 /***************************************************/
1835
1836 /**
1837  * Routine implementing fsload u-boot command. This routine tries to load
1838  * a requested file from jffs2/cramfs filesystem on a current partition.
1839  *
1840  * @param cmdtp command internal data
1841  * @param flag command flag
1842  * @param argc number of arguments supplied to the command
1843  * @param argv arguments list
1844  * @return 0 on success, 1 otherwise
1845  */
1846 int do_jffs2_fsload(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1847 {
1848         char *fsname;
1849         char *filename;
1850         int size;
1851         struct part_info *part;
1852         ulong offset = load_addr;
1853
1854         /* pre-set Boot file name */
1855         if ((filename = getenv("bootfile")) == NULL) {
1856                 filename = "uImage";
1857         }
1858
1859         if (argc == 2) {
1860                 filename = argv[1];
1861         }
1862         if (argc == 3) {
1863                 offset = simple_strtoul(argv[1], NULL, 16);
1864                 load_addr = offset;
1865                 filename = argv[2];
1866         }
1867
1868         /* make sure we are in sync with env variables */
1869         if (mtdparts_init() !=0)
1870                 return 1;
1871
1872         if ((part = jffs2_part_info(current_dev, current_partnum))){
1873
1874                 /* check partition type for cramfs */
1875                 fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
1876                 printf("### %s loading '%s' to 0x%lx\n", fsname, filename, offset);
1877
1878                 if (cramfs_check(part)) {
1879                         size = cramfs_load ((char *) offset, part, filename);
1880                 } else {
1881                         /* if this is not cramfs assume jffs2 */
1882                         size = jffs2_1pass_load((char *)offset, part, filename);
1883                 }
1884
1885                 if (size > 0) {
1886                         char buf[10];
1887                         printf("### %s load complete: %d bytes loaded to 0x%lx\n",
1888                                 fsname, size, offset);
1889                         sprintf(buf, "%x", size);
1890                         setenv("filesize", buf);
1891                 } else {
1892                         printf("### %s LOAD ERROR<%x> for %s!\n", fsname, size, filename);
1893                 }
1894
1895                 return !(size > 0);
1896         }
1897         return 1;
1898 }
1899
1900 /**
1901  * Routine implementing u-boot ls command which lists content of a given
1902  * directory on a current partition.
1903  *
1904  * @param cmdtp command internal data
1905  * @param flag command flag
1906  * @param argc number of arguments supplied to the command
1907  * @param argv arguments list
1908  * @return 0 on success, 1 otherwise
1909  */
1910 int do_jffs2_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1911 {
1912         char *filename = "/";
1913         int ret;
1914         struct part_info *part;
1915
1916         if (argc == 2)
1917                 filename = argv[1];
1918
1919         /* make sure we are in sync with env variables */
1920         if (mtdparts_init() !=0)
1921                 return 1;
1922
1923         if ((part = jffs2_part_info(current_dev, current_partnum))){
1924
1925                 /* check partition type for cramfs */
1926                 if (cramfs_check(part)) {
1927                         ret = cramfs_ls (part, filename);
1928                 } else {
1929                         /* if this is not cramfs assume jffs2 */
1930                         ret = jffs2_1pass_ls(part, filename);
1931                 }
1932
1933                 return ret ? 0 : 1;
1934         }
1935         return 1;
1936 }
1937
1938 /**
1939  * Routine implementing u-boot fsinfo command. This routine prints out
1940  * miscellaneous filesystem informations/statistics.
1941  *
1942  * @param cmdtp command internal data
1943  * @param flag command flag
1944  * @param argc number of arguments supplied to the command
1945  * @param argv arguments list
1946  * @return 0 on success, 1 otherwise
1947  */
1948 int do_jffs2_fsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1949 {
1950         struct part_info *part;
1951         char *fsname;
1952         int ret;
1953
1954         /* make sure we are in sync with env variables */
1955         if (mtdparts_init() !=0)
1956                 return 1;
1957
1958         if ((part = jffs2_part_info(current_dev, current_partnum))){
1959
1960                 /* check partition type for cramfs */
1961                 fsname = (cramfs_check(part) ? "CRAMFS" : "JFFS2");
1962                 printf("### filesystem type is %s\n", fsname);
1963
1964                 if (cramfs_check(part)) {
1965                         ret = cramfs_info (part);
1966                 } else {
1967                         /* if this is not cramfs assume jffs2 */
1968                         ret = jffs2_1pass_info(part);
1969                 }
1970
1971                 return ret ? 0 : 1;
1972         }
1973         return 1;
1974 }
1975
1976 /* command line only */
1977 #ifdef CONFIG_JFFS2_CMDLINE
1978 /**
1979  * Routine implementing u-boot chpart command. Sets new current partition based
1980  * on the user supplied partition id. For partition id format see find_dev_and_part().
1981  *
1982  * @param cmdtp command internal data
1983  * @param flag command flag
1984  * @param argc number of arguments supplied to the command
1985  * @param argv arguments list
1986  * @return 0 on success, 1 otherwise
1987  */
1988 int do_jffs2_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
1989 {
1990 /* command line only */
1991         struct mtd_device *dev;
1992         struct part_info *part;
1993         u8 pnum;
1994
1995         if (mtdparts_init() !=0)
1996                 return 1;
1997
1998         if (argc < 2) {
1999                 printf("no partition id specified\n");
2000                 return 1;
2001         }
2002
2003         if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
2004                 return 1;
2005
2006         current_dev = dev;
2007         current_partnum = pnum;
2008         current_save();
2009
2010         printf("partition changed to %s%d,%d\n",
2011                         MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
2012
2013         return 0;
2014 }
2015
2016 /**
2017  * Routine implementing u-boot mtdparts command. Initialize/update default global
2018  * partition list and process user partition request (list, add, del).
2019  *
2020  * @param cmdtp command internal data
2021  * @param flag command flag
2022  * @param argc number of arguments supplied to the command
2023  * @param argv arguments list
2024  * @return 0 on success, 1 otherwise
2025  */
2026 int do_jffs2_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
2027 {
2028         if (argc == 2) {
2029                 if (strcmp(argv[1], "default") == 0) {
2030                         setenv("mtdids", (char *)mtdids_default);
2031                         setenv("mtdparts", (char *)mtdparts_default);
2032                         setenv("partition", NULL);
2033
2034                         mtdparts_init();
2035                         return 0;
2036                 } else if (strcmp(argv[1], "delall") == 0) {
2037                         /* this may be the first run, initialize lists if needed */
2038                         mtdparts_init();
2039
2040                         setenv("mtdparts", NULL);
2041
2042                         /* devices_init() calls current_save() */
2043                         return devices_init();
2044                 }
2045         }
2046
2047         /* make sure we are in sync with env variables */
2048         if (mtdparts_init() != 0)
2049                 return 1;
2050
2051         if (argc == 1) {
2052                 list_partitions();
2053                 return 0;
2054         }
2055
2056         /* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
2057         if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) {
2058 #define PART_ADD_DESC_MAXLEN 64
2059                 char tmpbuf[PART_ADD_DESC_MAXLEN];
2060                 u8 type, num, len;
2061                 struct mtd_device *dev;
2062                 struct mtd_device *dev_tmp;
2063                 struct mtdids *id;
2064                 struct part_info *p;
2065
2066                 if (id_parse(argv[2], NULL, &type, &num) != 0)
2067                         return 1;
2068
2069                 if ((id = id_find(type, num)) == NULL) {
2070                         printf("no such device %s defined in mtdids variable\n", argv[2]);
2071                         return 1;
2072                 }
2073
2074                 len = strlen(id->mtd_id) + 1;   /* 'mtd_id:' */
2075                 len += strlen(argv[3]);         /* size@offset */
2076                 len += strlen(argv[4]) + 2;     /* '(' name ')' */
2077                 if (argv[5] && (strlen(argv[5]) == 2))
2078                         len += 2;               /* 'ro' */
2079
2080                 if (len >= PART_ADD_DESC_MAXLEN) {
2081                         printf("too long partition description\n");
2082                         return 1;
2083                 }
2084                 sprintf(tmpbuf, "%s:%s(%s)%s",
2085                                 id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
2086                 DEBUGF("add tmpbuf: %s\n", tmpbuf);
2087
2088                 if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
2089                         return 1;
2090
2091                 DEBUGF("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
2092                                 dev->id->num, dev->id->mtd_id);
2093
2094                 if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) {
2095                         device_add(dev);
2096                 } else {
2097                         /* merge new partition with existing ones*/
2098                         p = list_entry(dev->parts.next, struct part_info, link);
2099                         if (part_add(dev_tmp, p) != 0) {
2100                                 device_del(dev);
2101                                 return 1;
2102                         }
2103                 }
2104
2105                 if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
2106                         printf("generated mtdparts too long, reseting to null\n");
2107                         return 1;
2108                 }
2109
2110                 return 0;
2111         }
2112
2113         /* mtdparts del part-id */
2114         if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
2115                 DEBUGF("del: part-id = %s\n", argv[2]);
2116
2117                 return delete_partition(argv[2]);
2118         }
2119
2120         printf ("Usage:\n%s\n", cmdtp->usage);
2121         return 1;
2122 }
2123 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
2124
2125 /***************************************************/
2126 U_BOOT_CMD(
2127         fsload, 3,      0,      do_jffs2_fsload,
2128         "fsload\t- load binary file from a filesystem image\n",
2129         "[ off ] [ filename ]\n"
2130         "    - load binary file from flash bank\n"
2131         "      with offset 'off'\n"
2132 );
2133 U_BOOT_CMD(
2134         ls,     2,      1,      do_jffs2_ls,
2135         "ls\t- list files in a directory (default /)\n",
2136         "[ directory ]\n"
2137         "    - list files in a directory.\n"
2138 );
2139
2140 U_BOOT_CMD(
2141         fsinfo, 1,      1,      do_jffs2_fsinfo,
2142         "fsinfo\t- print information about filesystems\n",
2143         "    - print information about filesystems\n"
2144 );
2145
2146 #ifdef CONFIG_JFFS2_CMDLINE
2147 U_BOOT_CMD(
2148         chpart, 2,      0,      do_jffs2_chpart,
2149         "chpart\t- change active partition\n",
2150         "part-id\n"
2151         "    - change active partition (e.g. part-id = nand0,1)\n"
2152 );
2153
2154 U_BOOT_CMD(
2155         mtdparts,       6,      0,      do_jffs2_mtdparts,
2156         "mtdparts- define flash/nand partitions\n",
2157         "\n"
2158         "    - list partition table\n"
2159         "mtdparts delall\n"
2160         "    - delete all partitions\n"
2161         "mtdparts del part-id\n"
2162         "    - delete partition (e.g. part-id = nand0,1)\n"
2163         "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2164         "    - add partition\n"
2165         "mtdparts default\n"
2166         "    - reset partition table to defaults\n\n"
2167         "-----\n\n"
2168         "this command uses three environment variables:\n\n"
2169         "'partition' - keeps current partition identifier\n\n"
2170         "partition  := <part-id>\n"
2171         "<part-id>  := <dev-id>,part_num\n\n"
2172         "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
2173         "mtdids=<idmap>[,<idmap>,...]\n\n"
2174         "<idmap>    := <dev-id>=<mtd-id>\n"
2175         "<dev-id>   := 'nand'|'nor'<dev-num>\n"
2176         "<dev-num>  := mtd device number, 0...\n"
2177         "<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
2178         "'mtdparts' - partition list\n\n"
2179         "mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
2180         "<mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]\n"
2181         "<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
2182         "<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
2183         "<size>     := standard linux memsize OR '-' to denote all remaining space\n"
2184         "<offset>   := partition start offset within the device\n"
2185         "<name>     := '(' NAME ')'\n"
2186         "<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)\n"
2187 );
2188 #endif /* #ifdef CONFIG_JFFS2_CMDLINE */
2189
2190 /***************************************************/