Chapter 54. Using the NAND library

The eCos NAND library exposes two principal APIs: one for applications to use and the other to communicate with device drivers.

54.1. Configuring the NAND library

The following configuration options are provided. They affect the library globally, i.e. across all drivers.

Allows specific build options to be added to or removed from the CFLAGS list when building this library.
This is the master switch for all debug reporting from the library.

This is the default function that the library will use when sending debugging output. It must behave like printf. The default - cyg_nand_defaultprintf - is a wrapper to diag_printf.


Individual drivers may override this setting in their devinit routines by overwriting the pointer in the device struct.


Specifies the verbosity of the NAND library and device drivers. Ranges from 0 (off) to 9 (incredibly verbose); the default setting is 1. (Higher values are only likely to be of use during driver development, if ever.) When enabled, messages are printed using the per-device printf-like function (see above).


Should a serious problem be encountered it will always be reported the printf-like function, regardless of this setting. Such messages may be suppressed altogether by turning off CYGSEM_IO_NAND_DEBUG.

Globally disables all code which writes to NAND devices. This may be useful during driver development.
Sets a compile-time limit on the number of partitions any NAND device may have. The default is 4, which should be enough for most purposes; unnecessarily setting this higher wastes RAM.

Globally enables and disables the use of Bad Block Table.


This setting should not be disabled lightly! It is strongly recommended that you leave this setting enabled unless you have a very good reason to not use it. It is provided really as a convenience for allowing developers to recover their NAND from a confused state.

54.2. The NAND Application API

All of the functions described here are declared in the header file <cyg/nand/nand.h>, which should be included by all users of the NAND library.


Most of the functions in the library are declared as returning int. Unless otherwise stated, all functions return 0 for success, or a negative eCos error code if something went wrong.

54.2.1. Device initialisation and lookup

NAND devices are identified to the library by name. In many cases there will be only one, commonly named onboard, but this flexibility allows for easy expansion later without cross-device confusion.


The naming of NAND devices is set up by the code that instantiates their drivers. Normally this is done by the platform HAL port.

 __externC int cyg_nand_lookup(const char *devname, cyg_nand_device **dev_o);

On success, *dev_o will be set up to point to a cyg_nand_device struct. On failure, it will not; a return code of -ENOENT signifies that the requested device name was not found.

Applications will hardly, if ever, need to access the cyg_nand_device structs directly. The following members and convenience macros are most likely to be of relevance:

