[U-Boot-Users] [PATCH, resend] Support dynamic/patched NAND ENV offset

Hi!
I've first sent this on Feb 17, 2007. Unfortunately no reply was received. I think this is a quite useful feature, since a compile time offset into the NAND flash for the environment just doesn't work well with bad blocks ;)
This is the current version of the patch. I'd love to see it included. Thanks!
---
This patch adds support for CFG_ENV_OFFSET_PATCHED and CFG_ENV_OFFSET_OOB.
Both try to solve the problem of fixing the environment location in NAND flash at compile time, which doesn't work well if the NAND flash has a bad block at exactly that location.
CFG_ENV_OFFSET_PATCHED puts the environment in a global variable. You can then use the linker script to put that variable to a fixed location in the u-boot image. Then you can use bianry patching during the production flash process.
The idea of CFG_ENV_OFFSET_OOB is to store the environment offset in the NAND OOB data of block 0. We can do this in case the vendor makes a guarantee that block 0 never is a factory-default bad block.
Signed-off-by: Harald Welte laforge@openmoko.org
Index: u-boot/common/Makefile =================================================================== --- u-boot.orig/common/Makefile +++ u-boot/common/Makefile @@ -55,6 +55,7 @@ COBJS-$(CONFIG_CMD_DISPLAY) += cmd_display.o COBJS-$(CONFIG_CMD_DOC) += cmd_doc.o COBJS-$(CONFIG_CMD_DTT) += cmd_dtt.o +COBJS-y += cmd_dynenv.o COBJS-y += cmd_eeprom.o COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o Index: u-boot/common/cmd_dynenv.c =================================================================== --- /dev/null +++ u-boot/common/cmd_dynenv.c @@ -0,0 +1,106 @@ +/* + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Harald Welte laforge@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <environment.h> +#include <nand.h> +#include <asm/errno.h> +#include "cmd_nand.h" + +#if defined(CFG_ENV_OFFSET_OOB) + +int do_dynenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + struct mtd_info *mtd = &nand_info[0]; + int ret, size = 8; + uint8_t *buf; + + char *cmd = argv[1]; + + buf = malloc(mtd->oobsize); + if (!buf) + return -ENOMEM; + + ret = mtd->read_oob(mtd, 8, size, (size_t *) &size, (u_char *) buf); + if (!strcmp(cmd, "get")) { + + if (buf[0] == 'E' && buf[1] == 'N' && + buf[2] == 'V' && buf[3] == '0') + printf("0x%08x\n", *((u_int32_t *) &buf[4])); + else + printf("No dynamic environment marker in OOB block 0\n"); + + } else if (!strcmp(cmd, "set")) { + unsigned long addr, dummy; + + if (argc < 3) + goto usage; + + buf[0] = 'E'; + buf[1] = 'N'; + buf[2] = 'V'; + buf[3] = '0'; + + if (arg_off_size(argc-2, argv+2, mtd, &addr, &dummy) < 0) { + printf("Offset or partition name expected\n"); + goto fail; + } + if (!ret) { + uint8_t tmp[4]; + int i; + + memcpy(&tmp, &addr, 4); + for (i = 0; i != 4; i++) + if (tmp[i] & ~buf[i+4]) { + printf("ERROR: erase OOB block to " + "write this value\n"); + goto fail; + } + } + memcpy(buf+4, &addr, 4); + + printf("%02x %02x %02x %02x - %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + ret = mtd->write_oob(mtd, 8, size, (size_t *) &size, (u_char *) buf); + if (!ret) + CFG_ENV_OFFSET = addr; + } else + goto usage; + + free(buf); + return ret; + +usage: + printf("Usage:\n%s\n", cmdtp->usage); +fail: + free(buf); + return 1; +} + +U_BOOT_CMD(dynenv, 3, 1, do_dynenv, + "dynenv - dynamically placed (NAND) environment\n", + "dynenv set off - set enviromnent offset\n" + "dynenv get - get environment offset\n"); + +#endif /* CFG_ENV_OFFSET_OOB */ Index: u-boot/common/env_nand.c =================================================================== --- u-boot.orig/common/env_nand.c +++ u-boot/common/env_nand.c @@ -292,6 +292,33 @@ int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1, *tmp_env2;
+#if defined(CFG_ENV_OFFSET_OOB) + struct mtd_info *mtd = &nand_info[0]; + struct nand_chip *this = mtd->priv; + int buf_len; + uint8_t *buf; + + buf_len = (1 << this->bbt_erase_shift); + buf_len += (buf_len >> this->page_shift) * mtd->oobsize; + buf = malloc(buf_len); + if (!buf) + return; + + nand_read_raw(mtd, buf, 0, mtd->oobblock, mtd->oobsize); + if (buf[mtd->oobblock + 8 + 0] == 'E' && + buf[mtd->oobblock + 8 + 1] == 'N' && + buf[mtd->oobblock + 8 + 2] == 'V' && + buf[mtd->oobblock + 8 + 3] == '0') { + CFG_ENV_OFFSET = *((unsigned long *) &buf[mtd->oobblock + 8 + 4]); + /* fall through to the normal environment reading code below */ + free(buf); + puts("Found Environment offset in OOB..\n"); + } else { + free(buf); + return use_default(); + } +#endif + total = CFG_ENV_SIZE;
tmp_env1 = (env_t *) malloc(CFG_ENV_SIZE); Index: u-boot/common/environment.c =================================================================== --- u-boot.orig/common/environment.c +++ u-boot/common/environment.c @@ -29,6 +29,12 @@ #undef __ASSEMBLY__ #include <environment.h>
+#if defined(CFG_ENV_OFFSET_PATCHED) +unsigned long env_offset = CFG_ENV_OFFSET_PATCHED; +#elif defined(CFG_ENV_OFFSET_OOB) +unsigned long env_offset = CFG_ENV_OFFSET_OOB; +#endif + /* * Handle HOSTS that have prepended * crap on symbol names, not TARGETS. Index: u-boot/include/environment.h =================================================================== --- u-boot.orig/include/environment.h +++ u-boot/include/environment.h @@ -70,6 +70,10 @@ #endif /* CFG_ENV_IS_IN_FLASH */
#if defined(CFG_ENV_IS_IN_NAND) +#if defined(CFG_ENV_OFFSET_PATCHED) || defined(CFG_ENV_OFFSET_OOB) +extern unsigned long env_offset; +#define CFG_ENV_OFFSET env_offset +#else # ifndef CFG_ENV_OFFSET # error "Need to define CFG_ENV_OFFSET when using CFG_ENV_IS_IN_NAND" # endif @@ -82,6 +86,7 @@ # ifdef CFG_ENV_IS_EMBEDDED # define ENV_IS_EMBEDDED 1 # endif +#endif /* CFG_ENV_NAND_PATCHED */ #endif /* CFG_ENV_IS_IN_NAND */
#ifdef USE_HOSTCC Index: u-boot/common/cmd_nand.h =================================================================== --- /dev/null +++ u-boot/common/cmd_nand.h @@ -0,0 +1,33 @@ +/* + * cmd_nand.h - Convenience functions + * + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Werner Almesberger werner@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef CMD_NAND_H +#define CMD_NAND_H + +#include <nand.h> + + +/* common/cmd_nand.c */ +int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, + ulong *size); + +#endif /* CMD_NAND_H */ Index: u-boot/common/cmd_nand.c =================================================================== --- u-boot.orig/common/cmd_nand.c +++ u-boot/common/cmd_nand.c @@ -90,7 +90,7 @@ return (*p != '\0' && *endptr == '\0') ? 1 : 0; }
-static int +int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) { int idx = nand_curr_device;

On Mon, Jul 07, 2008 at 12:28:12AM +0800, Harald Welte wrote:
I've first sent this on Feb 17, 2007. Unfortunately no reply was received. I think this is a quite useful feature, since a compile time offset into the NAND flash for the environment just doesn't work well with bad blocks ;)
It works if you allow room for bad blocks within each partition, and treat the environment as its own partition. Current u-boot supports skipping bad blocks within a desginated environment region.
-Scott

On Mon, Jul 07, 2008 at 01:47:24PM -0500, Scott Wood wrote:
On Mon, Jul 07, 2008 at 12:28:12AM +0800, Harald Welte wrote:
I've first sent this on Feb 17, 2007. Unfortunately no reply was received. I think this is a quite useful feature, since a compile time offset into the NAND flash for the environment just doesn't work well with bad blocks ;)
It works if you allow room for bad blocks within each partition, and treat the environment as its own partition. Current u-boot supports skipping bad blocks within a desginated environment region.
which wastes a lot of space, if you have something like a 128kByte erase-block-size (like most 2kByte page size NAND's today)... so if you want to have redundancy and use some spare blocks you will end up with something on the order of 512kByte of wasted flash space to store a couple of hundreds of bytes environment. Not very elegant.
Furthermore, if you want to make sure it always works with any of your components that are within the spec of the manufacturer, then you will waste even more. The problem is that a new virgin component e.g. a 64MByte flash from Samsung can have already as many as 1.3MBytes of bad blocks. There is no guarantee that they are all within one of your partitions. So to only cope with factory bad blocks, you would have to have 1.3MBytes of spare space in each of your partitions! Don't you think that's a bit too much waste of space? (you can read the full rationale at http://wiki.openmoko.org/wiki/NAND_bad_blocks)
So I'd say that having the address of the environment block stored in the out-of-band area of the first block (which is always guaranteed to be good by the manufacturer) sounds like a much more elegant solution.
Therefore, I still believe that such a feature is useful and should be merged into u-boot. If there are problems with my particular implementation, I'm happy to address them.
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
Cheers,

Harald Welte wrote:
On Mon, Jul 07, 2008 at 01:47:24PM -0500, Scott Wood wrote:
It works if you allow room for bad blocks within each partition, and treat the environment as its own partition. Current u-boot supports skipping bad blocks within a desginated environment region.
which wastes a lot of space, if you have something like a 128kByte erase-block-size (like most 2kByte page size NAND's today)... so if you want to have redundancy and use some spare blocks you will end up with something on the order of 512kByte of wasted flash space to store a couple of hundreds of bytes environment. Not very elegant.
Furthermore, if you want to make sure it always works with any of your components that are within the spec of the manufacturer, then you will waste even more. The problem is that a new virgin component e.g. a 64MByte flash from Samsung can have already as many as 1.3MBytes of bad blocks.
Fair enough...
Therefore, I still believe that such a feature is useful and should be merged into u-boot. If there are problems with my particular implementation, I'm happy to address them.
Can you base it off of the testing branch of the u-boot-nand-flash repo?
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
Interesting... Would such a patch eliminate the need for this one, by making the environment a dynamic partition? Is there any (plan for) Linux support?
-Scott

In message 48739036.2080806@freescale.com you wrote:
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
Interesting... Would such a patch eliminate the need for this one, by making the environment a dynamic partition? Is there any (plan for) Linux support?
What's the relation of this to the mtdparts handling in U-Boot and Linux?
Best regards,
Wolfgang Denk

On Tue, Jul 08, 2008 at 11:12:31PM +0200, Wolfgang Denk wrote:
In message 48739036.2080806@freescale.com you wrote:
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
Interesting... Would such a patch eliminate the need for this one, by making the environment a dynamic partition? Is there any (plan for) Linux support?
What's the relation of this to the mtdparts handling in U-Boot and Linux?
see my other mail in this thread (just posted).
More precisely, the flow of events in a full dynenv + dynpart setup (like the three openmoko devices so far) is:
1) u-boot creates a bad block table as part of the production process of the device 2) afterwards, the device-unique partition table is created (net partition sizes as per board-level spec), including an environment partition. 3) the standard regular partition table is stored in the environment 4) the environment including the mtdparts variable is saved to the env partition 5) the nand flash offset to the environment partition is stored in the out-of-band area of the first (known-always-good) nand flash block
During boot, the regular mechanism of passing 'mtdparts' to the kernel is used.
This was the most elegant scheme that I could come up with to support a large number of factory-bad blocks at any given location in the flash while still keeping the amount of overhead per partition low.
Cheers,

In message 20080709002357.GS25698@prithivi.gnumonks.org you wrote:
More precisely, the flow of events in a full dynenv + dynpart setup (like the three openmoko devices so far) is:
- u-boot creates a bad block table as part of the production process of the device
- afterwards, the device-unique partition table is created (net partition sizes as per board-level spec), including an environment partition.
How do you "create" the partition table? Do you use the "mtdparts" command for this?
- the standard regular partition table is stored in the environment
How do you store it? In the same way as "mtdparts" is working?
Best regards,
Wolfgang Denk

On Wed, Jul 09, 2008 at 09:05:24AM +0200, Wolfgang Denk wrote:
In message 20080709002357.GS25698@prithivi.gnumonks.org you wrote:
More precisely, the flow of events in a full dynenv + dynpart setup (like the three openmoko devices so far) is:
- u-boot creates a bad block table as part of the production process of the device
- afterwards, the device-unique partition table is created (net partition sizes as per board-level spec), including an environment partition.
How do you "create" the partition table? Do you use the "mtdparts" command for this?
There is a new 'dynpart' command, which when executed uses the compile-time board-level net partiton sizes, combined with the bad-block-table to generate the device-specific 'dynamic' partition table. The result is stored in the mtdparts environment variable. Everything else is standard u-boot/kernel behaviour.
Please see the attached patch (just for reference, not inclusion yet) for the details of the implementation. This code was used successfully in the production/flashing of a couple of thousand devices, using the Samsung NAND flash chips I mentioned (up to 1.3MByte of bad blocks within 64MByte of total flash). We have seen quite a number of different device-specific partition tables, and everything has worked fine so far.
Just like with the 'dynenv' patch, I think it is something quite useful (if not neccessarry) for the economic large-scale production of NAND-flash only devices. We can always talk about the implementation details, and I'm willing to address any feedback regarding it.
- the standard regular partition table is stored in the environment
How do you store it? In the same way as "mtdparts" is working?
yes.

In message 20080709072510.GY25698@prithivi.gnumonks.org you wrote:
How do you "create" the partition table? Do you use the "mtdparts" command for this?
There is a new 'dynpart' command, which when executed uses the compile-time board-level net partiton sizes, combined with the bad-block-table to generate the device-specific 'dynamic' partition table. The result is stored in the mtdparts environment variable. Everything else is standard u-boot/kernel behaviour.
Maybe the new command should be implemented as subcommand to the "mtdparts" command so we have just one interface to manipulate the "mtdparts" variable.
- the standard regular partition table is stored in the environment
How do you store it? In the same way as "mtdparts" is working?
yes.
Thanks.
Best regards,
Wolfgang Denk

Hi Wolfgang,
thanks again for your quick response. This is really encouraging me to continue merging/submitting at the same pace ;)
On Wed, Jul 09, 2008 at 10:04:47AM +0200, Wolfgang Denk wrote:
In message 20080709072510.GY25698@prithivi.gnumonks.org you wrote:
How do you "create" the partition table? Do you use the "mtdparts" command for this?
There is a new 'dynpart' command, which when executed uses the compile-time board-level net partiton sizes, combined with the bad-block-table to generate the device-specific 'dynamic' partition table. The result is stored in the mtdparts environment variable. Everything else is standard u-boot/kernel behaviour.
Maybe the new command should be implemented as subcommand to the "mtdparts" command so we have just one interface to manipulate the "mtdparts" variable.
ok, no problem with that, I will change this before re-sending the patch (against u-boot-nand-flash/testing).
Cheers,

This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.e' commands) will produce a partition table that matches what was put their by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot as the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
---
NOTE: I'm not sure what to call the new subcommands, I chose 'spread' because of the way it changes the existing mtdparts variable; however, I am open to suggestions for a different name. I chose add.e/add.i because of the behaviour of the write.e/write.i commands; however, I am once again open to suggestion.
Testing was performed in the u-boot-omap-l1 tree [3]. Here is an example u-boot console session to demonstrate how the commands work:
---
U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts default U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.e nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.i nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b)
---
Here are the MAKEALL results:
./MAKEALL arm on master --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
./MAKEALL arm with this patch series applied --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks [3] git://arago-project.org/git/people/sekhar/u-boot-omapl1.git

Dear Ben Gardiner,
In message 1275423827-5803-1-git-send-email-bengardiner@nanometrics.ca you wrote:
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
Has this been tested on NOR flash devices, too?
Best regards,
Wolfgang Denk

Dear Ben Gardiner,
In message 1275423827-5803-1-git-send-email-bengardiner@nanometrics.ca you wrote:
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
Can we have a configuration that is neutral to the memory footprint for such boards that don't want to use any of the new features? It seems the patches always add ~ 200 bytes to the code size (on PowerPC).
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
On Tue, Jun 1, 2010 at 6:20 PM, Wolfgang Denk wd@denx.de wrote:
Has this been tested on NOR flash devices, too?
No, I'm sorry it hasn't -- but I'm glad you asked. On a closer look I think the changes in this patch series could result in the dereference of a null pointer when using a NOR flash device. The features are centered around calls to mtd->block_isbad() and I don't think this function pointer is set for NOR flash devices. I will add 'if(mtd->block_isbad())' checks to the code to prevent the potential dereference of a null pointer and resubmit.
On Tue, Jun 1, 2010 at 6:34 PM, Wolfgang Denk wd@denx.de wrote:
Can we have a configuration that is neutral to the memory footprint for such boards that don't want to use any of the new features? It seems the patches always add ~ 200 bytes to the code size (on PowerPC).
Yes, absolutely. I think that it is patch 2/4 which does not have the surrounding ifdefs of the other patches. I will make the changes in that patch conditional on a CONFIG_ define and resubmit shortly.
I was supposed to double check the image sizes before and after the patch -- as is directed in your wiki instructions, sorry for the omission. I will include a summary of the size differences in powerpc images with the resubmission of this patch series -- along with a './MAKEALL powerpc' output -- also as directed in your wiki instructions.
Best Regards,
Ben Gardiner
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.e' commands) will produce a partition table that matches what was put there by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot being the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
---
Changes in v2: * formating: spaces after 'if' and for * printing net partition sizes feature is now conditional on the new CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES macro; patch 2/4 was adding 264 bytes to the virtlab2 build -- now it adds 0 bytes -- see below for more binary size impact details * changed the net_part_size method to return the net size instead of using an output variable * checking mtd->block_isbad function pointer before dereferencing * there were some trailing whitespace errors when applying 3/4 and 4/4 that I have fixed now
NOTE: I'm not sure what to call the new subcommands, I chose 'spread' because of the way it changes the existing mtdparts variable; however, I am open to suggestions for a different name. I chose add.e/add.i because of the behaviour of the write.e/write.i commands; however, I am again open to suggestions.
Testing was performed in the u-boot-omap-l1 tree [3]. Here is an example u-boot console session to demonstrate how the commands work:
------------------------------------------------------------------------------- U-Boot > mtdparts default U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07020000 0x07000000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07ae0000 0 6: initrd_b 0x00400000 0x00400000 0x07ee0000 0 7: rootfs_b 0x07020000 0x07000000 (!) 0x082e0000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07040000 0x07020000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07b00000 0 6: initrd_b 0x00400000 0x00400000 0x07f00000 0 7: rootfs_b 0x07040000 0x07020000 (!) 0x08300000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.e nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.i nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) -------------------------------------------------------------------------------
Here are the MAKEALL results:
./MAKEALL arm on master --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
./MAKEALL arm with this patch series applied --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
./MAKEALL powerpc with this patch series applied --------------------- SUMMARY ---------------------------- Boards compiled: 354 Boards with warnings or errors: 15 ( MVBC_P BMW caddy2 MPC8536DS MPC8536DS_NAND MPC8536DS_SDCARD MPC8536DS_SPIFLASH MPC8544DS P2020DS P2020DS_36BIT PM854 AP1000 EVB64260 P3G4 ZUMA ) ----------------------------------------------------------
The binary size impact was checked using the da830evm config + NAND enabled for arm and the virtlab2 config for ppc. The entire patch series saves 64 bytes on arm and 4 bytes on ppc -- due to the regrouping of calls in 1/4. With all the config options introduced in this series enabled the patch series adds 1096 bytes on arm and 260 bytes on ppc.
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks [3] git://arago-project.org/git/people/sekhar/u-boot-omapl1.git

