Before you start, you will need to have sight of appropriate spec
sheets for both the NAND chip and the board into which it is connected,
and you need to know how the chip is to be partitioned.
A typical NAND device driver falls into two parts:
high-level operations specific to the NAND chip (page
reads and writes); and
board-specific plumbing (sending commands and data to
the chip; reading data back from the chip).
This distinction is important in the interests of code reuse;
the same part may appear on different boards, or indeed multiple times,
but connected differently. It need not be maintained if there are good
reasons not to.
The NAND library device interface consists
of a C struct, cyg_nand_device, comprising a number of
data fields and function pointers. Each NAND chip to be made available
to the library requires exactly one instance of this struct.
Tip: The cyg_nand_device structure includes a
void* priv member which is treated
as opaque. The driver may use this member as it sees fit; it is
intended to provide an easy means to identify the NAND array, MMIO
addresses or function pointers to use and so on. Typically this is
used by the chip driver for its own purposes, and includes a further
opaque member for the use of the HAL port.
The function pointers in the struct form the driver's high-level
functions; they make use of the low-level functions to talk to the
chip. We present the high-level functions first, although there is no
intrinsic reason to prefer either ordering during driver development.
The high-level chip-specific functions
are traditionally laid out as an inline
file in an appropriate package in devs/nand/CHIP.
The board-specific functions should normally appear in the platform HAL
and #include the inline.
Before embarking on the port, you should determine how the
NAND array will be partitioned. This is necessarily a board-specific
question, and your layout must accommodate any other software users of
the array. You will need to know either the fixed layout - converted to
eraseblock addresses - or how to determine the layout at initialisation
time.
Tip: It may be worthwhile to set up partitioning by way of some
parameters in your platform's CDL, with sensible defaults, instead of
outright hard-coding the partition layout.
The eCos NAND library provides per-device locking, to guard against
concurrent access during high-level operations. This support is fully
automatic; drivers need take no action to make use of it.
This strategy may not be sufficient on all target boards:
sometimes, accessing a NAND chip requires mediation by CPLD or other
device, which must be shared with other NAND chips or even other
peripherals. If this applies, it is the responsibility
of the driver and platform port to provide further locking as
appropriate!
Tip: When using mutexes in a driver, one should
use the driver API as defined in
<cyg/hal/drv_api.h> instead of the full kernel
API. This has the useful property that mutex operations are very cheaply
implemented when the eCos kernel is not present, such as when operating
in RedBoot.
An individual NAND chip driver must declare the largest page
size it supports by means of CDL. This is done with a statement like
the following in its cdl_package stanza:
requires ( CYGNUM_NAND_PAGEBUFFER >= 2048 )
Note: This requirement is due to the internal workings of the eCos NAND
library: a buffer is required for certain operations which
manipulate up to a NAND page worth of data, internally to the library.
This is declared once as a global buffer for safety under low-memory
conditions; a page may be too big to use temporary storage on the C stack,
and the NAND library deliberately avoids the use of
malloc.
By convention, a driver package would declare
CYGPKG_IO_NAND as its parent and use
cyg/devs/nand as its include_dir,
but there is no intrinsic reason why this should be so.