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 + + * src/stm3210e_eval_nand.c: Build fix for minimal configs. + +2009-11-12 Ross Younger + + * src/stm3210e_eval_nand.c: Add i/o heuristic speed-up. + +2009-11-09 Ross Younger + + * 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 + + * 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 + + * src/stm3210e_eval_nand.c: Added support for onboard NAND chip. + Based on work by Simon Kallweit. + 2009-02-04 Nick Garnett * 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 + +#include + +#include +#include +#include +#include +#include + +#include + +/* 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<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<>= 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 + +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 +#include +#include +#include +#include +#include +#include +#include + +#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; iecc->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<ecc->repair(dev, buf, sizeof buf, ecc1, ecc2); + CYG_ASSERTC(st==1); + CYG_ASSERTC(buf[1<ecc->repair(dev, buf, sizeof buf, ecc1, ecc2); + CYG_ASSERTC(st==1); + } + + int fail=0; + for (i=0; i