[PATCH v3 1/4] mtdparts: regroup calls to get_mtd_device_nm [PATCH v3 2/4] mtdparts: show net size in mtdparts list [PATCH v3 3/4] mtdparts: add new sub-command "spread" [PATCH v3 4/4] mtdparts: new add.e: add part skipping bad blocks
cmd_mtdparts.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 237 insertions(+), 19 deletions(-)
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.e' commands) will produce a partition table that matches what was put there by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot being the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de
---
V2: * formating: spaces after 'if' and for * printing net partition sizes feature is now conditional on the new CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES macro; patch 2/4 was adding 264 bytes to the virtlab2 build -- now it adds 0 bytes -- see below for more binary size impact details * changed the net_part_size method to return the net size instead of using an output variable * checking mtd->block_isbad function pointer before dereferencing * there were some trailing whitespace errors when applying 3/4 and 4/4 that I have fixed now
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * more checkpatch fixes * adding openmoko to the copyright statements in cmd_mtdparts.c
NOTE: I'm not sure what to call the new subcommands, I chose 'spread' because of the way it changes the existing mtdparts variable; however, I am open to suggestions for a different name. I chose add.e/add.i because of the behaviour of the write.e/write.i commands; however, I am again open to suggestions.
Testing was performed in the u-boot-omap-l1 tree [3]. Here is an example u-boot console session to demonstrate how the commands work:
------------------------------------------------------------------------------- U-Boot > mtdparts default U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07020000 0x07000000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07ae0000 0 6: initrd_b 0x00400000 0x00400000 0x07ee0000 0 7: rootfs_b 0x07020000 0x07000000 (!) 0x082e0000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07040000 0x07020000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07b00000 0 6: initrd_b 0x00400000 0x00400000 0x07f00000 0 7: rootfs_b 0x07040000 0x07020000 (!) 0x08300000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.e nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.i nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) -------------------------------------------------------------------------------
I verified the patches with checkpatch. The checkpatch.pl output follows:
total: 0 errors, 0 warnings, 71 lines checked
0001-PATCH-v3-1-4-mtdparts-regroup-calls-to-get_mtd_devic.patch has no obvious style problems and is ready for submission. total: 0 errors, 0 warnings, 85 lines checked
0002-PATCH-v3-2-4-mtdparts-show-net-size-in-mtdparts-list.patch has no obvious style problems and is ready for submission. total: 0 errors, 0 warnings, 148 lines checked
0003-PATCH-v3-3-4-mtdparts-add-new-sub-command-spread.patch has no obvious style problems and is ready for submission. total: 0 errors, 0 warnings, 69 lines checked
0004-PATCH-v3-4-4-mtdparts-new-add.e-add-part-skipping-ba.patch has no obvious style problems and is ready for submission.
I tested the binary size and compiler warnings on ARM9 and 8xx with the following commands:
#checkout u-boot/master, apply changes to da850evm_config for testing, #commit ./MAKEALL ARM9 2>&1 > ../makeall-master.log ./MAKEALL 8xx 2>&1 > ../makeall-master.log #apply patches, commit ./MAKEALL ARM9 2>&1 > ../makeall-spread.log ./MAKEALL 8xx 2>&1 > ../makeall-8xx-spread.log diff -burp ../makeall-8xx-master.log ../makeall-8xx-spread.log diff -burp ../makeall-master.log ../makeall-spread.log
The diff output of the 8xx build shows all binaries text sections decreased in size -- except for kmsupx4 and mgsuvd which appear larger but are the same size when built manually: @@ -45,7 +45,7 @@ Configuring for FLAGADM board... 121164 9096 14388 144648 23508 ./u-boot Configuring for FPS850L board... text data bss dec hex filename - 233060 12636 25472 271168 42340 ./u-boot + 233036 12632 25472 271140 42324 ./u-boot Configuring for GEN860T board... text data bss dec hex filename 207072 32432 18752 258256 3f0d0 ./u-boot @@ -102,7 +102,7 @@ Configuring for lwmon board... 214504 34208 218388 467100 7209c ./u-boot Configuring for kmsupx4 board... text data bss dec hex filename - 274920 19924 28748 323592 4f008 ./u-boot + 274936 19920 28748 323604 4f014 ./u-boot Configuring for MBX board... text data bss dec hex filename 104736 7388 13016 125140 1e8d4 ./u-boot @@ -111,7 +111,7 @@ Configuring for MBX860T board... 92380 6424 13172 111976 1b568 ./u-boot Configuring for mgsuvd board... text data bss dec hex filename - 277012 20048 28752 325812 4f8b4 ./u-boot + 277028 20044 28752 325824 4f8c0 ./u-boot Configuring for MHPC board... text data bss dec hex filename 150944 21984 16776 189704 2e508 ./u-boot @@ -201,19 +201,19 @@ Configuring for TOP860 board... 160456 27360 17336 205152 32160 ./u-boot Configuring for TQM823L board... text data bss dec hex filename - 256664 13888 25700 296252 4853c ./u-boot + 256640 13884 25700 296224 48520 ./u-boot Configuring for TQM823L_LCD board... text data bss dec hex filename - 265408 29636 25732 320776 4e508 ./u-boot + 265384 29632 25732 320748 4e4ec ./u-boot Configuring for TQM850L board... text data bss dec hex filename - 256544 13868 25700 296112 484b0 ./u-boot + 256520 13864 25700 296084 48494 ./u-boot Configuring for TQM855L board... text data bss dec hex filename - 258544 13964 27236 299744 492e0 ./u-boot + 258520 13960 27236 299716 492c4 ./u-boot Configuring for TQM860L board... text data bss dec hex filename - 258592 13972 27236 299800 49318 ./u-boot + 258568 13968 27236 299772 492fc ./u-boot Configuring for TQM885D board... text data bss dec hex filename 209964 14680 26052 250696 3d348 ./u-boot
The diff output of the ARM9 build shows a decrease in text size for all but the da850evm config in which the feature introduced by this patch series were enabled: @@ -48,7 +48,7 @@ Configuring for da830evm board... 147617 4888 295320 447825 6d551 ./u-boot Configuring for da850evm board... text data bss dec hex filename - 198497 10332 296608 505437 7b65d ./u-boot + 199590 10332 296608 506530 7baa2 ./u-boot Configuring for edb9301 board... text data bss dec hex filename 133899 3772 213400 351071 55b5f ./u-boot @@ -83,13 +83,13 @@ kirkwood_egiga.c:620: warning: dereferen 166164 8214 260736 435114 6a3aa ./u-boot Configuring for imx27lite board... text data bss dec hex filename - 195862 9876 236052 441790 6bdbe ./u-boot + 195784 9876 236052 441712 6bd70 ./u-boot Configuring for lpd7a400 board... text data bss dec hex filename 96349 3368 14496 114213 1be25 ./u-boot Configuring for magnesium board... text data bss dec hex filename - 195897 9892 236052 441841 6bdf1 ./u-boot + 195819 9892 236052 441763 6bda3 ./u-boot Configuring for mv88f6281gtw_ge board... kirkwood_egiga.c: In function 'kwgbe_recv': kirkwood_egiga.c:620: warning: dereferencing type-punned pointer will break strict-aliasing rules @@ -100,10 +100,10 @@ Configuring for mx1ads board... 105339 3740 14580 123659 1e30b ./u-boot Configuring for mx1fs2 board... text data bss dec hex filename - 97987 3240 4124 105351 19b87 ./u-boot + 97921 3240 4124 105285 19b45 ./u-boot Configuring for netstar board... text data bss dec hex filename - 173323 8060 14688 196071 2fde7 ./u-boot + 173269 8060 14688 196017 2fdb1 ./u-boot Configuring for nhk8815 board... text data bss dec hex filename 175012 10436 37144 222592 36580 ./u-boot @@ -129,7 +129,7 @@ Configuring for openrd_base board... kirkwood_egiga.c: In function 'kwgbe_recv': kirkwood_egiga.c:620: warning: dereferencing type-punned pointer will break strict-aliasing rules text data bss dec hex filename - 302083 9002 286656 597741 91eed ./u-boot + 302055 9002 286656 597713 91ed1 ./u-boot Configuring for rd6281a board... kirkwood_egiga.c: In function 'kwgbe_recv': kirkwood_egiga.c:620: warning: dereferencing type-punned pointer will break strict-aliasing rules @@ -145,7 +145,7 @@ Configuring for sheevaplug board... kirkwood_egiga.c: In function 'kwgbe_recv': kirkwood_egiga.c:620: warning: dereferencing type-punned pointer will break strict-aliasing rules text data bss dec hex filename - 319712 8990 286784 615486 9643e ./u-boot + 319684 8990 286784 615458 96422 ./u-boot Configuring for smdk2400 board... text data bss dec hex filename 97632 3440 14500 115572 1c374 ./u-boot @@ -168,10 +168,10 @@ Configuring for suen3 board... kirkwood_egiga.c: In function 'kwgbe_recv': kirkwood_egiga.c:620: warning: dereferencing type-punned pointer will break strict-aliasing rules text data bss dec hex filename - 244086 12716 25128 281930 44d4a ./u-boot + 244054 12716 25128 281898 44d2a ./u-boot Configuring for trab board... text data bss dec hex filename - 217847 14027 424192 656066 a02c2 ./u-boot + 217781 14027 424192 656000 a0280 ./u-boot Configuring for VCMA9 board... text data bss dec hex filename 177726 7504 261900 447130 6d29a ./u-boot @@ -189,7 +189,7 @@ Variant:: PB926EJ-S 85977 2920 12132 101029 18aa5 ./u-boot Configuring for voiceblue board... text data bss dec hex filename - 143684 4708 18916 167308 28d8c ./u-boot + 143614 4708 18916 167238 28d46 ./u-boot Configuring for davinci_dvevm board... text data bss dec hex filename 178646 5500 297984 482130 75b52 ./u-boot @@ -204,13 +204,13 @@ Configuring for davinci_sonata board... 145390 5296 55068 205754 323ba ./u-boot Configuring for davinci_dm355evm board... text data bss dec hex filename - 207288 8516 40864 256668 3ea9c ./u-boot + 207252 8516 40864 256632 3ea78 ./u-boot Configuring for davinci_dm355leopard board... text data bss dec hex filename - 206398 7904 40864 255166 3e4be ./u-boot + 206362 7904 40864 255130 3e49a ./u-boot Configuring for davinci_dm365evm board... text data bss dec hex filename - 243459 8704 297752 549915 8641b ./u-boot + 243423 8704 297752 549879 863f7 ./u-boot Configuring for davinci_dm6467evm board... text data bss dec hex filename 91806 4776 26100 122682 1df3a ./u-boot
The binary size impact was checked in a couple specific cases also: using the da830evm config + NAND enabled for arm and the virtlab2 config for ppc. In these cases the entire patch series saves 64 bytes on arm and 4 bytes on ppc -- due to the regrouping of calls in 1/4. With all the config options introduced in this series enabled the patch series adds 1096 bytes on arm and 260 bytes on ppc.
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks [3] git://arago-project.org/git/people/sekhar/u-boot-omapl1.git

Hi Wolfgang,
I rebased the mtdparts bad block skipping patches and reposted a v3 series [1] in the hopes that it would be considered for inclusion into v2010.09 . I hope that I have addressed the concerns you raised in your reviews [2] [3].
I said this earlier to Scott Wood: I am very much a novice here so I hope it is not rude of me to ask: is there any chance of having the "[PATCH v3 0/4] mtdparts: add bad-block skipping" [1] series included in v2010.09? Are there any concerns with respect to the functionality that this patch provides? Is there anything else that I can provide to help with getting this patch included?
Best Regards, Ben Gardiner
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/80843 [2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79418 [3] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79419
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

[PATCH v4 1/4] mtdparts: regroup calls to get_mtd_device_nm [PATCH v4 2/4] mtdparts: show net size in mtdparts list [PATCH v4 3/4] mtdparts: add new sub-command "spread" [PATCH v4 4/4] mtdparts: new add.spread: add part skipping bad blocks
common/cmd_mtdparts.c | 268 +++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 246 insertions(+), 22 deletions(-)
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.spread' commands) will produce a partition table that matches what was put there by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot being the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks
---
V2: * formating: spaces after 'if' and for * printing net partition sizes feature is now conditional on the new CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES macro; patch 2/4 was adding 264 bytes to the virtlab2 build -- now it adds 0 bytes -- see below for more binary size impact details * changed the net_part_size method to return the net size instead of using an output variable * checking mtd->block_isbad function pointer before dereferencing * there were some trailing whitespace errors when applying 3/4 and 4/4 that I have fixed now
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * more checkpatch fixes * adding openmoko to the copyright statements in cmd_mtdparts.c
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * re-grouped list_partition #ifdefs into one * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion.
Testing was performed with da850evm.h plus NAND enabled. Here is an example u-boot console session to demonstrate how the commands work:
------------------------------------------------------------------------------- U-Boot > mtdparts default U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07020000 0x07000000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07ae0000 0 6: initrd_b 0x00400000 0x00400000 0x07ee0000 0 7: rootfs_b 0x07020000 0x07000000 (!) 0x082e0000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07040000 0x07020000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07b00000 0 6: initrd_b 0x00400000 0x00400000 0x07f00000 0 7: rootfs_b 0x07040000 0x07020000 (!) 0x08300000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) -------------------------------------------------------------------------------
I verified the patches with checkpatch.
I tested the binary size and compiler warnings on ARM9 and 8xx. The patch series saves 24 bytes of text on 8xx and 54 bytes of text on arm9 -- when none of the config options are enabled. When the config options are enabled, text grows by 1023 bytes on arm9.

Ben Gardiner (5): mtdparts: regroup calls to get_mtd_device_nm mtd: add an mtd method for get_len_incl_bad() mtdparts: show net size in mtdparts list mtdparts: add new sub-command "spread" mtdparts: new add.spread: add part skipping bad blocks
common/cmd_mtdparts.c | 263 ++++++++++++++++++++++++++++++++++++----- drivers/mtd/mtdcore.c | 45 +++++++ drivers/mtd/nand/nand_util.c | 6 + include/linux/mtd/mtd.h | 4 +- 4 files changed, 285 insertions(+), 33 deletions(-)
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.spread' commands) will produce a partition table that matches what was put there by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot being the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks
---
V2: * formating: spaces after 'if' and for * printing net partition sizes feature is now conditional on the new CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES macro; patch 2/4 was adding 264 bytes to the virtlab2 build -- now it adds 0 bytes -- see below for more binary size impact details * changed the net_part_size method to return the net size instead of using an output variable * checking mtd->block_isbad function pointer before dereferencing * there were some trailing whitespace errors when applying 3/4 and 4/4 that I have fixed now
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * more checkpatch fixes * adding openmoko to the copyright statements in cmd_mtdparts.c
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * re-grouped list_partition #ifdefs into one * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion.
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered patches; V5 introduces patch 2/5 * return uint64_t instead of u32 for net_size * do a quick if((cond) return * calculate net_size by adding-up good blocks instead of subtracting bad blocks * try to strike a balance; reuse more code between the branches of #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) in print_partition_table * don't push dangling lines to the right * fix spelling errors in comments * more hanging arguments alignment * refactor the spread_partition function so that it delegates the bad-block counting to the mtd_get_len_incl_bad function -- as per Scott Woods' suggestion. * don't reproduce the same call to spread_partitions on either side of the check for existing device -- as per Scott Wood's comments.
Testing was performed with da850evm.h plus NAND enabled. Here is an example u-boot console session to demonstrate how the commands work:
------------------------------------------------------------------------------- U-Boot > mtdparts default U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07020000 0x07000000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07ae0000 0 6: initrd_b 0x00400000 0x00400000 0x07ee0000 0 7: rootfs_b 0x07020000 0x07000000 (!) 0x082e0000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07040000 0x07020000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07b00000 0 6: initrd_b 0x00400000 0x00400000 0x07f00000 0 7: rootfs_b 0x07040000 0x07020000 (!) 0x08300000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) -------------------------------------------------------------------------------
I verified the patches with checkpatch.
I confirmed that MAKEALL for 'ARM9' and '8xx' did not introduce any additional warnings.
I tested the binary size and compiler warnings on ARM9 and 8xx. The patch series saves 24 bytes of text on 8xx and 74 bytes of text on arm9 -- when none of the config options are enabled. When the config options are enabled, text grows by 1321 bytes on arm9.

The logic to 'spread' mtd partitions needs to calculate the length in the mtd device, including bad blocks.
This patch introduces a new function, mtd_get_len_incl_bad that can return both the length including bad blocks and whether that length was truncated on the device. This new function will be used by the mtdparts spread command later in this series. The definition of the function is #ifdef'd out in configurations that do not use the new 'mtdparts spread' command.
Signed-off-by: Ben Gardinerbengardiner@nanometrics.ca CC: Scott Wood scottwood@freescale.com
---
Note: the mtd_get_len_incl_bad() function could also be used by the get_len_incl_bad() function in nand_util.c except for the fact that boards can enable NAND support without enabling MTD support. A note has been added to get_len_incl_bad() to remind us to refactor when/if MTD support is available whenever NAND support is enabled.
V5: * introduced in v5 of this patchset --- drivers/mtd/mtdcore.c | 44 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_util.c | 6 +++++ include/linux/mtd/mtd.h | 4 ++- 3 files changed, 53 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6eb52ed..cb86657 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -142,3 +142,47 @@ void put_mtd_device(struct mtd_info *mtd) c = --mtd->usecount; BUG_ON(c < 0); } + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * mtd_get_len_incl_bad + * + * Check if length including bad blocks fits into device. + * + * @param mtd an MTD device + * @param offset offset in flash + * @param length image length + * @return image length including bad blocks in *len_incl_bad and whether or not + * the length returned was truncated in *truncated + */ +void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, + const uint64_t length, uint64_t *len_incl_bad, + int *truncated) +{ + *truncated = 0; + *len_incl_bad = 0; + + if (!mtd->block_isbad) { + *len_incl_bad = length; + return; + } + + uint64_t len_excl_bad = 0; + uint64_t block_len; + + while (len_excl_bad < length) { + block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); + + if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) + len_excl_bad += block_len; + + *len_incl_bad += block_len; + offset += block_len; + + if (offset >= mtd->size) { + *truncated = 1; + break; + } + } +} +#endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 29c42f7..622237f 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -435,6 +435,12 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset, const size_t length) { + /* + * TODO: replace this implementation with a call to + * mtd_get_len_incl_bad((struct mtd_info *) nand, ...) + * when CONFIG_MTD_DEVICE is required for NAND support + */ + size_t len_incl_bad = 0; size_t len_excl_bad = 0; size_t block_len; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 16556c4..8e8ec7c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -259,7 +259,9 @@ extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); extern struct mtd_info *get_mtd_device_nm(const char *name);
extern void put_mtd_device(struct mtd_info *mtd); - +extern void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, + const uint64_t length, uint64_t *len_incl_bad, + int *truncated); /* XXX U-BOOT XXX */ #if 0 struct mtd_notifier {

On Mon, 30 Aug 2010 13:38:57 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
The logic to 'spread' mtd partitions needs to calculate the length in the mtd device, including bad blocks.
This patch introduces a new function, mtd_get_len_incl_bad that can return both the length including bad blocks and whether that length was truncated on the device. This new function will be used by the mtdparts spread command later in this series. The definition of the function is #ifdef'd out in configurations that do not use the new 'mtdparts spread' command.
Signed-off-by: Ben Gardinerbengardiner@nanometrics.ca CC: Scott Wood scottwood@freescale.com
Note: the mtd_get_len_incl_bad() function could also be used by the get_len_incl_bad() function in nand_util.c except for the fact that boards can enable NAND support without enabling MTD support. A note has been added to get_len_incl_bad() to remind us to refactor when/if MTD support is available whenever NAND support is enabled.
V5:
- introduced in v5 of this patchset
drivers/mtd/mtdcore.c | 44 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_util.c | 6 +++++ include/linux/mtd/mtd.h | 4 ++- 3 files changed, 53 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6eb52ed..cb86657 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -142,3 +142,47 @@ void put_mtd_device(struct mtd_info *mtd) c = --mtd->usecount; BUG_ON(c < 0); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/**
- mtd_get_len_incl_bad
- Check if length including bad blocks fits into device.
- @param mtd an MTD device
- @param offset offset in flash
- @param length image length
- @return image length including bad blocks in *len_incl_bad and whether or not
the length returned was truncated in *truncated
- */
+void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
const uint64_t length, uint64_t *len_incl_bad,
int *truncated)
+{
- *truncated = 0;
- *len_incl_bad = 0;
- if (!mtd->block_isbad) {
*len_incl_bad = length;
return;
- }
- uint64_t len_excl_bad = 0;
- uint64_t block_len;
- while (len_excl_bad < length) {
block_len = mtd->erasesize - (offset & (mtd->erasesize - 1));
if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1)))
len_excl_bad += block_len;
*len_incl_bad += block_len;
offset += block_len;
if (offset >= mtd->size) {
*truncated = 1;
break;
}
- }
If this function is called with offset == mtd->size, you should return length zero and truncated, without calling block_isbad().
-Scott

On Mon, Aug 30, 2010 at 4:57 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, 30 Aug 2010 13:38:57 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
+void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
- const uint64_t length, uint64_t *len_incl_bad,
- int *truncated)
+{
- *truncated = 0;
- *len_incl_bad = 0;
- if (!mtd->block_isbad) {
- *len_incl_bad = length;
- return;
- }
- uint64_t len_excl_bad = 0;
- uint64_t block_len;
- while (len_excl_bad < length) {
- block_len = mtd->erasesize - (offset & (mtd->erasesize - 1));
- if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1)))
- len_excl_bad += block_len;
- *len_incl_bad += block_len;
- offset += block_len;
- if (offset >= mtd->size) {
- *truncated = 1;
- break;
- }
- }
If this function is called with offset == mtd->size, you should return length zero and truncated, without calling block_isbad().
Good point. Will do.
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

The logic to 'spread' mtd partitions needs to calculate the length in the mtd device, including bad blocks.
This patch introduces a new function, mtd_get_len_incl_bad that can return both the length including bad blocks and whether that length was truncated on the device. This new function will be used by the mtdparts spread command later in this series. The definition of the function is #ifdef'd out in configurations that do not use the new 'mtdparts spread' command.
Signed-off-by: Ben Gardinerbengardiner@nanometrics.ca CC: Scott Wood scottwood@freescale.com
---
Note: the mtd_get_len_incl_bad() function could also be used by the get_len_incl_bad() function in nand_util.c except for the fact that boards can enable NAND support without enabling MTD support. A note has been added to get_len_incl_bad() to remind us to refactor when/if MTD support is available whenever NAND support is enabled.
V5: * introduced in v5 of this patchset
V6: * return zero length and truncated if mtd_get_len_incl_bad is called with offset>=size -- as per Scott Wood's review comments
--- drivers/mtd/mtdcore.c | 49 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/nand_util.c | 6 +++++ include/linux/mtd/mtd.h | 4 ++- 3 files changed, 58 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6eb52ed..78f2a08 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -142,3 +142,52 @@ void put_mtd_device(struct mtd_info *mtd) c = --mtd->usecount; BUG_ON(c < 0); } + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * mtd_get_len_incl_bad + * + * Check if length including bad blocks fits into device. + * + * @param mtd an MTD device + * @param offset offset in flash + * @param length image length + * @return image length including bad blocks in *len_incl_bad and whether or not + * the length returned was truncated in *truncated + */ +void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, + const uint64_t length, uint64_t *len_incl_bad, + int *truncated) +{ + *truncated = 0; + *len_incl_bad = 0; + + if (offset >= mtd->size) { + *truncated = 1; + return; + } + + if (!mtd->block_isbad) { + *len_incl_bad = length; + return; + } + + uint64_t len_excl_bad = 0; + uint64_t block_len; + + while (len_excl_bad < length) { + block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); + + if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) + len_excl_bad += block_len; + + *len_incl_bad += block_len; + offset += block_len; + + if (offset >= mtd->size) { + *truncated = 1; + break; + } + } +} +#endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 29c42f7..622237f 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -435,6 +435,12 @@ int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset, const size_t length) { + /* + * TODO: replace this implementation with a call to + * mtd_get_len_incl_bad((struct mtd_info *) nand, ...) + * when CONFIG_MTD_DEVICE is required for NAND support + */ + size_t len_incl_bad = 0; size_t len_excl_bad = 0; size_t block_len; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 16556c4..8e8ec7c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -259,7 +259,9 @@ extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); extern struct mtd_info *get_mtd_device_nm(const char *name);
extern void put_mtd_device(struct mtd_info *mtd); - +extern void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, + const uint64_t length, uint64_t *len_incl_bad, + int *truncated); /* XXX U-BOOT XXX */ #if 0 struct mtd_notifier {

