diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/hal_cortexm_stm32_stm3210e_eval.cdl anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/hal_cortexm_stm32_stm3210e_eval.cdl
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/hal_cortexm_stm32_stm3210e_eval.cdl	2009-10-28 11:10:42.000000000 +0000
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/hal_cortexm_stm32_stm3210e_eval.cdl	2009-11-12 15:06:57.000000000 +0000
@@ -233,6 +233,36 @@
         }
     }
 
+    cdl_component CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL_OPTIONS {
+        display "stm3210e HAL build options"
+                flavor  none
+                description   "
+                        Package specific build options including control over
+                        compiler flags used only in building this HAL."
+
+                cdl_option CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL_CFLAGS_ADD {
+                        display "Additional compiler flags"
+                        flavor  data
+                        no_define
+                        default_value { "-Werror" }
+                        description   "
+                                This option modifies the set of compiler flags
+                                for building this HAL. These flags are used
+                                in addition to the set of global flags."
+                        }
+                cdl_option CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL_CFLAGS_REMOVE {
+                        display "Suppressed compiler flags"
+                        flavor  data
+                        no_define
+                        default_value { "" }
+                        description   "
+                                This option modifies the set of compiler flags
+                                for building this HAL. These flags are
+                                removed from the set of global flags if
+                                present."
+                }
+        }
+
     cdl_option CYGSEM_HAL_ROM_MONITOR {
         display       "Behave as a ROM monitor"
         flavor        bool
@@ -328,4 +358,79 @@
                      files."
     }
     
+    cdl_component CYGHWR_HAL_CORTEXM_STM3210E_EVAL_NAND {
+        display       "STM3210E EVAL NAND support"
+        parent        CYGPKG_IO_NAND
+        active_if     CYGPKG_IO_NAND
+        implements    CYGHWR_IO_NAND_DEVICE
+        requires      CYGPKG_DEVS_NAND_ST_NANDXXXX3A
+        requires      { (CYGPKG_REDBOOT && CYGPKG_FS_YAFFS) implies (CYGMEM_REDBOOT_WORKSPACE_HEAP_SIZE >= 0x20000) }
+
+        compile       -library=libextras.a stm3210e_eval_nand.c
+        description   "
+            This option enables support for the on-board ST NAND flash chip."
+
+        cdl_option CYGHWR_HAL_CORTEXM_STM3210E_EVAL_RB_ON_INT2 {
+            display       "Use NAND_RB line"
+            flavor        bool
+            default_value 0
+            description   "
+                The STM3210E board provides a jumper, JP7, which
+                routes the NAND ready/busy signal to either the
+                WAIT line (pins 1-2, the default as shipped), or to
+                FSMC_INT2 (pins 2-3). You must set
+                this option if the jumper is in the FSMC_INT2 position;
+                if you do not, the results are undefined.
+                (In WAIT mode, the FSMC automatically stalls further
+                accesses until the NAND signals it is ready: this is
+                code-size-efficient, but may cause problems with a
+                multi-threaded application.)
+                "
+        }
+
+        cdl_option CYGNUM_HAL_CORTEXM_STM3210E_EVAL_POLL_INTERVAL {
+            display         "Chip polling interval (us)"
+            flavor          data
+            active_if       CYGHWR_HAL_CORTEXM_STM3210E_EVAL_RB_ON_INT2
+            default_value   10
+            legal_values    1 to 1000
+            description     "
+                If the NAND_RB line is routed to INT2, the driver polls
+                that line whilst waiting for the chip to signal
+                that it has completed an operation.
+                This setting is the interval - in microseconds - the
+                NAND driver will sleep for during each wait cycle.
+                Lower values improve responsiveness slightly, at the cost
+                of stealing CPU time from any other threads which may be
+                running."
+        }
+
+        # Manual configuration of NAND partitions.
+        # We currently only provide manual config.
+
+        cdl_component CYGSEM_DEVS_NAND_STM3210E_EVAL_PARTITION_MANUAL_CONFIG {
+            display "Manual partition configuration"
+            flavor  bool
+            default_value   1
+            description "
+                Set in order to use CDL to manually configure the
+                partitions of the ST STM3210E EVAL on-board NAND."
+
+            set ::partition_device "STM3210E_EVAL"
+            script manual_partition.cdl
+        }
+    }
+
+    cdl_option CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL_TESTS {
+        display "STM3210E tests"
+        flavor  data
+        no_define
+        calculated {
+             ( CYGHWR_HAL_CORTEXM_STM3210E_EVAL_NAND ? "tests/eccwalk "  : "")
+       }
+
+        description   "
+            This option specifies the set of tests for the STM3210E HAL."
+   }
+
 }
diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/manual_partition.cdl anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/manual_partition.cdl
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/manual_partition.cdl	1970-01-01 01:00:00.000000000 +0100
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/cdl/manual_partition.cdl	2009-11-12 11:47:26.000000000 +0000
@@ -0,0 +1,124 @@
+# ====================================================================
+#
+#      manual_partition.cdl
+#
+#      NAND device manual partition logic
+#
+# ====================================================================
+# ####ECOSGPLCOPYRIGHTBEGIN####                                            
+# -------------------------------------------                              
+# This file is part of eCos, the Embedded Configurable Operating System.   
+# Copyright (C) 2009 eCosCentric Limited.
+#
+# eCos 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 or (at your option) any later      
+# version.                                                                 
+#
+# eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,    
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+#
+# As a special exception, if other files instantiate templates or use      
+# macros or inline functions from this file, or you compile this file      
+# and link it with other works to produce a work based on this file,       
+# this file does not by itself cause the resulting work to be covered by   
+# the GNU General Public License. However the source code for this file    
+# must still be made available in accordance with section (3) of the GNU   
+# General Public License v2.                                               
+#
+# This exception does not invalidate any other reasons why a work based    
+# on this file might be covered by the GNU General Public License.         
+# -------------------------------------------                              
+# ####ECOSGPLCOPYRIGHTEND####                                              
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      wry
+# Date:           2009-03-10
+#
+#####DESCRIPTIONEND####
+#
+# ====================================================================
+
+# This file can be used by drivers to implement manual CDL-driven
+# partition config.
+#
+# To use, create a cdl_component with an appropriate name; 
+# make it boolean or otherwise selectable (you might for example want
+# to turn it off, or pick partitioning strategy from a drop-down list),
+# then choose and set a name-fragment for your device before including
+# this script file along these lines:
+# 
+#	set ::partition_device "SYNTH"
+#	script manual_partition.cdl
+#
+# For a full example, see the synthetic NAND device.
+				 
+
+#######################################################
+
+# Partition table support
+
+# Every NAND board driver has to decide how it will determine the
+# NAND partition layout. In a simple case, there might be just
+# a single partition covering the whole chip. Alternatively,
+# the driver could read a partition table during _devinit, or 
+# the partition layout might be hard-wired (e.g. to inter-operate
+# with some other fixed code).
+
+###
+# Unfortunately, it is not currently possible within CDL to
+# directly access the value of CYGNUM_NAND_MAX_PARTITIONS in a
+# Tcl loop, so we cannot automatically have the second argument
+# to this for loop be { $::part < CYGNUM_NAND_MAX_PARTITIONS } .
+# The best we can do is pick an arbitrary number - we'll choose 4,
+# because that's the same as the default CYGNUM_NAND_MAX_PARTITIONS,
+# and put only that many in the loop. The current limitations of
+# also mean that we have to manually check for each of the four
+# partitions in C - so if you edit the number 4 below, be sure 
+# to tweak the corresponding macro use which makes use of it!
+#
+# (Four ought to be enough for everybody, shouldn't it?)
+###
+
+for { set ::part 0 } { $::part < 4 } { incr ::part } {
+	cdl_option CYGPKG_DEVS_NAND_[set ::partition_device]_PARTITION_[set ::part] {
+		active_if		$::part < CYGNUM_NAND_MAX_PARTITIONS
+		display "Configure partition [set ::part]"
+		flavor	bool
+		default_value	[set ::part] == 0
+		description "
+			Set in order to manually configure partition [set ::part]
+			in CDL."
+	}
+
+	cdl_option CYGNUM_DEVS_NAND_[set ::partition_device]_PARTITION_[set ::part]_BASE {
+		display "Partition [set ::part] base block address"
+		active_if		CYGPKG_DEVS_NAND_[set ::partition_device]_PARTITION_[set ::part] == 1
+		flavor			data
+		legal_values	0 to 0x7fffffff
+		default_value	0
+		description		"
+			The address (number) of the eraseblock at which
+			partition [set ::part] begins."
+	}
+
+	cdl_option CYGNUM_DEVS_NAND_[set ::partition_device]_PARTITION_[set ::part]_SIZE {
+		display "Partition [set ::part] size in blocks"
+		active_if		CYGPKG_DEVS_NAND_[set ::partition_device]_PARTITION_[set ::part] == 1
+		flavor			data
+		legal_values	0 to 0x7fffffff
+		default_value	0
+		description		"
+			The size of partition [set ::part], expressed
+			in eraseblocks. A value of 0 is special and 
+			consumes all the remaining space on the device."
+	}
+}
+# / for loop for partitions.
diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/ChangeLog anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/ChangeLog
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/ChangeLog	2009-10-28 11:10:42.000000000 +0000
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/ChangeLog	2009-11-17 11:03:07.000000000 +0000
@@ -1,3 +1,31 @@
+2009-11-13  Ross Younger  <wry@ecoscentric.com>
+
+	* src/stm3210e_eval_nand.c: Build fix for minimal configs.
+
+2009-11-12  Ross Younger  <wry@ecoscentric.com>
+
+	* src/stm3210e_eval_nand.c: Add i/o heuristic speed-up.
+
+2009-11-09  Ross Younger  <wry@ecoscentric.com>
+
+	* src/stm3210e_eval_nand.c: Update for nand interface changes.
+	* cdl/hal_cortexm_stm32_stm3210e_eval.cdl: Add 
+	  CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL_CFLAGS_{ADD,REMOVE},
+	  default to -Werror.
+
+2009-10-28  Ross Younger  <wry@ecoscentric.com>
+
+	* src/stm3210e_eval_nand.c: Implement hardware ECC.
+	Create eccwalk test to test it.
+	Add a nop to work around an apparent timing issue in the
+	NAND-specific FSMC setup when optimised.
+	Implement CYGNUM_HAL_CORTEXM_STM3210E_EVAL_POLL_INTERVAL.
+
+2009-08-26  Ross Younger  <wry@ecoscentric.com>
+
+	* src/stm3210e_eval_nand.c: Added support for onboard NAND chip.
+	Based on work by Simon Kallweit.
+
 2009-02-04  Nick Garnett  <nickg@ecoscentric.com>
 
 	* include/pkgconf/mlt_cortexm_stm3210e_eval_rom.ldi:
diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_misc.c anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_misc.c
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_misc.c	2009-10-28 11:10:42.000000000 +0000
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_misc.c	2009-11-12 11:47:26.000000000 +0000
@@ -94,7 +94,7 @@
                      CYGHWR_HAL_STM32_RCC_APB2ENR_IOPD |
                      CYGHWR_HAL_STM32_RCC_APB2ENR_IOPE |
                      CYGHWR_HAL_STM32_RCC_APB2ENR_IOPF |
-                     CYGHWR_HAL_STM32_RCC_APB2ENR_IOPG );
+                     CYGHWR_HAL_STM32_RCC_APB2ENR_IOPG);
 
     // Set all unused GPIO lines to input with pull down to prevent
     // them floating and annoying any external hardware.
diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_nand.c anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_nand.c
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_nand.c	1970-01-01 01:00:00.000000000 +0100
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/src/stm3210e_eval_nand.c	2009-11-17 11:03:07.000000000 +0000
@@ -0,0 +1,499 @@
+//=============================================================================
+//
+//      stm3210e_eval_nand.c
+//
+//      Cortex-M3 STM3210E EVAL NAND setup
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2009 Free Software Foundation
+//
+// eCos 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 or (at your option) any later      
+// version.                                                                 
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+//
+// As a special exception, if other files instantiate templates or use      
+// macros or inline functions from this file, or you compile this file      
+// and link it with other works to produce a work based on this file,       
+// this file does not by itself cause the resulting work to be covered by   
+// the GNU General Public License. However the source code for this file    
+// must still be made available in accordance with section (3) of the GNU   
+// General Public License v2.                                               
+//
+// This exception does not invalidate any other reasons why a work based    
+// on this file might be covered by the GNU General Public License.         
+// -------------------------------------------                              
+// ####ECOSGPLCOPYRIGHTEND####                                              
+//=============================================================================
+//#####DESCRIPTIONBEGIN####
+// Author(s):   Simon Kallweit, Ross Younger
+// Date:        2009-06-30
+//
+//####DESCRIPTIONEND####
+//=============================================================================
+
+#include <pkgconf/hal_cortexm_stm32_stm3210e_eval.h>
+
+#include <cyg/infra/cyg_ass.h>
+
+#include <cyg/nand/nand_device.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_diag.h>
+#include <cyg/hal/drv_api.h>
+
+#include <cyg/devs/nand/nandxxxx3a.h>
+
+/* Private structs. We need one nandxxxx3a_priv (and one of our privs) per
+ * instance of the chip. */
+
+struct _mypriv {
+    char dummy; // not currently used
+};
+
+static struct _mypriv _stm3210_nand_priv;
+
+#define CAST_MYPRIV(x) ((struct _mypriv *) (x))
+#define CAST_NANDPRIV(x) ((struct nandxxxx3a_priv *) (x))
+#define GET_MYPRIV(dev,var) struct _mypriv * var = CAST_MYPRIV(CAST_NANDPRIV((dev)->priv)->plat_priv);
+
+/* We could use 'priv' to supply the NAND addresses. This might be
+ * useful if there were multiple chips on a board, but for simplicity
+ * we're going to hard-wire it meantime. */
+#define NAND_BASE   CYGHWR_HAL_STM32_FSMC_BANK2_BASE
+#define NAND_CMD    (NAND_BASE | CYGHWR_HAL_STM32_FSMC_BANK_CMD)
+#define NAND_ADDR   (NAND_BASE | CYGHWR_HAL_STM32_FSMC_BANK_ADDR)
+
+/* Where can we read the Ready/!Busy signal? */
+#ifdef CYGHWR_HAL_CORTEXM_STM3210E_EVAL_RB_ON_INT2
+# define GPIO_NAND_RB_PIN CYGHWR_HAL_STM32_GPIO(G, 6, IN, PULLUP) // if JP7 2-3
+#else
+# define GPIO_NAND_RB_PIN CYGHWR_HAL_STM32_GPIO(D, 6, IN, PULLUP) // if JP7 1-2
+#endif
+
+/* CHIP OPERATIONS ================================================= */
+
+/* On this board, the memory controller is well set up, so we don't need
+ * to worry about timings for most operations. */
+
+static inline void write_cmd(cyg_nand_device *ctx, unsigned char cmd)
+{
+    HAL_WRITE_UINT8(NAND_CMD, cmd);
+}
+
+static inline void write_addrbytes(cyg_nand_device *ctx, CYG_BYTE *bytes, size_t n)
+{
+    HAL_WRITE_UINT8_VECTOR(NAND_ADDR, bytes, n, 0);
+}
+
+static inline unsigned char read_data_1(cyg_nand_device *ctx)
+{
+    unsigned char b;
+    HAL_READ_UINT8(NAND_BASE, b);
+    return b;
+}
+
+static inline void read_data_bulk(cyg_nand_device *ctx, unsigned char *dp, size_t n)
+{
+    // Most of the time, we expect to be dealing with word-aligned
+    // multiples of 4 bytes, so optimise for that case.
+    if ( ((CYG_ADDRWORD)dp&3)==0 && (n%4)==0) {
+        volatile cyg_uint8 const *a = (cyg_uint8*) NAND_BASE;
+        cyg_uint32 *ip = (cyg_uint32*) dp;
+        cyg_uint32 r;
+        n /= 4;
+#if (CYGINT_HAL_CORTEXM_BIGENDIAN == 1)
+#define ONEWORD do { r  = (*a) << 24; r |= (*a) << 16; r |= (*a) << 8; r |= *a; } while(0)
+#else
+#define ONEWORD do { r  = *a; r |= (*a) << 8; r |= (*a) << 16; r |= (*a) << 24; } while(0)
+#endif
+        while (n) {
+            ONEWORD;
+            *(ip++) = r;
+            --n;
+        }
+#undef ONEWORD
+    } else
+        HAL_READ_UINT8_VECTOR(NAND_BASE, dp, n, 0);
+}
+
+static inline void write_data_1(cyg_nand_device *ctx, unsigned char b)
+{
+    HAL_WRITE_UINT8(NAND_BASE,b);
+}
+
+static inline void write_data_bulk(cyg_nand_device *ctx, const unsigned char *dp, size_t n)
+{
+    if ( ((CYG_ADDRWORD)dp&3)==0 && (n%4)==0) {
+        volatile cyg_uint8 *a = (cyg_uint8*) NAND_BASE;
+        cyg_uint32 *ip = (cyg_uint32*) dp;
+        cyg_uint32 r;
+        n /= 4;
+#ifdef CYGHWR_HAL_ARM_BIGENDIAN
+#define ONEWORD do { *a = (r>>24)&0xff; *a = (r>>16)&0xff; (*a) = (r>>8) & 0xff; (*a) = r & 0xff; } while(0)
+#else
+#define ONEWORD do { *a = r & 0xff; *a = (r>>8) & 0xff; *a = (r>>16)&0xff; *a = (r>>24)&0xff; } while(0)
+#endif
+        while (n) {
+            r = *(ip++);
+            ONEWORD;
+            --n;
+        }
+#undef ONEWORD
+    } else
+        HAL_WRITE_UINT8_VECTOR(NAND_BASE, dp, n, 0);
+}
+
+/* READY line handling and fallback ================================ */
+
+// Forward defs:
+/* Waits either for the !BUSY line to signal that the device is finished,
+   or for the prescribed amount of time.
+ * wait_init specifies the time in microseconds to wait to ensure that
+   BUSY is asserted.
+ * wait_fallback specifies the worst-case time to wait (from the spec
+   sheet; again in microseconds) for the operation to complete. */
+static void wait_ready_or_time(cyg_nand_device *ctx,
+                               size_t wait_init, size_t wait_fallback);
+
+/* Waits either for the !BUSY line to signal that the device is finished,
+ * or (if not available) polls the chip by sending the Read Status command
+ * and waits for (response & mask) to be non-zero. */
+static void wait_ready_or_status(cyg_nand_device *ctx, CYG_BYTE mask);
+
+/* Case 1: The NAND_RDY line is not connected. ---------------------- */
+#ifndef CYGHWR_HAL_CORTEXM_STM3210E_EVAL_RB_ON_INT2
+
+// fwd def from the chip driver:
+static inline CYG_BYTE read_status(cyg_nand_device *dev);
+
+static void wait_ready_or_time(cyg_nand_device *ctx, size_t initial, size_t fallback)
+{
+    // With the jumper set to route the BUSY line to FSMC_WAIT,
+    // the FSMC will stall any attempted accesses to the NAND until the
+    // chip signals READY.
+    // On a read operation, the chip asserts BUSY tWHBL (100ns) or tWHALL
+    // (10ns) after we latch in the last address byte.
+    // Therefore we will wait the initial time (1us is overkill) to ensure
+    // it is asserted, but don't have to do anything further here.
+    cyg_int32 r;
+
+    HAL_DELAY_US(initial);
+
+    CYGHWR_HAL_STM32_GPIO_IN(GPIO_NAND_RB_PIN, &r);
+    CYG_ASSERTC(r==1);
+}
+
+static void wait_ready_or_status(cyg_nand_device *ctx, CYG_BYTE mask)
+{
+    // With the jumper set to route the BUSY line to FSMC_WAIT,
+    // the FSMC will stall any attempted accesses to the NAND until the
+    // chip signals READY.
+    //
+    // Therefore, we should just be able to read_status once and find
+    // that we stall until the operation has completed; IOW, `polls'
+    // should be 1 every time.  This has been confirmed experimentally.
+
+    st_nand_wait_for_BUSY(); // this (1us) is overkill
+    int polls=0;
+    int sta;
+    do {
+        sta = read_status(ctx);
+        HAL_DELAY_US(10);
+        ++polls;
+    } while (!(sta & mask));
+    NAND_CHATTER(8, ctx, "wait_status: pollcount %d status 0x%02x\n", polls, sta);
+}
+
+#else // CYGHWR_HAL_CORTEXM_STM3210E_EVAL_RB_ON_INT2
+/* Case 2: The RB line is connected to INT2. ------------------------ */
+
+// Common code
+
+/* Polls the !BUSY line. Returns 1 if ready, 0 if busy. */
+static inline int is_chip_ready(struct _mypriv *ctx)
+{
+    cyg_int32 rv;
+    CYGHWR_HAL_STM32_GPIO_IN(GPIO_NAND_RB_PIN, &rv);
+    return rv;
+}
+
+/* Polling loop, does not return until the chip is READY */
+static void wait_ready_polled(cyg_nand_device *ctx)
+{
+    int polls=0;
+    GET_MYPRIV(ctx, priv);
+    while (0==is_chip_ready(priv)) {
+        HAL_DELAY_US(CYGNUM_HAL_CORTEXM_STM3210E_EVAL_POLL_INTERVAL);
+        ++polls;
+    }
+    NAND_CHATTER(8, ctx, "!BUSY: pollcount %d\n",polls);
+}
+
+/* TODO: Code to achieve interrupt-driven sleep, as opposed to polling,
+ * would go here, enabled by its own CDL option.
+ * At the time of writing we have been unable to get this to work
+ * properly; the interrupt controller doesn't seem to detect the
+ * movement of the NAND RB line. */
+
+static void wait_ready_or_time(cyg_nand_device *ctx,
+                               size_t wait_init, size_t wait_fallback)
+{
+    wait_ready_polled(ctx);
+}
+
+static void wait_ready_or_status(cyg_nand_device *ctx, CYG_BYTE mask)
+{
+    wait_ready_polled(ctx);
+}
+
+#endif // ..._RB_ON_INT2
+
+static int nandxxxx3a_plf_init(cyg_nand_device *dev)
+{
+    CYG_ADDRESS base;
+    cyg_uint32 reg;
+
+    // Setup CLE, ALE, D0..D3, NOE, NWE, NCE2 pins
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D, 0, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D, 1, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D, 4, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D, 5, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D, 7, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D,11, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D,12, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D,14, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(D,15, OUT_50MHZ, AOPP));
+
+    // Setup D4..D7 pins
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(E, 7, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(E, 8, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(E, 9, OUT_50MHZ, AOPP));
+    CYGHWR_HAL_STM32_GPIO_SET(CYGHWR_HAL_STM32_GPIO(E,10, OUT_50MHZ, AOPP));
+
+    // Setup the relevant Ready/!Busy inputs as GPIO
+    CYGHWR_HAL_STM32_GPIO_SET(GPIO_NAND_RB_PIN);
+
+    // Setup FSMC for NAND
+    base = CYGHWR_HAL_STM32_FSMC;
+    reg = 0x01020301;
+    //reg = 0x0f0f0f0f;
+    HAL_WRITE_UINT32(base + CYGHWR_HAL_STM32_FSMC_PMEM2, reg);
+    HAL_WRITE_UINT32(base + CYGHWR_HAL_STM32_FSMC_PATT2, reg);
+    reg =
+          CYGHWR_HAL_STM32_FSMC_PCR_PWAITEN |
+          CYGHWR_HAL_STM32_FSMC_PCR_PTYP_NAND |
+          CYGHWR_HAL_STM32_FSMC_PCR_PWID_8 |
+          CYGHWR_HAL_STM32_FSMC_PCR_TCLR(0) |
+          CYGHWR_HAL_STM32_FSMC_PCR_TAR(0) |
+          CYGHWR_HAL_STM32_FSMC_PCR_ECCPS_512;
+    HAL_WRITE_UINT32(base + CYGHWR_HAL_STM32_FSMC_PCR2, reg);
+    reg |= CYGHWR_HAL_STM32_FSMC_PCR_PBKEN;
+    HAL_WRITE_UINT32(base + CYGHWR_HAL_STM32_FSMC_PCR2, reg);
+
+    /* Having just twiddled the FSMC, if we immediately try to talk to
+     * the NAND we sometimes (pretty reliably via certain code paths
+     * when compiled with -O2, it seems) die with a SIGBUS.
+     * This seemingly ineffectual talisman (which, incidentally,
+     * first manifested itself as "volatile int i = 42;" is all that's
+     * needed to perturb it away. */
+    asm("nop");
+
+#ifdef INITHOOK
+    INITHOOK(dev);
+#endif
+
+    return 0;
+}
+
+/* Partition support ===================================================
+ * Without Manual config, developer has to set up the partitions list
+ * by hand. */
+#ifdef CYGSEM_DEVS_NAND_STM3210E_EVAL_PARTITION_MANUAL_CONFIG
+
+static inline int stm3210e_init_manual_partition(cyg_nand_device *dev)
+{
+#define PARTITION(i) do {                                               \
+    cyg_nand_block_addr                                                 \
+        base = CYGNUM_DEVS_NAND_STM3210E_EVAL_PARTITION_ ## i ## _BASE, \
+        size = CYGNUM_DEVS_NAND_STM3210E_EVAL_PARTITION_ ## i ## _SIZE; \
+    dev->partition[i].dev = dev;                                        \
+    dev->partition[i].first = base;                                     \
+    dev->partition[i].last = size ?                                     \
+                base + size - 1: (1<<dev->blockcount_bits)-1;           \
+} while(0)
+
+#ifdef CYGPKG_DEVS_NAND_STM3210E_EVAL_PARTITION_0
+    PARTITION(0);
+#endif
+#ifdef CYGPKG_DEVS_NAND_STM3210E_EVAL_PARTITION_1
+    PARTITION(1);
+#endif
+#ifdef CYGPKG_DEVS_NAND_STM3210E_EVAL_PARTITION_2
+    PARTITION(2);
+#endif
+#ifdef CYGPKG_DEVS_NAND_STM3210E_EVAL_PARTITION_3
+    PARTITION(3);
+#endif
+    /* Oh for the ability to write a for-loop in pre-processor,
+       or a more powerful libcdl ... */
+    return 0;
+}
+
+#endif // CYGSEM_DEVS_NAND_STM3210E_EVAL_PARTITION_MANUAL_CONFIG
+
+static int nandxxxx3a_plf_partition_setup(cyg_nand_device *dev)
+{
+#ifdef CYGSEM_DEVS_NAND_STM3210E_EVAL_PARTITION_MANUAL_CONFIG
+    return stm3210e_init_manual_partition(dev);
+#else
+    return 0;
+#endif
+}
+
+/* Concurrent access protection ======================================== */
+
+// ... is not required on this platform.
+// (On some boards, chips might share a chip-select line, CPLD or similar.)
+
+static inline void nandxxxx3a_devlock(cyg_nand_device *dev)
+{
+}
+
+static inline void nandxxxx3a_devunlock(cyg_nand_device *dev)
+{
+}
+
+/* Hardware ECC ======================================================== */
+// TODO This could live in the variant HAL?
+
+static void stm3210e_ecc_init(cyg_nand_device *dev)
+{
+    cyg_uint32 cur;
+    CYG_ADDRWORD reg = CYGHWR_HAL_STM32_FSMC + CYGHWR_HAL_STM32_FSMC_PCR2;
+
+    HAL_READ_UINT32(reg, cur);
+    cur |= CYGHWR_HAL_STM32_FSMC_PCR_ECCEN;
+    HAL_WRITE_UINT32(reg, cur);
+}
+
+static void stm3210e_ecc_calc(cyg_nand_device *dev, const CYG_BYTE *dat, CYG_BYTE *ecc)
+{
+    cyg_uint32 code,set;
+    CYG_ADDRWORD eccr= CYGHWR_HAL_STM32_FSMC + CYGHWR_HAL_STM32_FSMC_ECCR2,
+                 ctrr= CYGHWR_HAL_STM32_FSMC + CYGHWR_HAL_STM32_FSMC_PCR2;
+    HAL_READ_UINT32(eccr, code);
+    code = ~code; // So an all-0xFF page has an ECC of 0xFF
+
+    ecc[0] = code & 0xFF;
+    ecc[1] = (code >> 8) & 0xFF;
+    ecc[2] = (code >>16) & 0xFF;
+    // The resultant ordering is therefore:
+    // byte 0: P8, P4, P2, P1 pairs (P1 at LSB)
+    // byte 1: P128, P64, P32, P16
+    // byte 2: P2048, P1024, P512, P256
+
+    HAL_READ_UINT32(ctrr, set);
+    set &= ~CYGHWR_HAL_STM32_FSMC_PCR_ECCEN;
+    HAL_WRITE_UINT32(ctrr, set);
+}
+
+static int stm3210e_ecc_repair(cyg_nand_device *dev,
+                            CYG_BYTE *dat, size_t nbytes,
+                            CYG_BYTE *read_ecc, const CYG_BYTE *calc_ecc)
+{
+    unsigned d1, d2, d3;
+
+    d1 = calc_ecc[0] ^ read_ecc[0];
+    d2 = calc_ecc[1] ^ read_ecc[1];
+    d3 = calc_ecc[2] ^ read_ecc[2];
+
+    if((d1|d2|d3) == 0)
+        return 0; // Nothing to do.
+
+    // Can we fix it? 12 bits should be set, and as they're stored
+    // in adjacent pairs within each byte we can use this neat XOR
+    // trick to make sure that precisely one bit of each pair is set.
+    unsigned a, b, c;
+    a = (d1 ^ (d1 >> 1)) & 0x55;
+    b = (d2 ^ (d2 >> 1)) & 0x55;
+    c = (d3 ^ (d3 >> 1)) & 0x55;
+
+    if ((a==b)&&(b==c)&&(c==0x55)) {
+        // Yes we can ! Figure out the offset.
+        unsigned byte, bit; 
+
+        bit = ( (d1 >> 1) & 1) |        // P1
+              ( (d1 >> 2) & 2) |        // P2
+              ( (d1 >> 3) & 4);         // and P4.
+
+        byte = ( (d1 >> 7) & 1) |       // P8
+               ( (d2 >> 0) & 2) |       // P16
+               ( (d2 >> 1) & 4) |       // P32
+               ( (d2 >> 2) & 8) |       // P64
+               ( (d2 >> 3) &16) |       // P128
+               ( (d3 << 4) &32) |       // P256
+               ( (d3 << 3) &64) |       // P512
+               ( (d3 << 2)&128) |       // P1024
+               ( (d3 << 1)&256);        // P2048
+
+        if (byte < nbytes)
+            dat[byte] ^= (1<<bit);
+
+        return 1;
+    }
+
+    // No? Is it an ECC dropout? Count the bits...
+    unsigned i=0;
+    while (d1) {
+        if (d1 & 1) ++i;
+        d1 >>= 1;
+    }
+    while (d2) {
+        if (d2 & 1) ++i;
+        d2 >>= 1;
+    }
+    while (d3) {
+        if (d3 & 1) ++i;
+        d3 >>= 1;
+    }
+    if (i==1) {
+        read_ecc[0] = calc_ecc[0];
+        read_ecc[1] = calc_ecc[1];
+        read_ecc[2] = calc_ecc[2];
+        return 2;
+    }
+    // Alas, it is uncorrectable.
+    return -1;
+}
+
+static CYG_NAND_ECC_ALG_HW(stm3210e_ecc, 512, 3, stm3210e_ecc_init, stm3210e_ecc_calc, stm3210e_ecc_repair);
+
+// Need to use a slightly modified OOB layout to satisfy nand_lookup's paranoia checks
+static const cyg_nand_oob_layout stm3210e_oob = {
+    .ecc_size = 3,
+    .ecc = { { .pos=0, .len=3 } },
+    /* 3, 6, 7 would be ECC in the Linux MTD world. 4/5 are avoided. */
+    .app_size = 8,
+    .app = { { .pos=3, .len=1 }, { .pos=6, .len=10} },
+};
+
+/* Putting it all together ... ========================================= */
+
+#include <cyg/devs/nand/nandxxxx3a.inl>
+
+NANDXXXX3A_DEVICE(stm3210e_nand, "onboard", 512, &_stm3210_nand_priv,
+                  &stm3210e_ecc, &stm3210e_oob);
+
diff -Nur anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/tests/eccwalk.c anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/tests/eccwalk.c
--- anon-hg/packages/hal/cortexm/stm32/stm3210e_eval/current/tests/eccwalk.c	1970-01-01 01:00:00.000000000 +0100
+++ anon+nand/packages/hal/cortexm/stm32/stm3210e_eval/current/tests/eccwalk.c	2009-11-12 11:49:06.000000000 +0000
@@ -0,0 +1,146 @@
+//=============================================================================
+//
+//      eccwalk.c
+//
+//      Walks some sample data through the NAND flash controller to
+//      check that the ECC computation and repair operate correctly.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2009 eCosCentric Limited.
+//
+// eCos 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 or (at your option) any later      
+// version.                                                                 
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+//
+// As a special exception, if other files instantiate templates or use      
+// macros or inline functions from this file, or you compile this file      
+// and link it with other works to produce a work based on this file,       
+// this file does not by itself cause the resulting work to be covered by   
+// the GNU General Public License. However the source code for this file    
+// must still be made available in accordance with section (3) of the GNU   
+// General Public License v2.                                               
+//
+// This exception does not invalidate any other reasons why a work based    
+// on this file might be covered by the GNU General Public License.         
+// -------------------------------------------                              
+// ####ECOSGPLCOPYRIGHTEND####                                              
+//=============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   wry
+// Date:        2009-10-27
+//
+//####DESCRIPTIONEND####
+//=============================================================================
+
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/testcase.h>
+#include <cyg/nand/nand.h>
+#include <cyg/nand/nand_devtab.h>
+#include <cyg/nand/util.h>
+#include <cyg/infra/diag.h>
+#include <stdio.h>
+#include <string.h>
+
+#define MUST(what) do { CYG_TEST_CHECK((what), #what); } while(0)
+
+#define datasize 512
+unsigned char buf[datasize];
+
+#define NAND_BASE   CYGHWR_HAL_STM32_FSMC_BANK2_BASE
+
+void ecc(cyg_nand_device *dev, const char *msg, CYG_BYTE *out)
+{
+    dev->ecc->init(dev);
+
+    // This is the sneaky bit: write data through the NAND controller
+    // so the ECC register updates, but not framed by NAND commands i.e.
+    // not actually affecting the chip.
+    // This is necessarily board-specific ...
+    HAL_WRITE_UINT8_VECTOR(NAND_BASE, buf, datasize, 0);
+
+    dev->ecc->calc(dev, 0, 256, out);
+    diag_printf("%s: ecc=%02x%02x%02x\n", msg, out[0], out[1], out[2]);
+}
+
+const char msg[]="ECC walker";
+
+int cyg_user_start(void)
+{
+    cyg_nand_device *dev;
+    int i;
+    CYG_BYTE ecc1[3], ecc2[3];
+
+    CYG_TEST_INIT();
+    CYG_TEST_INFO(msg);
+
+    if (cyg_nanddevtab == &cyg_nanddevtab_end)
+        CYG_TEST_NA("No NAND devices found");
+
+    CYG_TEST_CHECK(0==cyg_nand_lookup("onboard", &dev),"lookup failed");
+
+    // Plan: Start with a buffer full of 0xFF; then, changing one bit 
+    // at a time, compute a fresh ECC and check that the repair code
+    // does the right thing.
+
+    for (i=0; i<datasize; i++) buf[i]=0xff;
+    ecc(dev, "all-ff", ecc1);
+
+
+    for (i=0; i<8; i++) {
+        int st;
+        char msg[] = "bit X";
+        buf[0] ^= 1<<i;
+        msg[4] = '0' + i;
+        ecc(dev, msg, ecc2);
+
+        st = dev->ecc->repair(dev, buf, sizeof buf, ecc1, ecc2);
+        CYG_ASSERTC(st==1);
+        CYG_ASSERTC(buf[0] == 0xff);
+
+        ecc(dev, msg, ecc2);
+        st = dev->ecc->repair(dev, buf, sizeof buf, ecc1, ecc2);
+        CYG_ASSERTC(st==1);
+    }
+    for (i=0; i<9; i++) {
+        char msg[] = "bytelog X";
+        int st;
+        msg[8] = '0' + i;
+        buf[1<<i] ^= 1;
+        ecc(dev, msg, ecc2);
+        st = dev->ecc->repair(dev, buf, sizeof buf, ecc1, ecc2);
+        CYG_ASSERTC(st==1);
+        CYG_ASSERTC(buf[1<<i] == 0xff);
+
+        ecc(dev, msg, ecc2);
+        st = dev->ecc->repair(dev, buf, sizeof buf, ecc1, ecc2);
+        CYG_ASSERTC(st==1);
+    }
+
+    int fail=0;
+    for (i=0; i<datasize; i++) {
+        if (buf[i] != 0xff) {
+            ++fail;
+            diag_printf("buf[%d] == %02x != 0xff\n", i, buf[i]);
+        }
+    }
+
+    if (fail)
+        CYG_TEST_FAIL_FINISH(msg);
+    else
+        CYG_TEST_PASS_FINISH(msg);
+}
+
