[U-Boot] [PATCH] arm: iproc: add NAND driver
Steve Rae
srae at broadcom.com
Sun Aug 9 02:58:04 CEST 2015
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
/* 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
More information about the U-Boot
mailing list