Ben Gardiner (5): mtdparts: regroup calls to get_mtd_device_nm mtd: add an mtd method for get_len_incl_bad() mtdparts: show net size in mtdparts list mtdparts: add new sub-command "spread" mtdparts: new add.spread: add part skipping bad blocks
common/cmd_mtdparts.c | 260 +++++++++++++++++++++++++++++++++++++----- drivers/mtd/mtdcore.c | 49 ++++++++ drivers/mtd/nand/nand_util.c | 6 + include/linux/mtd/mtd.h | 4 +- 4 files changed, 287 insertions(+), 32 deletions(-)
This patch series is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de [1]. I started with Harald's original patch and migrated it to a new mtdparts sub-command and added an interface to the new functionality via a 'mtdparts add' variant.
I tried to keep it at the level of the mtd subsystem. Whereas the dynparts patch was limited to NAND flashes, I believe this patch will work on any mtd device that can report bad blocks.
These new commands can be useful when gang programming NAND chips where the gang programmer is capable only of skipping bad blocks. One can use a master image that contains images of each of the partitions padded-out to their spec'd sizes; when u-boot first comes up 'mtdparts default; mtdparts spread' (or a seq of 'mtdpart add.spread' commands) will produce a partition table that matches what was put there by the gang-programmer.
It can also be useful when doing in-situ programming with u-boot being the flash programmer as demonstrated by the openmoko project's use of the 'dynpart' command [2] upon which this patch series was based.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 [2] http://wiki.openmoko.org/wiki/NAND_bad_blocks
---
V2: * formating: spaces after 'if' and for * printing net partition sizes feature is now conditional on the new CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES macro; patch 2/4 was adding 264 bytes to the virtlab2 build -- now it adds 0 bytes -- see below for more binary size impact details * changed the net_part_size method to return the net size instead of using an output variable * checking mtd->block_isbad function pointer before dereferencing * there were some trailing whitespace errors when applying 3/4 and 4/4 that I have fixed now
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * more checkpatch fixes * adding openmoko to the copyright statements in cmd_mtdparts.c
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * re-grouped list_partition #ifdefs into one * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion.
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered patches; V5 introduces patch 2/5 * return uint64_t instead of u32 for net_size * do a quick if((cond) return * calculate net_size by adding-up good blocks instead of subtracting bad blocks * try to strike a balance; reuse more code between the branches of #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) in print_partition_table * don't push dangling lines to the right * fix spelling errors in comments * more hanging arguments alignment * refactor the spread_partition function so that it delegates the bad-block counting to the mtd_get_len_incl_bad function -- as per Scott Woods' suggestion. * don't reproduce the same call to spread_partitions on either side of the check for existing device -- as per Scott Wood's comments.
V6: * return zero length and truncated if mtd_get_len_incl_bad is called with offset>=size -- as per Scott Wood's review comments * fix indentation on list_entry * don't use mtd_get_len_incl_bad to get net_size anymore as it was a bad idea. Just count up the good blocks. * check truncated return value to handle trying to absorb bad blocks past the end of the device -- as per Scott Wood's review comments
Testing was performed with da850evm.h plus NAND enabled. Here is an example u-boot console session to demonstrate how the commands work:
------------------------------------------------------------------------------- U-Boot > mtdparts default U-Boot > nand bad
Device 0 bad blocks: 062c0000 0a140000 128a0000 12e20000 18bc0000 1ff80000 1ffa0000 1ffc0000 1ffe0000 U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07020000 0x07000000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07ae0000 0 6: initrd_b 0x00400000 0x00400000 0x07ee0000 0 7: rootfs_b 0x07020000 0x07000000 (!) 0x082e0000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdparts spread U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > mtdparts
device nand0 <davinci_nand.1>, # parts = 11 #: name size net size offset mask_flags 0: zero 0x000c0000 0x000c0000 0x00000000 1 1: conf 0x00200000 0x00200000 0x000c0000 0 2: kernel_a 0x00400000 0x00400000 0x002c0000 0 3: initrd_a 0x00400000 0x00400000 0x006c0000 0 4: rootfs_a 0x07040000 0x07020000 (!) 0x00ac0000 0 5: kernel_b 0x00400000 0x00400000 0x07b00000 0 6: initrd_b 0x00400000 0x00400000 0x07f00000 0 7: rootfs_b 0x07040000 0x07020000 (!) 0x08300000 0
active partition: nand0,0 - (zero) 0x00020000 @ 0x00000000
defaults: mtdids : nand0=davinci_nand.1 mtdparts: mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a), 4m(initrd_a),112m(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) U-Boot > U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b)
U-Boot > mtdpart del rootfs_b U-Boot > mtdpart add.spread nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),114816k(rootfs_b) U-Boot > U-Boot > mtdparts del rootfs_b U-Boot > mtdparts add nand0 112m rootfs_b U-Boot > print mtdparts mtdparts=mtdparts=davinci_nand.1:768k(zero)ro,2m(conf),4m(kernel_a),4m(initrd_a) ,114816k(rootfs_a),4m(kernel_b),4m(initrd_b),112m(rootfs_b) -------------------------------------------------------------------------------
I verified the patches with checkpatch.
I confirmed that MAKEALL for 'ARM9' and '8xx' did not introduce any additional warnings.
I tested the binary size and compiler warnings on ARM9 and 8xx. The patch series saves 24 bytes of text on 8xx and 30 bytes of text on arm9 -- when none of the config options are enabled. When the config options are enabled, text grows by 1413 bytes on arm9.

