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* privmember 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
By convention, a driver package would declare
CYGPKG_IO_NAND as its
parent and use
cyg/devs/nand as its
but there is no intrinsic reason why this should be so.