Index: c/src/lib/libbsp/arm/lpc32xx/ChangeLog
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/ChangeLog	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/ChangeLog	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -1,2 +1,13 @@
+2011-09-22	Sebastian Huber <sebastian.huber@embedded-brains.de>
+
+	* make/custom/lpc32xx.inc: Workaround for GCC bug 50106.
+	* include/lpc32xx.h: Fixed register map for NAND MLC.
+	* include/boot.h: Declare lpc32xx_set_boot_block_bad().
+	* misc/boot.c: Define lpc32xx_set_boot_block_bad().
+	* include/nand-mlc.h, misc/nand-mlc-erase-block-safe.c,
+	misc/nand-mlc-read-blocks.c, misc/nand-mlc-write-blocks.c,
+	misc/nand-mlc.c: Changed bad block handling.  Support for non-aligned
+	data.  Documentation.
+
 2011-08-08	Sebastian Huber <sebastian.huber@embedded-brains.de>
 
Index: c/src/lib/libbsp/arm/lpc32xx/include/boot.h
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/include/boot.h	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/include/boot.h	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -102,4 +102,8 @@
 );
 
+void lpc32xx_set_boot_block_bad(
+  lpc32xx_boot_block *boot_block
+);
+
 /** @} */
 
Index: c/src/lib/libbsp/arm/lpc32xx/include/lpc32xx.h
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/include/lpc32xx.h	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/include/lpc32xx.h	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -527,4 +527,5 @@
   uint32_t irq_mr;
   uint32_t irq_sr;
+  uint32_t reserved_2;
   uint32_t lock_pr;
   uint32_t isr;
Index: c/src/lib/libbsp/arm/lpc32xx/include/nand-mlc.h
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/include/nand-mlc.h	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/include/nand-mlc.h	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -75,4 +75,6 @@
 #define MLC_SMALL_DATA_SIZE 512
 #define MLC_SMALL_SPARE_SIZE 16
+#define MLC_SMALL_USER_SPARE_SIZE 6
+#define MLC_SMALL_ECC_SPARE_SIZE 10
 #define MLC_SMALL_DATA_WORD_COUNT (MLC_SMALL_DATA_SIZE / 4)
 #define MLC_SMALL_SPARE_WORD_COUNT (MLC_SMALL_SPARE_SIZE / 4)
@@ -137,4 +139,5 @@
 
 #define MLC_ISR_DECODER_FAILURE BSP_BIT32(6)
+#define MLC_ISR_SYMBOL_ERRORS(reg) BSP_FLD32GET(reg, 4, 5)
 #define MLC_ISR_ERRORS_DETECTED BSP_BIT32(3)
 #define MLC_ISR_ECC_READY BSP_BIT32(2)
@@ -166,19 +169,4 @@
 
 /** @} */
-
-#define MLC_BAD_BLOCK_MASK ((uint32_t) 0xff00)
-
-/**
- * @brief Bad block mark.
- * 
- * We define our own bad block mark to be able to recognize the blocks that
- * have been marked bad during operation later.
- */
-#define MLC_BAD_BLOCK_MARK ((uint32_t) 0xbadb)
-
-/**
- * @brief The bytes 4 and 5 are reserved for bad block handling.
- */
-#define MLC_RESERVED ((uint32_t) 0xffff)
 
 /**
@@ -210,20 +198,27 @@
 /**
  * @brief Selects small pages (512 Bytes user data and 16 Bytes spare data)
- * or large pages (2048 Bytes user data and 64 Bytes spare data).
+ * or large pages (2048 Bytes user data and 64 Bytes spare data) if not set.
  */
 #define MLC_SMALL_PAGES 0x1U
 
 /**
- * @Brief Selects 3/4 address cycles for small pages/large pages or 4/5
- * address cycles.
+ * @Brief Selects 4/5 address cycles for small/large pages or 3/4 address
+ * cycles if not set.
  */
 #define MLC_MANY_ADDRESS_CYCLES 0x2U
 
 /**
- * @brief Selects 64 or 128 pages per block in case of large pages.
+ * @brief Selects 64 pages per block or 128 pages per block if not set.
+ *
+ * This flag is only valid for large pages.
  */
 #define MLC_NORMAL_BLOCKS 0x4U
 
 /**
+ * @brief Selects 16-bit IO width or 8-bit IO width if not set.
+ */
+#define MLC_IO_WIDTH_16_BIT 0x8U
+
+/**
  * @brief Initializes the MLC NAND controller according to @a cfg.
  */