On Tue, Aug 31, 2010 at 05:47:59PM -0400, Ben Gardiner wrote:
Ben Gardiner (5): mtdparts: regroup calls to get_mtd_device_nm mtd: add an mtd method for get_len_incl_bad() mtdparts: show net size in mtdparts list mtdparts: add new sub-command "spread" mtdparts: new add.spread: add part skipping bad blocks
common/cmd_mtdparts.c | 260 +++++++++++++++++++++++++++++++++++++----- drivers/mtd/mtdcore.c | 49 ++++++++ drivers/mtd/nand/nand_util.c | 6 + include/linux/mtd/mtd.h | 4 +- 4 files changed, 287 insertions(+), 32 deletions(-)
Applied to u-boot-nand-flash next
Sent a followup patch for a couple minor issues.
-Scott

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Stefan Roese sr@denx.de CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: add space after 'if' * added acked-by tag as requested by Stefan
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style --- common/cmd_mtdparts.c | 45 ++++++++++++++++++++++++++++++--------------- 1 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index ceec5a9..772ad54 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,29 @@ static void current_save(void) index_partitions(); }
+ +/** + * Produce a mtd_info given a type and num. + * + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +320,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if (get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +702,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if (get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: spaces after 'if' and 'for' * the entire new feature is conditional on a macro, there is now a zero-byte binary size impact when the macro is not defined. * return the net parition size directly from net_part_size instead of using an output variable
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix line length over 80 chars * update copyright of cmd_mtdparts.c
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * removed copyright statement and changelog from file header * re-grouped list_partition #ifdefs into one * fixed multi-line comment style --- common/cmd_mtdparts.c | 70 +++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 65 insertions(+), 5 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 772ad54..500a38e 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1215,18 +1215,65 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) /** - * Format and print out a partition list for each device from global device - * list. + * Get the net size (w/o bad blocks) of the given partition. + * + * @param mtd the mtd info + * @param part the partition + * @return the calculated net size of this partition */ -static void list_partitions(void) +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part) +{ + if (mtd->block_isbad) { + u32 i, bb_delta = 0; + + for (i = 0; i < part->size; i += mtd->erasesize) { + if (mtd->block_isbad(mtd, part->offset + i)) + bb_delta += mtd->erasesize; + } + + return part->size - bb_delta; + } else { + return part->size; + } +} +#endif + +static void print_partition_table(void) { struct list_head *dentry, *pentry; struct part_info *part; struct mtd_device *dev; int part_num;
- debug("\n---list_partitions---\n"); +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + list_for_each(dentry, &devices) { + struct mtd_info *mtd; + + dev = list_entry(dentry, struct mtd_device, link); + 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"); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return; + + /* list partitions for given device */ + part_num = 0; + list_for_each(pentry, &dev->parts) { + u32 net_size; + char *size_note; + + part = list_entry(pentry, struct part_info, link); + net_size = net_part_size(mtd, part); + size_note = part->size == net_size ? " " : " (!)"; + printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n", + part_num, part->name, part->size, + net_size, size_note, part->offset, + part->mask_flags); +#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); printf("\ndevice %s%d <%s>, # parts = %d\n", @@ -1241,12 +1288,25 @@ static void list_partitions(void) printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", part_num, part->name, part->size, part->offset, part->mask_flags); - +#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ part_num++; } } + if (list_empty(&devices)) printf("no partitions defined\n"); +} + +/** + * Format and print out a partition list for each device from global device + * list. + */ +static void list_partitions(void) +{ + struct part_info *part; + + debug("\n---list_partitions---\n"); + print_partition_table();
/* current_mtd_dev is not NULL only when we have non empty device list */ if (current_mtd_dev) {

On Mon, Aug 09, 2010 at 04:43:58PM -0400, Ben Gardiner wrote:
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 772ad54..500a38e 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1215,18 +1215,65 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) /**
- Format and print out a partition list for each device from global device
- list.
- Get the net size (w/o bad blocks) of the given partition.
- @param mtd the mtd info
- @param part the partition
*/
- @return the calculated net size of this partition
-static void list_partitions(void) +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part)
Don't assume partition size fits in 32 bits. part->size is uint64_t.
+{
- if (mtd->block_isbad) {
u32 i, bb_delta = 0;
for (i = 0; i < part->size; i += mtd->erasesize) {
if (mtd->block_isbad(mtd, part->offset + i))
bb_delta += mtd->erasesize;
}
return part->size - bb_delta;
Seems like it'd be slightly simpler to just count up the good blocks, rather than count the bad blocks and subtract.
- } else {
return part->size;
- }
It's usually more readable to do this:
if (can't do this) return;
do this;
than this
if (can do this) do this; else don't;
When "do this" is more than a line or two, and there's nothing else to be done in the function afterward.
+} +#endif
+static void print_partition_table(void) { struct list_head *dentry, *pentry; struct part_info *part; struct mtd_device *dev; int part_num;
- debug("\n---list_partitions---\n");
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- list_for_each(dentry, &devices) {
struct mtd_info *mtd;
dev = list_entry(dentry, struct mtd_device, link);
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");
if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
return;
/* list partitions for given device */
part_num = 0;
list_for_each(pentry, &dev->parts) {
u32 net_size;
char *size_note;
part = list_entry(pentry, struct part_info, link);
net_size = net_part_size(mtd, part);
size_note = part->size == net_size ? " " : " (!)";
printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
part_num, part->name, part->size,
net_size, size_note, part->offset,
part->mask_flags);
+#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); printf("\ndevice %s%d <%s>, # parts = %d\n", @@ -1241,12 +1288,25 @@ static void list_partitions(void) printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", part_num, part->name, part->size, part->offset, part->mask_flags);
+#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
Is there any way you could share more of this between the two branches?
-Scott

Hi Scott,
Thank you for reviewing patches 2-4.
On Thu, Aug 26, 2010 at 2:57 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, Aug 09, 2010 at 04:43:58PM -0400, Ben Gardiner wrote:
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 772ad54..500a38e 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1215,18 +1215,65 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) /**
- Format and print out a partition list for each device from global device
- list.
- Get the net size (w/o bad blocks) of the given partition.
- @param mtd the mtd info
- @param part the partition
- @return the calculated net size of this partition
*/ -static void list_partitions(void) +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part)
Don't assume partition size fits in 32 bits. part->size is uint64_t.
My mistake.
+{
- if (mtd->block_isbad) {
- u32 i, bb_delta = 0;
- for (i = 0; i < part->size; i += mtd->erasesize) {
- if (mtd->block_isbad(mtd, part->offset + i))
- bb_delta += mtd->erasesize;
- }
- return part->size - bb_delta;
Seems like it'd be slightly simpler to just count up the good blocks, rather than count the bad blocks and subtract.
Will do.
- } else {
- return part->size;
- }
It's usually more readable to do this:
if (can't do this) return;
do this;
than this
if (can do this) do this; else don't;
When "do this" is more than a line or two, and there's nothing else to be done in the function afterward.
Right. I think you told me this in the env.oob review also. I'll keep this in mind for future submissions.
+} +#endif
+static void print_partition_table(void) { struct list_head *dentry, *pentry; struct part_info *part; struct mtd_device *dev; int part_num;
- debug("\n---list_partitions---\n");
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- list_for_each(dentry, &devices) {
- struct mtd_info *mtd;
- dev = list_entry(dentry, struct mtd_device, link);
- 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");
- if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
- return;
- /* list partitions for given device */
- part_num = 0;
- list_for_each(pentry, &dev->parts) {
- u32 net_size;
- char *size_note;
- part = list_entry(pentry, struct part_info, link);
- net_size = net_part_size(mtd, part);
- size_note = part->size == net_size ? " " : " (!)";
- printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
- part_num, part->name, part->size,
- net_size, size_note, part->offset,
- part->mask_flags);
+#else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); printf("\ndevice %s%d <%s>, # parts = %d\n", @@ -1241,12 +1288,25 @@ static void list_partitions(void) printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", part_num, part->name, part->size, part->offset, part->mask_flags);
+#endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
Is there any way you could share more of this between the two branches?
I definitely could. :)
I had everything-possible shared between the branches in v3 but I think I took it too far since:
On Sat, Aug 7, 2010 at 4:08 PM, Wolfgang Denk wd@denx.de wrote:
This is way too much #ifdef's here. Please separate the code and use a single #ifdef only.
I'll try my best to strike a balance here in v5.
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

On Thu, Aug 26, 2010 at 2:57 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, Aug 09, 2010 at 04:43:58PM -0400, Ben Gardiner wrote:
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 772ad54..500a38e 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1215,18 +1215,65 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) /**
- Format and print out a partition list for each device from global device
- list.
- Get the net size (w/o bad blocks) of the given partition.
- @param mtd the mtd info
- @param part the partition
- @return the calculated net size of this partition
*/ -static void list_partitions(void) +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part)
Don't assume partition size fits in 32 bits. part->size is uint64_t.
I understand that (at some point) we need to support part->size uint64_t but the HEAD of u-boot [1], u-boot-nand-flash/master [2] and u-boot-nand-flash/next [3] have u32 for part->size. Changing this to uint64_t would require reformatting the list_partitions size column as a 64bit hex number -- as would treating the net_size there as a uint64_t. This would be a change with impact of greater scope than intended with this patchset.
I hope it will be acceptable to do the net_part_size return as uin64_t and continue treating both the part->size and net_size as u32?
Best Regards, Ben Gardiner
[1] http://git.denx.de/?p=u-boot.git;a=blob;f=include/jffs2/load_kernel.h;h=906e... [2] http://git.denx.de/?p=u-boot/u-boot-nand-flash.git;a=blob;f=include/jffs2/lo... [3] http://git.denx.de/?p=u-boot/u-boot-nand-flash.git;a=blob;f=include/jffs2/lo...
--- Nanometrics Inc. http://www.nanometrics.ca

On 08/27/2010 10:44 AM, Ben Gardiner wrote:
On Thu, Aug 26, 2010 at 2:57 PM, Scott Woodscottwood@freescale.com wrote:
Don't assume partition size fits in 32 bits. part->size is uint64_t.
I understand that (at some point) we need to support part->size uint64_t but the HEAD of u-boot [1], u-boot-nand-flash/master [2] and u-boot-nand-flash/next [3] have u32 for part->size.
I see, I was looking at mtd_partition, not part_info. Why are we using jffs2's partition struct for anything other than jffs2? Why does jffs2 have its own partition struct at all?
-Scott

On Fri, Aug 27, 2010 at 12:02 PM, Scott Wood scottwood@freescale.com wrote:
On 08/27/2010 10:44 AM, Ben Gardiner wrote:
On Thu, Aug 26, 2010 at 2:57 PM, Scott Woodscottwood@freescale.com wrote:
Don't assume partition size fits in 32 bits. part->size is uint64_t.
I understand that (at some point) we need to support part->size uint64_t but the HEAD of u-boot [1], u-boot-nand-flash/master [2] and u-boot-nand-flash/next [3] have u32 for part->size.
I see, I was looking at mtd_partition, not part_info. Why are we using jffs2's partition struct for anything other than jffs2? Why does jffs2 have its own partition struct at all?
I don't know why. I can only offer the following information:
Cscope says that include/jffs2/load_kernel.h is the file in which part_info is defined.
'git blame' indicates that part_info has been used in common/cmd_mtdparts.c since 68d7d65100e84df00bca971c114092731b441090 when Stefan Roese sr@denx.de extracted the command from common/cmd_jffs2.c
The users of mtd_partition (add_one_partition and add_mtd_partitions) are called from ubi_dev_scan where the mtd_partition structure is intialized from a part_info structure.
Best Regards, Ben Gardiner
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
This patch is based on a port of the 'dynnamic partitions' feature by Harald Welte laforge@gnumonks.org; ported from commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git. Whereas Harald's feature used a compile-time array to specify partitions, the feature introduced by this patch uses the mtdparts environment variable.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Signed-off-by: Harald Welte laforge@gnumonks.org CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed * check for null mtd->block_isbad before dereferencing
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * update copyright statement of cmd_mtdparts.c to include openmoko's copyright of the 'dynamic partitions' functionality using commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git as reference.
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs --- common/cmd_mtdparts.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 116 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 500a38e..7e2e232 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,6 +15,9 @@ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 * kernel tree. * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte laforge@openmoko.org + * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH * @@ -1430,6 +1433,105 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next parition would start on a + * good blcok if it were adjacent to this partition. + * + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + u32 *next_offset) +{ + if (!mtd->block_isbad) + goto out; + + u32 i, bb_delta = 0; + + for (i = part->offset; i - bb_delta < part->offset + part->size; + i += mtd->erasesize) { + if (mtd->block_isbad(mtd, i)) + bb_delta += mtd->erasesize; + } + + /* + * Absorb bad blocks immeadiately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + while (i < mtd->size && mtd->block_isbad(mtd, i)) { + bb_delta += mtd->erasesize; + i += mtd->erasesize; + } + + if (part->offset + part->size + bb_delta > mtd->size) { + part->size = mtd->size - part->offset - bb_delta; + printf("truncated partition %s to %d bytes\n", part->name, + part->size); + } + + part->size += bb_delta; + +out: + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + u32 cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1920,6 +2022,11 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + return cmd_usage(cmdtp); }
@@ -1943,7 +2050,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

On Mon, Aug 09, 2010 at 04:43:59PM -0400, Ben Gardiner wrote:
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
u32 *next_offset)
As in patch 2, change u32 to uint64_t.
+{
- if (!mtd->block_isbad)
goto out;
- u32 i, bb_delta = 0;
- for (i = part->offset; i - bb_delta < part->offset + part->size;
i += mtd->erasesize) {
if (mtd->block_isbad(mtd, i))
bb_delta += mtd->erasesize;
- }
- /*
* Absorb bad blocks immeadiately following this
* partition also into the partition, such that
* the next partition starts with a good block.
*/
- while (i < mtd->size && mtd->block_isbad(mtd, i)) {
bb_delta += mtd->erasesize;
i += mtd->erasesize;
- }
Could this be refactored with get_len_incl_bad()? It should return both the updated length and a flag indicating whether it was truncated.
debug("spread_partitions: device = %s%d, partition %d ="
" (%s) 0x%08x@0x%08x\n",
MTD_DEV_TYPE(dev->id->type), dev->id->num,
part_num, part->name, part->size,
part->offset);
Why the extra indent on that last line?
IMHO, it's also nicer to line up continuation lines like this:
debug("spread_partitions..." " (%s) ..." MTD_DEV... part_num... part->offset);
-Scott

On Thu, Aug 26, 2010 at 5:12 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, Aug 09, 2010 at 04:43:59PM -0400, Ben Gardiner wrote:
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
- u32 *next_offset)
As in patch 2, change u32 to uint64_t.
Ok.
+{
- if (!mtd->block_isbad)
- goto out;
- u32 i, bb_delta = 0;
- for (i = part->offset; i - bb_delta < part->offset + part->size;
- i += mtd->erasesize) {
- if (mtd->block_isbad(mtd, i))
- bb_delta += mtd->erasesize;
- }
- /*
- * Absorb bad blocks immeadiately following this
- * partition also into the partition, such that
- * the next partition starts with a good block.
- */
- while (i < mtd->size && mtd->block_isbad(mtd, i)) {
- bb_delta += mtd->erasesize;
- i += mtd->erasesize;
- }
Could this be refactored with get_len_incl_bad()? It should return both the updated length and a flag indicating whether it was truncated.
Yes, I think so. Good point.
- debug("spread_partitions: device = %s%d, partition %d ="
- " (%s) 0x%08x@0x%08x\n",
- MTD_DEV_TYPE(dev->id->type), dev->id->num,
- part_num, part->name, part->size,
- part->offset);
Why the extra indent on that last line?
IMHO, it's also nicer to line up continuation lines like this:
debug("spread_partitions..." " (%s) ..." MTD_DEV... part_num... part->offset);
Right. I think I forgot also about this formatting requirement which you pointed out in the env.oob review. I'll get it right soon enough.
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

On Fri, Aug 27, 2010 at 9:51 AM, Ben Gardiner bengardiner@nanometrics.ca wrote:
On Thu, Aug 26, 2010 at 5:12 PM, Scott Wood scottwood@freescale.com wrote:
+{
- if (!mtd->block_isbad)
- goto out;
- u32 i, bb_delta = 0;
- for (i = part->offset; i - bb_delta < part->offset + part->size;
- i += mtd->erasesize) {
- if (mtd->block_isbad(mtd, i))
- bb_delta += mtd->erasesize;
- }
- /*
- * Absorb bad blocks immeadiately following this
- * partition also into the partition, such that
- * the next partition starts with a good block.
- */
- while (i < mtd->size && mtd->block_isbad(mtd, i)) {
- bb_delta += mtd->erasesize;
- i += mtd->erasesize;
- }
Could this be refactored with get_len_incl_bad()? It should return both the updated length and a flag indicating whether it was truncated.
Yes, I think so. Good point.
I have performed a refactoring but I have reached an impasse: the 'mtdparts spread' command is written for mtd devices whereas the get_len_incl_bad() function is for NAND devices. I extracted a function, mtd_get_len_incl_bad(), to which both the spread_partition and nand_utils.c:get_len_incl_bad() function then delegated. But since a board may have NAND enabled but not MTD_DEVICE (i.e. guruplug) I get link errors sometimes. ATM I'm thinking of leaving the original implementation of get_len_incl_bad in an #else. An alternative is to move 'mtdparts spread' to 'nand mtdparts.spread' -- only OneNAND and NAND devices (currently) have bad_block functions.
Best Regards, Ben Gardiner
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

On 08/27/2010 04:36 PM, Ben Gardiner wrote:
On Fri, Aug 27, 2010 at 9:51 AM, Ben Gardiner bengardiner@nanometrics.ca wrote: I have performed a refactoring but I have reached an impasse: the 'mtdparts spread' command is written for mtd devices whereas the get_len_incl_bad() function is for NAND devices. I extracted a function, mtd_get_len_incl_bad(), to which both the spread_partition and nand_utils.c:get_len_incl_bad() function then delegated.
I figured the NAND code could just call the MTD-ized get_len_incl_bad() directly.
But since a board may have NAND enabled but not MTD_DEVICE (i.e. guruplug) I get link errors sometimes.
Grr... Eventually we ought to make NAND depend on MTD_DEVICE. It's 808 bytes currently in my build, but if we could get rid of/reduce specialized client code, it could more than make up for it.
For now, I guess don't worry about sharing the code.
ATM I'm thinking of leaving the original implementation of get_len_incl_bad in an #else. An alternative is to move 'mtdparts spread' to 'nand mtdparts.spread' -- only OneNAND and NAND devices (currently) have bad_block functions.
There's too much duplication between NAND and OneNAND as is; I'd rather do it at the MTD layer.
-Scott

On Fri, Aug 27, 2010 at 5:46 PM, Scott Wood scottwood@freescale.com wrote:
On 08/27/2010 04:36 PM, Ben Gardiner wrote:
On Fri, Aug 27, 2010 at 9:51 AM, Ben Gardiner bengardiner@nanometrics.ca wrote: But since a board may have NAND enabled but not MTD_DEVICE (i.e. guruplug) I get link errors sometimes.
Grr... Eventually we ought to make NAND depend on MTD_DEVICE. It's 808 bytes currently in my build, but if we could get rid of/reduce specialized client code, it could more than make up for it.
For now, I guess don't worry about sharing the code.
Ok -- thanks, Scott. I'll re-spin on Monday.
ATM I'm thinking of leaving the original implementation of get_len_incl_bad in an #else. An alternative is to move 'mtdparts spread' to 'nand mtdparts.spread' -- only OneNAND and NAND devices (currently) have bad_block functions.
There's too much duplication between NAND and OneNAND as is; I'd rather do it at the MTD layer.
Whew -- I'm glad I don't have to migrate all of the patches to new files. :)
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

On 08/27/2010 04:46 PM, Scott Wood wrote:
On 08/27/2010 04:36 PM, Ben Gardiner wrote:
On Fri, Aug 27, 2010 at 9:51 AM, Ben Gardiner bengardiner@nanometrics.ca wrote: I have performed a refactoring but I have reached an impasse: the 'mtdparts spread' command is written for mtd devices whereas the get_len_incl_bad() function is for NAND devices. I extracted a function, mtd_get_len_incl_bad(), to which both the spread_partition and nand_utils.c:get_len_incl_bad() function then delegated.
I figured the NAND code could just call the MTD-ized get_len_incl_bad() directly.
But since a board may have NAND enabled but not MTD_DEVICE (i.e. guruplug) I get link errors sometimes.
Grr... Eventually we ought to make NAND depend on MTD_DEVICE. It's 808 bytes currently in my build, but if we could get rid of/reduce specialized client code, it could more than make up for it.
For now, I guess don't worry about sharing the code.
Plus, I've got some changes to the NAND command/util code I'm about to send out that touch this -- if sharing is going to be a pain, I can go back to the version that only passes back "fits with bad blocks", "fits with no bad blocks", or "doesn't fit", and doesn't deal with 64-bit sizes because it's only used by read/write which is limited by pointer size. That simpler version is 128 bytes smaller in my build.
-Scott

On Fri, Aug 27, 2010 at 5:59 PM, Scott Wood scottwood@freescale.com wrote:
On 08/27/2010 04:46 PM, Scott Wood wrote:
For now, I guess don't worry about sharing the code.
Plus, I've got some changes to the NAND command/util code I'm about to send out that touch this -- if sharing is going to be a pain, I can go back to the version that only passes back "fits with bad blocks", "fits with no bad blocks", or "doesn't fit", and doesn't deal with 64-bit sizes because it's only used by read/write which is limited by pointer size. That simpler version is 128 bytes smaller in my build.
I imagine you don't have to go back. I wouldn't want to make the merge harder; but as long as there is a way to get the the size-including-bad-blocks and truncation status given an offset and target size. Please continue your work and I'll find a way to make 'mtdparts spread' fit with it after your post..
Best Regards, Ben Gardiner
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

On Fri, 27 Aug 2010 23:59:13 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
On Fri, Aug 27, 2010 at 5:59 PM, Scott Wood scottwood@freescale.com wrote:
On 08/27/2010 04:46 PM, Scott Wood wrote:
For now, I guess don't worry about sharing the code.
Plus, I've got some changes to the NAND command/util code I'm about to send out that touch this -- if sharing is going to be a pain, I can go back to the version that only passes back "fits with bad blocks", "fits with no bad blocks", or "doesn't fit", and doesn't deal with 64-bit sizes because it's only used by read/write which is limited by pointer size. That simpler version is 128 bytes smaller in my build.
I imagine you don't have to go back.
I already did; it's smaller and slightly simpler for what it currently needs to do. It would still be easy to switch to using the MTD function later.
-Scott

On Mon, Aug 30, 2010 at 4:24 PM, Scott Wood scottwood@freescale.com wrote:
On Fri, 27 Aug 2010 23:59:13 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
On Fri, Aug 27, 2010 at 5:59 PM, Scott Wood scottwood@freescale.com wrote:
On 08/27/2010 04:46 PM, Scott Wood wrote:
For now, I guess don't worry about sharing the code.
Plus, I've got some changes to the NAND command/util code I'm about to send out that touch this -- if sharing is going to be a pain, I can go back to the version that only passes back "fits with bad blocks", "fits with no bad blocks", or "doesn't fit", and doesn't deal with 64-bit sizes because it's only used by read/write which is limited by pointer size. That simpler version is 128 bytes smaller in my build.
I imagine you don't have to go back.
I already did; it's smaller and slightly simpler for what it currently needs to do. It would still be easy to switch to using the MTD function later.
Ok, no problem. If you're interested in taking this series through the nand-testing tree I would be happy to rebase this series and integrate with your MTD changes.
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
This patch is based on a port of the 'dynnamic partitions' feature by Harald Welte laforge@gnumonks.org; ported from commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git. Whereas Harald's feature used a compile-time array to specify partitions, the feature introduced by this patch uses the mtdparts environment variable.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Signed-off-by: Harald Welte laforge@gnumonks.org CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed * check for null mtd->block_isbad before dereferencing
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * update copyright statement of cmd_mtdparts.c to include openmoko's copyright of the 'dynamic partitions' functionality using commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git as reference.
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 3/4 to 4/5 * use uint64_t for net_size * don't push dangling lines to the right * offsets to uint64_t * fix spelling errors in comments * more hanging arguments alignment * refactor the spread_partition function so that it delegates the bad-block counting to the mtd_get_len_incl_bad function -- as per Scott Woods' suggestion. --- common/cmd_mtdparts.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 109 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index a8912ed..fb8c77b 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,6 +15,9 @@ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 * kernel tree. * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte laforge@openmoko.org + * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH * @@ -1428,6 +1431,98 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next partition would start on a + * good block if it were adjacent to this partition. + * + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + uint64_t *next_offset) +{ + uint64_t net_size, padding_size = 0; + int truncated; + + mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size, + &truncated); + + /* + * Absorb bad blocks immediately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + if (!truncated) { + mtd_get_len_incl_bad(mtd, part->offset + net_size, + mtd->erasesize, &padding_size, &truncated); + padding_size -= mtd->erasesize; + } + + if (truncated) { + printf("truncated partition %s to %lld bytes\n", part->name, + (uint64_t) net_size + padding_size); + } + + part->size = net_size + padding_size; + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + uint64_t cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1918,6 +2013,11 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + return cmd_usage(cmdtp); }
@@ -1941,7 +2041,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

On Mon, 30 Aug 2010 13:38:59 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
uint64_t *next_offset)
+{
- uint64_t net_size, padding_size = 0;
- int truncated;
- mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
&truncated);
- /*
* Absorb bad blocks immediately following this
* partition also into the partition, such that
* the next partition starts with a good block.
*/
Why is the first block of a partition special?
- if (!truncated) {
mtd_get_len_incl_bad(mtd, part->offset + net_size,
mtd->erasesize, &padding_size, &truncated);
padding_size -= mtd->erasesize;
What if this is the last partition? You're relying on an implementation quick (bug?) that mtd_get_len_incl_bad() will let you exceed the device size by a block if you start there. If it returned the more expected zero in such a case, you'll end up subtracting a block from net_size.
- }
- if (truncated) {
} else {
-Scott

On Mon, 30 Aug 2010 16:01:05 -0500 Scott Wood scottwood@freescale.com wrote:
On Mon, 30 Aug 2010 13:38:59 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
- if (!truncated) {
mtd_get_len_incl_bad(mtd, part->offset + net_size,
mtd->erasesize, &padding_size, &truncated);
padding_size -= mtd->erasesize;
What if this is the last partition? You're relying on an implementation quick (bug?)
Grr, s/quick/quirk/
-Scot

On Mon, Aug 30, 2010 at 5:01 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, 30 Aug 2010 13:38:59 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
+static void spread_partition(struct mtd_info *mtd, struct part_info *part,
- uint64_t *next_offset)
+{
- uint64_t net_size, padding_size = 0;
- int truncated;
- mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
- &truncated);
- /*
- * Absorb bad blocks immediately following this
- * partition also into the partition, such that
- * the next partition starts with a good block.
- */
Why is the first block of a partition special?
This (arbitrary) decision to re-assign the bad-blocks that would normally be at the start of the next partition to the end of this partition is carried forward from the design of Harald Welte laforge@gnumonks.org from the openmoko u-boot feature [1].
Since the behaviour of the read and write commands (as you well know) is to skip bad blocks, the same end result of any read or write would be obtained regardless of whether the bad-blocks were assigned to the end of this partition or the start of the the next partition -- I think this is what you are getting at with your question: there is nothing special about the first block of a partition.
One particular advantage to assigning these bad blocks to the end of the partitions is that reads and writes on any partitions occurring later during the execution of u-boot (and of Linux if the same mtdparts are passed as a boot variable) will not have to skip immediately past the bad block(s) at the beginning of the partition.
I can easily reverse the behaviour here to keep the bad blocks at the beginning of the current partition if that is what you would prefer. But unless you say so I will keep it as-is to preserve the design from openmoko.
- if (!truncated) {
- mtd_get_len_incl_bad(mtd, part->offset + net_size,
- mtd->erasesize, &padding_size, &truncated);
- padding_size -= mtd->erasesize;
What if this is the last partition? You're relying on an implementation quick (bug?) that mtd_get_len_incl_bad() will let you exceed the device size by a block if you start there. If it returned the more expected zero in such a case, you'll end up subtracting a block from net_size.
On Mon, Aug 30, 2010 at 5:05 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, 30 Aug 2010 16:01:05 -0500 Scott Wood scottwood@freescale.com wrote: Grr, s/quick/quirk/
Got it.
You're absolutely right I will add an additional check of the truncated return value.
Best Regards, Ben Gardiner
[1] http://git.openmoko.org/?p=u-boot.git;a=commitdiff;h=e05835df019027391f58f9d...
--- Nanometrics Inc. http://www.nanometrics.ca

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
This patch is based on a port of the 'dynnamic partitions' feature by Harald Welte laforge@gnumonks.org; ported from commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git. Whereas Harald's feature used a compile-time array to specify partitions, the feature introduced by this patch uses the mtdparts environment variable.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Signed-off-by: Harald Welte laforge@gnumonks.org CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed * check for null mtd->block_isbad before dereferencing
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * update copyright statement of cmd_mtdparts.c to include openmoko's copyright of the 'dynamic partitions' functionality using commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git as reference.
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style * removed changelog and my (C) from file header * added source of port to the commit message * fixed multi-line comment style * indentation only by tabs
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 3/4 to 4/5 * use uint64_t for net_size * don't push dangling lines to the right * offsets to uint64_t * fix spelling errors in comments * more hanging arguments alignment * refactor the spread_partition function so that it delegates the bad-block counting to the mtd_get_len_incl_bad function -- as per Scott Woods' suggestion.
V6: * check truncated return value to handle trying to absorb bad blocks past the end of the device -- as per Scott Wood's review comments
--- common/cmd_mtdparts.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 112 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 266844f..347e409 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,6 +15,9 @@ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 * kernel tree. * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte laforge@openmoko.org + * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH * @@ -1424,6 +1427,101 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** + * Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next partition would start on a + * good block if it were adjacent to this partition. + * + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + uint64_t *next_offset) +{ + uint64_t net_size, padding_size = 0; + int truncated; + + mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size, + &truncated); + + /* + * Absorb bad blocks immediately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + if (!truncated) { + mtd_get_len_incl_bad(mtd, part->offset + net_size, + mtd->erasesize, &padding_size, &truncated); + if (truncated) + padding_size = 0; + else + padding_size -= mtd->erasesize; + } + + if (truncated) { + printf("truncated partition %s to %lld bytes\n", part->name, + (uint64_t) net_size + padding_size); + } + + part->size = net_size + padding_size; + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + uint64_t cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1914,6 +2012,11 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + return cmd_usage(cmdtp); }
@@ -1937,7 +2040,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

This patch adds a new 'mtdparts add' variant: add.spread. This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * updating copyright to include addition of add.e command
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * removed changelog from file header * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion. --- common/cmd_mtdparts.c | 36 +++++++++++++++++++++++++++++++++++- 1 files changed, 35 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 7e2e232..6e42eff 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1959,9 +1959,14 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + char *s; + struct mtd_info *mtd; + u32 next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1996,11 +2001,36 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + s = strchr(argv[1], '.'); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; +#endif + if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (s && !strcmp(s, ".spread")) { + p = list_entry(dev->parts.next, + struct part_info, link); + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n", p->name, + p->size); + } +#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (s && !strcmp(s, ".spread")) { + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n", p->name, + p->size); + } +#endif if (part_add(dev_tmp, p) != 0) { device_del(dev); return 1; @@ -2049,6 +2079,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

On Mon, Aug 09, 2010 at 04:44:00PM -0400, Ben Gardiner wrote:
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
s = strchr(argv[1], '.');
if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
return 1;
+#endif
- if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) {
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
if (s && !strcmp(s, ".spread")) {
No need for the strchr, just do "if (!strcmp(&argv[1][3], ".spread"))".
p = list_entry(dev->parts.next,
struct part_info, link);
spread_partition(mtd, p, &next_offset);
debug("increased %s to %d bytes\n", p->name,
p->size);
}
+#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
if (s && !strcmp(s, ".spread")) {
spread_partition(mtd, p, &next_offset);
debug("increased %s to %d bytes\n", p->name,
p->size);
}
+#endif
Don't duplicate this on both sides of the "if"; instead do something like:
p = list_entry(dev->parts.next...);
if (!strcmp(&argv[1][3], ".spread")) spread_partition(mtd, p, &next_offset);
if ((dev_tmp = ...) { device_add(dev); } else if (part_add(dev_tmp, p)) { device_del(dev); return 1; }
-Scott

On Thu, Aug 26, 2010 at 6:26 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, Aug 09, 2010 at 04:44:00PM -0400, Ben Gardiner wrote:
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
- s = strchr(argv[1], '.');
- if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
- return 1;
+#endif
if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { +#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
- if (s && !strcmp(s, ".spread")) {
No need for the strchr, just do "if (!strcmp(&argv[1][3], ".spread"))".
Thanks for pointing that out -- I see it now.
- p = list_entry(dev->parts.next,
- struct part_info, link);
- spread_partition(mtd, p, &next_offset);
- debug("increased %s to %d bytes\n", p->name,
- p->size);
- }
+#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
- if (s && !strcmp(s, ".spread")) {
- spread_partition(mtd, p, &next_offset);
- debug("increased %s to %d bytes\n", p->name,
- p->size);
- }
+#endif
Don't duplicate this on both sides of the "if"; instead do something like:
p = list_entry(dev->parts.next...);
if (!strcmp(&argv[1][3], ".spread")) spread_partition(mtd, p, &next_offset);
if ((dev_tmp = ...) { device_add(dev); } else if (part_add(dev_tmp, p)) { device_del(dev); return 1; }
Ok, I'll give it a shot.
Thank you again for your review and detailed comments. I appreciate you taking the time to help me get these patches prepared. I will integrate your comments on patches 2-4 shortly.
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

This patch adds a new 'mtdparts add' variant: add.spread. This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * updating copyright to include addition of add.e command
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * removed changelog from file header * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion.
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 4/4 to 5/5 * don't reproduce the same call to spread_partitions on either side of the check for existing device -- as per Scott Wood's comments. --- common/cmd_mtdparts.c | 34 ++++++++++++++++++++++++++-------- 1 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index fb8c77b..d8cda77 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1950,9 +1950,13 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + struct mtd_info *mtd; + uint64_t next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1987,15 +1991,25 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
- if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { + p = list_entry(dev->parts.next, struct part_info, link); + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + if (!strcmp(&argv[1][3], ".spread")) { + spread_partition(mtd, p, &next_offset); + debug("increased %s to %d bytes\n", p->name, p->size); + } +#endif + + dev_tmp = device_find(dev->id->type, dev->id->num); + if (dev_tmp == NULL) { device_add(dev); - } else { + } else if (part_add(dev_tmp, p) != 0) { /* merge new partition with existing ones*/ - p = list_entry(dev->parts.next, struct part_info, link); - if (part_add(dev_tmp, p) != 0) { - device_del(dev); - return 1; - } + device_del(dev); + return 1; }
if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { @@ -2040,6 +2054,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

This patch adds a new 'mtdparts add' variant: add.spread. This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de CC: Scott Wood scottwood@freescale.com
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * updating copyright to include addition of add.e command
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * removed changelog from file header * check for s == NULL when looking for '.' in command * do not include support for the '.i' synonym * rename to 'add.spread' to match 'mtdparts spread' as per Scott Wood's suggestion.
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 4/4 to 5/5 * don't reproduce the same call to spread_partitions on either side of the check for existing device -- as per Scott Wood's comments.
V6: * unchanged
--- common/cmd_mtdparts.c | 34 ++++++++++++++++++++++++++-------- 1 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 347e409..17865b7 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1949,9 +1949,13 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + struct mtd_info *mtd; + uint64_t next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1986,15 +1990,25 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
- if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { + p = list_entry(dev->parts.next, struct part_info, link); + +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + if (!strcmp(&argv[1][3], ".spread")) { + spread_partition(mtd, p, &next_offset); + debug("increased %s to %d bytes\n", p->name, p->size); + } +#endif + + dev_tmp = device_find(dev->id->type, dev->id->num); + if (dev_tmp == NULL) { device_add(dev); - } else { + } else if (part_add(dev_tmp, p) != 0) { /* merge new partition with existing ones*/ - p = list_entry(dev->parts.next, struct part_info, link); - if (part_add(dev_tmp, p) != 0) { - device_del(dev); - return 1; - } + device_del(dev); + return 1; }
if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { @@ -2039,6 +2053,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Stefan Roese sr@denx.de
---
V2: * formatting: add space after 'if' * added acked-by tag as requested by Stefan
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git --- common/cmd_mtdparts.c | 43 ++++++++++++++++++++++++++++--------------- 1 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 447486b..f1bed95 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,27 @@ static void current_save(void) index_partitions(); }
+ +/** Produce a mtd_info given a type and num + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +318,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if (get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +700,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if (get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Stefan Roese sr@denx.de CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: add space after 'if' * added acked-by tag as requested by Stefan
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 1/4 to 1/5 --- common/cmd_mtdparts.c | 45 ++++++++++++++++++++++++++++++--------------- 1 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index ceec5a9..772ad54 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,29 @@ static void current_save(void) index_partitions(); }
+ +/** + * Produce a mtd_info given a type and num. + * + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +320,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if (get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +702,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if (get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Stefan Roese sr@denx.de CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: add space after 'if' * added acked-by tag as requested by Stefan
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
V4: * rebased to b417260d871d4d8d336c160d95ed40cc8c0fb0fa of git://git.denx.de/u-boot.git * fixed multi-line comment style
V5: * rebased to 962ad59e25640e586e2bceabf67a628a27f8f508 of git://git.denx.de/u-boot.git * renumbered from 1/4 to 1/5
V6: * unchanged
--- common/cmd_mtdparts.c | 45 ++++++++++++++++++++++++++++++--------------- 1 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index ceec5a9..772ad54 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,29 @@ static void current_save(void) index_partitions(); }
+ +/** + * Produce a mtd_info given a type and num. + * + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +320,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if (get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +702,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if (get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de
---
V2: * formatting: spaces after 'if' and 'for' * the entire new feature is conditional on a macro, there is now a zero-byte binary size impact when the macro is not defined. * return the net parition size directly from net_part_size instead of using an output variable
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix line length over 80 chars * update copyright of cmd_mtdparts.c --- common/cmd_mtdparts.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index f1bed95..84acd62 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,6 +15,10 @@ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 * kernel tree. * + * (C) Copyright 2010 + * Ben Gardiner, Nanometrics Inc., bengardiner@nanometrics.ca + * Added net partition size output to mtdparts list command + * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH * @@ -1213,6 +1217,29 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) +/** get the net size (w/o bad blocks) of the given partition + * @param mtd the mtd info + * @param part the partition + * @return the calculated net size of this partition + */ +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part) +{ + if (mtd->block_isbad) { + u32 i, bb_delta = 0; + + for (i = 0; i < part->size; i += mtd->erasesize) { + if (mtd->block_isbad(mtd, part->offset + i)) + bb_delta += mtd->erasesize; + } + + return part->size - bb_delta; + } else { + return part->size; + } +} +#endif + /** * Format and print out a partition list for each device from global device * list. @@ -1223,6 +1250,10 @@ static void list_partitions(void) struct part_info *part; struct mtd_device *dev; int part_num; +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + struct mtd_info *mtd; + u32 net_size; +#endif
debug("\n---list_partitions---\n"); list_for_each(dentry, &devices) { @@ -1230,14 +1261,34 @@ static void list_partitions(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\toffset\t\tmask_flags\n"); + printf(" #: name\t\tsize\t\t" +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + "net size\t" +#endif + "offset\t\tmask_flags\n"); + +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return; +#endif
/* list partitions for given device */ part_num = 0; list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link); - printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + net_size = net_part_size(mtd, part); +#endif + printf("%2d: %-20s0x%08x\t" +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + "0x%08x%s\t" +#endif + "0x%08x\t%d\n", part_num, part->name, part->size, +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + net_size, + part->size == net_size ? " " : " (!)", +#endif part->offset, part->mask_flags);
part_num++;

Dear Ben Gardiner,
In message 1278366212-24023-3-git-send-email-bengardiner@nanometrics.ca you wrote:
This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Wolfgang Denk wd@denx.de
V2:
- formatting: spaces after 'if' and 'for'
- the entire new feature is conditional on a macro, there is now a zero-byte
binary size impact when the macro is not defined.
- return the net parition size directly from net_part_size instead of using
an output variable
V3:
- rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
- fix line length over 80 chars
- update copyright of cmd_mtdparts.c
That was a bad idea. We do not add change logs to files, because we have the full history of changes in git.
--- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,6 +15,10 @@
- Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
- kernel tree.
- (C) Copyright 2010
- Ben Gardiner, Nanometrics Inc., bengardiner@nanometrics.ca
- Added net partition size output to mtdparts list command
NAK for this hunk.
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) +/** get the net size (w/o bad blocks) of the given partition
- @param mtd the mtd info
- @param part the partition
- @return the calculated net size of this partition
- */
Incorrect multiline comment style.
printf(" #: name\t\tsize\t\t"
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
"net size\t"
+#endif
"offset\t\tmask_flags\n");
Incorrect indentation.
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
return;
+#endif
/* list partitions for given device */ part_num = 0; list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link);
printf("%2d: %-20s0x%08x\t0x%08x\t%d\n",
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
net_size = net_part_size(mtd, part);
+#endif
printf("%2d: %-20s0x%08x\t"
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
"0x%08x%s\t"
+#endif
"0x%08x\t%d\n", part_num, part->name, part->size,
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
net_size,
part->size == net_size ? " " : " (!)",
+#endif
This is way too much #ifdef's here. Please separate the code and use a single #ifdef only.
Best regards,
Wolfgang Denk

Dear Wolfgang,
On Sat, Aug 07, 2010 at 10:08:50PM +0200, Wolfgang Denk wrote:
- update copyright of cmd_mtdparts.c
That was a bad idea. We do not add change logs to files, because we have the full history of changes in git.
just to clearly understand your point: Your objection is against the
- Added net partition size output to mtdparts list command
in the file itself, as it should be a changelog. Your objection is (I hope) not against the author claiming copyright in the source code file itself, right?
Copyright claims are something that you can hardly deny any author in the work itself - particularly since the majority of distributions are made as tarballs that do not contain the full git history.
While even in the US there is now no legal requirement anymore to have this copyright notice to claim copyright, it is still common practise and a good idea. I definitely can confirm this from the actual GPL enforcement work that I've been doing, where you have to substantiate the fact that you actually do have copyright... and when the source file contains such indication, it is pretty straight forward. If you'd have to explain the workings of a revision control system and how to extract this information first, it will be significantly more complex.
Regards, Harald

Dear Harald Welte,
In message 20100808040649.GI12062@prithivi.gnumonks.org you wrote:
On Sat, Aug 07, 2010 at 10:08:50PM +0200, Wolfgang Denk wrote:
- update copyright of cmd_mtdparts.c
That was a bad idea. We do not add change logs to files, because we have the full history of changes in git.
just to clearly understand your point: Your objection is against the
- Added net partition size output to mtdparts list command
in the file itself, as it should be a changelog. Your objection is (I hope) not against the author claiming copyright in the source code file itself, right?
Correct.
Copyright claims are something that you can hardly deny any author in the work itself - particularly since the majority of distributions are made as tarballs that do not contain the full git history.
Agreed - at least for any significant contribution to the code. I don't think you mean that eauch author of a 10 line patch to a 1000 line file shouldbe added to the list of copyright holders?
In this respect, the patch we're discussing here is a bit on the border: it's adding some 1...2% to the code size of this file, and the code is not exactly challanging. I would not have asked to have my own (C) added in a similar situation.
But to make things clear: My objection was against adding change log information.
Best regards,
Wolfgang Denk

Hi Wolfgang,
Thank you for the review comments. Could you give me some more details on how you would like the following resolved?
On Sat, Aug 7, 2010 at 4:08 PM, Wolfgang Denk wd@denx.de wrote:
Dear Ben Gardiner,
In message 1278366212-24023-3-git-send-email-bengardiner@nanometrics.ca you wrote:
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
- return;
+#endif
/* list partitions for given device */ part_num = 0; list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link);
- printf("%2d: %-20s0x%08x\t0x%08x\t%d\n",
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- net_size = net_part_size(mtd, part);
+#endif
- printf("%2d: %-20s0x%08x\t"
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- "0x%08x%s\t"
+#endif
- "0x%08x\t%d\n",
part_num, part->name, part->size, +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
- net_size,
- part->size == net_size ? " " : " (!)",
+#endif
This is way too much #ifdef's here. Please separate the code and use a single #ifdef only.
Would it be acceptable to do something like the following?
static void print_partition_table(...) { #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) ... #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ ... #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ }
/** * Format and print out a partition list for each device from global device * list. */ static void list_partitions(void) { ...
print_partition_table(...)
/* current_mtd_dev is not NULL only when we have non empty device list */ if (current_mtd_dev) {
...
puts("mtdparts: "); puts(mtdparts_default ? mtdparts_default : "none"); puts("\n"); }
Best Regards,
Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

Dear Ben Gardiner,
In message AANLkTim8N9jxbSO_UzcDYX6ta+qkfa-ABO4GMpC2m771@mail.gmail.com you wrote:
Would it be acceptable to do something like the following?
static void print_partition_table(...) { #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) ... #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ ... #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */ }
If the whole body of the funtion is affectted, you better do:
#if defined(xxx) static void print_partition_table(...) { ... } #else static void print_partition_table(...) { ... } #endif
Best regards,
Wolfgang Denk

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Harald Welte laforge@gnumonks.org
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed * check for null mtd->block_isbad before dereferencing
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * update copyright statement of cmd_mtdparts.c to include openmoko's copyright of the 'dynamic partitions' functionality using commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git as reference. --- common/cmd_mtdparts.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 120 insertions(+), 2 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 84acd62..c1adbd4 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -15,9 +15,17 @@ * Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4 * kernel tree. * + * (C) Copyright 2008 + * Harald Welte, OpenMoko, Inc., Harald Welte laforge@openmoko.org + * Add support for 'dynamic partitions.' Net partition sizes specified at + * compile time where the layout in flash is adjusted in the presence of bad + * blocks. + * * (C) Copyright 2010 * Ben Gardiner, Nanometrics Inc., bengardiner@nanometrics.ca - * Added net partition size output to mtdparts list command + * Added net partition size output to mtdparts list command. + * Ported 'dynamic partitions' support to current u-boot 'mtdparts spread' + * command. Current mtdparts variable is now used as input. * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH @@ -1419,6 +1427,103 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next parition would start on a + * good blcok if it were adjacent to this partition + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, + u32 *next_offset) +{ + if (!mtd->block_isbad) + goto out; + + u32 i, bb_delta = 0; + + for (i = part->offset; + i - bb_delta < part->offset + part->size; + i += mtd->erasesize) { + if (mtd->block_isbad(mtd, i)) + bb_delta += mtd->erasesize; + } + + /* Absorb bad blocks immeadiately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + while (i < mtd->size && mtd->block_isbad(mtd, i)) { + bb_delta += mtd->erasesize; + i += mtd->erasesize; + } + + if (part->offset + part->size + bb_delta > mtd->size) { + part->size = mtd->size - part->offset - bb_delta; + printf("truncated partition %s to %d bytes\n", part->name, + part->size); + } + + part->size += bb_delta; + +out: + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + u32 cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, + part_num, part->name, part->size, + part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd, part, &cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1909,6 +2014,11 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) + return spread_partitions(); +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + cmd_usage(cmdtp); return 1; } @@ -1933,7 +2043,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

Dear Ben Gardiner,
In message 1278366212-24023-4-git-send-email-bengardiner@nanometrics.ca you wrote:
This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca CC: Harald Welte laforge@gnumonks.org
V2:
- formatting: spaces after 'if' and 'for'
- trailing whitespace removed
- check for null mtd->block_isbad before dereferencing
V3:
- rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
- fix more checkpatch errors
- update copyright statement of cmd_mtdparts.c to include openmoko's copyright of the 'dynamic partitions' functionality using commit e05835df019027391f58f9d8ce5e1257d6924798 of git://git.openmoko.org/u-boot.git as reference.
NAK. Please add this to the commit message, and add Harald's SoB line instead.
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** Increase the size of the given partition so that it's net size is at least
- as large as the size member and such that the next parition would start on a
- good blcok if it were adjacent to this partition
- @param mtd the mtd device
- @param part the partition
- @param next_offset pointer to the offset of the next partition after this
partition's size has been modified (output)
- */
Incorrect multiline comment style.
- for (i = part->offset;
i - bb_delta < part->offset + part->size;
i += mtd->erasesize) {
if (mtd->block_isbad(mtd, i))
bb_delta += mtd->erasesize;
- }
Indentation by TAB only, please.
- /* Absorb bad blocks immeadiately following this
* partition also into the partition, such that
* the next partition starts with a good block.
*/
Incorrect multiline comment style. More follow. Please fix globally.
Best regards,
Wolfgang Denk

This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i). This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
---
V2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed
V3: * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * fix more checkpatch errors * updating copyright to include addition of add.e command --- common/cmd_mtdparts.c | 38 +++++++++++++++++++++++++++++++++++++- 1 files changed, 37 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index c1adbd4..d57f9ad 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -26,6 +26,8 @@ * Added net partition size output to mtdparts list command. * Ported 'dynamic partitions' support to current u-boot 'mtdparts spread' * command. Current mtdparts variable is now used as input. + * Add the 'add.e' mtdparts command. Dynamic partitions can be added one at + * a time -- specified by size only. * * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * Copyright 2002 SYSGO Real-Time Solutions GmbH @@ -1951,9 +1953,14 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + char *s; + struct mtd_info *mtd; + u32 next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1988,11 +1995,36 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + s = strchr(argv[1], '.'); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; +#endif + if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + p = list_entry(dev->parts.next, + struct part_info, link); + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n", p->name, + p->size); + } +#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n", p->name, + p->size); + } +#endif if (part_add(dev_tmp, p) != 0) { device_del(dev); return 1; @@ -2042,6 +2074,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.e <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

Dear Ben Gardiner,
In message 1278366212-24023-5-git-send-email-bengardiner@nanometrics.ca you wrote:
This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i). This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
V2:
- formatting: spaces after 'if' and 'for'
- trailing whitespace removed
V3:
- rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git
- fix more checkpatch errors
- updating copyright to include addition of add.e command
NAK for this like for the previous patches.
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
s = strchr(argv[1], '.');
s == NULL if there is no '.' in the argument.
if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
return 1;
+#endif
- if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) {
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
if (!strcmp(s, ".e") || !strcmp(s, ".i")) {
So here you might dereference a NULL pointer. Sounds like a Bad Thing to me. Please add error checking / handling.
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD)
- "mtdparts add.e <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
- " - add partition, padding size by skipping bad blocks\n"
+#endif
Also document the ".i" version.
Best regards,
Wolfgang Denk

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Stefan Roese sr@denx.de --- Changes in v2: * formatting: add space after 'if' * added acked-by tag as requested by Stefan
--- common/cmd_mtdparts.c | 43 ++++++++++++++++++++++++++++--------------- 1 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 116e637..a6eeb41 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,27 @@ static void current_save(void) index_partitions(); }
+ +/** Produce a mtd_info given a type and num + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +318,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if (get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +700,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if (get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- Changes in v2: * formatting: spaces after 'if' and 'for' * the entire new feature is conditional on a macro, there is now a zero-byte binary size impact when the macro is not defined. * return the net parition size directly from net_part_size instead of using an output variable
--- common/cmd_mtdparts.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index a6eeb41..b36aac2 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1213,6 +1213,29 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) +/** get the net size (w/o bad blocks) of the given partition + * @param mtd the mtd info + * @param part the partition + * @return the calculated net size of this partition + */ +static u32 net_part_size(struct mtd_info *mtd, struct part_info *part) +{ + if (mtd->block_isbad) { + u32 i, bb_delta = 0; + + for (i = 0; i < part->size; i += mtd->erasesize) { + if (mtd->block_isbad(mtd, part->offset + i)) + bb_delta += mtd->erasesize; + } + + return part->size - bb_delta; + } else { + return part->size; + } +} +#endif + /** * Format and print out a partition list for each device from global device * list. @@ -1223,6 +1246,10 @@ static void list_partitions(void) struct part_info *part; struct mtd_device *dev; int part_num; +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + struct mtd_info *mtd; + u32 net_size; +#endif
debug("\n---list_partitions---\n"); list_for_each(dentry, &devices) { @@ -1230,14 +1257,33 @@ static void list_partitions(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\toffset\t\tmask_flags\n"); + printf(" #: name\t\tsize\t\t" +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + "net size\t" +#endif + "offset\t\tmask_flags\n"); + +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return; +#endif
/* list partitions for given device */ part_num = 0; list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link); - printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + net_size = net_part_size(mtd, part); +#endif + printf("%2d: %-20s0x%08x\t" +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + "0x%08x%s\t" +#endif + "0x%08x\t%d\n", part_num, part->name, part->size, +#if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) + net_size, part->size == net_size ? " " : " (!)", +#endif part->offset, part->mask_flags);
part_num++;

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- Changes in v2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed * check for null mtd->block_isbad before dereferencing
--- common/cmd_mtdparts.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 110 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index b36aac2..c52374d 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1414,6 +1414,100 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next parition would start on a + * good blcok if it were adjacent to this partition + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, u32 *next_offset) +{ + if(!mtd->block_isbad) + goto out; + + u32 i, bb_delta = 0; + + for (i = part->offset; + i - bb_delta < part->offset + part->size; + i += mtd->erasesize) { + if (mtd->block_isbad(mtd, i)) + bb_delta += mtd->erasesize; + } + + /* Absorb bad blocks immeadiately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + while(i < mtd->size && mtd->block_isbad(mtd, i)) { + bb_delta += mtd->erasesize; + i += mtd->erasesize; + } + + if (part->offset + part->size + bb_delta > mtd->size) { + part->size = mtd->size - part->offset - bb_delta; + printf("truncated partition %s to %d bytes\n", part->name, part->size); + } + + part->size += bb_delta; + +out: + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + u32 cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, part_num, + part->name, part->size, part->offset); + + if (cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd,part,&cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1904,6 +1998,13 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if ((argc == 2) && (strcmp(argv[1], "spread") == 0)) { + + return spread_partitions(); + } +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + cmd_usage(cmdtp); return 1; } @@ -1928,7 +2029,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i). This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- Changes in v2: * formatting: spaces after 'if' and 'for' * trailing whitespace removed
--- common/cmd_mtdparts.c | 36 +++++++++++++++++++++++++++++++++++- 1 files changed, 35 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index c52374d..c4da06e 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1935,9 +1935,14 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + char *s; + struct mtd_info *mtd; + u32 next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1972,11 +1977,36 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + s = strchr(argv[1], '.'); + + if (get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; +#endif + if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + p = list_entry(dev->parts.next, + struct part_info, link); + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n",p->name, + p->size); + } +#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n",p->name, + p->size); + } +#endif if (part_add(dev_tmp, p) != 0) { device_del(dev); return 1; @@ -2028,6 +2058,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.e <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

On Wed, 2 Jun 2010 11:58:39 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i).
Why multiple ways to say the same thing, in a new command with no legacy to be compatible with?
Even on the commands where .e and .i used to do something, that's now the default, and the suffix is unnecessary. So I don't see any need to mimic the syntax.
What do "e" and "i" even stand for? It looks like they were "for compatibility with older units" even when first committed.
-Scott

Hi Scott,
On Mon, Aug 9, 2010 at 2:25 PM, Scott Wood scottwood@freescale.com wrote:
On Wed, 2 Jun 2010 11:58:39 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i).
Why multiple ways to say the same thing, in a new command with no legacy to be compatible with?
Just mimicking the add syntax.
Even on the commands where .e and .i used to do something, that's now the default, and the suffix is unnecessary. So I don't see any need to mimic the syntax.
Ok. No objection here.
What do "e" and "i" even stand for? It looks like they were "for compatibility with older units" even when first committed.
I don't really know. Should I stick with 'add.e' or would you prefer 'add.spread' 'add.skip' or something else?
Best Regards, Ben Gardiner
--- Nanometrics Inc. http://www.nanometrics.ca

On Mon, 9 Aug 2010 14:39:20 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
Hi Scott,
On Mon, Aug 9, 2010 at 2:25 PM, Scott Wood scottwood@freescale.com wrote:
On Wed, 2 Jun 2010 11:58:39 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i).
Why multiple ways to say the same thing, in a new command with no legacy to be compatible with?
Just mimicking the add syntax.
Even on the commands where .e and .i used to do something, that's now the default, and the suffix is unnecessary. So I don't see any need to mimic the syntax.
Ok. No objection here.
What do "e" and "i" even stand for? It looks like they were "for compatibility with older units" even when first committed.
I don't really know. Should I stick with 'add.e' or would you prefer 'add.spread' 'add.skip' or something else?
".spread" is nice if it matches what the "mtdparts spread" command does to existing partitions, but ".skip" might make sense in other contexts that we want to be consistent with. For instance, we probably want to do something with the "nand erase" command to differentiate clearing space for a certain amount of data (e.g. $filesize) versus erasing a partition (where the bad blocks should already be included in the size).
Since the consistency arguments mostly cancel out, I'd go with ".spread", since it's different from read/write/erase in that you're not quite skipping anything, but incorporating room for the bad blocks into the partition.
-Scott

The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/cmd_mtdparts.c | 43 ++++++++++++++++++++++++++++--------------- 1 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 116e637..7a9768f 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,27 @@ static void current_save(void) index_partitions(); }
+ +/** Produce a mtd_info given a type and num + * @param type mtd type + * @param num mtd number + * @param mtd a pointer to an mtd_info instance (output) + * @return 0 if device is valid, 1 otherwise + */ +static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{ + char mtd_dev[16]; + + sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); + *mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } + + return 0; +} + /** * Performs sanity check for supplied flash partition. * Table of existing MTD flash devices is searched and partition device @@ -297,17 +318,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Partition %s not found on device %s!\n", part->name, mtd_dev); + if(get_mtd_info(id->type, id->num, &mtd)) return 1; - }
part->sector_size = mtd->erasesize;
@@ -684,20 +700,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /** * Check device number to be within valid range for given device type. * - * @param dev device to validate + * @param type mtd type + * @param num mtd number + * @param size a pointer to the size of the mtd device (output) * @return 0 if device is valid, 1 otherwise */ int mtd_device_validate(u8 type, u8 num, u32 *size) { - struct mtd_info *mtd; - char mtd_dev[16]; + struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - printf("Device %s not found!\n", mtd_dev); + if(get_mtd_info(type, num, &mtd)) return 1; - }
*size = mtd->size;

On Tuesday 01 June 2010 22:23:44 Ben Gardiner wrote:
The get_mtd_device_nm function is called in a couple places and the string that is passed to it is not really used after the calls.
This patch regroups the calls to this function into a new function, get_mtd_info.
Thanks. Some nitpicking comments below.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
common/cmd_mtdparts.c | 43 ++++++++++++++++++++++++++++--------------- 1 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 116e637..7a9768f 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -286,6 +286,27 @@ static void current_save(void) index_partitions(); }
+/** Produce a mtd_info given a type and num
- @param type mtd type
- @param num mtd number
- @param mtd a pointer to an mtd_info instance (output)
- @return 0 if device is valid, 1 otherwise
- */
+static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) +{
- char mtd_dev[16];
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
- *mtd = get_mtd_device_nm(mtd_dev);
- if (IS_ERR(*mtd)) {
printf("Device %s not found!\n", mtd_dev);
return 1;
- }
- return 0;
+}
/**
- Performs sanity check for supplied flash partition.
- Table of existing MTD flash devices is searched and partition device
@@ -297,17 +318,12 @@ static void current_save(void) */ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) {
- struct mtd_info *mtd;
- char mtd_dev[16];
- struct mtd_info *mtd = NULL; int i, j; ulong start;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(id->type), id->num);
- mtd = get_mtd_device_nm(mtd_dev);
- if (IS_ERR(mtd)) {
printf("Partition %s not found on device %s!\n", part->name,
mtd_dev);
- if(get_mtd_info(id->type, id->num, &mtd))
Space after "if" please.
return 1;
}
part->sector_size = mtd->erasesize;
@@ -684,20 +700,17 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i /**
- Check device number to be within valid range for given device type.
- @param dev device to validate
- @param type mtd type
- @param num mtd number
*/
- @param size a pointer to the size of the mtd device (output)
- @return 0 if device is valid, 1 otherwise
int mtd_device_validate(u8 type, u8 num, u32 *size) {
- struct mtd_info *mtd;
- char mtd_dev[16];
- struct mtd_info *mtd = NULL;
- sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
- mtd = get_mtd_device_nm(mtd_dev);
- if (IS_ERR(mtd)) {
printf("Device %s not found!\n", mtd_dev);
- if(get_mtd_info(type, num, &mtd))
Again, space after "if".
Please respin this patch and add my:
Acked-by: Stefan Roese sr@denx.de
Thanks.
Cheers, Stefan
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-0 Fax: (+49)-8142-66989-80 Email: office@denx.de

This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/cmd_mtdparts.c | 28 ++++++++++++++++++++++++++-- 1 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 7a9768f..24d27b9 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1213,6 +1213,23 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+/** get the net size (w/o bad blocks) of the given partition + * @param mtd the mtd info + * @param part the partition + * @param net_size the calculated net size of this partition (output) + */ +static void net_part_size(struct mtd_info *mtd, struct part_info *part, u32 *net_size) +{ + u32 i, bb_delta = 0; + + for(i = 0; i < part->size; i += mtd->erasesize) { + if(mtd->block_isbad(mtd, part->offset + i)) + bb_delta += mtd->erasesize; + } + + *net_size = part->size - bb_delta; +} + /** * Format and print out a partition list for each device from global device * list. @@ -1222,7 +1239,9 @@ static void list_partitions(void) struct list_head *dentry, *pentry; struct part_info *part; struct mtd_device *dev; + struct mtd_info *mtd; int part_num; + u32 net_size;
debug("\n---list_partitions---\n"); list_for_each(dentry, &devices) { @@ -1230,14 +1249,19 @@ static void list_partitions(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\toffset\t\tmask_flags\n"); + printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n"); + + if(get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return;
/* list partitions for given device */ part_num = 0; list_for_each(pentry, &dev->parts) { part = list_entry(pentry, struct part_info, link); - printf("%2d: %-20s0x%08x\t0x%08x\t%d\n", + net_part_size(mtd,part,&net_size); + printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n", part_num, part->name, part->size, + net_size, part->size == net_size ? " " : " (!)", part->offset, part->mask_flags);
part_num++;

On Tuesday 01 June 2010 22:23:45 Ben Gardiner wrote:
This patch adds an additional column to the output of list_partitions. The additional column will contain the net size and a '(!)' beside it if the net size is not equal to the partition size.
Please find some comments below.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
common/cmd_mtdparts.c | 28 ++++++++++++++++++++++++++-- 1 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 7a9768f..24d27b9 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1213,6 +1213,23 @@ static int generate_mtdparts_save(char *buf, u32 buflen) return ret; }
+/** get the net size (w/o bad blocks) of the given partition
- @param mtd the mtd info
- @param part the partition
- @param net_size the calculated net size of this partition (output)
- */
+static void net_part_size(struct mtd_info *mtd, struct part_info *part, u32 *net_size) +{
- u32 i, bb_delta = 0;
- for(i = 0; i < part->size; i += mtd->erasesize) {
if(mtd->block_isbad(mtd, part->offset + i))
Please add spaces after "for" and "if".
bb_delta += mtd->erasesize;
- }
- *net_size = part->size - bb_delta;
+}
Why don't you return net_size instead:
static u32 net_part_size(struct mtd_info *mtd, struct part_info *part) { ... return part->size - bb_delta; }
Thanks.
Cheers, Stefan
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-0 Fax: (+49)-8142-66989-80 Email: office@denx.de

This patch introduces the 'spread' sub-command of the mtdparts command. This command will modify the existing mtdparts variable by increasing the size of the partitions such that 1) each partition's net size is at least as large as the size specified in the mtdparts variable and 2) each partition starts on a good block.
The new subcommand is implemented by iterating over the mtd device partitions and collecting a bad blocks count in each -- including any trailing bad blocks -- and then modifying that partitions's part_info structure and checking if the modification affects the next partition.
---
This patch is based on the idea of Harald Welte laforge@gnumonks.org and the comments of Wolfgang Denk wd@denx.de in the review at http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43549 .
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/cmd_mtdparts.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 105 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 24d27b9..ae5e97d 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1392,6 +1392,95 @@ static int delete_partition(const char *id) return 1; }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) +/** Increase the size of the given partition so that it's net size is at least + * as large as the size member and such that the next parition would start on a + * good blcok if it were adjacent to this partition + * @param mtd the mtd device + * @param part the partition + * @param next_offset pointer to the offset of the next partition after this + * partition's size has been modified (output) + */ +static void spread_partition(struct mtd_info *mtd, struct part_info *part, u32 *next_offset) +{ + u32 i, bb_delta = 0; + + for(i = part->offset; + i - bb_delta < part->offset + part->size; + i += mtd->erasesize) { + if(mtd->block_isbad(mtd, i)) + bb_delta += mtd->erasesize; + } + + /* Absorb bad blocks immeadiately following this + * partition also into the partition, such that + * the next partition starts with a good block. + */ + while(i < mtd->size && mtd->block_isbad(mtd, i)) { + bb_delta += mtd->erasesize; + i += mtd->erasesize; + } + + if(part->offset + part->size + bb_delta > mtd->size) { + part->size = mtd->size - part->offset - bb_delta; + printf("truncated partition %s to %d bytes\n", part->name, part->size); + } + + part->size += bb_delta; + *next_offset = part->offset + part->size; +} + +/** + * Adjust all of the partition sizes, such that all partitions are at least + * as big as their mtdparts environment variable sizes and they each start + * on a good block. + * + * @return 0 on success, 1 otherwise + */ +static int spread_partitions(void) +{ + struct list_head *dentry, *pentry; + struct mtd_device *dev; + struct part_info *part; + struct mtd_info *mtd; + int part_num; + u32 cur_offs; + + list_for_each(dentry, &devices) { + dev = list_entry(dentry, struct mtd_device, link); + + if(get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; + + part_num = 0; + cur_offs = 0; + list_for_each(pentry, &dev->parts) { + part = list_entry(pentry, struct part_info, link); + + debug("spread_partitions: device = %s%d, partition %d =" + " (%s) 0x%08x@0x%08x\n", + MTD_DEV_TYPE(dev->id->type), dev->id->num, part_num, + part->name, part->size, part->offset); + + if(cur_offs > part->offset) + part->offset = cur_offs; + + spread_partition(mtd,part,&cur_offs); + + part_num++; + } + } + + index_partitions(); + + if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) { + printf("generated mtdparts too long, reseting to null\n"); + return 1; + } + return 0; +} +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + /** * Accept character string describing mtd partitions and call device_parse() * for each entry. Add created devices to the global devices list. @@ -1882,6 +1971,13 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) return delete_partition(argv[2]); }
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if((argc == 2) && (strcmp(argv[1], "spread") == 0)) { + + return spread_partitions(); + } +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ + cmd_usage(cmdtp); return 1; } @@ -1906,7 +2002,15 @@ U_BOOT_CMD( "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" "mtdparts default\n" - " - reset partition table to defaults\n\n" + " - reset partition table to defaults\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts spread\n" + " - adjust the sizes of the partitions so they are\n" + " at least as big as the mtdparts variable specifies\n" + " and they each start on a good block\n\n" +#else + "\n" +#endif /* CONFIG_CMD_MTDPARTS_SPREAD */ "-----\n\n" "this command uses three environment variables:\n\n" "'partition' - keeps current partition identifier\n\n"

