[U-Boot] [PATCH] arm: iproc: add NAND driver

Scott Wood scottwood at freescale.com
Wed Aug 12 03:17:21 CEST 2015


On Sat, Aug 08, 2015 at 05:58:04PM -0700, Steve Rae wrote:
> From: Jiandong Zheng <jdzheng at broadcom.com>
> 
> Add support for the iproc NAND, and enable on Cygnus and NSP boards.
> 
> Signed-off-by: Jiandong Zheng <jdzheng at broadcom.com>
> Signed-off-by: Steve Rae <srae at broadcom.com>
> ---
> 
>  arch/arm/include/asm/arch-bcmcygnus/configs.h |   11 +
>  arch/arm/include/asm/arch-bcmnsp/configs.h    |   11 +
>  drivers/mtd/nand/Makefile                     |    1 +
>  drivers/mtd/nand/iproc_nand.c                 | 1732 +++++++++++++++++++++++++
>  drivers/mtd/nand/iproc_nand_cygnus.h          |  111 ++
>  drivers/mtd/nand/iproc_nand_ns_plus.h         |  113 ++
>  6 files changed, 1979 insertions(+)
>  create mode 100644 drivers/mtd/nand/iproc_nand.c
>  create mode 100644 drivers/mtd/nand/iproc_nand_cygnus.h
>  create mode 100644 drivers/mtd/nand/iproc_nand_ns_plus.h
> 
> diff --git a/arch/arm/include/asm/arch-bcmcygnus/configs.h b/arch/arm/include/asm/arch-bcmcygnus/configs.h
> index 5354637..5338598 100644
> --- a/arch/arm/include/asm/arch-bcmcygnus/configs.h
> +++ b/arch/arm/include/asm/arch-bcmcygnus/configs.h
> @@ -10,6 +10,7 @@
>  #include <asm/iproc-common/configs.h>
>  
>  /* uArchitecture specifics */
> +#define CONFIG_CYGNUS

Undocumented, should be in kconfig, and the name is too vague.

>  /* Serial Info */
>  /* Post pad 3 bytes after each reg addr */
> @@ -22,4 +23,14 @@
>  #define CONFIG_CONS_INDEX		3
>  #define CONFIG_SYS_NS16550_COM3		0x18023000
>  
> +/* NAND configuration */
> +#define CONFIG_CMD_NAND
> +#define CONFIG_NAND_IPROC
> +#define CONFIG_IPROC_NAND_TIMING_MODE	5
> +#define CONFIG_SYS_NAND_BASE		0
> +#define CONFIG_SYS_MAX_NAND_DEVICE	1
> +#define CONFIG_SYS_MAX_NAND_CHIPS	1
> +#define CONFIG_SYS_NAND_SELF_INIT
> +#define CONFIG_SYS_NAND_ONFI_DETECTION

CONFIG_IPROC_NAND_TIMING_MODE and CONFIG_NAND_IPROC are undocumented, and
new symbols should be done in kconfig.

Please be consistent about CONFIG_NAND_IPROC* versus CONFIG_IPROC_NAND*
(I prefer the former).

If this timing mode is describing hardware, rather than user config, it
should be CONFIG_SYS_NAND_IPROC_TIMING_MODE.

>  #endif /* __ARCH_CONFIGS_H */
> diff --git a/arch/arm/include/asm/arch-bcmnsp/configs.h b/arch/arm/include/asm/arch-bcmnsp/configs.h
> index 786deae..66f2266 100644
> --- a/arch/arm/include/asm/arch-bcmnsp/configs.h
> +++ b/arch/arm/include/asm/arch-bcmnsp/configs.h
> @@ -10,6 +10,7 @@
>  #include <asm/iproc-common/configs.h>
>  
>  /* uArchitecture specifics */
> +#define CONFIG_NS_PLUS

Undocumented, should be in kconfig, and the name is too vague.

> +/*
> + * Definitions
> + */
> +#define DRV_NAME			"iproc_nand"

