The high-level functions provided by the chip driver are
typically created as an inline file providing a
fully-populated cyg_nand_dev_fns_v1 struct, instantiated
by the CYG_NAND_FUNS macro. The high-level driver should
not directly read or write to the hardware itself, but instead call into
functions in the low-level driver.
The form the low-level functions should take is not prescribed;
typically functions will be required to write commands to the device, to
read and write data, and to query any status line which may be present.
The high-level driver should normally provide a header file containing
prototypes for the functions it requires from the low-level driver.
(The low-level source file would provide the low-level functions required,
include the high-level include, then instantiate the combined driver
using the CYG_NAND_DEVICE macro.)
This source code layout is not intended as a prescription. It
would for example be entirely in order to store pointers to the low-level
functions in a struct and set priv to point
to that struct, which could be useful in some cases.
Note: The device driver must not call malloc
or otherwise allocate memory; all data should be in the stack or set
as globals. This is because the driver may be required to run within a
minimal eCos configuration.
These functions should all return 0 on success, or a negative
eCos error code. In the event of an error, do not
call back into the NAND library; use the NAND_CHATTER
macro to report, in case a human is watching, and return an error
code. The library will take care of ensuring the correct response to
the application and updating the BBT as necessary.
The devinit function is the most complex, and
logically one to write first. It is responsible for:
initialising the device, typically by sending a reset
command;
interrogating the device to confirm its presence and
properties;
setting up the partition table list (see
"Planning a port" above);
setting up mutexes as necessary (see
"Locking against concurrent access" above);
populating the other members of the
cyg_nand_device struct (see below).
Interrogating the device is normally performed by sending a
Read ID command and examining the result, which
typically encodes some or all of the chip parameters.
Given the similarity between many NAND parts, it may be possible
to write a generic driver to cover all of one or more manufacturer's
parts, or indeed for all ONFI-compliant parts. At the time of writing,
this has not yet been attempted.
The devinit function must set
up the following struct members:
page_bits
The
size of the regular (non-spare) part of a page, expressed as the logarithm
in base 2 of the number of bytes. For example, if pages are 2048 bytes
long, page_bits would be 11. Obviously,
the size of a page must be an exact power of two.
spare_per_page
The
number of bytes of spare area available in each
page.
block_page_bits
The
base-2 log of the number of pages per
eraseblock.
blockcount_bits
The
total number of erase blocks in the device, expressed as a base-2
log.
chipsize_log
The
total size of the chip, not counting the spare areas. This is required so
that the library can double-check that the given parameters make sense
by comparing with the preceding fields. Again, this field is itself a
base-2 logarithm.
bbt.data
Space
for the in-memory Bad Block Table for this device.
bbt.datasize
This
is the size of bbt.data, in bytes. At present,
this should be two bits times the number of blocks in the device; in
other words, 1<<(blockcount_bits-2) bytes.
The cyg_nand_device struct has two further members
ecc and oob
which must be set up to point to the ECC and OOB descriptors to use for
the device. This is normally done by the CYG_NAND_DEVICE
low-level instantiation macro, so will be better described in that
section, but at this level you should be aware that it is also safe
to set up the descriptor block during devinit.
for example if multiple semantics might be you had included logic to
detect what semantics to use.
The Bad Block Table itself is implemented in a way which intends
to be compatible with the Linux MTD layer. A full parameter struct is
not currently provided, though one may be in future.
The read and write operations are divided into three phases, with the following flow:
Begin. This is called once; the driver should lock any
platform-level mutex and send the command and address.
Stride. This is called one or more times to read the page
data from the device.
Note: The reason for this is if the platform
provides a NAND controller with hardware ECC: it is often necessary to
read out the ECC registers every so often.
Finish. This is called once; it should read or write
the spare area, (on programming) send a "program confirm" command and
check its status, and unlock any platform-level mutex.
Erasing is a single-shot call which should lock any
platform-specific mutex, send the command, check its status and unlock
the mutex.
static int my_read_begin(cyg_nand_device *dev, cyg_nand_page_addr page);
static int my_read_stride(cyg_nand_device *dev, void * dest, size_t size);
static int my_read_finish(cyg_nand_device *dev, void * spare, size_t spare_size);
static int my_write_begin(cyg_nand_device *dev, cyg_nand_page_addr page);
static int my_write_stride(cyg_nand_device *dev, const void * src, size_t size);
static int my_write_finish(cyg_nand_device *dev, const void * spare, size_t spare_size);
static int my_erase_block(cyg_nand_device *dev, cyg_nand_block_addr blk);
static int my_is_factory_bad(cyg_nand_device *dev, cyg_nand_block_addr blk);
The very first time a NAND chip is used, the library has to
scan it to check for factory-bad eraseblocks and build up the Bad Block
Table. This function is called repeatedly to do so, one block at a time;
it should return 1 if the block is marked bad, or 0 if the block appears
to be OK.
Typically this function will invoke read_page;
blocks are usually marked factory-bad by the presence of a particular
signature in the out-of-band area of the first or second page of that
block.
Warning
It is extremely important that you get this function
right; after an eraseblock has been written to, it is no longer possible
to reliably determine whether the block was factory-bad. It is never
safe to assume that the factory-bad signature for a chip is the same
as that of a similarly-sized chip or another by the same manufacturer;
always check the correct spec sheet for the actual
part or part-family in use!
Tip: Because this function is critical and a subtle error could
cripple your application some time later in the field when it runs
across undetected factory-bad blocks, you might find it handy to have
a double-check before proceeding. If you enable CYGSEM_IO_NAND_READONLY
in your eCos configuration during early development, you can safely fire
up a test application (which calls cyg_nand_lookup)
whilst watching the chatter output: the scan will be performed, but no BBT
will be written. You can then compare the number of bad blocks reported
against the manufacturer's specification of the maximum. Double-check
that your is_factory_bad function is correct before
enabling read-write mode!
This macro ties the above functions together into a struct whose
name is given as its first argument. The name of the resulting struct
must be quoted when the driver is formally instantiated, which is normally
done by the low-level functions.
Note: Earlier versions of this library used a slightly different
device interface, keyed off the macro CYG_NAND_FUNS. This interface has
been retired.