@@ -235,4 +230,6 @@
 
 uint32_t lpc32xx_mlc_block_count(void);
+
+uint32_t lpc32xx_mlc_io_width(void);
 
 void lpc32xx_mlc_write_protection(
@@ -246,7 +243,9 @@
  * @brief Reads the page with index @a page_index.
  *
- * 32-bit reads will be performed.
- *
- * Bytes 7 to 15 of the spare area will contain the ECC.
+ * Bytes 6 to 15 of the spare area will contain the ECC.
+ *
+ * If the read is successful, then the @a symbol_error_count will contain the
+ * number of detected symbol errors (0, 1, 2, 3, or 4), else the value will be
+ * 0xffffffff.  The @a symbol_error_count pointer may be @c NULL.
  *
  * @retval RTEMS_SUCCESSFUL Successful operation.
@@ -256,7 +255,26 @@
 rtems_status_code lpc32xx_mlc_read_page(
   uint32_t page_index,
-  uint32_t *data,
-  uint32_t *spare
-);
+  void *data,
+  void *spare,
+  uint32_t *symbol_error_count
+);
+
+/**
+ * @brief Checks if the block with index @a block_index is valid.
+ *
+ * The initial valid block information of the manufacturer will be used.
+ * Unfortunatly there seems to be no standard for this.  A block will be
+ * considered as bad if the first or second page of this block does not contain
+ * 0xff at the 6th byte of the spare area.  This should work for flashes with
+ * small pages and a 8-bit IO width.
+ *
+ * @retval RTEMS_SUCCESSFUL The block is valid.
+ * @retval RTEMS_INVALID_ID Invalid @a block_index value.
+ * @retval RTEMS_IO_ERROR Uncorrectable bit error.
+ * @retval RTEMS_INCORRECT_STATE The block is bad.
+ * @retval RTEMS_NOT_IMPLEMENTED No implementation available for this flash
+ * type.
+ */
+rtems_status_code lpc32xx_mlc_is_valid_block(uint32_t block_index);
 
 /**
@@ -265,5 +283,5 @@
  * @retval RTEMS_SUCCESSFUL Successful operation.
  * @retval RTEMS_INVALID_ID Invalid @a block_index value.
- * @retval RTEMS_IO_ERROR Erase error.
+ * @retval RTEMS_UNSATISFIED Erase error.
  */
 rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index);
@@ -273,11 +291,12 @@
  *
  * In case of an erase error all pages and the spare areas of this block are
- * programmed with zero values.  This will mark the first and second page as
- * bad.
+ * programmed with zero values.  This will hopefully mark the block as bad.
  *
  * @retval RTEMS_SUCCESSFUL Successful operation.
- * @retval RTEMS_INCORRECT_STATE The first or second page of this block is bad.
+ * @retval RTEMS_INCORRECT_STATE The block is bad.
  * @retval RTEMS_INVALID_ID Invalid @a block_index value.
- * @retval RTEMS_IO_ERROR Erase error.
+ * @retval RTEMS_UNSATISFIED Erase error.
+ * @retval RTEMS_NOT_IMPLEMENTED No implementation available for this flash
+ * type.
  */
 rtems_status_code lpc32xx_mlc_erase_block_safe(uint32_t block_index);
@@ -308,8 +327,6 @@
  * @brief Writes the page with index @a page_index.
  *
- * 32-bit writes will be performed.
- *
- * Bytes 7 to 15 of the spare area will be used for the automatically generated
- * ECC.
+ * Only the bytes 0 to 5 of the spare area can be used for user data, the bytes
+ * 6 to 15 will be used for the automatically generated ECC.
  *
  * @retval RTEMS_SUCCESSFUL Successful operation.
