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