struct _cyg_nand_device_t {
cyg_nand_printf pf; // Diagnostic printf-like function for this device to use. May be changed at runtime.
size_t page_bits; // log2 of no of regular bytes per page
size_t spare_per_page; // OOB area size in bytes
size_t block_page_bits; // log2 of no of pages per eraseblock
size_t blockcount_bits; // log2 of number of blocks
size_t chipsize_log; // log2 of total chip size in BYTES.

#define CYG_NAND_BYTES_PER_PAGE(dev) (1<<(dev)->page_bits)
#define CYG_NAND_SPARE_PER_PAGE(dev) ((dev)->spare_per_page)
#define CYG_NAND_PAGES_PER_BLOCK(dev) (1<<(dev)->block_page_bits)
#define CYG_NAND_BLOCKCOUNT(dev) (1<<(dev)->blockcount_bits)
#define CYG_NAND_CHIPSIZE(dev) (1<<(dev)->chipsize_log)
#define CYG_NAND_APPSPARE_PER_PAGE(dev) ((dev)->oob->app_size)
#define CYG_NAND_BYTES_PER_BLOCK(dev)   (1<<( (dev)->block_page_bits + (dev)->page_bits ))

54.2.2. NAND device addressing

NAND devices are arranged as a series of pages and eraseblocks. The eCos NAND library numbers pages and eraseblocks sequentially, both starting at 0 and continuing until the end of the chip. For example, eraseblock 0 might contain pages 0 through 63; eraseblock 1, pages 64 through 127; and so on.


This numbering scheme is independent of the device's addressing scheme. Take care, particularly when erasing blocks; some devices and some applications effectively express the location to erase as a page number (or, in NAND-speak, as the row address to erase from).


Most NAND chip manufacturers document restrictions on the order in which pages may be written to their device. Typically, individual pages within an eraseblock must be written in sequential order starting from the first, and random-order writes are prohibited or unspecified. The eCos NAND library does not attempt to police such restrictions; if at all unsure, check the spec sheet for the part. You have been warned!

NAND devices are widely considered to be arranged as one or more partitions, and the eCos NAND library supports this. However, there is no universal scheme for partition sizes to be supplied to the driver, unlike hard drives which encode a partition table into their first sector. Partition arrangements are often implicitly hardcoded, such as by byte address within the device, though they could be encoded in a "partition table", user-set, or even variable under software control by some esoteric rules. Therefore, every device driver is responsible for configuring its partition information as appropriate for the device, and this might for example appear as CDL options.


Be sure to read the notes associated with the device driver to understand how partitions are set up; if no notes are provided, look in its devinit code. NAND device partitions

After a NAND device has been initialised, its device struct contains a list of partitions. These are numbered from 0 and may go up to CYGNUM_NAND_MAX_PARTITIONS-1. Before an application can use the NAND device, it must obtain a partition context (pointer) with the following call:

__externC cyg_nand_partition* cyg_nand_get_partition(cyg_nand_device *dev, unsigned partno);

This call returns a pointer to the partition struct, not an error code. If the given partition number is inactive or invalid, it returns NULL. About the spare area

Every page on the NAND array has a small number of "spare" bytes associated with it. These are used by the NAND library to store the page's ECC; whatever is left over may be used by the application for whatever purposes may suit it.

Every page has CYG_NAND_APPSPARE_PER_PAGE(dev) bytes of spare area available to the application. (This amount is implicit from the driver configuration and cannot change during the lifetime of a device.)


Application spare bytes are not subject to the ECC. When reading the spare area data, you must be prepared to cope with the consequences of the (unlikely) event of a bit drop-out or other failure.

54.2.3. Manipulating the NAND array

Now, finally, given a cyg_nand_partition*, your application can make use of the NAND array with the following functions: Reading data

__externC int cyg_nand_read_page(cyg_nand_partition  *ctx,
                                 cyg_nand_page_addr  page,
                                 void *              dest,
                                 size_t              size,
                                 void *              spare,
                                 size_t              spare_size);

Reads a single page and its spare area. The data read from the chip will be automatically ECC-checked and repaired if necessary. Parameters are as follows:


The partition that data is to be read from.

page [2]

The page to be read, numbered from the start of the partition. As a double-check, the library will refuse the operation with -ENOENT if this address is not within partition ctx.


Where to put the data. May be NULL, in which case the page data is not read.


The maximum amount of data to read. (In any event, no more than a single page will be read, but if your application knows it doesn't need the whole page, you can place a cap here.)


Where to store the application data read from the spare area. This may be NULL if spare data is not required.


The maximum number of bytes to read from the spare area. This will not be more than CYG_NAND_APPSPARE_PER_PAGE(dev) bytes.

An error response of -EIO means that a multiple-bit I/O error has occurred in the page data, which the ECC could not repair. The library stores the data read from the device in *dest and *spare on a best-effort basis; it should not be relied upon. The application should take steps to salvage what it can and erase the block as soon as possible. Writing data

__externC int cyg_nand_write_page(cyg_nand_partition  *ctx,
                                  cyg_nand_page_addr  page,
                                  const void *        src,
                                  size_t              size,
                                  const void *        spare,
                                  size_t              spare_size);

Writes a single page and its spare area. The ECC will be computed and stored automatically. Parameters are as follows:


The partition that data is to be written to.


The page to be written, numbered from the start of the partition. As a double-check, the library will refuse the operation with -ENOENT if this address is not within partition ctx.


Where to read the data from. May be NULL, in which case the page data is not written.


The amount of data to write. (In any event, no more than a single page will be written.)


Where to read the data to go into the spare area; it will automatically be packed around the ECC as necessary. Again, this may be NULL if spare data is not required.


The number of bytes to write to the spare area. This should not be larger than CYG_NAND_APPSPARE_PER_PAGE(dev); if it is, only that many bytes will be stored.

An error response of -EIO means that the page write failed. The application should copy out any data it wishes to keep from the rest of the eraseblock, then call cyg_nand_bbt_markbad() to put the block beyond use. Erasing blocks

__externC int cyg_nand_erase_block(cyg_nand_partition *ctx, cyg_nand_block_addr blk);

The partition that data is to be erased from.


The block to be erased, numbered from the start of the partition. As a double-check, the library will refuse the operation with -ENOENT if this address is not within partition ctx.

An error response of -EIO means that the block erase failed. In this case, the library automatically marks the block as bad, and the application need take no further action. Common error returns

The following common error returns may be encountered when manipulating the NAND array using the above functions:


The operation could not be completed due to an I/O error. This may require the application to take further action; check the details provided above for the call you have just made.


The page or block address was not valid for the given partition.


The page (block) address was (within) a block that is marked bad.

54.2.4. Ancillary NAND functions

The following functions are provided to allow applications to interact with the Bad Block Table:

typedef enum {
} cyg_nand_bbt_status_t;

__externC int cyg_nand_bbt_query(cyg_nand_partition *ctx, cyg_nand_block_addr blk);

__externC int cyg_nand_bbt_markbad(cyg_nand_partition *ctx, cyg_nand_block_addr blk);

To determine the status of an eraseblock, use cyg_nand_bbt_query; this returns an enum from cyg_nand_bbt_status_t or a negative eCos error code. All blocks which return a non-0 enum value are considered inaccessible by applications.

Occasionally, it is necessary for applications to mark a block as bad. This most commonly happens when a write operation fails (see Section, “Writing data” above). To do this, call cyg_nand_bbt_markbad; the return is 0 for success, or a negative eCos error code. As with other calls, blocks are numbered from 0 at the start of the partition, and internally translated for the device as appropriate.

Both of these calls may foreseeably return -ENOENT if the given block address was not valid, or -EIO if something awful happened with the on-chip bad block table.

[2] This was changed in application interface v2; earlier page and block addresses were device-relative.