This patch adds a new 'mtdparts add' variant: add.e (with a synomym add.i). This command variant adds a new partition to the mtdparts variable but also increases the partitions size by skipping bad blocks and aggregating any additional bad blocks found at the end of the partition.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/cmd_mtdparts.c | 36 +++++++++++++++++++++++++++++++++++- 1 files changed, 35 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index ae5e97d..9ff33a7 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -1908,9 +1908,14 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) }
/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */ - if (((argc == 5) || (argc == 6)) && (strcmp(argv[1], "add") == 0)) { + if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) { #define PART_ADD_DESC_MAXLEN 64 char tmpbuf[PART_ADD_DESC_MAXLEN]; +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + char *s; + struct mtd_info *mtd; + u32 next_offset; +#endif u8 type, num, len; struct mtd_device *dev; struct mtd_device *dev_tmp; @@ -1945,11 +1950,36 @@ int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type), dev->id->num, dev->id->mtd_id);
+#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + s = strchr(argv[1], '.'); + + if(get_mtd_info(dev->id->type, dev->id->num, &mtd)) + return 1; +#endif + if ((dev_tmp = device_find(dev->id->type, dev->id->num)) == NULL) { +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + p = list_entry(dev->parts.next, + struct part_info, link); + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n",p->name, + p->size); + } +#endif device_add(dev); } else { /* merge new partition with existing ones*/ p = list_entry(dev->parts.next, struct part_info, link); +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + if (!strcmp(s, ".e") || !strcmp(s, ".i")) { + spread_partition(mtd, p, &next_offset); + + debug("increased %s to %d bytes\n",p->name, + p->size); + } +#endif if (part_add(dev_tmp, p) != 0) { device_del(dev); return 1; @@ -2001,6 +2031,10 @@ U_BOOT_CMD( " - delete partition (e.g. part-id = nand0,1)\n" "mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" " - add partition\n" +#if defined(CONFIG_CMD_MTDPARTS_SPREAD) + "mtdparts add.e <mtd-dev> <size>[@<offset>] [<name>] [ro]\n" + " - add partition, padding size by skipping bad blocks\n" +#endif "mtdparts default\n" " - reset partition table to defaults\n" #if defined(CONFIG_CMD_MTDPARTS_SPREAD)

On Tue, Jul 08, 2008 at 11:05:10AM -0500, Scott Wood wrote:
Therefore, I still believe that such a feature is useful and should be merged into u-boot. If there are problems with my particular implementation, I'm happy to address them.
Can you base it off of the testing branch of the u-boot-nand-flash repo?
Will do. Probably by tomorrow.
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
Interesting... Would such a patch eliminate the need for this one, by making the environment a dynamic partition? Is there any (plan for) Linux support?
no, the dynamic partition table is calculated once and then stored in the environment. Therefore, the environment has to be either stored outside the NAND flash, or located by the OOB marker. Therefore, the dynpart patch bases on top of the dynenv patch.
Linux has no need to support it, since it gets the device-unique partition table passed by the standard commandline mtdparts mechanism.

Hi again!
On Tue, Jul 08, 2008 at 11:05:10AM -0500, Scott Wood wrote:
Therefore, I still believe that such a feature is useful and should be merged into u-boot. If there are problems with my particular implementation, I'm happy to address them.
Can you base it off of the testing branch of the u-boot-nand-flash repo?
I just tried it, and the patch actually applies cleanly to the nand-flash/testing branch. So feel free to go ahead and merge :)
I also have another patchset for what I call 'dynpart' support, i.e. the dynamic calculation of a unit-specific partition table that ensures the net size of partitions are as per spec, no matter how many of the factory default blocks are located where. So it would even support NAND devices with a worse spec than the ones that we were using.
I'll base that also on top of nand-flash/testing and submit later. Will probably need some generalization for the board-level integration.
Cheers,

