This section is solely of interest to those who want to implement a NAND Flash Controller device driver. Application programs will use only the public ANC API. This section focuses on the information that is necessary for creating or instantiating a device driver.
The NAND Flash Controller library consists of a common upper part and device-specific lower parts. The common part translates API calls from the ANC to calls to the internal API of the device-specific parts. This internal API is called the NAND Flash Controller Command API, in this section abbreviated to the Command API. Any device driver must implement that Command API.
The common API is called from the ANC library. It resembles the ANC API, except that its functions operate on a NAND Controller. Its main functions are briefly documented here for completeness; in normal use, they are solely called from the ANC library.
The NAND Flash Controller Common API is specified in cyg/io/flash_nand_ctl.h.
int cyg_nand_ctl_dev_lookup(cyg_uint32 devno, cyg_uint32 anc_dev, cyg_nand_t *nand, cyg_nand_ctl_t **p_ctl); int cyg_nand_ctl_init(cyg_nand_ctl_t *ctl); int cyg_nand_ctl_chip_select(cyg_nand_ctl_t *ctl, int chip, int on); int cyg_nand_ctl_block_erase(cyg_nand_ctl_t *ctl, size_t page_addr); int cyg_nand_ctl_page_program(cyg_nand_ctl_t *ctl, const void *data, size_t column, size_t page, size_t len, const void *spare, size_t spare_len, int use_cache, int direct); int cyg_nand_ctl_page_read(cyg_nand_ctl_t *ctl, void *data, size_t column, size_t page, size_t len, void *spare, size_t spare_len, int use_cache, int direct); |
Functions cyg_nand_ctl_page_program() and
cyg_nand_ctl_page_read() take an extra parameter
direct. If it is 1, spare
matches the spare area without any processing. If it is 0, the
spare parameter is folded into the application fields
in the chip's spare area layout.
#ifdef CYGPKG_KERNEL int cyg_nand_ctl_mutex_lock(cyg_nand_ctl_t *ctl); int cyg_nand_ctl_mutex_unlock(cyg_nand_ctl_t *ctl); #endif |
Concurrency control is done at the controller level: each controller has its lock.
A NAND flash library must use ECCs to recover errors where possible or flag unrecoverable errors. On writing, the library must calculate ECCs and store the ECC values in the spare area of a page. The spare area of a chip is laid out according to a layout descriptor that has fields for a factory-bad-block marker, ECC data, and application spare data. On reading, ECCs must again be calculated, and these values are compared to the ECC values folded into the spare area. The result of this comparison is an ECC "syndrome" that indicates whether errors occurred, how many bits are in error, and, if the errors are recoverable, the bit positions of the erroneous bits.
This ECC error handling can be done completely in software, but NAND Flash Controllers may come with hardware to support ECC calculations: while data is transferred through them, ECC values are accumulated. After the data transfer the ECC values must be retrieved, and either written to the spare area, or compared to the stored ECC values.
There are two forms in which hardware ECC support comes. The usual kind (value-ECC controller) lets the accumulated ECC values be read, and software must handle further processing. The spare area is accessed in one operation, and ECC and application data are folded in or out. The second form (syndrome-ECC controller) doesn't provide the ECC values on read, but returns the ECC syndrome. This type of controller requires that the spare area is accessed for the ECC values immediately after data program or read. Often, the offset of the ECC fields within a chip's spare area layout is not 0, because that field is usually the bad-block marker. So, for this type of controller, the spare area is accessed in multiple operations: first the ECC, then the other fields. The controller itself handles the ECC values to calculate the syndrome, which must be retrieved from the controller after a read operation has completed.
The eCos NAND Flash Controller Common library invokes ECC routines wherever appropriate. The device driver must be configured with routines that calculate ECCs (when no hardware support is available), that repair data after a read using ECCs, or that retrieve an ECC syndrome in the case of a syndrome-ECC controller. In the case of value-ECC controllers, the routines that program or read data from a chip are expected to retrieve and return ECC values that are calculated by the controller.
When a controller has hardware ECC support, the routine to perform ECC repair must match the hardware ECC algorithm. Quite often, the hardware ECC algorithm is one of the well-known "Toshiba" or "Samsung" algorithms. Both these algorithms are bundled with the NAND Flash Controller library.
These ECC functions are defined in file cyg/io/flash_nand_ctl_ecc.h:
/**
* @param ctl controller
* @param data 256 bytes of data
* @param ecc 3 bytes of ECC for 256 bytes of data
*/
__externC int cyg_nand_ctl_ecc_toshiba_calc(cyg_nand_ctl_t *ctl,
const cyg_uint8 *data,
cyg_uint8 *ecc);
/**
* @param ctl controller
* @param data 256 bytes of data
* @param ecc 3 bytes of ECC for 256 bytes of data
*/
__externC int cyg_nand_ctl_ecc_toshiba_repair(cyg_nand_ctl_t *ctl,
cyg_uint8 *data,
cyg_uint8 *stored_ecc,
cyg_uint8 *calc_ecc);
/**
* @param ctl controller
* @param data 256 bytes of data
* @param ecc 3 bytes of ECC for 256 bytes of data
*/
__externC int cyg_nand_ctl_ecc_samsung_calc(cyg_nand_ctl_t *ctl,
const cyg_uint8 *data,
cyg_uint8 *ecc);
/**
* @param ctl controller
* @param data 256 bytes of data
* @param ecc 3 bytes of ECC for 256 bytes of data
*/
__externC int cyg_nand_ctl_ecc_samsung_repair(cyg_nand_ctl_t *ctl,
cyg_uint8 *data,
cyg_uint8 *stored_ecc,
cyg_uint8 *calc_ecc); |
The Controller device driver struct is specified in cyg/io/flash_nand_ctl.h:
/** ADT for the Controller Command functions */
typedef struct CYG_NAND_CTL_FUNS cyg_nand_ctl_funs_t;
/**
* Boolean capabilities of the NAND Flash Controller
*/
typedef enum CYG_NAND_CONTROLLER_FLAGS {
CYG_NAND_CTL_FLAGS_HAS_HARDWARE_ECC = (0x1 << 0),
CYG_NAND_CTL_FLAGS_HAS_ECC_SYNDROME = (0x1 << 1),
CYG_NAND_CTL_FLAGS_CHIPS_COMMON_BUS_WIDTH = (0x1 << 2),
CYG_NAND_CTL_FLAGS_CHIPS_COMMON_PAGE_SIZE = (0x1 << 3),
} cyg_nand_controller_flags_t;
struct CYG_NAND_CTL {
const cyg_nand_ctl_funs_t *funs; /**< Function pointers */
cyg_uint32 devno; /**< Controller devno */
cyg_uint32 anc_devno; /**< Owning ANC devno */
int n_chip;
cyg_nand_chip_t **chip; /**< My flash chips */
void *priv; /**< Device-specific data */
void *platform_priv; /**< Platform-specific data */
cyg_nand_ctl_info_t info; /**< For this controller */
cyg_nand_chip_t *current_chip; /**< Selected flash chip */
int initialized; /**< Device has been inited */
cyg_nand_t *anc; /**< My owner */
#ifdef CYGPKG_KERNEL
cyg_mutex_t mutex; /**< Thread safeness */
#endif
cyg_uint8 *stored_ecc;
cyg_uint8 *device_ecc;
cyg_uint8 spare_buffer[CYGNUM_NAND_SPARE_MAX];
} CYG_HAL_TABLE_TYPE;
#define CYGNUM_NAND_ECC_CHUNK 256
#define CYGNUM_NAND_ECC_PER_CHUNK 3
#define CYGNUM_NAND_ECC_BUFFER ((CYGNUM_NAND_PAGE_MAX / CYGNUM_NAND_ECC_CHUNK) * CYGNUM_NAND_ECC_PER_CHUNK)
/**
* Special value to signify that the ECC chunk size must be the chip's page size
*/
#define CYGNUM_NAND_ECC_CHUNK_PAGE_SIZE -1
#define CYGNUM_NAND_ECC_MAX(a, b) ((a) > (b) ? (a) : (b))
/**
* Helper macro to have some common expressions
*/
#define CYG_NAND_DRIVER_CTL_INSTANCE(name, i_funs, i_devno, i_anc, i_n_chip, i_priv, i_ecc_chunk_size, i_ecc_per_chunk, i_n_stored_ecc, i_n_device_ecc) \
\
static cyg_nand_chip_t *name ## _chips[i_n_chip]; \
\
static cyg_uint8 name ## _stored_ecc[i_n_stored_ecc]; \
static cyg_uint8 name ## _device_ecc[CYGNUM_NAND_ECC_MAX(i_n_device_ecc, i_n_stored_ecc)]; \
\
cyg_nand_ctl_t name CYG_HAL_TABLE_ENTRY(cyg_nand_dev_ctl) = { \
.funs = i_funs, \
.devno = i_devno, \
.anc_devno = i_anc, \
.n_chip = i_n_chip, \
.chip = name ## _chips, \
.priv = i_priv, \
.ecc_chunk_size = i_ecc_chunk_size, \
.ecc_per_chunk = i_ecc_per_chunk, \
.stored_ecc = name ## _stored_ecc, \
.device_ecc = name ## _device_ecc, \
}
/**
* Defaults for the buffer values:
* i_cc_chunk_size == 0: choose CYGNUM_NAND_ECC_CHUNK
* i_cc_chunk_size == CYGNUM_NAND_ECC_CHUNK_PAGE_SIZE:
* choose the chip's page size
* i_ecc_per_chunk == 0: choose CYGNUM_NAND_ECC_PER_CHUNK
* i_n_device_ecc == 0: choose i_ecc_per_chunk
*/
#define CYG_NAND_DRIVER_CTL(name, i_funs, i_devno, i_anc, i_n_chip, i_priv, i_ecc_chunk_size, i_ecc_per_chunk, i_n_device_ecc) \
CYG_NAND_DRIVER_CTL_INSTANCE(name, i_funs, i_devno, i_anc, i_n_chip, i_priv, \
(i_ecc_chunk_size) == 0 ? CYGNUM_NAND_ECC_CHUNK : (i_ecc_chunk_size), \
(i_ecc_per_chunk) == 0 ? CYGNUM_NAND_ECC_PER_CHUNK : (i_ecc_per_chunk), \
CYGNUM_NAND_PAGE_MAX / \
((i_ecc_chunk_size) == 0 ? CYGNUM_NAND_ECC_CHUNK : \
(i_ecc_chunk_size) == CYGNUM_NAND_ECC_CHUNK_PAGE_SIZE ? 1 : \
(i_ecc_chunk_size)), \
i_n_device_ecc) |
The Controller Common API contains a macro
CYG_NAND_DRIVER_CTL to instantiate a Controller device
driver. Its parameters anc (global ANC device number),
devno (per-ANC NAND controller device number),
n_chip (number of chips attached to this controller) are
usually specified in the CDL for the target. Parameter
priv is a generic pointer that can be used to point to
a device-specific data structure. funs is the function
dispatch block that is implemented by the Command part of the device driver.
The macro also instantiates some static arrays whose size depends on parameters
of this macro; in casu, parameters n_chip,
i_ecc_chunk_size,
i_ecc_per_chunk, and
i_n_device_ecc must be compile-time constants.
The buffers for ECC support are also configured by this macro. If the
controller has no hardware support, unset flag
CYG_NAND_CTL_FLAGS_HAS_HARDWARE_ECC in the info.flags
field of the controller struct; specify suitable ECC calculation and
repair routines in the controller funs struct,
and specify 0 to indicate a default value for
i_cc_chunk_size, i_ecc_per_chunk,
and i_n_device_ecc.
If the controller has hardware support for ECC calculation, set flag
CYG_NAND_CTL_FLAGS_HAS_HARDWARE_ECC in the info.flags
field of the controller struct, and provide an
ecc_repair() routine that matches the hardware ECC
algorithm.
info.flags
field of the controller struct, and provide a function
ecc_syndrome() that retrieves the ECC syndrome after
reading of data. For this type of controller, function
ecc_repair() is called with the ECC syndrome value in
stead of (the second of) the pair of ECC value sets. The controller device
driver must be instantiated with a value for
i_n_device_ecc that equals the size of the ECC syndrome.