I suggest using __func__ instead.

> +/*
> + * IRQ operations
> + */
> +#define NAND_ACK_IRQ(bit) writel(1, ((u32 *)ctrl.nand_intr_regs) + (bit))
> +
> +#define NAND_TEST_IRQ(bit) (readl(((u32 *)ctrl.nand_intr_regs) + (bit)) & 1)

This is the only place you ever use nand_intr_regs, so why not just
define it as a u32 pointer?  Also, use __iomem.

> +
> +/*
> + * Data access macros for endianness
> + */
> +#ifdef __LITTLE_ENDIAN
> +#define NAND_BEGIN_DATA_ACCESS()					\
> +	writel(readl(ctrl.nand_idm_io_ctrl_direct_reg) |		\
> +	       IDMFLD_NAND_IO_CONTROL_DIRECT_APB_LE_MODE,		\
> +	       ctrl.nand_idm_io_ctrl_direct_reg)
> +#define NAND_END_DATA_ACCESS()						\
> +	writel(readl(ctrl.nand_idm_io_ctrl_direct_reg) &		\
> +	       ~IDMFLD_NAND_IO_CONTROL_DIRECT_APB_LE_MODE,		\
> +	       ctrl.nand_idm_io_ctrl_direct_reg)
> +#else				/* !__LITTLE_ENDIAN */
> +#define NAND_BEGIN_DATA_ACCESS()
> +#define NAND_END_DATA_ACCESS()
> +#endif				/* !__LITTLE_ENDIAN */

Huh?