On Wed, Jul 09, 2008 at 01:28:42PM +0800, Harald Welte wrote:
Therefore, I still believe that such a feature is useful and should be merged into u-boot. If there are problems with my particular implementation, I'm happy to address them.
Can you base it off of the testing branch of the u-boot-nand-flash repo?
I just tried it, and the patch actually applies cleanly to the nand-flash/testing branch. So feel free to go ahead and merge :)
sorry, my fault. cleanly applying patch doesn't mean it actually compiles and/or uses the api's correctly.
I'm sending an updated patch ASAP.

Hi Scott!
This is now the 'dynenv' patch based on the u-boot-nand-flash/testing git tree:
==========================
NAND: Support dynamic location of enviromnent (CFG_ENV_OFFSET_OOB)
This patch enables the environment parttion to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CFG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'dynenv' command, which can be called from the u-boot command line. 'dynenv get' reads the address of the environment partition from the OOB data, 'dynenv set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
Signed-off-by: Harald Welte laforge@openmoko.org
diff --git a/common/Makefile b/common/Makefile index ecf755f..d1d50bf 100644 --- a/common/Makefile +++ b/common/Makefile @@ -55,6 +55,7 @@ endif COBJS-$(CONFIG_CMD_DISPLAY) += cmd_display.o COBJS-$(CONFIG_CMD_DOC) += cmd_doc.o COBJS-$(CONFIG_CMD_DTT) += cmd_dtt.o +COBJS-y += cmd_dynenv.o COBJS-y += cmd_eeprom.o COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o diff --git a/common/cmd_dynenv.c b/common/cmd_dynenv.c new file mode 100644 index 0000000..8fd21c1 --- /dev/null +++ b/common/cmd_dynenv.c @@ -0,0 +1,121 @@ +/* + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Harald Welte laforge@openmoko.org + * (C) Copyright 2008 Harald Welte laforge@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <environment.h> +#include <nand.h> +#include "cmd_nand.h" +#include <asm/errno.h> + +#if defined(CFG_ENV_OFFSET_OOB) + +int do_dynenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + struct mtd_info *mtd = &nand_info[0]; + int ret, size = 8; + struct mtd_oob_ops ops; + uint8_t *buf; + + char *cmd = argv[1]; + + buf = malloc(mtd->oobsize); + if (!buf) + return -ENOMEM; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = size; + + ret = mtd->read_oob(mtd, 8, &ops); + if (!strcmp(cmd, "get")) { + + if (buf[0] == 'E' && buf[1] == 'N' && + buf[2] == 'V' && buf[3] == '0') + printf("0x%08x\n", *((u_int32_t *) &buf[4])); + else + printf("No dynamic environment marker in OOB block 0\n"); + + } else if (!strcmp(cmd, "set")) { + unsigned long addr, dummy; + + if (argc < 3) + goto usage; + + buf[0] = 'E'; + buf[1] = 'N'; + buf[2] = 'V'; + buf[3] = '0'; + + if (arg_off_size(argc-2, argv+2, mtd, &addr, &dummy, 1) < 0) { + printf("Offset or partition name expected\n"); + goto fail; + } + if (!ret) { + uint8_t tmp[4]; + int i; + + memcpy(&tmp, &addr, 4); + for (i = 0; i != 4; i++) + if (tmp[i] & ~buf[i+4]) { + printf("ERROR: erase OOB block to " + "write this value\n"); + goto fail; + } + } + memcpy(buf+4, &addr, 4); + + printf("%02x %02x %02x %02x - %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = size; + ret = mtd->write_oob(mtd, 8, &ops); + if (!ret) + CFG_ENV_OFFSET = addr; + } else + goto usage; + + free(buf); + return ret; + +usage: + printf("Usage:\n%s\n", cmdtp->usage); +fail: + free(buf); + return 1; +} + +U_BOOT_CMD(dynenv, 3, 1, do_dynenv, + "dynenv - dynamically placed (NAND) environment\n", + "dynenv set off - set enviromnent offset\n" + "dynenv get - get environment offset\n"); + +#endif /* CFG_ENV_OFFSET_OOB */ diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 7c26ceb..a72e553 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -101,7 +101,7 @@ static inline int str2long(char *p, ulong *num) return (*p != '\0' && *endptr == '\0') ? 1 : 0; }
-static int +int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) { int idx = nand_curr_device; diff --git a/common/cmd_nand.h b/common/cmd_nand.h new file mode 100644 index 0000000..bedcda9 --- /dev/null +++ b/common/cmd_nand.h @@ -0,0 +1,33 @@ +/* + * cmd_nand.h - Convenience functions + * + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Werner Almesberger werner@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef CMD_NAND_H +#define CMD_NAND_H + +#include <nand.h> + + +/* common/cmd_nand.c */ +int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, + ulong *size, int net); + +#endif /* CMD_NAND_H */ diff --git a/common/env_nand.c b/common/env_nand.c index 1fe874a..14ea480 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -292,6 +292,33 @@ void env_relocate_spec (void) int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1, *tmp_env2;
+#if defined(CFG_ENV_OFFSET_OOB) + struct mtd_info *mtd = &nand_info[0]; + struct nand_chip *this = mtd->priv; + int buf_len; + uint8_t *buf; + + buf_len = (1 << this->bbt_erase_shift); + buf_len += (buf_len >> this->page_shift) * mtd->oobsize; + buf = malloc(buf_len); + if (!buf) + return; + + nand_read_raw(mtd, buf, 0, mtd->oobblock, mtd->oobsize); + if (buf[mtd->oobblock + 8 + 0] == 'E' && + buf[mtd->oobblock + 8 + 1] == 'N' && + buf[mtd->oobblock + 8 + 2] == 'V' && + buf[mtd->oobblock + 8 + 3] == '0') { + CFG_ENV_OFFSET = *((unsigned long *) &buf[mtd->oobblock + 8 + 4]); + /* fall through to the normal environment reading code below */ + free(buf); + puts("Found Environment offset in OOB..\n"); + } else { + free(buf); + return use_default(); + } +#endif + total = CFG_ENV_SIZE;
tmp_env1 = (env_t *) malloc(CFG_ENV_SIZE); diff --git a/common/environment.c b/common/environment.c index 3b9914f..50d5210 100644 --- a/common/environment.c +++ b/common/environment.c @@ -29,6 +29,12 @@ #undef __ASSEMBLY__ #include <environment.h>
+#if defined(CFG_ENV_OFFSET_PATCHED) +unsigned long env_offset = CFG_ENV_OFFSET_PATCHED; +#elif defined(CFG_ENV_OFFSET_OOB) +unsigned long env_offset = CFG_ENV_OFFSET_OOB; +#endif + /* * Handle HOSTS that have prepended * crap on symbol names, not TARGETS. diff --git a/include/environment.h b/include/environment.h index bf9f669..3256044 100644 --- a/include/environment.h +++ b/include/environment.h @@ -70,6 +70,10 @@ #endif /* CFG_ENV_IS_IN_FLASH */
#if defined(CFG_ENV_IS_IN_NAND) +#if defined(CFG_ENV_OFFSET_PATCHED) || defined(CFG_ENV_OFFSET_OOB) +extern unsigned long env_offset; +#define CFG_ENV_OFFSET env_offset +#else # ifndef CFG_ENV_OFFSET # error "Need to define CFG_ENV_OFFSET when using CFG_ENV_IS_IN_NAND" # endif @@ -82,6 +86,7 @@ # ifdef CFG_ENV_IS_EMBEDDED # define ENV_IS_EMBEDDED 1 # endif +#endif /* CFG_ENV_NAND_PATCHED */ #endif /* CFG_ENV_IS_IN_NAND */
#ifdef USE_HOSTCC

On Wed, Jul 09, 2008 at 04:11:29PM +0800, Harald Welte wrote:
+#if defined(CFG_ENV_OFFSET_OOB)
Can you push this conditional into the Makefile?
+int do_dynenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{
- struct mtd_info *mtd = &nand_info[0];
- int ret, size = 8;
- struct mtd_oob_ops ops;
- uint8_t *buf;
- char *cmd = argv[1];
- buf = malloc(mtd->oobsize);
- if (!buf)
return -ENOMEM;
- ops.mode = MTD_OOB_RAW;
Use MTD_OOB_AUTO, or else you'll conflict with bad block markers, ECC, etc.
- ops.ooboffs = 0;
- ops.ooblen = mtd->oobsize;
- ops.oobbuf = buf;
- ops.datbuf = buf;
You're passing the same buffer for data and oob? And the buffer is only oob-sized?
- ops.len = size;
- ret = mtd->read_oob(mtd, 8, &ops);
- if (!strcmp(cmd, "get")) {
if (buf[0] == 'E' && buf[1] == 'N' &&
buf[2] == 'V' && buf[3] == '0')
Use strcmp().
printf("0x%08x\n", *((u_int32_t *) &buf[4]));
This violates C99 aliasing rules, and could cause unaligned accesses (likewise elsewhere).
Use a union, or just declare it as uint32_t or u32 (not u_int32_t) and cast to char for strcmp() or define the magic value as an integer rather than a string.
The last option is probably the best, in terms of keeping code simple and small.
else
printf("No dynamic environment marker in OOB block 0\n");
- } else if (!strcmp(cmd, "set")) {
No blank lines at beginning/end of blocks.
unsigned long addr, dummy;
if (argc < 3)
goto usage;
buf[0] = 'E';
buf[1] = 'N';
buf[2] = 'V';
buf[3] = '0';
if (arg_off_size(argc-2, argv+2, mtd, &addr, &dummy, 1) < 0) {
Spaces around operators (here and elsewhere).
printf("Offset or partition name expected\n");
goto fail;
}
if (!ret) {
Why are you checking the success of read_oob here, and not for "get"?
uint8_t tmp[4];
int i;
memcpy(&tmp, &addr, 4);
for (i = 0; i != 4; i++)
if (tmp[i] & ~buf[i+4]) {
printf("ERROR: erase OOB block to "
"write this value\n");
goto fail;
}
}
This would be much simpler if you used integer accesses on a u32 array.
What does "erase OOB block" mean? Don't you mean "erase block zero"?
Are you really intending to support changing the location multiple times per erase, as long as bits are only cleared? Sure, it's technically possible, but still...
ret = mtd->write_oob(mtd, 8, &ops);
if (!ret)
CFG_ENV_OFFSET = addr;
And silently fail if write_oob fails?
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 7c26ceb..a72e553 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -101,7 +101,7 @@ static inline int str2long(char *p, ulong *num) return (*p != '\0' && *endptr == '\0') ? 1 : 0; }
-static int +int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size)
This is too generic a name for a global function that is NAND-specific.
-Scott

