diff -Nur anon-hg/packages/hal/arm/lpc24xx/ea2468/current/cdl/hal_arm_lpc24xx_ea2468.cdl anon+nand/packages/hal/arm/lpc24xx/ea2468/current/cdl/hal_arm_lpc24xx_ea2468.cdl --- anon-hg/packages/hal/arm/lpc24xx/ea2468/current/cdl/hal_arm_lpc24xx_ea2468.cdl 2009-10-28 11:10:41.000000000 +0000 +++ anon+nand/packages/hal/arm/lpc24xx/ea2468/current/cdl/hal_arm_lpc24xx_ea2468.cdl 2009-11-12 11:47:25.000000000 +0000 @@ -321,7 +321,63 @@ "" } } } - + + cdl_component CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND { + display "EA LPC2xxx NAND support" + parent CYGPKG_IO_NAND + active_if CYGPKG_IO_NAND + implements CYGHWR_IO_NAND_DEVICE + requires CYGPKG_DEVS_NAND_SAMSUNG_K9 + requires { (CYGPKG_REDBOOT && CYGPKG_FS_YAFFS) implies (CYGMEM_REDBOOT_WORKSPACE_HEAP_SIZE >= 0x20000) } + + compile -library=libextras.a ea_lpc2468_nand.c + description "This option enables support for the on-board Samsung + K9F1G08U0A NAND flash chip." + + cdl_option CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_USE_NAND_RDY { + display "Use NAND_RDY line" + flavor bool + default_value 0 + description "The EA OEM Base Board provides a jumper which + connects the NAND_RDY line with P2.12 on the CPU. + This option allows the driver to query that line. + You should not set this option if the jumper is not + connected; the results of interacting with the + chip will be undefined." + } + + cdl_option CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND_RDY_USE_INTERRUPT { + display "Configure NAND_RDY line as an interrupt" + flavor bool + default_value CYGPKG_KERNEL + active_if (CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_USE_NAND_RDY && CYGPKG_KERNEL) + description "This option configures the CPU pin P2.12 + - to which the NAND_RDY line is assumed to be connected - + as an interrupt (EINT2). This is normally the preferred + configuration when the NAND_RDY line is connected, as + it allows a thread waiting for the + NAND device to sleep for exactly the right amount of time. + If this is disabled, the NAND_RDY line is polled in a + loop which consumes unnecessary CPU cycles." + } + + # Manual configuration of NAND partitions. + # We currently only provide manual config. + + + cdl_component CYGSEM_DEVS_NAND_EA_LPC2468_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 EA LPC2468 on-board NAND." + + set ::partition_device "EA_LPC2468" + script manual_partition.cdl + } + } + cdl_option CYGPKG_HAL_ARM_LPC24XX_EA2468_TESTS { display "Tests for LPC2468 OEM board HAL" flavor data diff -Nur anon-hg/packages/hal/arm/lpc24xx/ea2468/current/cdl/manual_partition.cdl anon+nand/packages/hal/arm/lpc24xx/ea2468/current/cdl/manual_partition.cdl --- anon-hg/packages/hal/arm/lpc24xx/ea2468/current/cdl/manual_partition.cdl 1970-01-01 01:00:00.000000000 +0100 +++ anon+nand/packages/hal/arm/lpc24xx/ea2468/current/cdl/manual_partition.cdl 2009-11-12 11:47:25.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/arm/lpc24xx/ea2468/current/ChangeLog anon+nand/packages/hal/arm/lpc24xx/ea2468/current/ChangeLog --- anon-hg/packages/hal/arm/lpc24xx/ea2468/current/ChangeLog 2009-10-28 11:10:41.000000000 +0000 +++ anon+nand/packages/hal/arm/lpc24xx/ea2468/current/ChangeLog 2009-11-12 15:06:57.000000000 +0000 @@ -1,3 +1,45 @@ +2009-11-10 Ross Younger + + * ea_lpc2468_nand.c: Fix interrupt-mode brokenness when the debug + level was low. + Add heuristic to improve I/O throughput. + Tune the EMC timings to speed up NAND access. + +2009-11-06 Ross Younger + + * ea_lpc2468_nand.c: Use mtd_ecc256_fast, not linux_mtd_ecc. + +2009-08-18 Ross Younger + + * ea_lpc_2468_nand.c: Minor bugfix - attach on the interrupt handle, + not the interrupt object, even if it's currently inconsequential. + +2009-07-02 Ross Younger + + * ea_lpc_2468_nand.c: Small optimisations: + + Condition-out poll-counting unless it's being reported. + + Properly defined initial-wait semantics for wait_ready_polled; + tweak callers to match. + + Don't bother with an interrupt-assisted sleep for a small + time (i.e. a page read). + +2009-06-05 Ross Younger + + * ea_lpc_2468_nand.c: refactor for k9fxx08x0x changes; retab. + * * BBT per-chip + * * provide for locking though we don't use it on this board. + +2009-05-27 Ross Younger + + * src/ea_lpc_2468_nand.c: Rip out eCosPro HAL ifdefs & partial rewrite + for clarity. + +2009-05-14 Ross Younger + + * cdl/hal_arm_lpc24xx_ea2468.cdl: Add NAND support and options + * cdl/manual_partition.cdl: Created + * src/ea_lpc_2468_nand.c: Created + 2009-01-31 Bart Veer * cdl/hal_arm_lpc24xx_ea2468.cdl: update compiler flags for gcc 4.x diff -Nur anon-hg/packages/hal/arm/lpc24xx/ea2468/current/src/ea_lpc2468_nand.c anon+nand/packages/hal/arm/lpc24xx/ea2468/current/src/ea_lpc2468_nand.c --- anon-hg/packages/hal/arm/lpc24xx/ea2468/current/src/ea_lpc2468_nand.c 1970-01-01 01:00:00.000000000 +0100 +++ anon+nand/packages/hal/arm/lpc24xx/ea2468/current/src/ea_lpc2468_nand.c 2009-11-13 10:36:21.000000000 +0000 @@ -0,0 +1,526 @@ +//============================================================================= +// +// ea_lpc2468_nand.c +// +// NAND flash support for the Embedded Artists LPC2468 OEM board. +// (This is the version for the eCos HAL for that board.) +// +//============================================================================= +// ####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-03 +// +//####DESCRIPTIONEND#### +//============================================================================= + +#include + +#include +#include +#include +#include + +#include + +/* Private structs. We need one k9_priv (and one of our privs) per + * instance of the chip. */ + +struct _mypriv { +#ifdef CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND_RDY_USE_INTERRUPT + cyg_drv_mutex_t cvmux; // Protects CV + cyg_drv_cond_t cv; // Used to sleep on interrupt + cyg_handle_t inthdl; // interrupt handle + cyg_interrupt intr; // interrupt object + + volatile int ready; // Set when interrupt fires. Protected by DSR lock. +#else + char dummy; // shush, gcc +#endif +}; + +static struct _mypriv _ea_lpc2468_nand_priv; +static k9_priv _k9_ea_lpc2468_priv = { &_ea_lpc2468_nand_priv }; + +#define CAST_MYPRIV(x) ((struct _mypriv *) (x)) +#define CAST_K9PRIV(x) ((k9_priv *) (x)) +#define GET_MYPRIV(dev,var) struct _mypriv * var = CAST_MYPRIV(CAST_K9PRIV((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 0x81000000 +#define NAND_CMD (NAND_BASE | (1<<20)) +#define NAND_ADDR (NAND_BASE | (1<<19)) + +static inline void tweak_pin_register(CYG_ADDRWORD reg, cyg_uint32 clearbits, cyg_uint32 setbits) +{ + cyg_uint32 val; + HAL_READ_UINT32(reg, val); + val &= ~clearbits; + val |= setbits; + HAL_WRITE_UINT32(reg, val); +} + +/* 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_ARM_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); + +#define POLL_INTERVAL 10 /* us */ + +#define POLLCOUNT_COUNTING_LEVEL 7 +#if defined(CYGSEM_IO_NAND_DEBUG_LEVEL) && (CYGSEM_IO_NAND_DEBUG_LEVEL >= POLLCOUNT_COUNTING_LEVEL) +#define REPORT_POLLS +#endif + +/* Case 1: The NAND_RDY line is not connected. ---------------------- */ +#ifndef CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_USE_NAND_RDY + +// fwd def from the k9 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) +{ + NAND_CHATTER(8, ctx, "Waiting %d us for operation\n", initial+fallback); + HAL_DELAY_US(initial+fallback); +} + +static void wait_ready_or_status(cyg_nand_device *dev, CYG_BYTE mask) +{ + // The Ready line won't be ready for at least tWB (100ns), so out of + // paranoia we'll wait at least that long before reading Status. + + k9_WAIT_tWB(); // this (1us) is overkill + +#ifdef REPORT_POLLS + int polls=0; +#endif + int sta; + do { + sta = read_status(dev); + HAL_DELAY_US(POLL_INTERVAL); +#ifdef REPORT_POLLS + ++polls; +#endif + } while (!(sta & mask)); +#ifdef REPORT_POLLS + NAND_CHATTER(8, dev, "wait_status: pollcount %d\n",polls); +#endif +} + +#else // CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_USE_NAND_RDY +/* Case 2: The NAND_RDY line *is* connected. ------------------------ */ + +// Common code + +/* Polls the !BUSY line. Returns 1 if ready, 0 if busy. */ +static inline int is_chip_ready(cyg_nand_device *ctx) +{ + cyg_uint32 rv; + HAL_READ_UINT32( + CYGARC_HAL_LPC24XX_REG_FIO_BASE + CYGARC_HAL_LPC24XX_REG_FIO2PIN, + rv); + rv = rv & (1<<12); + return rv; +} + +static void wait_ready_polled(cyg_nand_device *ctx); + +# ifdef CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND_RDY_USE_INTERRUPT +/* Case 2A: We are using sleep+interrupt wherever possible. */ + +# define NAND_RDY_VECTOR CYGNUM_HAL_INTERRUPT_EINT2 +# define NAND_RDY_PRIO 12 + +/* Sleeps until woken by interrupt on the READY/!BUSY line. + * Obviously, this doesn't work (in fact is illegal) if the scheduler + * isn't running, so we fall back to polling the READY line in that case + * (see wait_ready below). */ +static void wait_ready_interrupt(cyg_nand_device *ctx) +{ + GET_MYPRIV(ctx,priv); + cyg_drv_mutex_lock(&priv->cvmux); + cyg_drv_dsr_lock(); + priv->ready = 0; + NAND_CHATTER(8, ctx, "sleep for busy\n"); + cyg_drv_interrupt_unmask(NAND_RDY_VECTOR); + while (!priv->ready) + cyg_drv_cond_wait(&priv->cv); + cyg_drv_interrupt_mask(NAND_RDY_VECTOR); + cyg_drv_dsr_unlock(); + cyg_drv_mutex_unlock(&priv->cvmux); +} + +static +cyg_uint32 nand_rdy_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + if (vector != NAND_RDY_VECTOR) return 0; + cyg_drv_interrupt_acknowledge(vector); + return CYG_ISR_HANDLED|CYG_ISR_CALL_DSR; +} + +static +void nand_rdy_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + struct _mypriv *priv = CAST_MYPRIV(data); + priv->ready=1; + cyg_drv_cond_broadcast(&priv->cv); +} + +static void wait_ready(cyg_nand_device *ctx) +{ + int scheduler_off= (cyg_thread_self() == cyg_thread_idle_thread()); + if (scheduler_off) { + NAND_CHATTER(7, ctx, "Polling as scheduler is off\n"); + k9_WAIT_tWB(); + wait_ready_polled(ctx); + } else { + wait_ready_interrupt(ctx); + } +} + +/* Interrupt mode has high overheads (several hundred us). + * For quick operations - page reading @ 25us and certain reset cases - + * it's not worth it on this board, so we just hang around for the + * prescribed time. + */ +#define WAIT_MIN_INTERRUPTMODE_THRESHOLD 400 /* useconds */ + +static void wait_ready_or_time(cyg_nand_device *ctx, + size_t wait_init, size_t wait_fallback) +{ + /* Page reading is very quick (25us), so it's not worth the + * overhead of interrupt mode. */ + if (wait_fallback > WAIT_MIN_INTERRUPTMODE_THRESHOLD) { + wait_ready(ctx); + } else { + HAL_DELAY_US(wait_init); + wait_ready_polled(ctx); + } +} + +static void wait_ready_or_status(cyg_nand_device *ctx, CYG_BYTE mask) +{ + /* These ops are page programming (300-700us) block erase (2-3ms), + * so if interrupt mode is enabled, we'll use it. */ + wait_ready(ctx); +} + + +# define INITHOOK ea_plf_init_interrupt +static int ea_plf_init_interrupt(cyg_nand_device *ctx) +{ + GET_MYPRIV(ctx,priv); + + // Direction -> input + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_FIO_BASE + CYGARC_HAL_LPC24XX_REG_FIO2DIR, + 1 << 12, 0); + + // Function -> EINT2 + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_PIN_BASE + CYGARC_HAL_LPC24XX_REG_PINSEL4, + 3 << 24, 1 << 24); + + // Mask -> pin active + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_FIO_BASE + CYGARC_HAL_LPC24XX_REG_FIO2MASK, + 1 << 12, 0); + + cyg_drv_mutex_init(&priv->cvmux); + cyg_drv_cond_init(&priv->cv, &priv->cvmux); + + cyg_interrupt_configure(NAND_RDY_VECTOR, false /* edge triggered */, true /* high */); + cyg_drv_interrupt_create(NAND_RDY_VECTOR, NAND_RDY_PRIO, (cyg_addrword_t)priv, nand_rdy_ISR, nand_rdy_DSR, &priv->inthdl, &priv->intr); + cyg_drv_interrupt_attach(priv->inthdl); + cyg_drv_interrupt_mask(NAND_RDY_VECTOR); + return 0; +} + +# else +/* Case 2B: We are never using sleep+interrupt. */ + +static void wait_ready_or_time(cyg_nand_device *ctx, + size_t wait_init, size_t wait_fallback) +{ + HAL_DELAY_US(wait_init); + wait_ready_polled(ctx); +} + +static void wait_ready_or_status(cyg_nand_device *ctx, CYG_BYTE mask) +{ + k9_WAIT_tWB(); + wait_ready_polled(ctx); +} + +# define INITHOOK ea_plf_init_nointerrupt +static inline int ea_plf_init_nointerrupt(cyg_nand_device *ctx) +{ + // Direction -> input + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_FIO_BASE + CYGARC_HAL_LPC24XX_REG_FIO2DIR, + 1<<12, 0); + + // Function -> GPIO + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_PIN_BASE + CYGARC_HAL_LPC24XX_REG_PINSEL4, + 3<<24, 0); + + // Mask -> pin active + tweak_pin_register( + CYGARC_HAL_LPC24XX_REG_FIO_BASE + CYGARC_HAL_LPC24XX_REG_FIO2MASK, + 1<<12, 0); + return 0; + (void)ctx; +} + +# endif + +/* Polling loop, does not return until the chip is READY. + * Callers should themselves wait for tWB or other initial time + * to ensure that READY is deasserted. */ +static void wait_ready_polled(cyg_nand_device *ctx) +{ +#ifdef REPORT_POLLS + int polls=0; +#endif +#ifdef CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND_RDY_USE_INTERRUPT + cyg_drv_interrupt_unmask(NAND_RDY_VECTOR); +#endif + while (0==is_chip_ready(ctx)) { + HAL_DELAY_US(POLL_INTERVAL); +#ifdef REPORT_POLLS + ++polls; +#endif + } +#ifdef CYGHWR_HAL_ARM_LPC2XXX_EA_LPC2468_NAND_RDY_USE_INTERRUPT + cyg_drv_interrupt_mask(NAND_RDY_VECTOR); +#endif +#ifdef REPORT_POLLS + NAND_CHATTER(8, ctx, "!BUSY: pollcount %d\n",polls); +#endif +} + +#endif // USE_INTERRUPT ? + +static int k9_plf_init(cyg_nand_device *dev) +{ + //GET_MYPRIV(dev,priv); + + // Default platform EMC timings on this board are a bit off for + // efficient use of the NAND. + + unsigned ns_per_cclk = 1 + 1000000000 / CYGNUM_HAL_ARM_LPC24XX_CLOCK_SPEED; + + // NB! These figures are interpreted from the K9 datasheet. + // WaitOEN: No delay required + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_EMC_BASE + CYGARC_HAL_LPC24XX_REG_EMCS_WAITO_EN1, 0); + + // WaitRead: 25ns (EMC delays for CCLK * (1+n) ) + unsigned waitread = 25 / ns_per_cclk; // +1 to round up, -1 to account for the implicit CCLK + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_EMC_BASE + CYGARC_HAL_LPC24XX_REG_EMCS_WAITRD1, waitread); + + // WaitWEN: No delay required, use the shortest that the EMC allows + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_EMC_BASE + CYGARC_HAL_LPC24XX_REG_EMCS_WAITW_EN1, 0); + // WaitWrite: 25ns (EMC delays for CCLK * (2+n) ) + unsigned waitwrite = 25 / ns_per_cclk; // +1 to round up, -2 to account for the implicit 2CCLK, but don't underflow... + if (waitwrite != 0) --waitwrite; + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_EMC_BASE + CYGARC_HAL_LPC24XX_REG_EMCS_WAITWR1, waitwrite); + + // WaitTurn: 10ns (EMC delays for CCLK * (1+n) ) + unsigned waitturn = 10 / ns_per_cclk; // +1 to round up, -1 to account for the implicit CCLK + HAL_WRITE_UINT32(CYGARC_HAL_LPC24XX_REG_EMC_BASE + CYGARC_HAL_LPC24XX_REG_EMCS_WAITTURN1, waitturn); + + +#ifdef INITHOOK + return INITHOOK(dev); +#else + return 0; +#endif +} + +/* Partition support =================================================== + * Without Manual config, developer has to set up the partitions list + * by hand. */ +#ifdef CYGSEM_DEVS_NAND_EA_LPC2468_PARTITION_MANUAL_CONFIG + +static inline int ea_init_manual_partition(cyg_nand_device *dev) +{ +#define PARTITION(i) do { \ + cyg_nand_block_addr \ + base = CYGNUM_DEVS_NAND_EA_LPC2468_PARTITION_ ## i ## _BASE, \ + size = CYGNUM_DEVS_NAND_EA_LPC2468_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_EA_LPC2468_PARTITION_0 + PARTITION(0); +#endif +#ifdef CYGPKG_DEVS_NAND_EA_LPC2468_PARTITION_1 + PARTITION(1); +#endif +#ifdef CYGPKG_DEVS_NAND_EA_LPC2468_PARTITION_2 + PARTITION(2); +#endif +#ifdef CYGPKG_DEVS_NAND_EA_LPC2468_PARTITION_3 + PARTITION(3); +#endif + /* Oh for the ability to write a for-loop in pre-processor, + or a more powerful libcdl ... */ + return 0; +} +#define PARTITIONHOOK ea_init_manual_partition + +#endif // CYGSEM_DEVS_NAND_EA_LPC2468_PARTITION_MANUAL_CONFIG + +static int k9_plf_partition_setup(cyg_nand_device *dev) +{ +#ifdef PARTITIONHOOK + return PARTITIONHOOK(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 k9_devlock(cyg_nand_device *dev) +{ +} + +static inline void k9_devunlock(cyg_nand_device *dev) +{ +} + +/* Putting it all together ... ========================================= */ + +#include + +CYG_NAND_DEVICE(ea_nand, "onboard", &k9f8_funs, &_k9_ea_lpc2468_priv, + &mtd_ecc256_fast, &nand_mtd_oob_64); +