@@ -319,6 +336,6 @@
 rtems_status_code lpc32xx_mlc_write_page_with_ecc(
   uint32_t page_index,
-  const uint32_t *data,
-  const uint32_t *spare
+  const void *data,
+  const void *spare
 );
 
@@ -378,15 +395,6 @@
 static inline bool lpc32xx_mlc_is_bad_page(const uint32_t *spare)
 {
-  return (spare [1] & MLC_BAD_BLOCK_MASK) != MLC_BAD_BLOCK_MASK;
-}
-
-static inline void lpc32xx_mlc_set_bad_page(uint32_t *spare)
-{
-  spare [1] = MLC_BAD_BLOCK_MARK;
-}
-
-static inline void lpc32xx_mlc_set_reserved(uint32_t *spare)
-{
-  spare [1] = MLC_RESERVED;
+  uint32_t valid_block_mask = 0xff00;
+  return (spare [1] & valid_block_mask) != valid_block_mask;
 }
 
Index: c/src/lib/libbsp/arm/lpc32xx/make/custom/lpc32xx.inc
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/make/custom/lpc32xx.inc	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/make/custom/lpc32xx.inc	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -12,3 +12,3 @@
 	-fno-schedule-insns2
 
-CFLAGS_OPTIMIZE_V = -Os -g
+CFLAGS_OPTIMIZE_V = -O2 -g
Index: c/src/lib/libbsp/arm/lpc32xx/misc/boot.c
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/misc/boot.c	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/misc/boot.c	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -53,2 +53,9 @@
   boot_block->field.d12 = 0xaa;
 }
+
+void lpc32xx_set_boot_block_bad(
+  lpc32xx_boot_block *boot_block
+)
+{
+  boot_block->field.d12 = 0;
+}
Index: c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-erase-block-safe.c
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-erase-block-safe.c	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-erase-block-safe.c	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -41,18 +41,51 @@
 }
 
