* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
+ * Copyright 2010 Freescale Semiconductor
+ * The portions of this file whose copyright is held by Freescale and which
+ * are not considered a derived work of GPL v2-only code may be distributed
+ * and/or modified 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.
*/
#include <common.h>
#endif
/**
- * get_len_incl_bad
+ * check_skip_len
*
- * Check if length including bad blocks fits into device.
+ * Check if there are any bad blocks, and whether length including bad
+ * blocks fits into device
*
* @param nand NAND device
* @param offset offset in flash
* @param length image length
- * @return image length including bad blocks
+ * @return 0 if the image fits and there are no bad blocks
+ * 1 if the image fits, but there are bad blocks
+ * -1 if the image does not fit
*/
-static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset,
- const size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
{
- size_t len_incl_bad = 0;
size_t len_excl_bad = 0;
- size_t block_len;
+ int ret = 0;
while (len_excl_bad < length) {
- block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+ size_t block_len, block_off;
+ loff_t block_start;
- if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))
- len_excl_bad += block_len;
+ if (offset >= nand->size)
+ return -1;
- len_incl_bad += block_len;
- offset += block_len;
+ block_start = offset & ~(loff_t)(nand->erasesize - 1);
+ block_off = offset & (nand->erasesize - 1);
+ block_len = nand->erasesize - block_off;
- if (offset >= nand->size)
- break;
+ if (!nand_block_isbad(nand, block_start))
+ len_excl_bad += block_len;
+ else
+ ret = 1;
+
+ offset += block_len;
}
- return len_incl_bad;
+ return ret;
}
/**
{
int rval;
size_t left_to_write = *length;
- size_t len_incl_bad;
u_char *p_buffer = buffer;
+ int need_skip;
- /* Reject writes, which are not page aligned */
- if ((offset & (nand->writesize - 1)) != 0 ||
- (*length & (nand->writesize - 1)) != 0) {
+ /*
+ * nand_write() handles unaligned, partial page writes.
+ *
+ * We allow length to be unaligned, for convenience in
+ * using the $filesize variable.
+ *
+ * However, starting at an unaligned offset makes the
+ * semantics of bad block skipping ambiguous (really,
+ * you should only start a block skipping access at a
+ * partition boundary). So don't try to handle that.
+ */
+ if ((offset & (nand->writesize - 1)) != 0) {
printf ("Attempt to write non page aligned data\n");
+ *length = 0;
return -EINVAL;
}
- len_incl_bad = get_len_incl_bad (nand, offset, *length);
-
- if ((offset + len_incl_bad) > nand->size) {
+ need_skip = check_skip_len(nand, offset, *length);
+ if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
+ *length = 0;
return -EINVAL;
}
- if (len_incl_bad == *length) {
+ if (!need_skip) {
rval = nand_write (nand, offset, length, buffer);
- if (rval != 0)
- printf ("NAND write to offset %llx failed %d\n",
- offset, rval);
+ if (rval == 0)
+ return 0;
+ *length = 0;
+ printf ("NAND write to offset %llx failed %d\n",
+ offset, rval);
return rval;
}
{
int rval;
size_t left_to_read = *length;
- size_t len_incl_bad;
u_char *p_buffer = buffer;
+ int need_skip;
- len_incl_bad = get_len_incl_bad (nand, offset, *length);
+ if ((offset & (nand->writesize - 1)) != 0) {
+ printf ("Attempt to read non page aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
- if ((offset + len_incl_bad) > nand->size) {
+ need_skip = check_skip_len(nand, offset, *length);
+ if (need_skip < 0) {
printf ("Attempt to read outside the flash area\n");
+ *length = 0;
return -EINVAL;
}
- if (len_incl_bad == *length) {
+ if (!need_skip) {
rval = nand_read (nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
return 0;
+
+ *length = 0;
printf ("NAND read from offset %llx failed %d\n",
offset, rval);
return rval;