nand: Introduce CONFIG_SYS_NAND_SELF_INIT

This allows a driver to run code between nand_scan_ident() and
nand_scan_tail(), among other things.  See the additions to
doc/README.nand for details.

To allow a gradual transition, Boards that don't set
CONFIG_SYS_NAND_SELF_INIT will still be initialized the old way, but
new drivers should not require this, and existing drivers should be
converted when convenient.

Signed-off-by: Scott Wood <scottwood@freescale.com>
This commit is contained in:
Scott Wood 2012-01-12 19:07:23 -06:00
parent 25efd99dbb
commit 578931b34d
3 changed files with 126 additions and 37 deletions

View file

@ -120,6 +120,68 @@ Configuration Options:
CONFIG_SYS_NAND_MAX_CHIPS
The maximum number of NAND chips per device to be supported.
CONFIG_SYS_NAND_SELF_INIT
Traditionally, glue code in drivers/mtd/nand/nand.c has driven
the initialization process -- it provides the mtd and nand
structs, calls a board init function for a specific device,
calls nand_scan(), and registers with mtd.
This arrangement does not provide drivers with the flexibility to
run code between nand_scan_ident() and nand_scan_tail(), or other
deviations from the "normal" flow.
If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/nand.c
will make one call to board_nand_init(), with no arguments. That
function is responsible for calling a driver init function for
each NAND device on the board, that performs all initialization
tasks except setting mtd->name, and registering with the rest of
U-Boot. Those last tasks are accomplished by calling nand_register()
on the new mtd device.
Example of new init to be added to the end of an existing driver
init:
/*
* devnum is the device number to be used in nand commands
* and in mtd->name. Must be less than
* CONFIG_SYS_NAND_MAX_DEVICE.
*/
mtd = &nand_info[devnum];
/* chip is struct nand_chip, and is now provided by the driver. */
mtd->priv = &chip;
/*
* Fill in appropriate values if this driver uses these fields,
* or uses the standard read_byte/write_buf/etc. functions from
* nand_base.c that use these fields.
*/
chip.IO_ADDR_R = ...;
chip.IO_ADDR_W = ...;
if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL))
error out
/*
* Insert here any code you wish to run after the chip has been
* identified, but before any other I/O is done.
*/
if (nand_scan_tail(mtd))
error out
if (nand_register(devnum))
error out
In addition to providing more flexibility to the driver, it reduces
the difference between a U-Boot driver and its Linux counterpart.
nand_init() is now reduced to calling board_nand_init() once, and
printing a size summary. This should also make it easier to
transition to delayed NAND initialization.
Please convert your driver even if you don't need the extra
flexibility, so that one day we can eliminate the old mechanism.
NOTE:
=====

View file

@ -23,6 +23,7 @@
#include <common.h>
#include <nand.h>
#include <errno.h>
#ifndef CONFIG_SYS_NAND_BASE_LIST
#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
@ -31,63 +32,84 @@
DECLARE_GLOBAL_DATA_PTR;
int nand_curr_device = -1;
nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
#ifndef CONFIG_SYS_NAND_SELF_INIT
static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
static const char default_nand_name[] = "nand";
static __attribute__((unused)) char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
ulong base_addr)
{
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
static int __attribute__((unused)) i = 0;
if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand) == 0) {
if (nand_scan(mtd, maxchips) == 0) {
if (!mtd->name)
mtd->name = (char *)default_nand_name;
#ifdef CONFIG_NEEDS_MANUAL_RELOC
else
mtd->name += gd->reloc_off;
#endif
static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
static unsigned long total_nand_size; /* in kiB */
/* Register an initialized NAND mtd device with the U-Boot NAND command. */
int nand_register(int devnum)
{
struct mtd_info *mtd;
if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE)
return -EINVAL;
mtd = &nand_info[devnum];
sprintf(dev_name[devnum], "nand%d", devnum);
mtd->name = dev_name[devnum];
#ifdef CONFIG_MTD_DEVICE
/*
* Add MTD device so that we can reference it later
* via the mtdcore infrastructure (e.g. ubi).
*/
sprintf(dev_name[i], "nand%d", i);
mtd->name = dev_name[i++];
add_mtd_device(mtd);
#endif
} else
mtd->name = NULL;
} else {
mtd->name = NULL;
mtd->size = 0;
}
total_nand_size += mtd->size / 1024;
if (nand_curr_device == -1)
nand_curr_device = devnum;
return 0;
}
#ifndef CONFIG_SYS_NAND_SELF_INIT
static void nand_init_chip(int i)
{
struct mtd_info *mtd = &nand_info[i];
struct nand_chip *nand = &nand_chip[i];
ulong base_addr = base_address[i];
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
if (board_nand_init(nand))
return;
if (nand_scan(mtd, maxchips))
return;
nand_register(i);
}
#endif
void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
int i;
unsigned int size = 0;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
size += nand_info[i].size / 1024;
if (nand_curr_device == -1)
nand_curr_device = i;
}
printf("%u MiB\n", size / 1024);
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i);
#endif
printf("%lu MiB\n", total_nand_size / 1024);
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*

View file

@ -30,7 +30,12 @@ extern void nand_init(void);
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#ifdef CONFIG_SYS_NAND_SELF_INIT
void board_nand_init(void);
int nand_register(int devnum);
#else
extern int board_nand_init(struct nand_chip *nand);
#endif
typedef struct mtd_info nand_info_t;