User API: the Abstract NAND Chip (ANC) API

The functions described below are declared in the header file cyg/io/nand_flash.h which users of the NAND Flash library should include.

Initialization of the NAND Flash library

The NAND Flash library needs to be initialized before other NAND Flash operations can be performed. There is a global initialization call. Then, the initialization of an ANC is a two-step process. In between, the application can configure some properties of the ANC.

typedef int cyg_nand_printf(const char *fmt, ...);

__externC int cyg_nand_init(cyg_nand_printf *pf,
                            void *(*alloc)(size_t size),
                            void (*free)(void *ptr));

Global initalization for the NAND Flash module. The parameter pf is a pointer to a function which is to be used for diagnostic output. Typically the function diag_printf() will be passed. Normally this function is not used by the higher layer of the library unless CYGSEM_IO_NAND_CHATTER is enabled. Passing a NULL is not recommended, even when CYGSEM_IO_NAND_CHATTER is disabled. The lower layers of the library may unconditionally call this function, especially when errors occur, probably resulting in a more serious error/crash!

Parameters alloc and free must be function pointers to a memory allocator. The NAND Flash library needs to allocate memory for two types of use:

  1. its internal BBT (Bad Block Table). The amount that is needed is: 2 bits per block of NAND chip. The number of blocks that should be counted is the physical number of blocks, not the abstract number of blocks used by the ANC, which can be very different;

  2. a cyg_nand_err per thread, to support thread-local error reports.

The application can statically allocate a buffer and provide an alloc() function to hand out chunks from it, or it can use C's standard malloc() and free(). The implementation must protect against concurrent use.

First-stage initialization

The device driver chain for the ANC library and its underlying controller and chip libraries must be 'looked up' or mounted before they can be used. Looking up the ANC driver automatically initializes and mounts the NAND flash controllers and chips that are specified in the target CDL.

typedef struct CYG_NAND cyg_nand_t;

__externC int cyg_nand_dev_lookup(cyg_uint32 devno, cyg_nand_t **nand);

This provides the user with a pointer to the ANC nand given its device number (usually 0, as specified in the target cdl). The opaque pointer cyg_nand_t *nand is used in all other accesses to the ANC API. After this lookup, the ANC configuration can be inspected and tuned.

Retrieving information about the ANC

typedef struct CYG_NAND_INFO {
    size_t     page_size;       /* bytes */
    size_t     spare_size;      /* bytes per page */
    size_t     block_size;      /* bytes */
    int        n_blocks;
    int        max_programs_per_page;
    ...
} cyg_nand_info_t;

__externC const cyg_nand_info_t *cyg_nand_get_info(const cyg_nand_t *nand);

cyg_nand_get_info() returns the layout of the ANC. If the ANC has not initialized successfully, NULL is returned. The layout of the ANC must be used by a client application (e.g. a NAND Flash filesystem) that is layered on top of the ANC.

nand_info->max_programs_per_page reflects a peculiarity of NAND flash. A page can be programmed only a (small) number of times before it needs to be erased; then it can be programmed again.

Specifying application information to the ANC

Before the second initialization stage, some properties of the ANC can be configured. These are en/disabling of ECC generation, en/disabling of a Bad Block Table (BBT), and specification of the layout of the spare area. By default, ECC and BBT are enabled, and a suitable spare layout is chosen.

The eCos NAND flash implementation attempts to offer on-chip compatibility with the metadata used by the Linux NAND flash layer MTD. The parts that attempt to be compatible with MTD are:

  1. the layout of the spare areas. The spare area of a page commonly has slots for a bad block marker, for ECC values, for data that is available to the application (like filesystem meta-data), and possibly more. These slots are scattered into the page's spare area according to a spare layout. MTD specifies a layout for different spare sizes;

  2. the bad block table (BBT). At its first usage (by either MTD or the eCos NAND Flash library), a NAND chip is scanned for factory-bad blocks. This information is stored on-chip, with a recognizable (and hopefully completely distinctive) marker pattern in the spare area that belongs to the block/page for the BBT. The blocks that contain the BBT are marked unusable for ANC applications. Besides that, the Bad Block Table is transparent to users of the ANC API.

Suitable defaults that follow the MTD automatic layout are provided when an application has no need for a custom spare layout. If a custom spare layout is required, a scatter table for the placement of ECC bytes and application bytes must be provided.

typedef enum CYG_NAND_FLAGS {
    CYG_NAND_FLAGS_HAS_ECC              = (0x1 << 0),
    CYG_NAND_FLAGS_HAS_BBT              = (0x1 << 1),
    CYG_NAND_FLAGS_ECC_ERROR_ON_ERASED  = (0x1 << 2),
} cyg_nand_flags_t;


