Low-level (board) functions

The set and prototypes of the functions required here will necessarily depend on the board and to a lesser extent on the NAND part itself. The following functionality is typically required:

Talking to the chip

It is impossible to prescribe how to achieve this, as it depends entirely on how the NAND part is wired up on the board.

The ideal situation is that the NAND part is wired in via the CPU's memory controller and that the controller is set up to do most of the hard work for you. In that case, reading and writing the device is as simple as accessing the correct memory-mapped I/O address; usually different address ranges connect to the device's command, address and data registers respectively.

Tip: The HAL provides a number of macros in <cyg/hal/hal_io.h> to read and write memory-mapped I/O.

Note: On platforms with an MMU, MMIO may be rerouted to different addresses to those on the board spec sheet. Check the MMU setup in the platform HAL.

On some platforms, you may have to invoke an FPGA or CPLD to be able to talk to the NAND chip. This might typically take the form of a handful of MMIO accesses, but should hopefully be fairly straightforward once you've figured out how the components interrelate.

The worst case is where you have no support from any sort of controller hardware and have to bit-bang GPIO lines to talk to the chip. This is a much more involved process; you have to take great care to get the timings right with carefully tuned delays. The result is usually quite CPU intensive, and could be clock speed sensitive too; you should check for and take account of any CDL settings in the architecture and variant HAL which allow the CPU clock frequency to be changed.

Tip: If your low-level functions take a cyg_nand_device pointer as an argument, you can use its priv member to hold or point to some relevant data like the MMIO addresses to use, which is preferable to hard-coding them. Indeed, if you wish your board port to support more than one chip, you should use the priv member to distinguish between them.

Setting up the chip partition table

It is the responsibility of the high-level devinit function to set up the device's partition table. (It may be appropriate for it to invoke a low-level function to do this.)

The partition definition is an array of cyg_nand_partition entries in the cyg_nand_device.

struct _cyg_nand_partition_t {
cyg_nand_device *dev;
cyg_nand_block_addr first;
cyg_nand_block_addr last;
};
typedef struct _cyg_nand_partition_t cyg_nand_partition;

struct _cyg_nand_device_t {
...
cyg_nand_partition partition[CYGNUM_NAND_MAX_PARTITIONS];
...
};

Application-visible partition numbers are simply indexes into this array.

Putting it all together...

Finally, with everything else in place, we turn to the CYG_NAND_DEVICE macro to instantiate it.

CYG_NAND_DEVICE(my_nand, "onboard", &mydev_funs, &my_priv_struct, &linux_mtd_ecc, &nand_mtd_oob_64);

In order, the arguments to this macro are:

The macro invokes the appropriate linker magic to pull all the compiled NAND device structs into one section so the NAND library can find them.