This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com in http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916.
This patch was made against master branch of git://git.denx.de/u-boot-nand-flash.git with the recent patch from Wolfgang Denk to fix the undefined __aeabi_unwind_cpp_pr0() problem: http://download.gmane.org/gmane.comp.boot-loaders.u-boot/78612/78613
Testing was performed with the patch applied to the master branch of git://arago-project.org/git/people/sekhar/u-boot-omapl1.git , which is forked from uboot's v2009.11 a200a7c04d89853d2a1395b96d8ca5e3dd754551 tag -- this is because the board I am actually using -- the da850 / OMAP L138 -- does not have config.
This patch enables the environment parttion to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'dynenv' command, which can be called from the u-boot command line. 'dynenv get' reads the address of the environment partition from the OOB data, 'dynenv set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/Makefile | 1 + common/cmd_dynenv.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ common/cmd_nand.c | 10 ++-- common/cmd_nand.h | 33 ++++++++++++++ common/env_nand.c | 52 +++++++++++++++++++++++ include/environment.h | 21 +++++++--- include/nand.h | 6 +++ 7 files changed, 224 insertions(+), 11 deletions(-) create mode 100644 common/cmd_dynenv.c create mode 100644 common/cmd_nand.h
diff --git a/common/Makefile b/common/Makefile index dbf7a05..83520d7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -85,6 +85,7 @@ COBJS-$(CONFIG_CMD_DIAG) += cmd_diag.o endif COBJS-$(CONFIG_CMD_DISPLAY) += cmd_display.o COBJS-$(CONFIG_CMD_DTT) += cmd_dtt.o +COBJS-$(CONFIG_ENV_OFFSET_OOB) += cmd_dynenv.o COBJS-$(CONFIG_CMD_ECHO) += cmd_echo.o COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += cmd_eeprom.o COBJS-$(CONFIG_CMD_EEPROM) += cmd_eeprom.o diff --git a/common/cmd_dynenv.c b/common/cmd_dynenv.c new file mode 100644 index 0000000..5167875 --- /dev/null +++ b/common/cmd_dynenv.c @@ -0,0 +1,112 @@ +/* + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Harald Welte laforge@openmoko.org + * (C) Copyright 2008 Harald Welte laforge@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <malloc.h> +#include <environment.h> +#include <nand.h> +#include "cmd_nand.h" +#include <asm/errno.h> + +unsigned long env_offset; + +int do_dynenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int ret; + uint32_t *oob_buf; + + char *cmd = argv[1]; + + if (!strcmp(cmd, "get")) { + ret = get_dynenv(&CONFIG_ENV_OFFSET); + if(!ret) { + printf("0x%08lx\n", CONFIG_ENV_OFFSET); + } + else { + return 1; + } + } + else if (!strcmp(cmd, "set")) { + ulong addr, dummy_size; + struct mtd_info *mtd = &nand_info[0]; + struct mtd_oob_ops ops; + + if (argc < 3) + goto usage; + + if (nand_arg_off_size(argc-2, argv + 2, mtd, &addr, &dummy_size, 1) < 0) { + printf("Offset or partition name expected\n"); + return 1; + } + + if(mtd->oobavail < CONFIG_ENV_OFFSET_SIZE){ + printf("Insufficient available OOB bytes: %d OOB bytes available but %d required for dynenv support\n",mtd->oobavail,8); + } + + oob_buf = malloc(mtd->oobsize); + if(!oob_buf) + return -ENOMEM; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = CONFIG_ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = mtd->read_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops); + oob_buf[0] = ENV_OOB_MARKER; + + if (!ret) { + if(addr & ~oob_buf[1]) { + printf("ERROR: erase OOB block 0 to " + "write this value\n"); + goto fail; + } + } + oob_buf[1] = addr; + + ret = mtd->write_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops); + if (!ret) + CONFIG_ENV_OFFSET = addr; + else { + printf("Error writing OOB block 0\n"); + goto fail; + } + + free(oob_buf); + } else + goto usage; + + return ret; + +fail: + free(oob_buf); + return 1; +usage: + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD(dynenv, 4, 1, do_dynenv, + "dynenv - dynamically placed (NAND) environment", + "set off - set enviromnent offset\n" + "dynenv get - get environment offset"); \ No newline at end of file diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 9b0c930..7575904 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -89,8 +89,8 @@ static inline int str2long(char *p, ulong *num) return (*p != '\0' && *endptr == '\0') ? 1 : 0; }
-static int -arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) +int +nand_arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size) { int idx = nand_curr_device; #if defined(CONFIG_CMD_MTDPARTS) @@ -305,7 +305,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
printf("\nNAND %s: ", scrub ? "scrub" : "erase"); /* skip first two or three arguments, look for offset and size */ - if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) + if (nand_arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) return 1;
memset(&opts, 0, sizeof(opts)); @@ -372,7 +372,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); - if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) + if (nand_arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1;
s = strchr(cmd, '.'); @@ -462,7 +462,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) }
if (strcmp(cmd, "unlock") == 0) { - if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) + if (nand_arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) return 1;
if (!nand_unlock(nand, off, size)) { diff --git a/common/cmd_nand.h b/common/cmd_nand.h new file mode 100644 index 0000000..023ed4f --- /dev/null +++ b/common/cmd_nand.h @@ -0,0 +1,33 @@ +/* + * cmd_nand.h - Convenience functions + * + * (C) Copyright 2006-2007 OpenMoko, Inc. + * Author: Werner Almesberger werner@openmoko.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef CMD_NAND_H +#define CMD_NAND_H + +#include <nand.h> + + +/* common/cmd_nand.c */ +int nand_arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, + ulong *size, int net); + +#endif /* CMD_NAND_H */ diff --git a/common/env_nand.c b/common/env_nand.c index a15a950..ee55877 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -38,6 +38,7 @@ #include <linux/stddef.h> #include <malloc.h> #include <nand.h> +#include <asm/errno.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -288,6 +289,46 @@ int readenv (size_t offset, u_char * buf) return 0; }
+#ifdef CONFIG_ENV_OFFSET_OOB +int get_dynenv(unsigned long *result) +{ + struct mtd_info *mtd = &nand_info[0]; + struct mtd_oob_ops ops; + uint32_t *oob_buf; + int ret; + + oob_buf = malloc(mtd->oobsize); + if(!oob_buf) + return -ENOMEM; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = CONFIG_ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = mtd->read_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops); + + if(!ret) { + if(oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1]; + } + else { + printf("No dynamic environment marker in OOB block 0\n"); + ret = -ENOENT; + goto fail; + } + } + else { + printf("error reading OOB block 0\n"); + } +fail: + free(oob_buf); + + return ret; +} +#endif + #ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -357,12 +398,23 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_dynenv(&CONFIG_ENV_OFFSET); + if(!ret) { + /* fall through to the normal environment reading code below */ + printf("Found Environment offset in OOB..\n"); + } else { + return use_default(); + } +#endif + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default();
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); + #endif /* ! ENV_IS_EMBEDDED */ } #endif /* CONFIG_ENV_OFFSET_REDUND */ diff --git a/include/environment.h b/include/environment.h index b9924fd..03b6c92 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB is set" +# endif +extern unsigned long env_offset; +# undef CONFIG_ENV_OFFSET +# define CONFIG_ENV_OFFSET env_offset +# else +# ifndef CONFIG_ENV_OFFSET +# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" +# endif +# ifdef CONFIG_ENV_OFFSET_REDUND +# define CONFIG_SYS_REDUNDAND_ENVIRONMENT +# endif +# endif /* CONFIG_ENV_OFFSET_OOB */ # ifndef CONFIG_ENV_SIZE # error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_NAND" # endif -# ifdef CONFIG_ENV_OFFSET_REDUND -# define CONFIG_SYS_REDUNDAND_ENVIRONMENT -# endif #endif /* CONFIG_ENV_IS_IN_NAND */
#if defined(CONFIG_ENV_IS_IN_MG_DISK) diff --git a/include/nand.h b/include/nand.h index 2a81597..29238c8 100644 --- a/include/nand.h +++ b/include/nand.h @@ -130,3 +130,9 @@ void board_nand_select_device(struct nand_chip *nand, int chip); __attribute__((noreturn)) void nand_boot(void);
#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +#define ENV_OOB_MARKER 0x30564e45 //"ENV0" in little-endian +#define CONFIG_ENV_OFFSET_SIZE 8 +int get_dynenv(unsigned long *result); +#endif

On Mon, May 17, 2010 at 05:04:30PM -0400, Ben Gardiner wrote:
diff --git a/common/cmd_dynenv.c b/common/cmd_dynenv.c new file mode 100644 index 0000000..5167875 --- /dev/null +++ b/common/cmd_dynenv.c @@ -0,0 +1,112 @@ +/*
- (C) Copyright 2006-2007 OpenMoko, Inc.
- Author: Harald Welte laforge@openmoko.org
- (C) Copyright 2008 Harald Welte laforge@openmoko.org
Is this correct and up-to-date?
+unsigned long env_offset;
This is a pretty generic name for something NAND-specific -- as is "dynenv".
Maybe this should be a nand subcommand? Putting it in cmd_nand.c would also eliminate the need to export arg_off_size().
if(mtd->oobavail < CONFIG_ENV_OFFSET_SIZE){
Please put a space after "if" and before the opening brace (in fact, there's no need for the braces at all on this one-liner).
printf("Insufficient available OOB bytes: %d OOB bytes available but %d required for dynenv support\n",mtd->oobavail,8);
Keep lines under 80 columns (both in source code and in output), and put spaces after commas.
}
oob_buf = malloc(mtd->oobsize);
if(!oob_buf)
return -ENOMEM;
Let the user know it didn't work?
You only really need 8 bytes, why not just put it on the stack? Likewise in get_dynenv().
ops.datbuf = NULL;
ops.mode = MTD_OOB_AUTO;
ops.ooboffs = 0;
ops.ooblen = CONFIG_ENV_OFFSET_SIZE;
ops.oobbuf = (void *) oob_buf;
ret = mtd->read_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops);
oob_buf[0] = ENV_OOB_MARKER;
if (!ret) {
if(addr & ~oob_buf[1]) {
printf("ERROR: erase OOB block 0 to "
"write this value\n");
You cannot erase OOB without erasing the entire block, so this message is a little confusing.
Do you really expect to make use of the ability to set a new address without erasing, if it only clears bits?
ret = mtd->write_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops);
if (!ret)
CONFIG_ENV_OFFSET = addr;
else {
printf("Error writing OOB block 0\n");
goto fail;
}
If you put braces on one side of the else, put them on both.
free(oob_buf);
- } else
goto usage;
Likewise.
Is there anything you can do on "dynenv set" so that the user won't have to reboot after setting the dynenv to be able to saveenv into the new environment?
+U_BOOT_CMD(dynenv, 4, 1, do_dynenv,
- "dynenv - dynamically placed (NAND) environment",
- "set off - set enviromnent offset\n"
- "dynenv get - get environment offset");
\ No newline at end of file
Please put a newline at the end of the file.
diff --git a/common/cmd_nand.h b/common/cmd_nand.h new file mode 100644 index 0000000..023ed4f --- /dev/null +++ b/common/cmd_nand.h @@ -0,0 +1,33 @@ +/*
- cmd_nand.h - Convenience functions
- (C) Copyright 2006-2007 OpenMoko, Inc.
- Author: Werner Almesberger werner@openmoko.org
Is this really the right copyright/authorship for this file?
+#ifndef CMD_NAND_H +#define CMD_NAND_H
+#include <nand.h>
+/* common/cmd_nand.c */ +int nand_arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off,
- ulong *size, int net);
Just put it in nand.h.
- if(!ret) {
if(oob_buf[0] == ENV_OOB_MARKER) {
*result = oob_buf[1];
Should probably encode the environment location as a block number, rather than as a byte, for flashes larger than 4GiB (there are other places in the environment handling where this won't work, but let's not add more).
}
else {
} else {
#ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -357,12 +398,23 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB)
- ret = get_dynenv(&CONFIG_ENV_OFFSET);
Taking the address of a macro looks really weird. This will only work if the macro is defined as env_offset, so why not just use env_offset?
ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default();
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default();
#endif /* ! ENV_IS_EMBEDDED */
Unrelated whitespace change, please leave out.
#endif /* CONFIG_ENV_OFFSET_REDUND */ diff --git a/include/environment.h b/include/environment.h index b9924fd..03b6c92 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB is set" +# endif +extern unsigned long env_offset; +# undef CONFIG_ENV_OFFSET +# define CONFIG_ENV_OFFSET env_offset
Don't undef CONFIG_ENV_OFFSET, we want the build to fail if the user tries to do it both ways (just like if CONFIG_ENV_OFFSET_REDUND is specified).
+#define CONFIG_ENV_OFFSET_SIZE 8
Why is this configurable?
-Scott

Thank you for the thorough review, Scott.
On Wed, May 26, 2010 at 6:58 PM, Scott Wood scottwood@freescale.com wrote:
On Mon, May 17, 2010 at 05:04:30PM -0400, Ben Gardiner wrote:
diff --git a/common/cmd_dynenv.c b/common/cmd_dynenv.c new file mode 100644 index 0000000..5167875 --- /dev/null +++ b/common/cmd_dynenv.c @@ -0,0 +1,112 @@ +/*
- (C) Copyright 2006-2007 OpenMoko, Inc.
- Author: Harald Welte laforge@openmoko.org
- (C) Copyright 2008 Harald Welte laforge@openmoko.org
Is this correct and up-to-date?
The code was taken from Harald's patches he submitted to the mailing list. I didn't do much to them except to re-factor the 'get' functionality. Should I have added myself / my organization to the copyright list? I figured my changes were not enough to warrant the additional attribution of copyright.
+unsigned long env_offset;
This is a pretty generic name for something NAND-specific -- as is "dynenv".
Maybe this should be a nand subcommand? Putting it in cmd_nand.c would also eliminate the need to export arg_off_size().
I'm glad you brought this up. I was wondering if the command might be better suited under the nand command -- I agree that it would eliminate the export of arg_off_size and all the renames, which would be very nice. Would 'nand dynenv' do? How about 'nand env.oob' ?
- if(mtd->oobavail < CONFIG_ENV_OFFSET_SIZE){
Please put a space after "if" and before the opening brace (in fact, there's no need for the braces at all on this one-liner).
will do
- printf("Insufficient available OOB bytes: %d OOB bytes available but %d required for dynenv support\n",mtd->oobavail,8);
Keep lines under 80 columns (both in source code and in output), and put spaces after commas.
sorry about that, I should know better :)
- }
- oob_buf = malloc(mtd->oobsize);
- if(!oob_buf)
- return -ENOMEM;
Let the user know it didn't work?
You only really need 8 bytes, why not just put it on the stack? Likewise in get_dynenv().
good ideas
- ops.datbuf = NULL;
- ops.mode = MTD_OOB_AUTO;
- ops.ooboffs = 0;
- ops.ooblen = CONFIG_ENV_OFFSET_SIZE;
- ops.oobbuf = (void *) oob_buf;
- ret = mtd->read_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops);
- oob_buf[0] = ENV_OOB_MARKER;
- if (!ret) {
- if(addr & ~oob_buf[1]) {
- printf("ERROR: erase OOB block 0 to "
- "write this value\n");
You cannot erase OOB without erasing the entire block, so this message is a little confusing.
Do you really expect to make use of the ability to set a new address without erasing, if it only clears bits?
No, I suppose not. The main use-case seems to be 'dynenv set' on a freshly erased nand when using u-boot for programming. OTOH I have noticed that (one of) the nand erase utility that comes with the TI OMAP L138 EVM erases the NAND w/o erasing the OOB; so it was useful during testing to know if I was able to write the correct value to the OOB. I could replace this with a read-back check to accomplish the same task -- this will also make 'set' commit the new offset 'live' (see below)
- ret = mtd->write_oob(mtd, CONFIG_ENV_OFFSET_SIZE, &ops);
- if (!ret)
- CONFIG_ENV_OFFSET = addr;
- else {
- printf("Error writing OOB block 0\n");
- goto fail;
- }
If you put braces on one side of the else, put them on both.
- free(oob_buf);
- } else
- goto usage;
Likewise.
will do
Is there anything you can do on "dynenv set" so that the user won't have to reboot after setting the dynenv to be able to saveenv into the new environment?
Good catch. Yes I think this could also be handled by replacing the OOB bit-check with a call and check of the 'dynenv get' command after set. In this way the global variable that has been swapped inplace of CONFIG_ENV_OFFSET will have the value of the newly set dynamic environment.
+U_BOOT_CMD(dynenv, 4, 1, do_dynenv,
- "dynenv - dynamically placed (NAND) environment",
- "set off - set enviromnent offset\n"
- "dynenv get - get environment offset");
\ No newline at end of file
Please put a newline at the end of the file.
sorry I really should have caught that one.
diff --git a/common/cmd_nand.h b/common/cmd_nand.h new file mode 100644 index 0000000..023ed4f --- /dev/null +++ b/common/cmd_nand.h @@ -0,0 +1,33 @@ +/*
- cmd_nand.h - Convenience functions
- (C) Copyright 2006-2007 OpenMoko, Inc.
- Author: Werner Almesberger werner@openmoko.org
Is this really the right copyright/authorship for this file?
see answer above; but this file will be removed in the next version since I will stick the dynenv command under the nand command.
- if(!ret) {
- if(oob_buf[0] == ENV_OOB_MARKER) {
- *result = oob_buf[1];
Should probably encode the environment location as a block number, rather than as a byte, for flashes larger than 4GiB (there are other places in the environment handling where this won't work, but let's not add more).
good idea
- }
- else {
} else {
#ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -357,12 +398,23 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB)
- ret = get_dynenv(&CONFIG_ENV_OFFSET);
Taking the address of a macro looks really weird. This will only work if the macro is defined as env_offset, so why not just use env_offset?
Yeah... it's pretty weird, I'll subs env_offset here. :)
ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default();
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default();
#endif /* ! ENV_IS_EMBEDDED */
Unrelated whitespace change, please leave out.
will do
#endif /* CONFIG_ENV_OFFSET_REDUND */ diff --git a/include/environment.h b/include/environment.h index b9924fd..03b6c92 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB is set" +# endif +extern unsigned long env_offset; +# undef CONFIG_ENV_OFFSET +# define CONFIG_ENV_OFFSET env_offset
Don't undef CONFIG_ENV_OFFSET, we want the build to fail if the user tries to do it both ways (just like if CONFIG_ENV_OFFSET_REDUND is specified).
will do
+#define CONFIG_ENV_OFFSET_SIZE 8
Why is this configurable?
Can't think of a good reason. I'll just make a #define in a restricted scope to avoid magic number 8.
Best Regards, Ben Gardiner

This is a re-submission of the patch by Harald Welte laforge@openmoko.org with modifications to make it a sub-command and changes as suggested by Scott Wood scottwood@freescale.com in http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916.
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'nand env.oob' command, which can be called from the u-boot command line. 'nand env.oob get' reads the address of the environment partition from the OOB data, 'nand env.oob set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
---
Changes in v2: * don't use generic names for the env-offset global and the comand * make a sub-command of the nand command * store the offset in units of eraseblocks * allocate oob write/read buffers on stack * verify write of new offset * make setting new offset affect the live values * update copyright of file * use the global variable instead of the macro in address statements * don't make the oob used bytes configurable * don't undef CONFIG_ENV_OFFSET
Tested with the da850 support patch series from Sudhakar Rjashekhara [1]
./MAKEALL arm on master --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
./MAKEALL arm with the patch --------------------- SUMMARY ---------------------------- Boards compiled: 150 Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50 SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3 afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2 CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570 pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4 ixdp425 ixdpg425 pdnb3 scpu ) ----------------------------------------------------------
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/78947
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca --- common/cmd_nand.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 45 ++++++++++++++++++++ include/environment.h | 20 ++++++--- include/nand.h | 9 ++++ 4 files changed, 174 insertions(+), 7 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index f611fd7..5af5b9e 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -6,6 +6,8 @@ * * Added 16-bit nand support * (C) 2004 Texas Instruments + * Added env offset in OOB + * (C) 2010 Nanometrics, Inc. */
#include <common.h> @@ -193,6 +195,91 @@ static void do_nand_status(nand_info_t *nand) } #endif
+#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, + int argc, char *argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + + char *cmd = argv[1]; + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (!ret) { + printf("0x%08lx\n", nand_env_oob_offset); + } + else { + return 1; + } + } else if (!strcmp(cmd, "set")) { + ulong addr; + size_t dummy_size; + struct mtd_oob_ops ops; + + if (argc < 3) + goto usage; + + if (arg_off_size(argc-2, argv + 2, nand, &addr, + &dummy_size) < 0) { + printf("Offset or partition name expected\n"); + return 1; + } + + if (nand->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes: %d OOB bytes" + " available but %d required for env.oob support\n" + ,nand->oobavail,ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (nand->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / nand->erasesize; + + ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); + if (!ret) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB " + "failed: 0x%08lx expected but got " + "0x%08lx\n", addr, nand_env_oob_offset); + return 1; + } + } else { + printf("Error writing OOB block 0\n"); + return ret; + } + } else { + goto usage; + } + + return ret; + +usage: + cmd_usage(cmdtp); + return 1; +} + +#endif + static void nand_print_info(int idx) { nand_info_t *nand = &nand_info[idx]; @@ -272,9 +359,20 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) + strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 +#ifdef CONFIG_ENV_OFFSET_OOB + && strcmp(cmd, "env.oob") != 0 +#endif + ) goto usage;
+#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd,"env.oob") == 0) { + return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); + } +#endif + /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[nand_curr_device].name) { @@ -502,6 +600,13 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section" #endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif );
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/common/env_nand.c b/common/env_nand.c index a15a950..2289eb2 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -38,6 +38,7 @@ #include <linux/stddef.h> #include <malloc.h> #include <nand.h> +#include <asm/errno.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -288,6 +289,40 @@ int readenv (size_t offset, u_char * buf) return 0; }
+#ifdef CONFIG_ENV_OFFSET_OOB +int get_nand_env_oob(nand_info_t *nand, unsigned long *result) +{ + struct mtd_oob_ops ops; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + int ret; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + + if (!ret) { + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; + } else { + printf("No dynamic environment marker in OOB block 0\n"); + ret = -ENOENT; + goto fail; + } + } + else { + printf("error reading OOB block 0\n"); + } +fail: + return ret; +} +#endif + #ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -357,6 +392,16 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); + if(!ret) { + /* fall through to the normal environment reading code below */ + printf("Found Environment offset in OOB..\n"); + } else { + return use_default(); + } +#endif + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default(); diff --git a/include/environment.h b/include/environment.h index b9924fd..9820d23 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,23 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB is set" +# endif +extern unsigned long nand_env_oob_offset; +# define CONFIG_ENV_OFFSET nand_env_oob_offset +# else +# ifndef CONFIG_ENV_OFFSET +# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" +# endif +# ifdef CONFIG_ENV_OFFSET_REDUND +# define CONFIG_SYS_REDUNDAND_ENVIRONMENT +# endif +# endif /* CONFIG_ENV_OFFSET_OOB */ # ifndef CONFIG_ENV_SIZE # error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_NAND" # endif -# ifdef CONFIG_ENV_OFFSET_REDUND -# define CONFIG_SYS_REDUNDAND_ENVIRONMENT -# endif #endif /* CONFIG_ENV_IS_IN_NAND */
#if defined(CONFIG_ENV_IS_IN_MG_DISK) diff --git a/include/nand.h b/include/nand.h index 2a81597..8bdf419 100644 --- a/include/nand.h +++ b/include/nand.h @@ -130,3 +130,12 @@ void board_nand_select_device(struct nand_chip *nand, int chip); __attribute__((noreturn)) void nand_boot(void);
#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +#define ENV_OOB_MARKER 0x30425645 /*"EVB0" in little-endian -- offset is stored + as block number*/ +#define ENV_OOB_MARKER_OLD 0x30564e45 /*"ENV0" in little-endian -- offset is + stored as byte number */ +#define ENV_OFFSET_SIZE 8 +int get_nand_env_oob(nand_info_t *nand, unsigned long *result); +#endif

This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'nand env.oob' command, which can be called from the u-boot command line. 'nand env.oob get' reads the address of the environment partition from the OOB data, 'nand env.oob set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916 [2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79195
---
Changes in v3: * updated commit message * rebased to 39ddd10b046fb791f47281ffb2100be01909ad72 of git://git.denx.de/u-boot.git * tested using small config changes to include/configs/da850evm.h
Changes in v2: * don't use generic names for the env-offset global and the comand * make a sub-command of the nand command * store the offset in units of eraseblocks * allocate oob write/read buffers on stack * verify write of new offset * make setting new offset affect the live values * update copyright of file * use the global variable instead of the macro in address statements * don't make the oob used bytes configurable * don't undef CONFIG_ENV_OFFSET
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca
--- common/cmd_nand.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 45 ++++++++++++++++++++ include/environment.h | 20 ++++++--- include/nand.h | 9 ++++ 4 files changed, 174 insertions(+), 7 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index f611fd7..5af5b9e 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -6,6 +6,8 @@ * * Added 16-bit nand support * (C) 2004 Texas Instruments + * Added env offset in OOB + * (C) 2010 Nanometrics, Inc. */
#include <common.h> @@ -193,6 +195,91 @@ static void do_nand_status(nand_info_t *nand) } #endif
+#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, + int argc, char *argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + + char *cmd = argv[1]; + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (!ret) { + printf("0x%08lx\n", nand_env_oob_offset); + } + else { + return 1; + } + } else if (!strcmp(cmd, "set")) { + ulong addr; + size_t dummy_size; + struct mtd_oob_ops ops; + + if (argc < 3) + goto usage; + + if (arg_off_size(argc-2, argv + 2, nand, &addr, + &dummy_size) < 0) { + printf("Offset or partition name expected\n"); + return 1; + } + + if (nand->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes: %d OOB bytes" + " available but %d required for env.oob support\n" + ,nand->oobavail,ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (nand->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / nand->erasesize; + + ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); + if (!ret) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB " + "failed: 0x%08lx expected but got " + "0x%08lx\n", addr, nand_env_oob_offset); + return 1; + } + } else { + printf("Error writing OOB block 0\n"); + return ret; + } + } else { + goto usage; + } + + return ret; + +usage: + cmd_usage(cmdtp); + return 1; +} + +#endif + static void nand_print_info(int idx) { nand_info_t *nand = &nand_info[idx]; @@ -272,9 +359,20 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) + strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 +#ifdef CONFIG_ENV_OFFSET_OOB + && strcmp(cmd, "env.oob") != 0 +#endif + ) goto usage;
+#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd,"env.oob") == 0) { + return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); + } +#endif + /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[nand_curr_device].name) { @@ -502,6 +600,13 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section" #endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif );
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/common/env_nand.c b/common/env_nand.c index a15a950..2289eb2 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -38,6 +38,7 @@ #include <linux/stddef.h> #include <malloc.h> #include <nand.h> +#include <asm/errno.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -288,6 +289,40 @@ int readenv (size_t offset, u_char * buf) return 0; }
+#ifdef CONFIG_ENV_OFFSET_OOB +int get_nand_env_oob(nand_info_t *nand, unsigned long *result) +{ + struct mtd_oob_ops ops; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + int ret; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + + if (!ret) { + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; + } else { + printf("No dynamic environment marker in OOB block 0\n"); + ret = -ENOENT; + goto fail; + } + } + else { + printf("error reading OOB block 0\n"); + } +fail: + return ret; +} +#endif + #ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -357,6 +392,16 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); + if(!ret) { + /* fall through to the normal environment reading code below */ + printf("Found Environment offset in OOB..\n"); + } else { + return use_default(); + } +#endif + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default(); diff --git a/include/environment.h b/include/environment.h index b9924fd..9820d23 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,23 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB is set" +# endif +extern unsigned long nand_env_oob_offset; +# define CONFIG_ENV_OFFSET nand_env_oob_offset +# else +# ifndef CONFIG_ENV_OFFSET +# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" +# endif +# ifdef CONFIG_ENV_OFFSET_REDUND +# define CONFIG_SYS_REDUNDAND_ENVIRONMENT +# endif +# endif /* CONFIG_ENV_OFFSET_OOB */ # ifndef CONFIG_ENV_SIZE # error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_NAND" # endif -# ifdef CONFIG_ENV_OFFSET_REDUND -# define CONFIG_SYS_REDUNDAND_ENVIRONMENT -# endif #endif /* CONFIG_ENV_IS_IN_NAND */
#if defined(CONFIG_ENV_IS_IN_MG_DISK) diff --git a/include/nand.h b/include/nand.h index 2a81597..8bdf419 100644 --- a/include/nand.h +++ b/include/nand.h @@ -130,3 +130,12 @@ void board_nand_select_device(struct nand_chip *nand, int chip); __attribute__((noreturn)) void nand_boot(void);
#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +#define ENV_OOB_MARKER 0x30425645 /*"EVB0" in little-endian -- offset is stored + as block number*/ +#define ENV_OOB_MARKER_OLD 0x30564e45 /*"ENV0" in little-endian -- offset is + stored as byte number */ +#define ENV_OFFSET_SIZE 8 +int get_nand_env_oob(nand_info_t *nand, unsigned long *result); +#endif

Dear Ben Gardiner,
In message 1277933528-8335-1-git-send-email-bengardiner@nanometrics.ca you wrote:
This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
Does this work with redundant environment in NAND?
Best regards,
Wolfgang Denk

On Wed, Jun 30, 2010 at 5:41 PM, Wolfgang Denk wd@denx.de wrote:
Dear Ben Gardiner,
In message 1277933528-8335-1-git-send-email-bengardiner@nanometrics.ca you wrote:
This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
Does this work with redundant environment in NAND?
Currently the use of CONFIG_ENV_OFFSET_OOB and CONFIG_ENV_OFFSET_REDUND is disabled. I think it could work when the env is in its own NAND partition (i.e. !ENV_IS_EMBEDDED and !CONFIG_NAND_ENV_DST).
We could require that the redundant env is CONFIG_ENV_SIZE after the offset specified in the OOB or that two offsets are store in the OOB area. The latter requires that the OOB size is large enough to store two offsets. The former requires that CONFIG_ENV_SIZE is a multiple of the erase block size and perhaps additional skip-if-bad-block behaviour.
Furthermore, to implement env.oob compatibility with redundant environments I believe that the use of the CONFIG_ENV_OFFSET_REDUND macro needs to be split into a 'use redundant env' macro and a 'value of redundant env offset' macro so that we don't have to redefine CONFIG_ENV_OFFSET_REDUND. For instance, I would change saveenv in env_nand.c to look something like:
---
#ifdef CONFIG_USE_ENV_REDUND int saveenv(void) { ... ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr);
---
I see that compatibility of this feature with redundant environments would definitely increase its utility. Can I put that into a second patch series and leave this patch as-is to be considered for inclusion? Would you prefer that I re-spin this patch into a series that includes support for redundant environment?
Best Regards, Ben Gardiner

On 7/1/2010 3:02 AM, Ben Gardiner wrote:
This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'nand env.oob' command, which can be called from the u-boot command line. 'nand env.oob get' reads the address of the environment partition from the OOB data, 'nand env.oob set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
Hello Ben,
This is a nice idea. I see that the logic needs 8bytes at offset 0 in block 0 oob area. This means that it would also overwrite the offset 5 (which is the bad block marker in case of small page devices) and offset 0 (bad block marker in large page devs)
Am I missing something here
Regards Vipin

On Wed, Jun 30, 2010 at 11:37 PM, Vipin KUMAR vipin.kumar@st.com wrote:
Hello Ben,
This is a nice idea. I see that the logic needs 8bytes at offset 0 in block 0 oob area. This means that it would also overwrite the offset 5 (which is the bad block marker in case of small page devices) and offset 0 (bad block marker in large page devs)
Am I missing something here
I can't take credit for the idea -- it is Harald's.
The original patch from Harald Welte wrote the env offset to bytes 8..15 explicitly. The location in the OOB to which the env offset is written was made implicit when I ported to write_oob using struct mtd_oob_ops with mode MTD_OOB_AUTO as suggested by Scott Wood in the review of Harald's patch [1].
With MTD_OOB_AUTO the reads+writes should be performed automatically in the 'free' region of the OOB (specified by the .oobfree member of nand_ecclayout). At least that's what is supposed to be happening... I welcome any further concerns that you may have.
Best Regards,
Ben Gardiner
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916
-- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

Dear Ben,
thanks again for moving those patches forward.
However,
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index f611fd7..5af5b9e 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -6,6 +6,8 @@
- Added 16-bit nand support
- (C) 2004 Texas Instruments
- Added env offset in OOB
*/
- (C) 2010 Nanometrics, Inc.
I think if you add a (C) statement to code that is based on my work, I believei it is fair to add a (C) statement for OpenMoko Inc. for whom I was working at that time. After all, they hold the copyright to the original OOB environment offset (and have licensed the code under GPL)
Thanks for your consideration, Harald

Dear Harald,
On Thu, Jul 1, 2010 at 3:17 AM, Harald Welte laforge@gnumonks.org wrote:
I think if you add a (C) statement to code that is based on my work, I believei it is fair to add a (C) statement for OpenMoko Inc. for whom I was working at that time. After all, they hold the copyright to the original OOB environment offset (and have licensed the code under GPL)
You're right -- I'm sorry for that omission. It was not intentional.
Thank you for pointing this out. I will add the "(C) Copyright 2006-2007 OpenMoko, Inc." statement found currently in the openmoko u-boot tree [1] with remark "'dynenv' Dynamic environment offset in NAND OOB" and post an updated patch shortly.
Best Regards,
Ben Gardiner
[1] http://git.openmoko.org/?p=u-boot.git;a=blob_plain;f=common/cmd_dynenv.c;hb=...
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition.
With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block.
This patch introduces the 'nand env.oob' command, which can be called from the u-boot command line. 'nand env.oob get' reads the address of the environment partition from the OOB data, 'nand env.oob set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name.
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916 [2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79195
Signed-off-by: Ben Gardiner bengardiner@nanometrics.ca Acked-by: Harald Welte laforge@gnumonks.org
---
Changes in v4: * Added 'Acked-by Harald Welte' with permission. * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of git://git.denx.de/u-boot.git * adding OpenMoko copyright statement as requested by Harald Welte laforge@gnumonks.org in review * committing forgotten fixes for checkpath errors -- only two warnings remain, both of which are not applicable * fix warning created by passing const pointer to the do_nand_env_oob function which did not declare argv as a const pointer
Changes in v3: * updated commit message * rebased to 39ddd10b046fb791f47281ffb2100be01909ad72 of git://git.denx.de/u-boot.git * tested using small config changes to include/configs/da850evm.h
Changes in v2: * don't use generic names for the env-offset global and the comand * make a sub-command of the nand command * store the offset in units of eraseblocks * allocate oob write/read buffers on stack * verify write of new offset * make setting new offset affect the live values * update copyright of file * use the global variable instead of the macro in address statements * don't make the oob used bytes configurable * don't undef CONFIG_ENV_OFFSET
I verified the patch with checkpath.pl. The checkpatch.pl output follows:
WARNING: suspect code indent for conditional statements (8, 14) + if (strcmp(cmd, "env.oob") == 0) + return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1);
WARNING: Use #include <linux/errno.h> instead of <asm/errno.h> +#include <asm/errno.h>
total: 0 errors, 2 warnings, 247 lines checked
Neither of these warnings appear to be applicable.
I tested the binary size and compiler warnings on ARM9 and 8xx with the following commands:
#checkout u-boot/master, apply changes to da850evm_config for testing, #commit ./MAKEALL ARM9 2>&1 > ../makeall-master.log ./MAKEALL 8xx 2>&1 > ../makeall-master.log #apply patch, commit ./MAKEALL ARM9 2>&1 > ../makeall-env_oob.log ./MAKEALL 8xx 2>&1 > ../makeall-8xx-env_oob.log diff -burp ../makeall-8xx-master.log ../makeall-8xx-env_oob.log diff -burp ../makeall-master.log ../makeall-env_oob.log
The only output of the diff commands was in the modified da850evm_config. The diff shows the text section has grown by 1352 bytes with the feature introduced by this patch enabled.
@@ -48,7 +48,7 @@ Configuring for da830evm board... 147617 4888 295320 447825 6d551 ./u-boot Configuring for da850evm board... text data bss dec hex filename - 198497 10332 296608 505437 7b65d ./u-boot + 199849 10332 296612 506793 7bba9 ./u-boot Configuring for edb9301 board... text data bss dec hex filename 133899 3772 213400 351071 55b5f ./u-boot --- common/cmd_nand.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 46 +++++++++++++++++++++ include/environment.h | 21 +++++++--- include/nand.h | 9 ++++ 4 files changed, 176 insertions(+), 7 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index ea80555..a4c67c1 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -4,6 +4,10 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse dwmw2@infradead.org * + * Ported 'dynenv' to 'nand env.oob' command + * (C) 2010 Nanometrics, Inc. + * 'dynenv' -- Dynamic environment offset in NAND OOB + * (C) Copyright 2006-2007 OpenMoko, Inc. * Added 16-bit nand support * (C) 2004 Texas Instruments */ @@ -193,6 +197,90 @@ static void do_nand_status(nand_info_t *nand) } #endif
+#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, + int argc, char * const argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + + char *cmd = argv[1]; + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (!ret) + printf("0x%08lx\n", nand_env_oob_offset); + else + return 1; + } else if (!strcmp(cmd, "set")) { + ulong addr; + size_t dummy_size; + struct mtd_oob_ops ops; + + if (argc < 3) + goto usage; + + if (arg_off_size(argc-2, argv + 2, nand, &addr, + &dummy_size) < 0) { + printf("Offset or partition name expected\n"); + return 1; + } + + if (nand->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes: %d OOB bytes" + " available but %d required for env.oob support\n", + nand->oobavail, + ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (nand->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / nand->erasesize; + + ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); + if (!ret) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB " + "failed: 0x%08lx expected but got " + "0x%08lx\n", addr, nand_env_oob_offset); + return 1; + } + } else { + printf("Error writing OOB block 0\n"); + return ret; + } + } else { + goto usage; + } + + return ret; + +usage: + cmd_usage(cmdtp); + return 1; +} + +#endif + static void nand_print_info(int idx) { nand_info_t *nand = &nand_info[idx]; @@ -272,9 +360,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) + strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 +#ifdef CONFIG_ENV_OFFSET_OOB + && strcmp(cmd, "env.oob") != 0 +#endif + ) goto usage;
+#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd, "env.oob") == 0) + return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); +#endif + /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[nand_curr_device].name) { @@ -502,6 +600,13 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section" #endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif );
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/common/env_nand.c b/common/env_nand.c index 50bc111..47d9848 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -38,6 +38,7 @@ #include <linux/stddef.h> #include <malloc.h> #include <nand.h> +#include <asm/errno.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -284,6 +285,40 @@ int readenv (size_t offset, u_char * buf) return 0; }
+#ifdef CONFIG_ENV_OFFSET_OOB +int get_nand_env_oob(nand_info_t *nand, unsigned long *result) +{ + struct mtd_oob_ops ops; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + int ret; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + + if (!ret) { + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; + } else { + printf("No dynamic environment marker in OOB block 0" + "\n"); + ret = -ENOENT; + goto fail; + } + } else { + printf("error reading OOB block 0\n"); + } +fail: + return ret; +} +#endif + #ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -353,6 +388,17 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret;
+#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); + /* If unable to read environment offset from NAND OOB then fall through + * to the normal environment reading code below + */ + if (!ret) + printf("Found Environment offset in OOB..\n"); + else + return use_default(); +#endif + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default(); diff --git a/include/environment.h b/include/environment.h index 203f731..fbccf6a 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif +# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB" +# error "is set" +# endif +extern unsigned long nand_env_oob_offset; +# define CONFIG_ENV_OFFSET nand_env_oob_offset +# else +# ifndef CONFIG_ENV_OFFSET +# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" +# endif +# ifdef CONFIG_ENV_OFFSET_REDUND +# define CONFIG_SYS_REDUNDAND_ENVIRONMENT +# endif +# endif /* CONFIG_ENV_OFFSET_OOB */ # ifndef CONFIG_ENV_SIZE # error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_NAND" # endif -# ifdef CONFIG_ENV_OFFSET_REDUND -# define CONFIG_SYS_REDUNDAND_ENVIRONMENT -# endif #endif /* CONFIG_ENV_IS_IN_NAND */
#if defined(CONFIG_ENV_IS_IN_MG_DISK) diff --git a/include/nand.h b/include/nand.h index 2a81597..8bdf419 100644 --- a/include/nand.h +++ b/include/nand.h @@ -130,3 +130,12 @@ void board_nand_select_device(struct nand_chip *nand, int chip); __attribute__((noreturn)) void nand_boot(void);
#endif + +#ifdef CONFIG_ENV_OFFSET_OOB +#define ENV_OOB_MARKER 0x30425645 /*"EVB0" in little-endian -- offset is stored + as block number*/ +#define ENV_OOB_MARKER_OLD 0x30564e45 /*"ENV0" in little-endian -- offset is + stored as byte number */ +#define ENV_OFFSET_SIZE 8 +int get_nand_env_oob(nand_info_t *nand, unsigned long *result); +#endif