-static bool is_bad_page(
+static rtems_status_code is_valid_page(
   uint32_t page_begin,
   uint32_t page_offset
 )
 {
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
   uint32_t spare [MLC_LARGE_SPARE_WORD_COUNT];
 
   memset(spare, 0, MLC_LARGE_SPARE_SIZE);
-  lpc32xx_mlc_read_page(
+
+  sc = lpc32xx_mlc_read_page(
     page_begin + page_offset,
     lpc32xx_magic_zero_begin,
-    spare
+    spare,
+    NULL
   );
-  return lpc32xx_mlc_is_bad_page(spare);
+  if (sc == RTEMS_SUCCESSFUL) {
+    if (lpc32xx_mlc_is_bad_page(spare)) {
+      sc = RTEMS_INCORRECT_STATE;
+    }
+  }
+
+  return sc;
+}
+
+static rtems_status_code is_valid_block(uint32_t page_begin)
+{
+  rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+  if (lpc32xx_mlc_page_size() == 512 && lpc32xx_mlc_io_width() == 8) {
+    sc = is_valid_page(page_begin, 0);
+    if (sc == RTEMS_SUCCESSFUL) {
+      sc = is_valid_page(page_begin, 1);
+    }
+  } else {
+    sc = RTEMS_NOT_IMPLEMENTED;
+  }
+
+  return sc;
+}
+
+rtems_status_code lpc32xx_mlc_is_valid_block(uint32_t block_index)
+{
+  uint32_t pages_per_block = lpc32xx_mlc_pages_per_block();
+  uint32_t page_begin = block_index * pages_per_block;
+
+  return is_valid_block(page_begin);
 }
 
@@ -65,20 +98,13 @@
   rtems_status_code sc = RTEMS_SUCCESSFUL;
 
-  if (is_bad_page(page_begin, 0)) {
-    return RTEMS_INCORRECT_STATE;
+  sc = is_valid_block(page_begin);
+  if (sc == RTEMS_SUCCESSFUL) {
+    sc = lpc32xx_mlc_erase_block(block_index);
+    if (sc == RTEMS_UNSATISFIED) {
+      lpc32xx_mlc_zero_pages(page_begin, page_end);
+    }
   }
 
-  if (is_bad_page(page_begin, 1)) {
-    return RTEMS_INCORRECT_STATE;
-  }
-
-  sc = lpc32xx_mlc_erase_block(block_index);
-  if (sc != RTEMS_SUCCESSFUL) {
-    lpc32xx_mlc_zero_pages(page_begin, page_end);
-
-    return RTEMS_IO_ERROR;
-  }
-
-  return RTEMS_SUCCESSFUL;
+  return sc;
 }
 
Index: c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-read-blocks.c	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -37,5 +37,5 @@
   }
 
-  sc = lpc32xx_mlc_read_page(page_index, page_data, page_spare);
+  sc = lpc32xx_mlc_read_page(page_index, page_data, page_spare, NULL);
   if (possible_bad_page && lpc32xx_mlc_is_bad_page(page_spare)) {
     return RTEMS_UNSATISFIED;
Index: c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc-write-blocks.c	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -85,5 +85,5 @@
         );
         if (sc != RTEMS_SUCCESSFUL) {
-          lpc32xx_mlc_erase_block_safe_3(block, page_begin, page_end);
+          lpc32xx_mlc_erase_block(block);
           lpc32xx_mlc_zero_pages(page_begin, page_end);
           current = last;
Index: c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c
===================================================================
--- c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c	(revision 7441603540575b6d762411f11ab354ec65495652)
+++ c/src/lib/libbsp/arm/lpc32xx/misc/nand-mlc.c	(revision 82525a756dbb42af35d4815e064254dc298ad3bc)
@@ -8,10 +8,11 @@
 
 /*
- * Copyright (c) 2010
- * embedded brains GmbH
- * Obere Lagerstr. 30
- * D-82178 Puchheim
- * Germany
- * <rtems@embedded-brains.de>
+ * Copyright (c) 2010-2011 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems@embedded-brains.de>
  *
  * The license and distribution terms for this file may be
@@ -71,4 +72,13 @@
 {
   return mlc_block_count;
+}
+
+uint32_t lpc32xx_mlc_io_width(void)
+{
+  if ((mlc_flags & MLC_IO_WIDTH_16_BIT) == 0) {
+    return 8;
+  } else {
+    return 16;
+  }
 }
 
@@ -194,8 +204,14 @@
 }
 
+bool is_word_aligned(const void *data, const void *spare)
+{
+  return (((uintptr_t) data) | ((uintptr_t) spare)) % 4 == 0;
+}
+
 rtems_status_code lpc32xx_mlc_read_page(
   uint32_t page_index,
-  uint32_t *data,
-  uint32_t *spare
+  void *data,
+  void *spare,
+  uint32_t *symbol_error_count_ptr
 )
 {
@@ -205,4 +221,8 @@
   size_t i = 0;
   uint32_t isr = 0;
+  uint32_t symbol_error_count = 0xffffffff;
+  bool aligned = is_word_aligned(data, spare);
+  uint8_t *current_data = data;
+  uint8_t *current_spare = spare;
 
   if (page_index >= mlc_page_count) {
@@ -219,11 +239,23 @@
 
   for (sp = 0; sc == RTEMS_SUCCESSFUL && sp < small_pages_count; ++sp) {
+    uint32_t *aligned_data = (uint32_t *) current_data;
+    uint32_t *aligned_spare = (uint32_t *) current_spare;
+
     mlc->ecc_dec = 0;
 
-    for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
-      data [i] = mlc->data.w32;
-    }
-    for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
-      spare [i] = mlc->data.w32;
+    if (aligned) {
+      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+        aligned_data [i] = mlc->data.w32;
+      }
+      for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
+        aligned_spare [i] = mlc->data.w32;
+      }
+    } else {
+      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
+        current_data [i] = mlc->data.w8;
+      }
+      for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
+        current_spare [i] = mlc->data.w8;
+      }
     }
 
@@ -231,13 +263,27 @@
 
     isr = mlc->isr;
-    if ((isr & MLC_ISR_ERRORS_DETECTED) != 0) {
+    if ((isr & MLC_ISR_ERRORS_DETECTED) == 0) {
+      symbol_error_count = 0;
+    } else {
       if ((isr & MLC_ISR_DECODER_FAILURE) == 0) {
-        mlc->rubp = 0;
-        for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
-          data [i] = mlc->buff.w32;
-        }
-        mlc->robp = 0;
-        for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
-          spare [i] = mlc->buff.w32;
+        symbol_error_count = MLC_ISR_SYMBOL_ERRORS(isr);
+        if (aligned) {
+          mlc->rubp = 0;
+          for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+            aligned_data [i] = mlc->buff.w32;
+          }
+          mlc->robp = 0;
+          for (i = 0; i < MLC_SMALL_SPARE_WORD_COUNT; ++i) {
+            aligned_spare [i] = mlc->buff.w32;
+          }
+        } else {
+          mlc->rubp = 0;
+          for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
+            current_data [i] = mlc->buff.w8;
+          }
+          mlc->robp = 0;
+          for (i = 0; i < MLC_SMALL_SPARE_SIZE; ++i) {
+            current_spare [i] = mlc->buff.w8;
+          }
         }
       } else {
@@ -246,6 +292,10 @@
     }
 
-    data += MLC_SMALL_DATA_WORD_COUNT;
-    spare += MLC_SMALL_SPARE_WORD_COUNT;
+    current_data += MLC_SMALL_DATA_SIZE;
+    current_spare += MLC_SMALL_SPARE_SIZE;
+  }
+
+  if (symbol_error_count_ptr != NULL) {
+    *symbol_error_count_ptr = symbol_error_count;
   }
 
@@ -269,5 +319,5 @@
 rtems_status_code lpc32xx_mlc_erase_block(uint32_t block_index)
 {
-  rtems_status_code sc = RTEMS_IO_ERROR;
+  rtems_status_code sc = RTEMS_UNSATISFIED;
 
   if (block_index >= mlc_block_count) {
@@ -289,12 +339,16 @@
 rtems_status_code lpc32xx_mlc_write_page_with_ecc(
   uint32_t page_index,
-  const uint32_t *data,
-  const uint32_t *spare
+  const void *data,
+  const void *spare
 )
 {
   rtems_status_code sc = RTEMS_IO_ERROR;
-  size_t small_pages_count = mlc_small_pages() ? 1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
+  size_t small_pages_count = mlc_small_pages() ?
+    1 : MLC_SMALL_PAGES_PER_LARGE_PAGE;
   size_t sp = 0;
   size_t i = 0;
+  bool aligned = is_word_aligned(data, spare);
+  const uint8_t *current_data = data;
+  const uint8_t *current_spare = spare;
 
   if (page_index >= mlc_page_count) {
@@ -309,15 +363,27 @@
     mlc->ecc_enc = 0;
 
-    for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
-      mlc->data.w32 = data [i];
-    }
-    mlc->data.w32 = spare [0];
-    mlc->data.w16 = (uint16_t) spare [1];
+    if (aligned) {
+      const uint32_t *aligned_data = (const uint32_t *) current_data;
+      const uint32_t *aligned_spare = (const uint32_t *) current_spare;
+
+      for (i = 0; i < MLC_SMALL_DATA_WORD_COUNT; ++i) {
+        mlc->data.w32 = aligned_data [i];
+      }
+      mlc->data.w32 = aligned_spare [0];
+      mlc->data.w16 = (uint16_t) aligned_spare [1];
+    } else {
+      for (i = 0; i < MLC_SMALL_DATA_SIZE; ++i) {
+        mlc->data.w8 = current_data [i];
+      }
+      for (i = 0; i < MLC_SMALL_USER_SPARE_SIZE; ++i) {
+        mlc->data.w8 = current_spare [i];
+      }
+    }
     mlc->wpr = 0;
 
     mlc_wait(MLC_ISR_CONTROLLER_READY);
 
-    data += MLC_SMALL_DATA_WORD_COUNT;
-    spare += MLC_SMALL_SPARE_WORD_COUNT;
+    current_data += MLC_SMALL_DATA_SIZE;
+    current_spare += MLC_SMALL_SPARE_SIZE;
   }
 