/**
 * Spare area layouts that follow the MTD autoplacement schemes
 */

typedef struct CYG_NAND_SCATTER_ELT {
    size_t                      start;
    size_t                      size;
} cyg_nand_scatter_elt_t;

typedef struct CYG_NAND_CHIP_SCATTER {
    size_t                      size;
    const cyg_nand_scatter_elt_t *scatter;
} cyg_nand_scatter_t;

/** Utility function */
__externC size_t cyg_nand_scatter_size(const cyg_nand_scatter_t *scatter);

typedef struct CYG_NAND_CHIP_SPARE_LAYOUT_T {
    cyg_nand_scatter_t          ecc;
    cyg_nand_scatter_t          user;
    int                         bad_block_index;
    size_t                      size;           /* size of spare info */
    size_t                      ecc_size;       /* cache ECC size */
} cyg_nand_chip_spare_layout_t;


typedef struct CYG_NAND_APP_INFO {
            /** Specify whether
             * -  the NAND Flash library should calculate ECCs and attempt to
             *    repair corrupted data
             * - the NAND Flash library should maintain a BBT */
    cyg_nand_flags_t            flags;
            /** Optionally set to a pointer to your custom spare layout.
             * By default, an MTD auto layout is chosen that fits the ANC spare
             * size. If this pointer is overwritten, no copy is made. The
             * application must ensure that the data it points to stays
             * alive. */
    const cyg_nand_chip_spare_layout_t *spare_layout;
} cyg_nand_app_info_t;

__externC cyg_nand_app_info_t *cyg_nand_app_info(cyg_nand_t *nand);

The interface here intends to allow the use of statically allocated data structures. The ANC publishes a pointer to its internal structure cyg_nand_app_info_t *app_info. An application can overwrite the fields of that structure. If app_info->spare_layout is overwritten, it must point to a persistent structure: ANC does not make a copy of it.

Second-stage initialization

__externC int cyg_nand_start(cyg_nand_t *nand);

Erasing blocks of NAND Flash

Blocks of NAND Flash can be erased using the following function:

__externC int cyg_nand_block_erase(cyg_nand_t *nand, size_t page);

page is a row address of a page in the block to be erased. It should be noted that NAND Flash devices are block-oriented when erasing. It is not possible to erase a few bytes within a block, the whole block will be erased.

Programming NAND Flash

Programming of the flash is achieved using the following function.

__externC int cyg_nand_page_program(cyg_nand_t *nand,
                                    const void *data, size_t column, size_t page, size_t len,
                                    const void *spare, size_t spare_len,
                                    int use_cache);

data is the application buffer to read data from. (column, page) is the address within the NAND device to be programmed. column is in bytes (even if the chips under the ANC are of x16 organisation). len is the number of bytes to be program into the page. A program operation cannot address more than one page at a time, so column + len must not exceed the page size. spare is the application buffer to read spare_len bytes of spare information from. use_cache is a boolean that specifies whether this program operation will be immediately followed by more programs. In that case, the ANC can use the CACHED_PROGRAM optimization if its chips support that. data and spare may be NULL if no data should be read from them.

Reading from NAND Flash

__externC int cyg_nand_page_read(cyg_nand_t *nand,
                                 void *data, size_t column, size_t page, size_t len,
                                 void *spare, size_t spare_len,
                                 int use_cache);

data is the application buffer to read data into. (column, page) is the address within the NAND device. column is in bytes (even if the chips under the ANC are of x16 organisation). len is the number of bytes to be read from the page. A read operation cannot address more than one page at a time, so column + len must not exceed the page size. spare is the application buffer to read spare_len bytes of spare information into. use_cache is a boolean that specifies whether this read will be immediately followed by more reads. In that case, the ANC can use the CACHED_READ optimization if its chips support that. data and spare may be NULL if no data should be read into them.

typedef enum CYG_NAND_BLOCK_STATE {
    CYG_NAND_BLOCK_STATE_GOOD               = 0x00,
    CYG_NAND_BLOCK_STATE_WORN_BAD           = 0x01,
    CYG_NAND_BLOCK_STATE_RESERVED_BAD       = 0x02,
    CYG_NAND_BLOCK_STATE_FACTORY_BAD        = 0x03,
} cyg_nand_block_state_t;

__externC int cyg_nand_block_query(cyg_nand_t *nand, size_t page, cyg_nand_block_state_t *state);

Checks the BBT (Bad Block Table) if the block that containts this page is good, worn bad, reserved, or factory-bad. If no BBT is used, attempts to check the chip for factory-bad marks.

__externC int cyg_nand_block_mark_bad(cyg_nand_t *nand, size_t page);

Marks the Bad Block Table that the block that contains this page is unusable (because of wear).