Hi Scott,
I was hoping that the env OOB patch [1] would be considered for inclusion into this merge window [2] but I saw that your pull request on friday [3] did not include the patch. There has been no reply by you to version 2-4 of the patch but I believe that I successfully made the modifications requested by you in your review of version 1 of the patch [4].
I am still very much a novice in this list and I apologize if it is rude of me to ask: is there any chance for "[PATCH v4] NAND: environment offset in OOB (CONFIG_ENV_OFFSET_OOB)" [1] to be included in v2010.09 via the u-boot-nand-flash tree? Are there any concerns with respect to the functionality that this patch provides? Is there anything else that I can provide to help with getting this patch included?
Best Regards, Ben Gardiner
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/80832 [2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/80606 [3] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/81004 [4] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79195
--- Nanometrics Inc. +1 (613) 592-6776 x239 http://www.nanometrics.ca

On Mon, 12 Jul 2010 12:17:23 -0400 Ben Gardiner bengardiner@nanometrics.ca wrote:
Hi Scott,
I was hoping that the env OOB patch [1] would be considered for inclusion into this merge window [2] but I saw that your pull request on friday [3] did not include the patch. There has been no reply by you to version 2-4 of the patch but I believe that I successfully made the modifications requested by you in your review of version 1 of the patch [4].
Sorry, I missed it -- I'll look at it today.
-Scott

I thought I sent this yesterday when applying the patch, but I guess not...
On Mon, Jul 05, 2010 at 01:27:07PM -0400, Ben Gardiner wrote:
This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
Applied to u-boot-nand-flash.
I sent a followup patch for some minor formatting issues.
I verified the patch with checkpath.pl. The checkpatch.pl output follows:
WARNING: suspect code indent for conditional statements (8, 14)
if (strcmp(cmd, "env.oob") == 0)
return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1);
IMHO, "indent with tabs" is more important than "max 80 characters" -- but both can be met nicely using a continuation line.
@@ -48,7 +48,7 @@ Configuring for da830evm board... 147617 4888 295320 447825 6d551 ./u-boot Configuring for da850evm board... text data bss dec hex filename
- 198497 10332 296608 505437 7b65d ./u-boot
- 199849 10332 296612 506793 7bba9 ./u-boot
Configuring for edb9301 board... text data bss dec hex filename 133899 3772 213400 351071 55b5f ./u-boot
Don't put patch fragments outside the patch itself, it confuses git.
common/cmd_nand.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 46 +++++++++++++++++++++ include/environment.h | 21 +++++++--- include/nand.h | 9 ++++ 4 files changed, 176 insertions(+), 7 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index ea80555..a4c67c1 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -4,6 +4,10 @@
- (c) 1999 Machine Vision Holdings, Inc.
- (c) 1999, 2000 David Woodhouse dwmw2@infradead.org
- Ported 'dynenv' to 'nand env.oob' command
- (C) 2010 Nanometrics, Inc.
- 'dynenv' -- Dynamic environment offset in NAND OOB
*/
- (C) Copyright 2006-2007 OpenMoko, Inc.
- Added 16-bit nand support
- (C) 2004 Texas Instruments
@@ -193,6 +197,90 @@ static void do_nand_status(nand_info_t *nand) } #endif
+#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset;
Might want to switch to loff_t, even though arg_off_size won't actually let you enter a 64-bit offset yet.
diff --git a/include/environment.h b/include/environment.h index 203f731..fbccf6a 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif
+# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB" +# error "is set" +# endif
This could be supported pretty easily if we require the redundant env range to come immediately after the regular env range -- no need to track separate offsets.
-Scott

On Tue, Jul 13, 2010 at 3:26 PM, Scott Wood scottwood@freescale.com wrote:
I thought I sent this yesterday when applying the patch, but I guess not...
On Mon, Jul 05, 2010 at 01:27:07PM -0400, Ben Gardiner wrote:
This is a re-submission of the patch by Harald Welte laforge@openmoko.org with minor modifications for rebase and changes as suggested by Scott Wood scottwood@freescale.com [1] [2].
Applied to u-boot-nand-flash.
I sent a followup patch for some minor formatting issues.
Thank you, Scott.
I verified the patch with checkpath.pl. The checkpatch.pl output follows:
WARNING: suspect code indent for conditional statements (8, 14)
- if (strcmp(cmd, "env.oob") == 0)
- return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1);
IMHO, "indent with tabs" is more important than "max 80 characters" -- but both can be met nicely using a continuation line.
@@ -48,7 +48,7 @@ Configuring for da830evm board... 147617 4888 295320 447825 6d551 ./u-boot Configuring for da850evm board... text data bss dec hex filename
- 198497 10332 296608 505437 7b65d ./u-boot
- 199849 10332 296612 506793 7bba9 ./u-boot
Configuring for edb9301 board... text data bss dec hex filename 133899 3772 213400 351071 55b5f ./u-boot
Don't put patch fragments outside the patch itself, it confuses git.
Thanks for the tips -- I appreciate it.
common/cmd_nand.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 46 +++++++++++++++++++++ include/environment.h | 21 +++++++--- include/nand.h | 9 ++++ 4 files changed, 176 insertions(+), 7 deletions(-)
diff --git a/common/cmd_nand.c b/common/cmd_nand.c index ea80555..a4c67c1 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -4,6 +4,10 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse dwmw2@infradead.org *
- Ported 'dynenv' to 'nand env.oob' command
- (C) 2010 Nanometrics, Inc.
- 'dynenv' -- Dynamic environment offset in NAND OOB
- (C) Copyright 2006-2007 OpenMoko, Inc.
* Added 16-bit nand support * (C) 2004 Texas Instruments */ @@ -193,6 +197,90 @@ static void do_nand_status(nand_info_t *nand) } #endif
+#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset;
Might want to switch to loff_t, even though arg_off_size won't actually let you enter a 64-bit offset yet.
Sure. I can incorporate into the next patch series that introduces CONFIG_ENV_OFFSET_REDUND support.
diff --git a/include/environment.h b/include/environment.h index 203f731..fbccf6a 100644 --- a/include/environment.h +++ b/include/environment.h @@ -74,15 +74,24 @@ #endif /* CONFIG_ENV_IS_IN_FLASH */
#if defined(CONFIG_ENV_IS_IN_NAND) -# ifndef CONFIG_ENV_OFFSET -# error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND" -# endif
+# if defined(CONFIG_ENV_OFFSET_OOB) +# ifdef CONFIG_ENV_OFFSET_REDUND +# error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB" +# error "is set" +# endif
This could be supported pretty easily if we require the redundant env range to come immediately after the regular env range -- no need to track separate offsets.
Ok. I like this option best also [1]. I'm not sure if I'll get to providing the next patch series this week.
Best Regards,
Ben Gardiner
[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/80675
--- Nanometrics Inc. http://www.nanometrics.ca
participants (7)
-
Ben Gardiner
-
Harald Welte
-
Harald Welte
-
Scott Wood
-
Stefan Roese
-
Vipin KUMAR
-
Wolfgang Denk