From: Jiandong Zheng <jdzh...@broadcom.com>

Add support for the iproc NAND, and enable on Cygnus and NSP boards.

Signed-off-by: Jiandong Zheng <jdzh...@broadcom.com>
Signed-off-by: Steve Rae <s...@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
 
 /* 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
+
 #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
 
 /* Serial Info */
 /* no padding */
@@ -19,4 +20,14 @@
 #define CONFIG_CONS_INDEX              1
 #define CONFIG_SYS_NS16550_COM1                0x18000300
 
+/* 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
+
 #endif /* __ARCH_CONFIGS_H */
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 347ea62..e111b1a 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
 obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o
 obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
 obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o
+obj-$(CONFIG_NAND_IPROC) += iproc_nand.o
 obj-$(CONFIG_NAND_JZ4740) += jz4740_nand.o
 obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o
 obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
diff --git a/drivers/mtd/nand/iproc_nand.c b/drivers/mtd/nand/iproc_nand.c
new file mode 100644
index 0000000..c96f432
--- /dev/null
+++ b/drivers/mtd/nand/iproc_nand.c
@@ -0,0 +1,1732 @@
+/*
+ * Copyright 2015 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <asm-generic/unaligned.h>
+
+#include <common.h>
+#include <nand.h>
+#include <malloc.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+#if defined(CONFIG_CYGNUS)
+#include "iproc_nand_cygnus.h"
+#elif defined(CONFIG_NS_PLUS)
+#include "iproc_nand_ns_plus.h"
+#else
+#error "Unsupported configuration"
+#endif
+
+/*
+ * Definitions
+ */
+#define DRV_NAME                       "iproc_nand"
+
+/*
+ * iProc NAND flash commands
+ */
+#define CMD_PAGE_READ                  0x01
+#define CMD_SPARE_AREA_READ            0x02
+#define CMD_STATUS_READ                        0x03
+#define CMD_PROGRAM_PAGE               0x04
+#define CMD_DEVICE_ID_READ             0x07
+#define CMD_BLOCK_ERASE                        0x08
+#define CMD_FLASH_RESET                        0x09
+#define CMD_PARAMETER_READ             0x0e
+
+/*
+ * NAND controller register offset
+ */
+#define NCREG_CMD_START                        0x004   /* Command Start */
+#define NCREG_CMD_EXT_ADDRESS          0x008   /* Command Extended Address */
+#define NCREG_CMD_ADDRESS              0x00c   /* Command Address */
+#define NCREG_INTFC_STATUS             0x014   /* Interface Status */
+#define NCREG_CS_NAND_SELECT           0x018   /* EBI CS Select */
+#define NCREG_CS_NAND_XOR              0x01c   /* EBI CS Address XOR with
+                                                  1FC0 Control */
+#define NCREG_ACC_CONTROL_CS0          0x050   /* Access Control CS0 */
+#define NCREG_CONFIG_CS0               0x054   /* Config CS0 */
+#define NCREG_TIMING_1_CS0             0x058   /* Timing Parameters 1 CS0 */
+#define NCREG_TIMING_2_CS0             0x05c   /* Timing Parameters 2 CS0 */
+#define NCREG_CORR_STAT_THRESHOLD      0x0c0   /* Correctable Error Reporting
+                                                  Threshold */
+#define NCREG_UNCORR_ERROR_COUNT       0x0fc   /* Total Uncorrectable Error
+                                                  Count */
+#define NCREG_CORR_ERROR_COUNT         0x100   /* Correctable Error Count */
+#define NCREG_INIT_STATUS              0x144   /* Initialization status */
+#define NCREG_SEMAPHORE                        0x150   /* Semaphore */
+#define NCREG_FLASH_DEVICE_ID          0x194   /* Device ID */
+#define NCREG_FLASH_DEVICE_ID_EXT      0x198   /* Extended Device ID */
+#define NCREG_SPARE_AREA_READ_OFS_0    0x200   /* Spare Area Read Bytes */
+#define NCREG_SPARE_AREA_WRITE_OFS_0   0x280   /* Spare Area Write Bytes */
+#define NCREG_FLASH_CACHE_BASE         0x400   /* Cache Buffer Access */
+#define NCREG_INTERRUPT_BASE           0xf00   /* Interrupt Base Address */
+
+/*
+ * Required NAND controller register fields
+ */
+#define NCFLD_CMD_START_OPCODE_SHIFT                           24
+#define NCFLD_INTFC_STATUS_FLASH_STATUS_MASK                   0x000000FF
+#define NCFLD_CS_NAND_SELECT_AUTO_DEVID_CONFIG                 0x40000000
+#define NCFLD_CS_NAND_SELECT_WP                                        
0x20000000
+#define NCFLD_CS_NAND_SELECT_DIRECT_ACCESS_CS_MASK             0x000000FF
+#define NCFLD_CS_NAND_XOR_CS_MASK                              0x000000FF
+
+#define NCFLD_CONFIG_CS0_BLOCK_SIZE_MASK                       0x70000000
+#define NCFLD_CONFIG_CS0_BLOCK_SIZE_SHIFT                      28
+#define NCFLD_CONFIG_CS0_DEVICE_SIZE_MASK                      0x0f000000
+#define NCFLD_CONFIG_CS0_DEVICE_SIZE_SHIFT                     24
+#define NCFLD_CONFIG_CS0_DEVICE_WIDTH_MASK                     0x00800000
+#define NCFLD_CONFIG_CS0_DEVICE_WIDTH_SHIFT                    23
+#define NCFLD_CONFIG_CS0_PAGE_SIZE_MASK                                
0x00300000
+#define NCFLD_CONFIG_CS0_PAGE_SIZE_SHIFT                       20
+
+#define NCFLD_CONFIG_CS0_FUL_ADR_BYTES_MASK                    0x00070000
+#define NCFLD_CONFIG_CS0_FUL_ADR_BYTES_SHIFT                   16
+#define NCFLD_CONFIG_CS0_COL_ADR_BYTES_MASK                    0x00007000
+#define NCFLD_CONFIG_CS0_COL_ADR_BYTES_SHIFT                   12
+#define NCFLD_CONFIG_CS0_BLK_ADR_BYTES_MASK                    0x00000700
+#define NCFLD_CONFIG_CS0_BLK_ADR_BYTES_SHIFT                   8
+
+#define NCFLD_ACC_CONTROL_CS0_RD_ECC_EN_MASK                   0x80000000
+#define NCFLD_ACC_CONTROL_CS0_RD_ECC_EN_SHIFT                  31
+#define NCFLD_ACC_CONTROL_CS0_WR_ECC_EN_MASK                   0x40000000
+#define NCFLD_ACC_CONTROL_CS0_WR_ECC_EN_SHIFT                  30
+#define NCFLD_ACC_CONTROL_CS0_FAST_PGM_RDIN_MASK               0x10000000
+#define NCFLD_ACC_CONTROL_CS0_FAST_PGM_RDIN_SHIFT              28
+#define NCFLD_ACC_CONTROL_CS0_RD_ERASED_ECC_EN_MASK            0x08000000
+#define NCFLD_ACC_CONTROL_CS0_RD_ERASED_ECC_EN_SHIFT           27
+#define NCFLD_ACC_CONTROL_CS0_PARTIAL_PAGE_EN_MASK             0x04000000
+#define NCFLD_ACC_CONTROL_CS0_PARTIAL_PAGE_EN_SHIFT            26
+
+#define NCFLD_ACC_CONTROL_CS0_PAGE_HIT_EN_MASK                 0x01000000
+#define NCFLD_ACC_CONTROL_CS0_PAGE_HIT_EN_SHIFT                        24
+#define NCFLD_ACC_CONTROL_CS0_ECC_LEVEL_MASK                   0x001f0000
+#define NCFLD_ACC_CONTROL_CS0_ECC_LEVEL_SHIFT                  16
+#define NCFLD_ACC_CONTROL_CS0_SECTOR_SIZE_1K_MASK              0x00000080
+#define NCFLD_ACC_CONTROL_CS0_SECTOR_SIZE_1K_SHIFT             7
+#define NCFLD_ACC_CONTROL_CS0_SPARE_AREA_SIZE_MASK             0x0000007f
+#define NCFLD_ACC_CONTROL_CS0_SPARE_AREA_SIZE_SHIFT            0
+
+#define NCFLD_CORR_STAT_THRESHOLD_CS0_MASK                     0x0000003f
+#define NCFLD_CORR_STAT_THRESHOLD_CS1_SHIFT                    6
+#define NCFLD_INIT_STATUS_INIT_SUCCESS                         0x20000000
+
+/*
+ * IDM NAND register offset
+ */
+#define IDMREG_IO_CONTROL_DIRECT                               0x408
+#define IDMREG_IO_STATUS                                       0x500
+#define IDMREG_RESET_CONTROL                                   0x800
+
+/*
+ * Required IDM NAND IO Control register fields
+ */
+#define IDMFLD_NAND_IO_CONTROL_DIRECT_AXI_BE_MODE              (1UL << 28)
+#define IDMFLD_NAND_IO_CONTROL_DIRECT_APB_LE_MODE              (1UL << 24)
+#define IDMFLD_NAND_IO_CONTROL_DIRECT_IRQ_SHIFT                        2
+
+/*
+ * Interrupts
+ */
+#define NCINTR_NP_READ                                         0
+#define NCINTR_BLKERA                                          1
+#define NCINTR_CPYBK                                           2
+#define NCINTR_PGMPG                                           3
+#define NCINTR_CTLRDY                                          4
+#define NCINTR_RBPIN                                           5
+#define NCINTR_UNC                                             6
+#define NCINTR_CORR                                            7
+
+/* 512B flash cache in the NAND controller HW */
+#define FC_SHIFT       9U
+#define FC_BYTES       512U
+#define FC_WORDS       (FC_BYTES >> 2)
+#define FC(x)          (NCREG_FLASH_CACHE_BASE + ((x) << 2))
+
+/* 64B oob cache in the NAND controller HW */
+#define MAX_CONTROLLER_OOB_BYTES       64
+#define MAX_CONTROLLER_OOB_WORDS       (MAX_CONTROLLER_OOB_BYTES >> 2)
+
+/*
+ * Register access macros - NAND flash controller
+ */
+#define NAND_REG_RD(x)         readl(ctrl.nand_regs + (x))
+#define NAND_REG_WR(x, y)      writel((y), ctrl.nand_regs + (x))
+#define NAND_REG_CLR(x, y)     NAND_REG_WR((x), NAND_REG_RD(x) & ~(y))
+#define NAND_REG_SET(x, y)     NAND_REG_WR((x), NAND_REG_RD(x) | (y))
+
+/*
+ * 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)
+
+/*
+ * 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 */
+
+/*
+ * 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)
+
+/*
+ * 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;
+};
+
+#define ONFI_TIMING_MODES      6
+struct nand_timing {
+       u32 timing1;
+       u32 timing2;
+};
+
+struct nand_ctrl_data {
+       uint32_t chip_select_max;
+       struct nand_strap_bitfield strap_type_bitfield;
+       struct nand_strap_bitfield strap_page_bitfield;
+       struct nand_strap_bitfield strap_width_bitfield;
+       struct nand_strap_type strap_types[16];
+       uint32_t strap_page_sizes[4];
+       struct nand_timing onfi_tmode[ONFI_TIMING_MODES];
+};
+
+/*
+ * This flag controls if WP stays on between erase/write
+ * commands to mitigate flash corruption due to power glitches.
+ * Values:
+ * WP_NOT_USED: WP is not used or not available
+ * WP_SET_BY_DEFAULT: WP is set by default, cleared for erase/write operations
+ * WP_ALWAYS_CLEARED: WP is always cleared
+ */
+enum iproc_nand_wp_mode {
+       WP_NOT_USED = 0,
+       WP_SET_BY_DEFAULT = 1,
+       WP_ALWAYS_CLEARED = 2,
+};
+
+struct iproc_nand_controller {
+       struct nand_hw_control controller;
+       int cmd_pending;
+       int auto_inited;
+       /*
+        * This flag indicates that existing data cache should not be used.
+        * When PAGE_HIT is enabled and a read operation is performed from
+        * the same address, the controller is not reading from the NAND,
+        * considering that data is already available in the controller data
+        * cache. In that case the correctable or uncorrectable interrupts
+        * are not asserted, so the read would appear successful.
+        * This flag will be used to temporary disable PAGE_HIT to force the
+        * data to be read again form the NAND and have correct ECC results.
+        */
+       int data_cache_invalid;
+
+       /*
+        * The percentage of the BCH ECC correction capability
+        * above which correctable errors will be reported.
+        */
+       int corr_threshold_percent;
+
+       void *nand_regs;
+       void *nand_intr_regs;
+       void *nand_idm_regs;
+       void *nand_idm_io_ctrl_direct_reg;
+       void *nand_strap_regs;
+
+       int strap_type;
+       int strap_page_size;
+
+       unsigned int max_cs;
+
+       /*
+        * This flag controls NAND interface timing.
+        * Values:
+        * -1:          use current timing register values
+        * [0-5]:       change timing register values to comply with ONFI
+        *              timing mode [0-5]
+        */
+       int tmode;
+
+       enum iproc_nand_wp_mode wp_mode;
+
+       struct nand_ctrl_data const *data;
+};
+
+enum iproc_nand_ecc_code {
+       ECC_CODE_BCH,
+       ECC_CODE_HAMMING,
+};
+
+struct iproc_nand_cfg {
+       u64 device_size;
+       unsigned int block_size;
+       unsigned int page_size;
+       unsigned int spare_area_size;
+       unsigned int device_width;
+       unsigned int col_adr_bytes;
+       unsigned int blk_adr_bytes;
+       unsigned int ful_adr_bytes;
+       unsigned int sector_size_1k;
+       unsigned int ecc_level;
+       enum iproc_nand_ecc_code ecc_code;
+};
+
+struct iproc_nand_host {
+       u32 buf[FC_WORDS];
+       struct nand_chip chip;
+       struct mtd_info *mtd;
+       int cs;
+       unsigned int last_cmd;
+       unsigned int last_byte;
+       u64 last_addr;
+       struct iproc_nand_cfg hwcfg;
+};
+
+static struct iproc_nand_host nand_host;
+
+static struct nand_ecclayout iproc_nand_oob_layout;
+
+/*
+ * global variables
+ */
+static struct iproc_nand_controller ctrl;
+
+/* maximum BCH ECC level for 512B and 1024B sectors */
+static const uint8_t iproc_max_bch_ecc_level[2] = { 17, 20 };
+
+/* BCH ECC bytes required per 512B */
+static const uint8_t iproc_bch_ecc_bytes[] = {
+       0, 2, 4, 6, 7, 9, 11, 13, 14, 16, 18, 20, 21, 23, 25,
+       27, 28, 30, 32, 34, 35
+};
+
+/* SoC specific data */
+static const struct nand_ctrl_data soc_nand_ctrl_data = {
+       .chip_select_max = NAND_MAX_CS,
+       .strap_type_bitfield = {
+               .mask = NAND_STRAP_TYPE_MASK,
+               .shift = NAND_STRAP_TYPE_SHIFT,
+       },
+       .strap_page_bitfield = {
+               .mask = NAND_STRAP_PAGE_MASK,
+               .shift = NAND_STRAP_PAGE_SHIFT,
+       },
+       .strap_width_bitfield = {
+               .mask = NAND_STRAP_WIDTH_MASK,
+               .shift = NAND_STRAP_WIDTH_SHIFT,
+       },
+       .strap_types = NAND_STRAP_TYPE_DATA,
+       .strap_page_sizes = NAND_STRAP_PAGE_DATA,
+       .onfi_tmode = NAND_TIMING_DATA,
+};
+
+/*
+ * Internal support functions
+ */
+static inline int fls64(u64 x)
+{
+       u32 h = x >> 32;
+       if (h)
+               return fls(h) + 32;
+       return fls(x);
+}
+
+static void iproc_nand_wp(struct mtd_info *mtd, int wp)
+{
+       if (ctrl.wp_mode == WP_SET_BY_DEFAULT) {
+               static int old_wp = -1;
+               if (old_wp != wp) {
+                       debug("%s: WP %s\n", __func__, wp ? "on" : "off");
+                       old_wp = wp;
+               }
+               if (wp) {
+                       NAND_REG_SET(NCREG_CS_NAND_SELECT,
+                                    NCFLD_CS_NAND_SELECT_WP);
+               } else {
+                       NAND_REG_CLR(NCREG_CS_NAND_SELECT,
+                                    NCFLD_CS_NAND_SELECT_WP);
+               }
+       }
+}
+
+/* Helper functions for reading and writing OOB registers. */
+static inline unsigned char oob_reg_read(int offs)
+{
+       if (offs >= MAX_CONTROLLER_OOB_BYTES)
+               return 0x77;
+
+       /* APB read is big endian */
+       return NAND_REG_RD(NCREG_SPARE_AREA_READ_OFS_0 + (offs & ~0x03))
+              >> (24 - ((offs & 0x03) << 3));
+}
+
+static int wait_for_completion_timeout(int us)
+{
+       BUG_ON(ctrl.cmd_pending == 0);
+       while (us > 0) {
+               if (NAND_TEST_IRQ(NCINTR_CTLRDY)) {
+                       NAND_ACK_IRQ(NCINTR_CTLRDY);
+                       break;
+               }
+               __udelay(10);
+               us -= 10;
+       }
+       return us;
+}
+
+static void iproc_nand_send_cmd(int cmd)
+{
+       debug("%s: native cmd %d addr_lo 0x%lx\n", __func__, cmd,
+             (unsigned long)NAND_REG_RD(NCREG_CMD_ADDRESS));
+       BUG_ON(ctrl.cmd_pending != 0);
+       ctrl.cmd_pending = cmd;
+
+       NAND_REG_WR(NCREG_CMD_START, cmd << NCFLD_CMD_START_OPCODE_SHIFT);
+}
+
+/*
+ * NAND MTD API: read/program/erase
+ */
+
+static void iproc_nand_cmd_ctrl(struct mtd_info *mtd,
+                               int dat, unsigned int ctrl)
+{
+       /* intentionally left blank */
+}
+
+static int iproc_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+
+       debug("%s: native cmd %d\n", __func__, ctrl.cmd_pending);
+       if (ctrl.cmd_pending &&
+           wait_for_completion_timeout(200000) <= 0) {
+               printf(DRV_NAME ": timeout waiting for command %u (%ld)\n",
+                      host->last_cmd,
+                      (unsigned long)NAND_REG_RD(NCREG_CMD_START) >> 24);
+               printf(DRV_NAME ": irq status %08lx, intfc status %08lx\n",
+                      (unsigned long)NAND_TEST_IRQ(NCINTR_CTLRDY),
+                      (unsigned long)NAND_REG_RD(NCREG_INTFC_STATUS));
+       }
+       ctrl.cmd_pending = 0;
+       return NAND_REG_RD(NCREG_INTFC_STATUS) &
+              NCFLD_INTFC_STATUS_FLASH_STATUS_MASK;
+}
+
+static void iproc_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+                              int column, int page_addr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+       u64 addr = (u64) page_addr << chip->page_shift;
+       int native_cmd = 0;
+
+       if (command == NAND_CMD_READID || command == NAND_CMD_PARAM)
+               addr = (u64) column;
+
+       debug("%s: cmd 0x%x addr 0x%llx\n", __func__, command,
+             (unsigned long long)addr);
+       host->last_cmd = command;
+       host->last_byte = 0;
+       host->last_addr = addr;
+
+       switch (command) {
+       case NAND_CMD_RESET:
+               native_cmd = CMD_FLASH_RESET;
+               break;
+       case NAND_CMD_STATUS:
+               native_cmd = CMD_STATUS_READ;
+               break;
+       case NAND_CMD_READID:
+               native_cmd = CMD_DEVICE_ID_READ;
+               break;
+       case NAND_CMD_READOOB:
+               native_cmd = CMD_SPARE_AREA_READ;
+               break;
+       case NAND_CMD_ERASE1:
+               native_cmd = CMD_BLOCK_ERASE;
+               iproc_nand_wp(mtd, 0);
+               break;
+       case NAND_CMD_PARAM:
+               native_cmd = CMD_PARAMETER_READ;
+               break;
+       default:
+               return;
+       }
+
+       NAND_REG_WR(NCREG_CMD_EXT_ADDRESS,
+                   (host->cs << 16) | ((addr >> 32) & 0xffff));
+       NAND_REG_WR(NCREG_CMD_ADDRESS, addr & 0xffffffff);
+
+       iproc_nand_send_cmd(native_cmd);
+       iproc_nand_waitfunc(mtd, chip);
+
+       if (command == NAND_CMD_ERASE1)
+               iproc_nand_wp(mtd, 1);
+}
+
+static void iproc_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+
+       if (cs < ctrl.max_cs) {
+               debug("%s: cs %d\n", __func__, cs);
+               host->cs = cs;
+       } else {
+               printf(DRV_NAME ": invalid cs %d ignored\n", cs);
+       }
+}
+
+static uint8_t iproc_nand_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+       uint8_t ret = 0;
+
+       switch (host->last_cmd) {
+       case NAND_CMD_READID:
+               if (host->last_byte < 4)
+                       ret = NAND_REG_RD(NCREG_FLASH_DEVICE_ID) >>
+                             (24 - (host->last_byte << 3));
+               else if (host->last_byte < 8)
+                       ret = NAND_REG_RD(NCREG_FLASH_DEVICE_ID_EXT) >>
+                             (56 - (host->last_byte << 3));
+               break;
+
+       case NAND_CMD_READOOB:
+               ret = oob_reg_read(host->last_byte);
+               break;
+
+       case NAND_CMD_STATUS:
+               ret = NAND_REG_RD(NCREG_INTFC_STATUS) &
+                     NCFLD_INTFC_STATUS_FLASH_STATUS_MASK;
+               if (ctrl.wp_mode == WP_SET_BY_DEFAULT) {
+                       /* hide WP status from MTD */
+                       ret |= NAND_STATUS_WP;
+               }
+               break;
+
+       case NAND_CMD_PARAM:
+               if (host->last_byte < FC_BYTES)
+                       ret = NAND_REG_RD(FC(host->last_byte >> 2)) >>
+                             (24 - ((host->last_byte & 0x03) << 3));
+               break;
+       }
+
+       debug("%s: byte = 0x%02x\n", __func__, ret);
+       host->last_byte++;
+
+       return ret;
+}
+
+static void iproc_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++, buf++)
+               *buf = iproc_nand_read_byte(mtd);
+}
+
+static void iproc_nand_cache_read(u32 **buf, u8 **oob, int oob_bytes)
+{
+       int i;
+       u32 w = 0;
+
+       debug("%s buf %p oob %p oob_bytes %d\n", __func__,
+             *buf, *oob, oob_bytes);
+
+       if (likely(*buf)) {
+               NAND_BEGIN_DATA_ACCESS();
+               for (i = 0; i < FC_WORDS; i++, (*buf)++)
+                       **buf = NAND_REG_RD(FC(i));
+               NAND_END_DATA_ACCESS();
+       }
+
+       if (*oob && oob_bytes > 0) {
+               for (i = 0; i < oob_bytes; i++, (*oob)++) {
+                       if ((i & 0x3) == 0)
+                               w = NAND_REG_RD(NCREG_SPARE_AREA_READ_OFS_0 +
+                                               i);
+                       /* APB read is big endian */
+                       **oob = w >> (24 - ((i & 0x03) << 3));
+               }
+       }
+}
+
+/*
+ * This function counts the 0 bits in a sector data and oob bytes.
+ * If the count result is less than a threshold it considers that
+ * the sector is an erased sector and it sets all its data and oob
+ * bits to 1.
+ * The threshold is set to half of the ECC strength to allow for
+ * a sector that also has some bits stuck on 1 to be correctable.
+ */
+static int erased_sector(struct mtd_info *mtd, u8 *buf, u8 *oob,
+                        unsigned int *bitflips)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+       int data_bytes = 512 << host->hwcfg.sector_size_1k;
+       int oob_bytes = host->hwcfg.spare_area_size <<
+                               host->hwcfg.sector_size_1k;
+       /* set the bitflip threshold to half of the BCH ECC strength */
+       int threshold = (host->hwcfg.ecc_level <<
+                               host->hwcfg.sector_size_1k) / 2;
+       int counter = 0;
+       int i;
+
+       debug("%s buf %p oob %p\n", __func__, buf, oob);
+
+       if (host->hwcfg.ecc_code != ECC_CODE_BCH)
+               return 0;
+
+       /* count bitflips in OOB first */
+       for (i = 0; i < oob_bytes; i++) {
+               counter += hweight8(~oob[i]);
+               if (counter > threshold)
+                       return 0;
+       }
+
+       /* count bitflips in data */
+       for (i = 0; i < data_bytes; i++) {
+               counter += hweight8(~buf[i]);
+               if (counter > threshold)
+                       return 0;
+       }
+
+       /* clear data and oob */
+       memset(buf, 0xFF, data_bytes);
+       memset(oob, 0xFF, oob_bytes);
+
+       *bitflips = counter;
+       return 1;
+}
+
+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;
+                               }
+                               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);
+
+       return iproc_nand_read(mtd, chip, host->last_addr,
+                              mtd->writesize >> FC_SHIFT, (u32 *)buf,
+                              (u8 *)chip->oob_poi);
+}
+
+static int iproc_nand_read_page_raw(struct mtd_info *mtd,
+                                   struct nand_chip *chip,
+                                   uint8_t *buf,
+                                   int oob_required,
+                                   int page)
+{
+       struct iproc_nand_host *host = chip->priv;
+       int ret;
+
+       BUG_ON(!buf);
+
+       WR_ACC_CONTROL(host->cs, RD_ECC_EN, 0);
+       ret = iproc_nand_read(mtd, chip, host->last_addr,
+                             mtd->writesize >> FC_SHIFT,
+                             (u32 *)buf,
+                             (u8 *)chip->oob_poi);
+       WR_ACC_CONTROL(host->cs, RD_ECC_EN, 1);
+       return ret;
+}
+
+static int iproc_nand_read_oob(struct mtd_info *mtd,
+                              struct nand_chip *chip, int page)
+{
+       return iproc_nand_read(mtd, chip, (u64)page << chip->page_shift,
+                              mtd->writesize >> FC_SHIFT,
+                              NULL, (u8 *)chip->oob_poi);
+}
+
+static int iproc_nand_read_oob_raw(struct mtd_info *mtd,
+                                  struct nand_chip *chip, int page)
+{
+       struct iproc_nand_host *host = chip->priv;
+       int ret;
+
+       WR_ACC_CONTROL(host->cs, RD_ECC_EN, 0);
+       ret = iproc_nand_read(mtd, chip, (u64)page << chip->page_shift,
+                             mtd->writesize >> FC_SHIFT, NULL,
+                             (u8 *)chip->oob_poi);
+       WR_ACC_CONTROL(host->cs, RD_ECC_EN, 1);
+       return ret;
+}
+
+static void iproc_nand_cache_write(const u32 **buf, u8 **oob, int oob_bytes)
+{
+       int i;
+       u32 w = 0;
+
+       debug("%s buf %p oob %p oob_bytes %d\n", __func__,
+             *buf, *oob, oob_bytes);
+
+       if (*buf) {
+               NAND_BEGIN_DATA_ACCESS();
+               for (i = 0; i < FC_WORDS; i++, (*buf)++)
+                       NAND_REG_WR(FC(i), **buf);
+               NAND_END_DATA_ACCESS();
+       } else {
+               for (i = 0; i < FC_WORDS; i++)
+                       NAND_REG_WR(FC(i), 0xffffffff);
+       }
+
+       if (*oob) {
+               for (i = 0; i < oob_bytes; i++, (*oob)++) {
+                       w <<= 8;
+                       w |= **oob;
+                       if ((i & 0x3) == 0x3)
+                               NAND_REG_WR(NCREG_SPARE_AREA_WRITE_OFS_0 +
+                                           (i & ~0x3), w);
+               } /* fill the remaining OOB bytes with 0xFF */
+               for (i = oob_bytes; i < MAX_CONTROLLER_OOB_BYTES; i++) {
+                       w <<= 8;
+                       w |= 0xFF;
+                       if ((i & 0x3) == 0x3)
+                               NAND_REG_WR(NCREG_SPARE_AREA_WRITE_OFS_0 +
+                                           (i & ~0x3), w);
+               }
+       } else {
+               for (i = 0; i < MAX_CONTROLLER_OOB_WORDS; i++)
+                       NAND_REG_WR(NCREG_SPARE_AREA_WRITE_OFS_0 + (i << 2),
+                                   0xffffffff);
+       }
+}
+
+static int iproc_nand_write(struct mtd_info *mtd,
+                           struct nand_chip *chip, u64 addr,
+                           const u32 *buf, u8 *oob)
+{
+       struct iproc_nand_host *host = chip->priv;
+       unsigned int trans = mtd->writesize >> FC_SHIFT;
+       unsigned int i;
+       int status;
+       int oob_bytes;
+       int ret = 0;
+
+       debug("%s %llx <- %p\n", __func__, (unsigned long long)addr, buf);
+
+       if (unlikely((u32)buf & 0x03)) {
+               printf(DRV_NAME ": unaligned buffer: %p\n", buf);
+               buf = (u32 *)((u32)buf & ~0x03);
+       }
+
+       iproc_nand_wp(mtd, 0);
+
+       NAND_REG_WR(NCREG_CMD_EXT_ADDRESS,
+                   (host->cs << 16) | ((addr >> 32) & 0xffff));
+
+       for (i = 0; i < trans; i++, addr += FC_BYTES) {
+               /* full address MUST be set before populating FC */
+               NAND_REG_WR(NCREG_CMD_ADDRESS, addr & 0xffffffff);
+
+               oob_bytes = 0;
+               if (oob) {
+                       /* 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_write(&buf, &oob, oob_bytes);
+
+               /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
+               iproc_nand_send_cmd(CMD_PROGRAM_PAGE);
+               status = iproc_nand_waitfunc(mtd, chip);
+
+               if (status & NAND_STATUS_FAIL) {
+                       printf(DRV_NAME ": program failed at %llx\n",
+                              (unsigned long long)addr);
+                       ret = -EIO;
+                       break;
+               }
+       }
+       iproc_nand_wp(mtd, 1);
+       return ret;
+}
+
+static int iproc_nand_write_page(struct mtd_info *mtd,
+                                struct nand_chip *chip,
+                                const uint8_t *buf,
+                                int oob_required)
+{
+       struct iproc_nand_host *host = chip->priv;
+       uint8_t const *p, *e;
+
+       BUG_ON(!buf);
+       /*
+        * don't write the page if it contains only FFs
+        * (to avoid generating ECC) since we consider it
+        * as an empty page (data could be written later).
+        */
+       for (p = buf, e = p + mtd->writesize; p < e; p++)
+               if (*p != 0xFF)
+                       break;
+       if (p == e) {
+               if (!oob_required)
+                       return 0;
+               for (p = chip->oob_poi, e = p + mtd->oobsize; p < e; p++)
+                       if (*p != 0xFF)
+                               break;
+               if (p == e)
+                       return 0;
+       }
+
+       return iproc_nand_write(mtd, chip, host->last_addr, (u32 *)buf,
+                               oob_required ? (u8 *)chip->oob_poi : NULL);
+}
+
+static int iproc_nand_write_page_raw(struct mtd_info *mtd,
+                                    struct nand_chip *chip,
+                                    const uint8_t *buf,
+                                    int oob_required)
+{
+       int ret;
+       struct iproc_nand_host *host = chip->priv;
+
+       BUG_ON(!buf);
+
+       WR_ACC_CONTROL(host->cs, WR_ECC_EN, 0);
+       ret = iproc_nand_write(mtd, chip, host->last_addr, (u32 *)buf,
+                              oob_required ? (u8 *)chip->oob_poi : NULL);
+       WR_ACC_CONTROL(host->cs, WR_ECC_EN, 1);
+       return ret;
+}
+
+static int iproc_nand_write_oob(struct mtd_info *mtd,
+                               struct nand_chip *chip, int page)
+{
+       return iproc_nand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
+                               (u8 *)chip->oob_poi);
+}
+
+static int iproc_nand_write_oob_raw(struct mtd_info *mtd,
+                                   struct nand_chip *chip, int page)
+{
+       struct iproc_nand_host *host = chip->priv;
+       int ret;
+
+       WR_ACC_CONTROL(host->cs, WR_ECC_EN, 0);
+       ret = iproc_nand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
+                              (u8 *)chip->oob_poi);
+       WR_ACC_CONTROL(host->cs, WR_ECC_EN, 1);
+       return ret;
+}
+
+static int iproc_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct iproc_nand_host *host = chip->priv;
+       u64 addr = (u64)ofs;
+       u32 w;
+       u8 bad;
+
+       debug("%s %llx\n", __func__, (unsigned long long)ofs);
+
+       NAND_REG_WR(NCREG_CMD_EXT_ADDRESS,
+                   (host->cs << 16) | ((addr >> 32) & 0xffff));
+       NAND_REG_WR(NCREG_CMD_ADDRESS, addr & 0xffffffff);
+       iproc_nand_send_cmd(CMD_PAGE_READ);
+       iproc_nand_waitfunc(mtd, chip);
+
+       /*
+        * force next read to be done from NAND,
+        * to have correct ECC correction status.
+        */
+       ctrl.data_cache_invalid = 1;
+
+       /* APB read is big endian */
+       w = NAND_REG_RD(NCREG_SPARE_AREA_READ_OFS_0 +
+               (chip->badblockpos & ~0x3));
+       bad = w >> (24 - ((chip->badblockpos & 0x03) << 3));
+       if (bad != 0xFF) {
+               debug("%s %llx BAD BLOCK\n", __func__,
+                     (unsigned long long)ofs);
+               return 1;
+       }
+       return 0;
+}
+
+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);
+}
+
+/*
+ * 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;
+               }
+       if (!found)
+               printf(DRV_NAME ": invalid block size %u\n",
+                      cfg->block_size);
+
+       for (i = 0, found = 0; i < ARRAY_SIZE(page_sizes); i++)
+               if (page_sizes[i] == cfg->page_size) {
+                       WR_CONFIG(host->cs, PAGE_SIZE, i);
+                       found = 1;
+               }
+       if (!found)
+               printf(DRV_NAME ": invalid page size %u\n",
+                      cfg->page_size);
+
+       if (fls64(cfg->device_size) < 23)
+               printf(DRV_NAME ": invalid device size 0x%llx\n",
+                      (unsigned long long)cfg->device_size);
+
+       if (cfg->ecc_code == ECC_CODE_BCH) {
+               if ((cfg->ecc_level >
+                    iproc_max_bch_ecc_level[cfg->sector_size_1k]) ||
+                   (iproc_bch_ecc_bytes[cfg->ecc_level] >
+                    cfg->spare_area_size))
+                       printf(DRV_NAME ": invalid BCH ECC configuration: 
%u/%u\n",
+                              cfg->ecc_level << cfg->sector_size_1k,
+                              cfg->sector_size_1k ? 1024 : 512);
+       }
+
+       if (cfg->ecc_code == ECC_CODE_HAMMING) {
+               if (!((cfg->ecc_level == 15) &&
+                     (cfg->spare_area_size == 16) &&
+                     (cfg->sector_size_1k == 0)))
+                       printf(DRV_NAME ": invalid HAMMING ECC 
configuration\n");
+       }
+
+       WR_CONFIG(host->cs, DEVICE_SIZE, fls64(cfg->device_size) - 23);
+       WR_CONFIG(host->cs, DEVICE_WIDTH, cfg->device_width == 16 ? 1 : 0);
+       WR_CONFIG(host->cs, COL_ADR_BYTES, cfg->col_adr_bytes);
+       WR_CONFIG(host->cs, BLK_ADR_BYTES, cfg->blk_adr_bytes);
+       WR_CONFIG(host->cs, FUL_ADR_BYTES, cfg->ful_adr_bytes);
+
+       WR_ACC_CONTROL(host->cs, SPARE_AREA_SIZE, cfg->spare_area_size);
+       WR_ACC_CONTROL(host->cs, SECTOR_SIZE_1K, cfg->sector_size_1k);
+
+       WR_ACC_CONTROL(host->cs, ECC_LEVEL, cfg->ecc_level);
+}
+
+static void iproc_nand_get_cfg(struct iproc_nand_host *host,
+                              struct iproc_nand_cfg *cfg)
+{
+       cfg->block_size = RD_CONFIG(host->cs, BLOCK_SIZE);
+       cfg->device_size = (4ULL << 20) << RD_CONFIG(host->cs, DEVICE_SIZE);
+       cfg->page_size = RD_CONFIG(host->cs, PAGE_SIZE);
+       cfg->device_width = RD_CONFIG(host->cs, DEVICE_WIDTH) ? 16 : 8;
+       cfg->col_adr_bytes = RD_CONFIG(host->cs, COL_ADR_BYTES);
+       cfg->blk_adr_bytes = RD_CONFIG(host->cs, BLK_ADR_BYTES);
+       cfg->ful_adr_bytes = RD_CONFIG(host->cs, FUL_ADR_BYTES);
+       cfg->spare_area_size = RD_ACC_CONTROL(host->cs, SPARE_AREA_SIZE);
+       cfg->sector_size_1k = RD_ACC_CONTROL(host->cs, SECTOR_SIZE_1K);
+       cfg->ecc_level = RD_ACC_CONTROL(host->cs, ECC_LEVEL);
+
+       if (cfg->block_size < ARRAY_SIZE(block_sizes))
+               cfg->block_size = block_sizes[cfg->block_size] << 10;
+       else
+               cfg->block_size = 128 << 10;
+
+       if (cfg->page_size < ARRAY_SIZE(page_sizes))
+               cfg->page_size = page_sizes[cfg->page_size];
+       else
+               cfg->page_size = 2048;
+
+       /* special case: using Hamming code */
+       if ((cfg->ecc_level == 15) && (cfg->spare_area_size == 16) &&
+           (cfg->sector_size_1k == 0))
+               cfg->ecc_code = ECC_CODE_HAMMING;
+       else
+               cfg->ecc_code = ECC_CODE_BCH;
+}
+
+static void iproc_nand_print_cfg(struct iproc_nand_cfg *cfg)
+{
+       printf("NAND %u-bit %lluMiB total, %uKiB blocks, %u%s pages\n"
+              "     %ubit/%uB %s-ECC %uB/512B OOB\n",
+              cfg->device_width,
+              (unsigned long long)cfg->device_size >> 20,
+              cfg->block_size >> 10,
+              cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
+              cfg->page_size >= 1024 ? "KiB" : "B",
+              cfg->ecc_code == ECC_CODE_HAMMING ?
+                               1 : cfg->ecc_level << cfg->sector_size_1k,
+              512 << cfg->sector_size_1k,
+              cfg->ecc_code == ECC_CODE_HAMMING ? "HAMMING" : "BCH",
+              cfg->spare_area_size);
+}
+
+static int iproc_nand_setup_dev(struct iproc_nand_host *host)
+{
+       struct mtd_info *mtd = host->mtd;
+       struct nand_chip *chip = &host->chip;
+       struct iproc_nand_cfg orig_cfg, new_cfg;
+       struct nand_oobfree *free = iproc_nand_oob_layout.oobfree;
+
+       uint8_t steps;
+       uint8_t eccbytes;
+       uint8_t eccstrength;
+       int threshold;
+
+       iproc_nand_get_cfg(host, &orig_cfg);
+       host->hwcfg = orig_cfg;
+
+       memset(&new_cfg, 0, sizeof(new_cfg));
+       new_cfg.device_size = mtd->size;
+       new_cfg.block_size = mtd->erasesize;
+       new_cfg.page_size = mtd->writesize;
+       new_cfg.spare_area_size = mtd->oobsize / (mtd->writesize >> FC_SHIFT);
+       new_cfg.device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
+       new_cfg.col_adr_bytes = 2;
+
+       if (mtd->writesize > 512)
+               if (mtd->size >= (256 << 20))
+                       new_cfg.blk_adr_bytes = 3;
+               else
+                       new_cfg.blk_adr_bytes = 2;
+       else if (mtd->size >= (64 << 20))
+               new_cfg.blk_adr_bytes = 3;
+       else
+               new_cfg.blk_adr_bytes = 2;
+       new_cfg.ful_adr_bytes = new_cfg.blk_adr_bytes + new_cfg.col_adr_bytes;
+
+       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");
+               }
+       }
+
+       /* decide ECC settings ourselves if it's not initialized before */
+       if (!ctrl.auto_inited) {
+               printf(DRV_NAME " straps: page 0x%x type 0x%x\n",
+                      ctrl.strap_page_size, ctrl.strap_type);
+
+               /* check if strap settings are valid */
+               if (ctrl.strap_type > 0 &&
+                   ctrl.data->strap_page_sizes[ctrl.strap_page_size] ==
+                   new_cfg.page_size &&
+                   ctrl.data->strap_types[ctrl.strap_type].spare_size <=
+                   new_cfg.spare_area_size) {
+                       /* it's valid, follow the strap settings */
+                       new_cfg.spare_area_size =
+                           ctrl.data->strap_types[ctrl.strap_type].spare_size;
+                       new_cfg.sector_size_1k =
+                           ctrl.data->strap_types[ctrl.strap_type].sector_1k;
+                       new_cfg.ecc_level =
+                           ctrl.data->strap_types[ctrl.strap_type].ecc_level;
+                       if (ctrl.strap_page_size == 0) {
+                               new_cfg.blk_adr_bytes = 2;
+                               new_cfg.ful_adr_bytes = 4;
+                       } else {
+                               new_cfg.blk_adr_bytes = 3;
+                               new_cfg.ful_adr_bytes = 5;
+                       }
+
+                       /* special case: using Hamming code */
+                       if ((new_cfg.ecc_level == 15) &&
+                           (new_cfg.spare_area_size == 16) &&
+                           (new_cfg.sector_size_1k == 0))
+                               new_cfg.ecc_code = ECC_CODE_HAMMING;
+                       else
+                               new_cfg.ecc_code = ECC_CODE_BCH;
+
+                       printf(DRV_NAME ": following strap settings\n");
+
+               } else {
+                       /*
+                        * strap settings are not valid,
+                        * decide the settings on our own
+                        */
+
+                       /* trying to fit with available strap settings */
+                       new_cfg.spare_area_size =
+                           new_cfg.spare_area_size >= 27 ? 27 : 16;
+                       new_cfg.sector_size_1k = 0;
+                       new_cfg.ecc_code = ECC_CODE_BCH;
+                       if (new_cfg.spare_area_size == 27) {
+                               new_cfg.ecc_level = 12;
+                               new_cfg.sector_size_1k =
+                                   (new_cfg.page_size >= 2048) ? 1 : 0;
+                       } else if (chip->badblockpos ==
+                                  NAND_SMALL_BADBLOCK_POS) {
+                               new_cfg.ecc_level = 4;
+                       } else {
+                               new_cfg.ecc_level = 8;
+                       }
+
+                       printf(DRV_NAME ": overriding invalid strap 
settings\n");
+               }
+
+               iproc_nand_set_cfg(host, &new_cfg);
+               host->hwcfg = new_cfg;
+       }
+
+       iproc_nand_print_cfg(&new_cfg);
+
+       WR_ACC_CONTROL(host->cs, RD_ECC_EN, 1);
+       WR_ACC_CONTROL(host->cs, WR_ECC_EN, 1);
+       WR_ACC_CONTROL(host->cs, FAST_PGM_RDIN, 0);
+       WR_ACC_CONTROL(host->cs, RD_ERASED_ECC_EN, 0);
+       WR_ACC_CONTROL(host->cs, PARTIAL_PAGE_EN, 0);
+       WR_ACC_CONTROL(host->cs, PAGE_HIT_EN, 1);
+
+       if (new_cfg.ecc_code == ECC_CODE_BCH) {
+               /* threshold = ceil(ECC-strength * percentage) */
+               threshold = ((new_cfg.ecc_level << new_cfg.sector_size_1k) *
+                            ctrl.corr_threshold_percent + 99) / 100;
+               WR_CORR_THRESH(host->cs, threshold);
+               printf(DRV_NAME
+                      ": ECC correction status threshold set to %d bit\n",
+                      threshold);
+       } else {
+               WR_CORR_THRESH(host->cs, 1);
+       }
+
+       /* adjust MTD oobsize according to the configuration */
+       mtd->oobsize = new_cfg.spare_area_size * (mtd->writesize >> FC_SHIFT);
+
+       /* adjust ECC layout for storing usb OOB data */
+       free->length = 0;
+       steps = mtd->writesize >> FC_SHIFT;
+       if (new_cfg.ecc_code == ECC_CODE_HAMMING) {
+               eccbytes = 3;
+               eccstrength = 1;
+       } else {
+               eccbytes = iproc_bch_ecc_bytes[new_cfg.ecc_level];
+               eccstrength = new_cfg.ecc_level << new_cfg.sector_size_1k;
+       }
+
+       /*
+        * these are not really used;
+        * we still prepare them for safety
+        */
+       iproc_nand_oob_layout.eccbytes = eccbytes * steps;
+       chip->ecc.bytes = eccbytes;
+       chip->ecc.strength = eccstrength;
+       chip->ecc.size = 512 << new_cfg.sector_size_1k;
+
+       /* create oobfree for storing user OOB data */
+       if (new_cfg.spare_area_size > eccbytes) {
+               unsigned int spare_size;
+               uint8_t i, cnt;
+
+               spare_size =
+                   new_cfg.spare_area_size << new_cfg.sector_size_1k;
+               eccbytes <<= new_cfg.sector_size_1k;
+               steps >>= new_cfg.sector_size_1k;
+               if (steps > MTD_MAX_OOBFREE_ENTRIES_LARGE)
+                       steps = MTD_MAX_OOBFREE_ENTRIES_LARGE;
+               for (i = 0, cnt = 0;
+                    i < steps && cnt < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++) {
+                       if (new_cfg.ecc_code == ECC_CODE_HAMMING) {
+                               /*
+                                * Hamming code: ECC bytes are 6~8;
+                                * first part here
+                                */
+                               free->offset = i * spare_size;
+                               free->length = 6;
+
+                       } else {
+                               /* BCH: ECC bytes at the bottom */
+                               free->offset = i * spare_size;
+                               free->length = spare_size - eccbytes;
+                       }
+
+                       /* reserve the first two bytes of the page */
+                       if (i == 0) {
+                               if (free->length <= 2) {
+                                       /*
+                                        * don't claim this entry if
+                                        * less than 2 bytes
+                                        */
+                                       continue;
+                               }
+                               free->offset += 2;
+                               free->length -= 2;
+                       }
+
+                       if (new_cfg.ecc_code == ECC_CODE_HAMMING) {
+                               /* Hamming code: the 2nd free part */
+                               free++;
+                               cnt++;
+                               if (cnt < MTD_MAX_OOBFREE_ENTRIES_LARGE) {
+                                       free->offset =
+                                           i * spare_size + 9;
+                                       free->length = 7;
+                               } else {
+                                       /* the structure limits us */
+                                       break;
+                               }
+                       }
+
+                       free++;
+                       cnt++;
+               }
+               if (cnt < MTD_MAX_OOBFREE_ENTRIES_LARGE) {
+                       /* terminator */
+                       free->length = 0;
+               }
+
+               /* print out oob space information */
+               free = iproc_nand_oob_layout.oobfree;
+               if (free->length) {
+                       spare_size = 0;
+                       while (free->length) {
+                               spare_size += free->length;
+                               free++;
+                       }
+                       printf(DRV_NAME ": user oob per page: %u bytes (%u 
steps)\n",
+                              spare_size, (int)steps);
+               }
+       }
+
+       if (iproc_nand_oob_layout.oobfree[0].length == 0)
+               printf(DRV_NAME ": no oob space available\n");
+
+       return 0;
+}
+
+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));
+               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);
+}
+
+static void iproc_nand_ctrl_setup(void)
+{
+#ifdef CONFIG_IPROC_NAND_AUTO_INIT
+       int timeout;
+#endif
+
+       ctrl.cmd_pending = 0;
+       ctrl.auto_inited = 0;
+
+       /* initialize SoC specific data */
+       ctrl.data = &soc_nand_ctrl_data;
+
+       /* initialize register addresses */
+       ctrl.nand_regs = (void *)REG_NAND_BASE;
+       ctrl.nand_intr_regs = ctrl.nand_regs + NCREG_INTERRUPT_BASE;
+       ctrl.nand_idm_regs = (void *)REG_NAND_IDM_BASE;
+       ctrl.nand_idm_io_ctrl_direct_reg = ctrl.nand_idm_regs +
+                                               IDMREG_IO_CONTROL_DIRECT;
+#ifdef REG_NAND_STRAPS_BASE
+       ctrl.nand_strap_regs = (void *)REG_NAND_STRAPS_BASE;
+#else
+       ctrl.nand_strap_regs = ctrl.nand_idm_regs + IDMREG_IO_STATUS;
+#endif
+
+       /* get strap values */
+       ctrl.strap_type = NAND_STRAP_TYPE;
+       ctrl.strap_page_size = NAND_STRAP_PAGE;
+
+#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
+
+       /*
+        * configure the percentage of the BCH ECC correction capability
+        * above which correctable errors will be reported.
+        */
+#ifdef CONFIG_IPROC_NAND_CORR_THRESHOLD_PERCENT
+#if ((CONFIG_IPROC_NAND_CORR_THRESHOLD_PERCENT < 0) || \
+       (CONFIG_IPROC_NAND_CORR_THRESHOLD_PERCENT > 100))
+#error "Invalid CONFIG_IPROC_NAND_CORR_THRESHOLD_PERCENT value"
+#endif
+       ctrl.corr_threshold_percent = CONFIG_IPROC_NAND_CORR_THRESHOLD_PERCENT;
+#else
+       ctrl.corr_threshold_percent = 60; /* 60% of ECC strength by default */
+#endif
+
+       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);
+
+#ifdef CONFIG_IPROC_NAND_AUTO_INIT
+       /* reset the NAND controller */
+       writel(1, ctrl.nand_idm_regs + IDMREG_RESET_CONTROL);
+       __udelay(1);
+       writel(0, ctrl.nand_idm_regs + IDMREG_RESET_CONTROL);
+       __udelay(10);
+
+       /* execute controller auto-init */
+       NAND_REG_CLR(NCREG_CS_NAND_SELECT,
+                    NCFLD_CS_NAND_SELECT_AUTO_DEVID_CONFIG);
+       NAND_REG_SET(NCREG_CS_NAND_SELECT,
+                    NCFLD_CS_NAND_SELECT_AUTO_DEVID_CONFIG);
+       timeout = 100000;
+       ctrl.auto_inited = 1;
+       while (timeout > 0) {
+               __udelay(10);
+               if (NCFLD_INIT_STATUS_INIT_SUCCESS &
+                   NAND_REG_RD(NCREG_INIT_STATUS)) {
+                       printf(DRV_NAME ": auto-init success\n");
+                       break;
+               }
+               timeout -= 10;
+       }
+       if (timeout <= 0) {
+               ctrl.auto_inited = 0;
+               printf(DRV_NAME ": auto-init failed\n");
+       }
+       debug("%s: auto-init status 0x%x\n", __func__,
+             NAND_REG_RD(NCREG_INIT_STATUS));
+#else
+       /* check if auto-init was done due to strap_nand_flash */
+       if (NAND_REG_RD(NCREG_CS_NAND_SELECT) &
+           NCFLD_CS_NAND_SELECT_AUTO_DEVID_CONFIG)
+               ctrl.auto_inited = 1;
+       /* check if auto-init was done by previous boot stage */
+       if (NAND_REG_RD(NCREG_SEMAPHORE) == 0xFF)
+               ctrl.auto_inited = 1;
+#endif
+
+       /* perform basic controller initialization */
+       NAND_REG_CLR(NCREG_CS_NAND_SELECT,
+                    NCFLD_CS_NAND_SELECT_AUTO_DEVID_CONFIG);
+       NAND_REG_CLR(NCREG_CS_NAND_SELECT,
+                    NCFLD_CS_NAND_SELECT_DIRECT_ACCESS_CS_MASK);
+       NAND_REG_CLR(NCREG_CS_NAND_XOR, NCFLD_CS_NAND_XOR_CS_MASK);
+
+       if (ctrl.wp_mode == WP_ALWAYS_CLEARED) {
+               /* permanently remove write-protection */
+               NAND_REG_CLR(NCREG_CS_NAND_SELECT, NCFLD_CS_NAND_SELECT_WP);
+       }
+
+       /* clear IRQ */
+       NAND_ACK_IRQ(NCINTR_CTLRDY);
+
+       return;
+}
+
+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;
+
+       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;
+
+       iproc_nand_timing_setup(host);
+
+       if (iproc_nand_setup_dev(host) || nand_scan_tail(mtd))
+               return -ENXIO;
+
+       nand_register(0);
+
+       /* set semaphore to indicate initializatin done */
+       NAND_REG_WR(NCREG_SEMAPHORE, 0xFF);
+
+       printf(DRV_NAME ": NAND controller driver is loaded\n");
+       return 0;
+}
+
+void board_nand_init(void)
+{
+       printf(DRV_NAME " controller\n");
+       iproc_nand_ctrl_setup();
+       if (iproc_nand_init())
+               printf(DRV_NAME ": NAND initialization failed\n");
+       else
+               printf("NAND:  ");      /* size will be filled in by u-boot */
+}
diff --git a/drivers/mtd/nand/iproc_nand_cygnus.h 
b/drivers/mtd/nand/iproc_nand_cygnus.h
new file mode 100644
index 0000000..26a00a9
--- /dev/null
+++ b/drivers/mtd/nand/iproc_nand_cygnus.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _IPROC_NAND_CYGNUS_H_
+#define _IPROC_NAND_CYGNUS_H_
+
+/*
+ * SoC specific definitions (Cygnus)
+ */
+
+#define REG_NAND_BASE                          0x18046000
+#define REG_NAND_IDM_BASE                      0xf8105000
+
+#define NAND_STRAP_TYPE_MASK                   0x000f0000
+#define NAND_STRAP_TYPE_SHIFT                  16
+#define NAND_STRAP_PAGE_MASK                   0x00300000
+#define NAND_STRAP_PAGE_SHIFT                  20
+#define NAND_STRAP_WIDTH_MASK                  0x01000000
+#define NAND_STRAP_WIDTH_SHIFT                 24
+
+#define NAND_STRAP_TYPE_DATA                   \
+/* sector_1k, ecclevel, spare_size */          \
+{                                              \
+       { 0,  0, 16 },                          \
+       { 0,  1, 16 },                          \
+       { 0,  4, 16 },                          \
+       { 0,  8, 16 },                          \
+       { 0,  8, 27 },                          \
+       { 0, 12, 27 },                          \
+       { 1, 12, 27 },                          \
+       { 1, 15, 27 },                          \
+       { 1, 20, 45 }                           \
+}
+
+#define NAND_STRAP_PAGE_DATA                   \
+{                                              \
+       1024, 2048, 4096, 8192                  \
+}
+
+/*
+ * iProc NAND timing configurations for ONFI timing modes [0-5]
+ *
+ * Clock tick = 10ns
+ * Multiplier:
+ * x1: tWP tWH tRP tREH tCLH tALH
+ * x2: tCS tADL tWB tWHR
+ */
+#define NAND_TIMING_DATA                                       \
+{                                                              \
+       /* ONFI timing mode 0 :                         */      \
+       /* tWC=100ns tWP=50ns tWH=30ns                  */      \
+       /* tRC=100ns tRP=50ns tREH=30ns                 */      \
+       /* tCS=70ns tCLH=20ns tALH=20ns tADL=200ns      */      \
+       /* tWB=200ns tWHR=120ns tREA=40ns               */      \
+       {                                                       \
+               .timing1 = 0x6565435b,                          \
+               .timing2 = 0x00001e85,                          \
+       },                                                      \
+       /* ONFI timing mode 1 :                         */      \
+       /* tWC=45 tWP=25ns tWH=15ns                     */      \
+       /* tRC=50 tRP=25ns tREH=15ns                    */      \
+       /* tCS=35ns tCLH=10ns tALH=10ns tADL=100ns      */      \
+       /* tWB=100ns tWHR=80ns tREA=30ns                */      \
+       {                                                       \
+               .timing1 = 0x33333236,                          \
+               .timing2 = 0x00001064,                          \
+       },                                                      \
+       /* ONFI timing mode 2 :                         */      \
+       /* tWC=35ns tWP=17ns tWH=15ns                   */      \
+       /* tRC=35ns tRP=17ns tREH=15ns                  */      \
+       /* tCS=25ns tCLH=10ns tALH=10ns tADL=100ns      */      \
+       /* tWB=100ns tWHR=80ns tREA=25ns                */      \
+       {                                                       \
+               .timing1 = 0x32322226,                          \
+               .timing2 = 0x00001063,                          \
+       },                                                      \
+       /* ONFI timing mode 3 :                         */      \
+       /* tWC=30ns tWP=15ns tWH=10ns                   */      \
+       /* tRC=30ns tRP=15ns tREH=10ns                  */      \
+       /* tCS=25ns tCLH=5ns tALH=5ns tADL=100ns        */      \
+       /* tWB=100ns tWHR=60ns tREA=20ns                */      \
+       {                                                       \
+               .timing1 = 0x22222126,                          \
+               .timing2 = 0x00001043,                          \
+       },                                                      \
+       /* ONFI timing mode 4 :                         */      \
+       /* tWC=25ns tWP=12ns tWH=10ns                   */      \
+       /* tRC=25ns tRP=12ns tREH=10ns                  */      \
+       /* tCS=20ns tCLH=5ns tALH=5ns tADL=70ns         */      \
+       /* tWB=100ns tWHR=60ns tREA=20ns                */      \
+       {                                                       \
+               .timing1 = 0x21212114,                          \
+               .timing2 = 0x00001042,                          \
+       },                                                      \
+       /* ONFI timing mode 5 :                         */      \
+       /* tWC=20ns tWP=10ns tWH=7ns                    */      \
+       /* tRC=20ns tRP=10ns tREH=7ns                   */      \
+       /* tCS=15ns tCLH=5ns tALH=5ns tADL=70ns         */      \
+       /* tWB=100ns tWHR=60ns tREA=16ns                */      \
+       {                                                       \
+               .timing1 = 0x11111114,                          \
+               .timing2 = 0x00001042,                          \
+       },                                                      \
+}
+
+#define NAND_MAX_CS    2
+
+#endif /* _IPROC_NAND_CYGNUS_H_ */
diff --git a/drivers/mtd/nand/iproc_nand_ns_plus.h 
b/drivers/mtd/nand/iproc_nand_ns_plus.h
new file mode 100644
index 0000000..41d5da7
--- /dev/null
+++ b/drivers/mtd/nand/iproc_nand_ns_plus.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2015 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _IPROC_NAND_NS_PLUS_H_
+#define _IPROC_NAND_NS_PLUS_H_
+
+/*
+ * SoC specific definitions (NorthStar Plus)
+ */
+
+#define REG_NAND_BASE                          0x18026000
+#define REG_NAND_IDM_BASE                      0x1811b000
+#define REG_NAND_STRAPS_BASE                   0x1803f2a0
+
+#define NAND_STRAP_TYPE_MASK                   0x0000f000
+#define NAND_STRAP_TYPE_SHIFT                  12
+#define NAND_STRAP_PAGE_MASK                   0x00000c00
+#define NAND_STRAP_PAGE_SHIFT                  10
+/* No bus width strap */
+#define NAND_STRAP_WIDTH_MASK                  0x0
+#define NAND_STRAP_WIDTH_SHIFT                 0
+
+#define NAND_STRAP_TYPE_DATA                   \
+/* sector_1k, ecclevel, spare_size */          \
+{                                              \
+       { 0,  0, 16 },                          \
+       { 0, 15, 16 }, /* Hamming ECC */        \
+       { 0,  4, 16 },                          \
+       { 0,  8, 16 },                          \
+       { 0,  8, 27 },                          \
+       { 0, 12, 27 },                          \
+       { 1, 12, 27 },                          \
+       { 1, 15, 27 },                          \
+       { 1, 20, 45 }                           \
+}
+
+#define NAND_STRAP_PAGE_DATA                   \
+{                                              \
+       2048, 2048, 4096, 8192                  \
+}
+
+/*
+ * iProc NAND timing configurations for ONFI timing modes [0-5]
+ *
+ * Clock tick = 4ns
+ * Multiplier:
+ * x1: tWP tWH tRP tREH tCLH tALH
+ * x4: tCS tADL tWB tWHR
+ */
+#define NAND_TIMING_DATA                                       \
+{                                                              \
+       /* ONFI timing mode 0 :                         */      \
+       /* tWC=100ns tWP=50ns tWH=30ns                  */      \
+       /* tRC=100ns tRP=50ns tREH=30ns                 */      \
+       /* tCS=70ns tCLH=20ns tALH=20ns tADL=200ns      */      \
+       /* tWB=200ns tWHR=120ns tREA=40ns               */      \
+       {                                                       \
+               .timing1 = 0xfafa558d,                          \
+               .timing2 = 0x00001a85,                          \
+       },                                                      \
+       /* ONFI timing mode 1 :                         */      \
+       /* tWC=45 tWP=25ns tWH=15ns                     */      \
+       /* tRC=50 tRP=25ns tREH=15ns                    */      \
+       /* tCS=35ns tCLH=10ns tALH=10ns tADL=100ns      */      \
+       /* tWB=100ns tWHR=80ns tREA=30ns                */      \
+       {                                                       \
+               .timing1 = 0x85853347,                          \
+               .timing2 = 0x00000e64,                          \
+       },                                                      \
+       /* ONFI timing mode 2 :                         */      \
+       /* tWC=35ns tWP=17ns tWH=15ns                   */      \
+       /* tRC=35ns tRP=17ns tREH=15ns                  */      \
+       /* tCS=25ns tCLH=10ns tALH=10ns tADL=100ns      */      \
+       /* tWB=100ns tWHR=80ns tREA=25ns                */      \
+       {                                                       \
+               .timing1 = 0x54542347,                          \
+               .timing2 = 0x00000e63,                          \
+       },                                                      \
+       /* ONFI timing mode 3 :                         */      \
+       /* tWC=30ns tWP=15ns tWH=10ns                   */      \
+       /* tRC=30ns tRP=15ns tREH=10ns                  */      \
+       /* tCS=25ns tCLH=5ns tALH=5ns tADL=100ns        */      \
+       /* tWB=100ns tWHR=60ns tREA=20ns                */      \
+       {                                                       \
+               .timing1 = 0x44442237,                          \
+               .timing2 = 0x00000e43,                          \
+       },                                                      \
+       /* ONFI timing mode 4 :                         */      \
+       /* tWC=25ns tWP=12ns tWH=10ns                   */      \
+       /* tRC=25ns tRP=12ns tREH=10ns                  */      \
+       /* tCS=20ns tCLH=5ns tALH=5ns tADL=70ns         */      \
+       /* tWB=100ns tWHR=60ns tREA=20ns                */      \
+       {                                                       \
+               .timing1 = 0x43432235,                          \
+               .timing2 = 0x00000e42,                          \
+       },                                                      \
+       /* ONFI timing mode 5 :                         */      \
+       /* tWC=20ns tWP=10ns tWH=7ns                    */      \
+       /* tRC=20ns tRP=10ns tREH=7ns                   */      \
+       /* tCS=15ns tCLH=5ns tALH=5ns tADL=70ns         */      \
+       /* tWB=100ns tWHR=60ns tREA=16ns                */      \
+       {                                                       \
+               .timing1 = 0x32321225,                          \
+               .timing2 = 0x00000e42,                          \
+       },                                                      \
+}
+
+#define NAND_MAX_CS    1
+
+#endif /* _IPROC_NAND_NS_PLUS_H_ */
-- 
1.8.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to