> +
> +/*
> + * Misc NAND controller configuration/status macros
> + */
> +#define NC_REG_CONFIG(cs) (NCREG_CONFIG_CS0 + ((cs) << 4))
> +
> +#define WR_CONFIG(cs, field, val) do {					\
> +	u32 reg = NC_REG_CONFIG(cs), contents = NAND_REG_RD(reg);	\
> +	contents &= ~(NCFLD_CONFIG_CS0_##field##_MASK);			\
> +	contents |= (val) << NCFLD_CONFIG_CS0_##field##_SHIFT;		\
> +	NAND_REG_WR(reg, contents);					\
> +} while (0)
> +
> +#define RD_CONFIG(cs, field)						\
> +	((NAND_REG_RD(NC_REG_CONFIG(cs)) & NCFLD_CONFIG_CS0_##field##_MASK) \
> +	>> NCFLD_CONFIG_CS0_##field##_SHIFT)
> +
> +#define NC_REG_ACC_CONTROL(cs) (NCREG_ACC_CONTROL_CS0 + ((cs) << 4))
> +
> +#define WR_ACC_CONTROL(cs, field, val) do {				\
> +	u32 reg = NC_REG_ACC_CONTROL(cs), contents = NAND_REG_RD(reg);	\
> +	contents &= ~(NCFLD_ACC_CONTROL_CS0_##field##_MASK);		\
> +	contents |= (val) << NCFLD_ACC_CONTROL_CS0_##field##_SHIFT;	\
> +	NAND_REG_WR(reg, contents);					\
> +} while (0)
> +
> +#define RD_ACC_CONTROL(cs, field)					\
> +	((NAND_REG_RD(NC_REG_ACC_CONTROL(cs)) &				\
> +	 NCFLD_ACC_CONTROL_CS0_##field##_MASK)				\
> +	 >> NCFLD_ACC_CONTROL_CS0_##field##_SHIFT)
> +
> +#define CORR_ERROR_COUNT (NAND_REG_RD(NCREG_CORR_ERROR_COUNT))
> +#define UNCORR_ERROR_COUNT (NAND_REG_RD(NCREG_UNCORR_ERROR_COUNT))
> +
> +#define WR_CORR_THRESH(cs, val) do {					\
> +	u32 contents = NAND_REG_RD(NCREG_CORR_STAT_THRESHOLD);		\
> +	u32 shift = NCFLD_CORR_STAT_THRESHOLD_CS1_SHIFT * (cs);		\
> +	contents &= ~(NCFLD_CORR_STAT_THRESHOLD_CS0_MASK << shift);	\
> +	contents |= ((val) & NCFLD_CORR_STAT_THRESHOLD_CS0_MASK) << shift; \
> +	NAND_REG_WR(NCREG_CORR_STAT_THRESHOLD, contents);		\
> +} while (0)
> +
> +#define NC_REG_TIMING1(cs) (NCREG_TIMING_1_CS0 + ((cs) << 4))
> +#define NC_REG_TIMING2(cs) (NCREG_TIMING_2_CS0 + ((cs) << 4))
> +
> +#define NAND_STRAP_TYPE							\
> +	((readl(ctrl.nand_strap_regs) & ctrl.data->strap_type_bitfield.mask) \
> +	>> ctrl.data->strap_type_bitfield.shift)
> +#define NAND_STRAP_PAGE							\
> +	((readl(ctrl.nand_strap_regs) & ctrl.data->strap_page_bitfield.mask) \
> +	>> ctrl.data->strap_page_bitfield.shift)

Please use functions instead of macros where possible.

> +
> +/*
> + * Internal structures
> + */
> +struct nand_strap_type {
> +	uint8_t sector_1k;
> +	uint8_t ecc_level;
> +	uint16_t spare_size;
> +};
> +
> +struct nand_strap_bitfield {
> +	uint32_t mask;
> +	uint32_t shift;
> +};

What does "strap" mean here?

> +/*
> + * Internal support functions
> + */
> +static inline int fls64(u64 x)
> +{
> +	u32 h = x >> 32;
> +	if (h)
> +		return fls(h) + 32;
> +	return fls(x);
> +}

Put this in a generic header.

> +/*
> + * NAND MTD API: read/program/erase
> + */
> +
> +static void iproc_nand_cmd_ctrl(struct mtd_info *mtd,
> +				int dat, unsigned int ctrl)
> +{
> +	/* intentionally left blank */
> +}

Leave cmd_ctrl NULL instead of making it empty.

> +static int iproc_nand_read(struct mtd_info *mtd,
> +			   struct nand_chip *chip, u64 addr, unsigned int trans,
> +			   u32 *buf, u8 *oob)
> +{
> +	struct iproc_nand_host *host = chip->priv;
> +	u64 start_addr = addr;
> +	int i;
> +	int oob_bytes;
> +	unsigned int max_bitflips, bitflips;
> +	unsigned int corr_error_count, uncorr_error_count;
> +	u32 *sector_buf = buf;
> +	u8 *sector_oob = oob;
> +
> +	debug("%s %llx -> %p (trans %x)\n", __func__,
> +	      (unsigned long long)addr, buf, trans);
> +
> +	BUG_ON(!oob);
> +
> +	NAND_ACK_IRQ(NCINTR_UNC);
> +	NAND_ACK_IRQ(NCINTR_CORR);
> +	max_bitflips = 0;
> +	corr_error_count = 0;
> +	uncorr_error_count = 0;
> +
> +	NAND_REG_WR(NCREG_CMD_EXT_ADDRESS,
> +		    (host->cs << 16) | ((addr >> 32) & 0xffff));
> +
> +	for (i = 0; i < trans; i++, addr += FC_BYTES) {
> +		if (!host->hwcfg.sector_size_1k || ((i & 0x1) == 0)) {
> +			sector_buf = buf;
> +			sector_oob = oob;
> +		}
> +
> +		NAND_REG_WR(NCREG_CMD_ADDRESS, addr & 0xffffffff);
> +
> +		if (ctrl.data_cache_invalid) {
> +			if ((i == 0) && RD_ACC_CONTROL(host->cs, PAGE_HIT_EN))
> +				/*
> +				 * temporarily disable the PAGE_HIT to force
> +				 * data to be read from NAND
> +				 */
> +				WR_ACC_CONTROL(host->cs, PAGE_HIT_EN, 0);
> +			else
> +				ctrl.data_cache_invalid = 0;
> +		}
> +
> +		/* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
> +		iproc_nand_send_cmd(CMD_PAGE_READ);
> +		iproc_nand_waitfunc(mtd, chip);
> +
> +		/* OOB bytes per sector */
> +		oob_bytes = (mtd->oobsize / trans) <<
> +				host->hwcfg.sector_size_1k;
> +		/* OOB bytes per 512B transfer */
> +		if (host->hwcfg.sector_size_1k && (i & 0x01))
> +			oob_bytes = max(0, oob_bytes -
> +					MAX_CONTROLLER_OOB_BYTES);
> +		oob_bytes = min(oob_bytes, MAX_CONTROLLER_OOB_BYTES);
> +
> +		iproc_nand_cache_read(&buf, &oob, oob_bytes);
> +
> +		if (ctrl.data_cache_invalid) {
> +			/* re-enable PAGE_HIT */
> +			WR_ACC_CONTROL(host->cs, PAGE_HIT_EN, 1);
> +			ctrl.data_cache_invalid = 0;
> +		}
> +
> +		if (buf && (!host->hwcfg.sector_size_1k || (i & 0x1))) {
> +			/* check uncorrectable errors */
> +			if (NAND_TEST_IRQ(NCINTR_UNC)) {
> +				if (erased_sector(mtd,
> +						  (u8 *)sector_buf,
> +						  sector_oob,
> +						  &bitflips)) {
> +					corr_error_count += bitflips;
> +					if (bitflips > max_bitflips)
> +						max_bitflips = bitflips;
> +				} else {
> +					uncorr_error_count += 1;
> +				}

This level of indentation should be a hint that this function needs to be
broken up into smaller ones.

> +				NAND_ACK_IRQ(NCINTR_UNC);
> +				ctrl.data_cache_invalid = 1;
> +			}
> +			/* check correctable errors */
> +			if (NAND_TEST_IRQ(NCINTR_CORR)) {
> +				bitflips = CORR_ERROR_COUNT;
> +				corr_error_count += bitflips;
> +				if (bitflips > max_bitflips)
> +					max_bitflips = bitflips;
> +				NAND_ACK_IRQ(NCINTR_CORR);
> +				ctrl.data_cache_invalid = 1;
> +			}
> +		}
> +	}
> +	if (uncorr_error_count) {
> +		printf(DRV_NAME ": %d uncorrectable errors at 0x%llx\n",
> +		       uncorr_error_count, (unsigned long long)start_addr);
> +		mtd->ecc_stats.failed += uncorr_error_count;
> +		/* NAND layer expects zero on ECC errors */
> +		return 0;
> +	}
> +	if (max_bitflips) {
> +		debug("%s: corrected %d bit errors at 0x%llx\n",
> +		      __func__, max_bitflips, (unsigned long long)start_addr);
> +		mtd->ecc_stats.corrected += corr_error_count;
> +		return max_bitflips;
> +	}
> +
> +	return 0;
> +}
> +
> +static int iproc_nand_read_page(struct mtd_info *mtd,
> +				struct nand_chip *chip,
> +				uint8_t *buf,
> +				int oob_required,
> +				int page)
> +{
> +	struct iproc_nand_host *host = chip->priv;
> +
> +	BUG_ON(!buf);
> +

What's with all these !buf checks?  Do you think it's more likely that
you'll get a NULL buf than any of the other pointers that get passed in?

> +static int iproc_nand_mark_bad(struct mtd_info *mtd, loff_t ofs)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	u64 blk_addr = (u64)ofs;
> +
> +	debug("%s %llx\n", __func__, (unsigned long long)ofs);
> +
> +	/* get NAND block address */
> +	blk_addr &= ~((1 << chip->phys_erase_shift) - 1);
> +
> +	memset(chip->oob_poi, 0xff, mtd->oobsize);
> +	chip->oob_poi[chip->badblockpos] = 0;
> +
> +	return iproc_nand_write(mtd, chip, blk_addr, NULL,
> +				(u8 *)chip->oob_poi);
> +}

Why do you need a custom version of this?

> +
> +/*
> + * Per-CS setup (1 NAND device)
> + */
> +
> +static const unsigned int block_sizes[] = { 8, 16, 128, 256, 512, 1024, 2048 };
> +static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192 };
> +
> +static void iproc_nand_set_cfg(struct iproc_nand_host *host,
> +			       struct iproc_nand_cfg *cfg)
> +{
> +	int i, found;
> +
> +	for (i = 0, found = 0; i < ARRAY_SIZE(block_sizes); i++)
> +		if ((block_sizes[i] << 10) == cfg->block_size) {
> +			WR_CONFIG(host->cs, BLOCK_SIZE, i);
> +			found = 1;
> +		}

U-Boot coding style requires braces around multiline statements.

> +	debug("%s: mtd configuration\n"
> +	      "\tdevice_size 0x%llx\n"
> +	      "\tblock_size 0x%x\n"
> +	      "\tpage_size 0x%x\n"
> +	      "\tdevice_width 0x%x\n"
> +	      "\tcol_adr_bytes 0x%x\n"
> +	      "\tblk_adr_bytes 0x%x\n"
> +	      "\tspare_area_size 0x%x\n"
> +	      "\tecc_level 0x%x\n"
> +	      "\tsector_size_1k 0x%x\n",
> +	      __func__,
> +	      new_cfg.device_size,
> +	      new_cfg.block_size,
> +	      new_cfg.page_size,
> +	      new_cfg.device_width,
> +	      new_cfg.col_adr_bytes,
> +	      new_cfg.blk_adr_bytes,
> +	      new_cfg.spare_area_size,
> +	      new_cfg.ecc_level,
> +	      new_cfg.sector_size_1k);
> +
> +	/* check settings determined by controller auto-init */
> +	if (ctrl.auto_inited) {
> +		debug("%s: auto-init configuration\n"
> +		      "\tdevice_size 0x%llx\n"
> +		      "\tblock_size 0x%x\n"
> +		      "\tpage_size 0x%x\n"
> +		      "\tdevice_width 0x%x\n"
> +		      "\tcol_adr_bytes 0x%x\n"
> +		      "\tblk_adr_bytes 0x%x\n"
> +		      "\tspare_area_size 0x%x\n"
> +		      "\tecc_level 0x%x\n"
> +		      "\tsector_size_1k 0x%x\n",
> +		      __func__,
> +		      orig_cfg.device_size,
> +		      orig_cfg.block_size,
> +		      orig_cfg.page_size,
> +		      orig_cfg.device_width,
> +		      orig_cfg.col_adr_bytes,
> +		      orig_cfg.blk_adr_bytes,
> +		      orig_cfg.spare_area_size,
> +		      orig_cfg.ecc_level,
> +		      orig_cfg.sector_size_1k);
> +		/* check basic device attributes first */
> +		if (orig_cfg.device_size != new_cfg.device_size ||
> +		    orig_cfg.block_size != new_cfg.block_size ||
> +		    orig_cfg.page_size != new_cfg.page_size ||
> +		    orig_cfg.device_width != new_cfg.device_width ||
> +		    orig_cfg.col_adr_bytes != new_cfg.col_adr_bytes ||
> +		    orig_cfg.blk_adr_bytes != new_cfg.blk_adr_bytes ||
> +		    orig_cfg.ful_adr_bytes != new_cfg.ful_adr_bytes ||
> +		    orig_cfg.ecc_level == 0 ||
> +		    ((orig_cfg.ecc_code == ECC_CODE_BCH) &&
> +		     (orig_cfg.ecc_level >
> +		      iproc_max_bch_ecc_level[orig_cfg.sector_size_1k])) ||
> +		    orig_cfg.spare_area_size > new_cfg.spare_area_size ||
> +		    ((orig_cfg.ecc_code == ECC_CODE_BCH) &&
> +		     (iproc_bch_ecc_bytes[orig_cfg.ecc_level] >
> +		      orig_cfg.spare_area_size))) {
> +			/* ignore invalid auto-init settings */
> +			ctrl.auto_inited = 0;
> +			printf(DRV_NAME ": invalid auto-init settings\n");
> +
> +		} else {
> +			/* auto-init has initialized the flash correctly. */
> +			new_cfg = orig_cfg;
> +			printf(DRV_NAME ": following auto-init settings\n");
> +		}
> +	}

This is a bit elaborate... what is "auto-init"?

> +static void iproc_nand_timing_setup(struct iproc_nand_host *host)
> +{
> +	struct nand_chip *chip = &host->chip;
> +	int onfi_tmode; /* bit mask of supported onfi timing modes */
> +
> +	/*
> +	 * ctrl.tmode has the configured tmode upper limit [0-5]
> +	 * or is -1 to indicate power-on default timing
> +	 */
> +	if (ctrl.tmode < 0 || ctrl.tmode >= ONFI_TIMING_MODES)
> +		return;
> +
> +	if (chip->onfi_version) {
> +		onfi_tmode = le16_to_cpu(get_unaligned(
> +				(u16 *)&chip->onfi_params.async_timing_mode));

Fix onfi_get_async_timing_mode() instead, and have it use
get_unaligned_le16().

> +		if ((onfi_tmode == 0) || (onfi_tmode & ~0x3F)) { 
> +			printf(DRV_NAME
> +			       ": invalid ONFI timing mode ignored 0x%x\n",
> +			       onfi_tmode);
> +		} else {
> +			/*
> +			 * select the maximum supported ONFI timing mode
> +			 * that is lower than the configured limit
> +			 */
> +			while (ctrl.tmode > 0) {
> +				if (onfi_tmode & (1 << ctrl.tmode))
> +					break;
> +				ctrl.tmode--;
> +			}
> +		}
> +	}
> +
> +	NAND_REG_WR(NC_REG_TIMING1(host->cs),
> +		    ctrl.data->onfi_tmode[ctrl.tmode].timing1);
> +	NAND_REG_WR(NC_REG_TIMING2(host->cs),
> +		    ctrl.data->onfi_tmode[ctrl.tmode].timing2);
> +	printf(DRV_NAME ": timing mode %d\n", ctrl.tmode);

This verbosity is not appropriate for normal driver init.

> +#ifdef CONFIG_SYS_MAX_NAND_CHIPS
> +#if (CONFIG_SYS_MAX_NAND_CHIPS > NAND_MAX_CS)
> +#error "Invalid CONFIG_SYS_MAX_NAND_CHIPS value"
> +#endif
> +	ctrl.max_cs = CONFIG_SYS_MAX_NAND_CHIPS;
> +#else
> +	ctrl.max_cs = 1;
> +#endif
> +
> +	/* write protect configuration */
> +#ifdef CONFIG_IPROC_NAND_WP_MODE
> +#if ((CONFIG_IPROC_NAND_WP_MODE < 0) || \
> +	(CONFIG_IPROC_NAND_WP_MODE > WP_ALWAYS_CLEARED))
> +#error "Invalid CONFIG_IPROC_NAND_WP_MODE value"
> +#endif
> +	ctrl.wp_mode = CONFIG_IPROC_NAND_WP_MODE;
> +#else
> +	ctrl.wp_mode = WP_SET_BY_DEFAULT;
> +#endif
> +
> +	/* timing mode configuration */
> +#ifdef CONFIG_IPROC_NAND_TIMING_MODE
> +#if ((CONFIG_IPROC_NAND_TIMING_MODE < 0) || \
> +	(CONFIG_IPROC_NAND_TIMING_MODE >= ONFI_TIMING_MODES))
> +#error "Invalid CONFIG_IPROC_NAND_TIMING_MODE value"
> +#endif
> +	ctrl.tmode = CONFIG_IPROC_NAND_TIMING_MODE;
> +#else
> +	ctrl.tmode = -1; /* use default timing configuration */
> +#endif

These would be more readable as:

#ifdef CONFIG_FOO
#define CONFIG_FOO <default_value>
#endif

...

BUILD_BUG_ON(CONFIG_FOO is bad);
ctrl.foo = CONFIG_FOO;

> +	debug("%s: nand_regs %p\n", __func__, ctrl.nand_regs);
> +	debug("%s: nand_intr_regs %p\n", __func__, ctrl.nand_intr_regs);
> +	debug("%s: nand_idm_regs %p\n", __func__, ctrl.nand_idm_regs);
> +	debug("%s: nand_idm_io_ctrl_direct_reg %p\n", __func__,
> +	      ctrl.nand_idm_io_ctrl_direct_reg);
> +	debug("%s: nand_strap_regs %p\n", __func__, ctrl.nand_strap_regs);
> +	debug("%s: max_cs %d\n", __func__, ctrl.max_cs);
> +	debug("%s: wp_mode %d\n", __func__, ctrl.wp_mode);

This driver has a lot of debug clutter.  Is it really 

> +#ifdef CONFIG_IPROC_NAND_AUTO_INIT

Undocumented, and should be in kconfig.

> +int iproc_nand_init(void)
> +{
> +	struct iproc_nand_host *host;
> +	struct mtd_info *mtd;
> +	struct nand_chip *chip;
> +
> +	NAND_REG_WR(NCREG_SEMAPHORE, 0);
> +
> +	host = &nand_host;
> +	memset(host, 0, sizeof(*host));
> +
> +	host->mtd = &nand_info[0];
> +
> +	host->cs = 0;
> +
> +	mtd = host->mtd;
> +	chip = &host->chip;
> +
> +	chip->priv = host;
> +	mtd->priv = chip;
> +	mtd->name = "iproc_nand";
> +	/*
> +	 * set mtd bitflip threshold to 1 as the desired threshold will
> +	 * be set in the controller register.
> +	 */
> +	mtd->bitflip_threshold = 1;
> +
> +	chip->IO_ADDR_R = (void *)0xdeadbeef;
> +	chip->IO_ADDR_W = (void *)0xdeadbeef;

Why?

> +	chip->cmd_ctrl = iproc_nand_cmd_ctrl;
> +	chip->cmdfunc = iproc_nand_cmdfunc;
> +	chip->waitfunc = iproc_nand_waitfunc;
> +	chip->read_byte = iproc_nand_read_byte;
> +	chip->read_buf = iproc_nand_read_buf;
> +	if (ctrl.max_cs > 1)
> +		chip->select_chip = iproc_nand_select_chip;
> +
> +	chip->block_bad = iproc_nand_block_bad;
> +	chip->block_markbad = iproc_nand_mark_bad;
> +
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.layout = &iproc_nand_oob_layout;
> +	chip->ecc.read_page = iproc_nand_read_page;
> +	chip->ecc.write_page = iproc_nand_write_page;
> +	chip->ecc.read_page_raw = iproc_nand_read_page_raw;
> +	chip->ecc.write_page_raw = iproc_nand_write_page_raw;
> +
> +	chip->ecc.write_oob_raw = iproc_nand_write_oob_raw;
> +	chip->ecc.read_oob_raw = iproc_nand_read_oob_raw;
> +
> +	chip->ecc.read_oob = (void *)iproc_nand_read_oob;
> +	chip->ecc.write_oob = iproc_nand_write_oob;
> +
> +	chip->controller = &ctrl.controller;
> +
> +	if (nand_scan_ident(mtd, ctrl.max_cs, NULL))
> +		return -ENXIO;
> +
> +	chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_SKIP_BBTSCAN;
> +
> +	chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;

Why skip the BBT scan?

-Scott


More information about the U-Boot mailing list