summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/raw')
-rw-r--r--drivers/mtd/nand/raw/Kconfig297
-rw-r--r--drivers/mtd/nand/raw/Makefile77
-rw-r--r--drivers/mtd/nand/raw/am335x_spl_bch.c225
-rw-r--r--drivers/mtd/nand/raw/arasan_nfc.c1270
-rw-r--r--drivers/mtd/nand/raw/atmel_nand.c1511
-rw-r--r--drivers/mtd/nand/raw/atmel_nand_ecc.h203
-rw-r--r--drivers/mtd/nand/raw/davinci_nand.c833
-rw-r--r--drivers/mtd/nand/raw/denali.c1371
-rw-r--r--drivers/mtd/nand/raw/denali.h325
-rw-r--r--drivers/mtd/nand/raw/denali_dt.c122
-rw-r--r--drivers/mtd/nand/raw/denali_spl.c228
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_nand.c810
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_spl.c167
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c1064
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_spl.c306
-rw-r--r--drivers/mtd/nand/raw/fsl_upm.c184
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c518
-rw-r--r--drivers/mtd/nand/raw/kb9202_nand.c133
-rw-r--r--drivers/mtd/nand/raw/kirkwood_nand.c91
-rw-r--r--drivers/mtd/nand/raw/kmeter1_nand.c122
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_nand_mlc.c761
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_nand_slc.c597
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c1307
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.h208
-rw-r--r--drivers/mtd/nand/raw/mxc_nand_spl.c350
-rw-r--r--drivers/mtd/nand/raw/mxs_nand.c1302
-rw-r--r--drivers/mtd/nand/raw/mxs_nand.h73
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_dt.c94
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_spl.c264
-rw-r--r--drivers/mtd/nand/raw/nand.c175
-rw-r--r--drivers/mtd/nand/raw/nand_base.c4619
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c1373
-rw-r--r--drivers/mtd/nand/raw/nand_bch.c231
-rw-r--r--drivers/mtd/nand/raw/nand_ecc.c174
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c209
-rw-r--r--drivers/mtd/nand/raw/nand_plat.c64
-rw-r--r--drivers/mtd/nand/raw/nand_spl_load.c41
-rw-r--r--drivers/mtd/nand/raw/nand_spl_loaders.c104
-rw-r--r--drivers/mtd/nand/raw/nand_spl_simple.c240
-rw-r--r--drivers/mtd/nand/raw/nand_timings.c334
-rw-r--r--drivers/mtd/nand/raw/nand_util.c904
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c193
-rw-r--r--drivers/mtd/nand/raw/omap_gpmc.c1037
-rw-r--r--drivers/mtd/nand/raw/pxa3xx_nand.c1828
-rw-r--r--drivers/mtd/nand/raw/pxa3xx_nand.h64
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c1850
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand_spl.c548
-rw-r--r--drivers/mtd/nand/raw/tegra_nand.c1002
-rw-r--r--drivers/mtd/nand/raw/tegra_nand.h240
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c768
-rw-r--r--drivers/mtd/nand/raw/zynq_nand.c1254
51 files changed, 32065 insertions, 0 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
new file mode 100644
index 0000000000..008f7b4b4b
--- /dev/null
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -0,0 +1,297 @@
+
+menuconfig NAND
+ bool "Raw NAND Device Support"
+if NAND
+
+config SYS_NAND_SELF_INIT
+ bool
+ help
+ This option, if enabled, provides more flexible and linux-like
+ NAND initialization process.
+
+config NAND_ATMEL
+ bool "Support Atmel NAND controller"
+ imply SYS_NAND_USE_FLASH_BBT
+ help
+ Enable this driver for NAND flash platforms using an Atmel NAND
+ controller.
+
+config NAND_DAVINCI
+ bool "Support TI Davinci NAND controller"
+ help
+ Enable this driver for NAND flash controllers available in TI Davinci
+ and Keystone2 platforms
+
+config NAND_DENALI
+ bool
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+
+config NAND_DENALI_DT
+ bool "Support Denali NAND controller as a DT device"
+ select NAND_DENALI
+ depends on OF_CONTROL && DM
+ help
+ Enable the driver for NAND flash on platforms using a Denali NAND
+ controller as a DT device.
+
+config NAND_DENALI_SPARE_AREA_SKIP_BYTES
+ int "Number of bytes skipped in OOB area"
+ depends on NAND_DENALI
+ range 0 63
+ help
+ This option specifies the number of bytes to skip from the beginning
+ of OOB area before last ECC sector data starts. This is potentially
+ used to preserve the bad block marker in the OOB area.
+
+config NAND_LPC32XX_SLC
+ bool "Support LPC32XX_SLC controller"
+ help
+ Enable the LPC32XX SLC NAND controller.
+
+config NAND_OMAP_GPMC
+ bool "Support OMAP GPMC NAND controller"
+ depends on ARCH_OMAP2PLUS
+ help
+ Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms.
+ GPMC controller is used for parallel NAND flash devices, and can
+ do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8
+ and BCH16 ECC algorithms.
+
+config NAND_OMAP_GPMC_PREFETCH
+ bool "Enable GPMC Prefetch"
+ depends on NAND_OMAP_GPMC
+ default y
+ help
+ On OMAP platforms that use the GPMC controller
+ (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that
+ uses the prefetch mode to speed up read operations.
+
+config NAND_OMAP_ELM
+ bool "Enable ELM driver for OMAPxx and AMxx platforms."
+ depends on NAND_OMAP_GPMC && !OMAP34XX
+ help
+ ELM controller is used for ECC error detection (not ECC calculation)
+ of BCH4, BCH8 and BCH16 ECC algorithms.
+ Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine,
+ thus such SoC platforms need to depend on software library for ECC error
+ detection. However ECC calculation on such plaforms would still be
+ done by GPMC controller.
+
+config NAND_VF610_NFC
+ bool "Support for Freescale NFC for VF610"
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ help
+ Enables support for NAND Flash Controller on some Freescale
+ processors like the VF610, MCF54418 or Kinetis K70.
+ The driver supports a maximum 2k page size. The driver
+ currently does not support hardware ECC.
+
+choice
+ prompt "Hardware ECC strength"
+ depends on NAND_VF610_NFC
+ default SYS_NAND_VF610_NFC_45_ECC_BYTES
+ help
+ Select the ECC strength used in the hardware BCH ECC block.
+
+config SYS_NAND_VF610_NFC_45_ECC_BYTES
+ bool "24-error correction (45 ECC bytes)"
+
+config SYS_NAND_VF610_NFC_60_ECC_BYTES
+ bool "32-error correction (60 ECC bytes)"
+
+endchoice
+
+config NAND_PXA3XX
+ bool "Support for NAND on PXA3xx and Armada 370/XP/38x"
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ help
+ This enables the driver for the NAND flash device found on
+ PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
+
+config NAND_SUNXI
+ bool "Support for NAND on Allwinner SoCs"
+ default ARCH_SUNXI
+ depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I
+ select SYS_NAND_SELF_INIT
+ select SYS_NAND_U_BOOT_LOCATIONS
+ select SPL_NAND_SUPPORT
+ imply CMD_NAND
+ ---help---
+ Enable support for NAND. This option enables the standard and
+ SPL drivers.
+ The SPL driver only supports reading from the NAND using DMA
+ transfers.
+
+if NAND_SUNXI
+
+config NAND_SUNXI_SPL_ECC_STRENGTH
+ int "Allwinner NAND SPL ECC Strength"
+ default 64
+
+config NAND_SUNXI_SPL_ECC_SIZE
+ int "Allwinner NAND SPL ECC Step Size"
+ default 1024
+
+config NAND_SUNXI_SPL_USABLE_PAGE_SIZE
+ int "Allwinner NAND SPL Usable Page Size"
+ default 1024
+
+endif
+
+config NAND_ARASAN
+ bool "Configure Arasan Nand"
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ help
+ This enables Nand driver support for Arasan nand flash
+ controller. This uses the hardware ECC for read and
+ write operations.
+
+config NAND_MXC
+ bool "MXC NAND support"
+ depends on CPU_ARM926EJS || CPU_ARM1136 || MX5
+ imply CMD_NAND
+ help
+ This enables the NAND driver for the NAND flash controller on the
+ i.MX27 / i.MX31 / i.MX5 rocessors.
+
+config NAND_MXS
+ bool "MXS NAND support"
+ depends on MX23 || MX28 || MX6 || MX7
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ select APBH_DMA
+ select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7
+ select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7
+ help
+ This enables NAND driver for the NAND flash controller on the
+ MXS processors.
+
+if NAND_MXS
+
+config NAND_MXS_DT
+ bool "Support MXS NAND controller as a DT device"
+ depends on OF_CONTROL && MTD
+ help
+ Enable the driver for MXS NAND flash on platforms using
+ device tree.
+
+config NAND_MXS_USE_MINIMUM_ECC
+ bool "Use minimum ECC strength supported by the controller"
+ default false
+
+endif
+
+config NAND_ZYNQ
+ bool "Support for Zynq Nand controller"
+ select SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ help
+ This enables Nand driver support for Nand flash controller
+ found on Zynq SoC.
+
+config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS
+ bool "Enable use of 1st stage bootloader timing for NAND"
+ depends on NAND_ZYNQ
+ help
+ This flag prevent U-boot reconfigure NAND flash controller and reuse
+ the NAND timing from 1st stage bootloader.
+
+comment "Generic NAND options"
+
+config SYS_NAND_BLOCK_SIZE
+ hex "NAND chip eraseblock size"
+ depends on ARCH_SUNXI
+ help
+ Number of data bytes in one eraseblock for the NAND chip on the
+ board. This is the multiple of NAND_PAGE_SIZE and the number of
+ pages.
+
+config SYS_NAND_PAGE_SIZE
+ hex "NAND chip page size"
+ depends on ARCH_SUNXI
+ help
+ Number of data bytes in one page for the NAND chip on the
+ board, not including the OOB area.
+
+config SYS_NAND_OOBSIZE
+ hex "NAND chip OOB size"
+ depends on ARCH_SUNXI
+ help
+ Number of bytes in the Out-Of-Band area for the NAND chip on
+ the board.
+
+# Enhance depends when converting drivers to Kconfig which use this config
+# option (mxc_nand, ndfc, omap_gpmc).
+config SYS_NAND_BUSWIDTH_16BIT
+ bool "Use 16-bit NAND interface"
+ depends on NAND_VF610_NFC || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI
+ help
+ Indicates that NAND device has 16-bit wide data-bus. In absence of this
+ config, bus-width of NAND device is assumed to be either 8-bit and later
+ determined by reading ONFI params.
+ Above config is useful when NAND device's bus-width information cannot
+ be determined from on-chip ONFI params, like in following scenarios:
+ - SPL boot does not support reading of ONFI parameters. This is done to
+ keep SPL code foot-print small.
+ - In current U-Boot flow using nand_init(), driver initialization
+ happens in board_nand_init() which is called before any device probe
+ (nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are
+ not available while configuring controller. So a static CONFIG_NAND_xx
+ is needed to know the device's bus-width in advance.
+
+if SPL
+
+config SYS_NAND_U_BOOT_LOCATIONS
+ bool "Define U-boot binaries locations in NAND"
+ help
+ Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig.
+ This option should not be enabled when compiling U-boot for boards
+ defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/<board>.h
+ file.
+
+config SYS_NAND_U_BOOT_OFFS
+ hex "Location in NAND to read U-Boot from"
+ default 0x800000 if NAND_SUNXI
+ depends on SYS_NAND_U_BOOT_LOCATIONS
+ help
+ Set the offset from the start of the nand where u-boot should be
+ loaded from.
+
+config SYS_NAND_U_BOOT_OFFS_REDUND
+ hex "Location in NAND to read U-Boot from"
+ default SYS_NAND_U_BOOT_OFFS
+ depends on SYS_NAND_U_BOOT_LOCATIONS
+ help
+ Set the offset from the start of the nand where the redundant u-boot
+ should be loaded from.
+
+config SPL_NAND_AM33XX_BCH
+ bool "Enables SPL-NAND driver which supports ELM based"
+ depends on NAND_OMAP_GPMC && !OMAP34XX
+ default y
+ help
+ Hardware ECC correction. This is useful for platforms which have ELM
+ hardware engine and use NAND boot mode.
+ Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine,
+ so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling
+ SPL-NAND driver with software ECC correction support.
+
+config SPL_NAND_DENALI
+ bool "Support Denali NAND controller for SPL"
+ help
+ This is a small implementation of the Denali NAND controller
+ for use on SPL.
+
+config SPL_NAND_SIMPLE
+ bool "Use simple SPL NAND driver"
+ depends on !SPL_NAND_AM33XX_BCH
+ help
+ Support for NAND boot using simple NAND drivers that
+ expose the cmd_ctrl() interface.
+endif
+
+endif # if NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
new file mode 100644
index 0000000000..c61e3f3839
--- /dev/null
+++ b/drivers/mtd/nand/raw/Makefile
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+ifdef CONFIG_SPL_BUILD
+
+ifdef CONFIG_SPL_NAND_DRIVERS
+NORMAL_DRIVERS=y
+endif
+
+obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o
+obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o
+obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
+obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
+obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o
+obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o
+obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o
+obj-$(CONFIG_SPL_NAND_INIT) += nand.o
+ifeq ($(CONFIG_SPL_ENV_SUPPORT),y)
+obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
+endif
+
+else # not spl
+
+NORMAL_DRIVERS=y
+
+obj-y += nand.o
+obj-y += nand_bbt.o
+obj-y += nand_ids.o
+obj-y += nand_util.o
+obj-y += nand_ecc.o
+obj-y += nand_base.o
+obj-y += nand_timings.o
+
+endif # not spl
+
+ifdef NORMAL_DRIVERS
+
+obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
+
+obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
+obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
+obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
+obj-$(CONFIG_NAND_DENALI) += denali.o
+obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o
+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_KB9202) += kb9202_nand.o
+obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
+obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
+obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o
+obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o
+obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
+obj-$(CONFIG_NAND_MXC) += mxc_nand.o
+obj-$(CONFIG_NAND_MXS) += mxs_nand.o
+obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o
+obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o
+obj-$(CONFIG_NAND_SPEAR) += spr_nand.o
+obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o
+obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
+obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
+obj-$(CONFIG_NAND_PLAT) += nand_plat.o
+obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
+obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
+
+else # minimal SPL drivers
+
+obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
+obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
+obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
+obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
+obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
+
+endif # drivers
diff --git a/drivers/mtd/nand/raw/am335x_spl_bch.c b/drivers/mtd/nand/raw/am335x_spl_bch.c
new file mode 100644
index 0000000000..ba2f33a96e
--- /dev/null
+++ b/drivers/mtd/nand/raw/am335x_spl_bch.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2012
+ * Konstantin Kozhevnikov, Cogent Embedded
+ *
+ * based on nand_spl_simple code
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+#include <linux/mtd/nand_ecc.h>
+
+static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
+static struct mtd_info *mtd;
+static struct nand_chip nand_chip;
+
+#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
+ CONFIG_SYS_NAND_ECCSIZE)
+#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
+
+
+/*
+ * NAND command for large page NAND devices (2k)
+ */
+static int nand_command(int block, int page, uint32_t offs,
+ u8 cmd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ void (*hwctrl)(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl) = this->cmd_ctrl;
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ /* Emulate NAND_CMD_READOOB */
+ if (cmd == NAND_CMD_READOOB) {
+ offs += CONFIG_SYS_NAND_PAGE_SIZE;
+ cmd = NAND_CMD_READ0;
+ }
+
+ /* Begin command latch cycle */
+ hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+
+ if (cmd == NAND_CMD_RESET) {
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Apply this short delay always to ensure that we do wait
+ * tWB in any case on any machine.
+ */
+ ndelay(150);
+
+ while (!this->dev_ready(mtd))
+ ;
+ return 0;
+ }
+
+ /* Shift the offset from byte addressing to word addressing. */
+ if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
+ offs >>= 1;
+
+ /* Set ALE and clear CLE to start address cycle */
+ /* Column address */
+ hwctrl(mtd, offs & 0xff,
+ NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
+ hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
+ /* Row address */
+ if (cmd != NAND_CMD_RNDOUT) {
+ hwctrl(mtd, (page_addr & 0xff),
+ NAND_CTRL_ALE); /* A[19:12] */
+ hwctrl(mtd, ((page_addr >> 8) & 0xff),
+ NAND_CTRL_ALE); /* A[27:20] */
+#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
+ /* One more address cycle for devices > 128MiB */
+ hwctrl(mtd, (page_addr >> 16) & 0x0f,
+ NAND_CTRL_ALE); /* A[31:28] */
+#endif
+ }
+
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+
+ /*
+ * Program and erase have their own busy handlers status, sequential
+ * in and status need no delay.
+ */
+ switch (cmd) {
+ case NAND_CMD_CACHEDPROG:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_RNDIN:
+ case NAND_CMD_STATUS:
+ return 0;
+
+ case NAND_CMD_RNDOUT:
+ /* No ready / busy check necessary */
+ hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE |
+ NAND_CTRL_CHANGE);
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ return 0;
+
+ case NAND_CMD_READ0:
+ /* Latch in address */
+ hwctrl(mtd, NAND_CMD_READSTART,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ }
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(150);
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ return 0;
+}
+
+static int nand_is_bad_block(int block)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS,
+ NAND_CMD_READOOB);
+
+ /*
+ * Read one byte (or two if it's a 16 bit chip).
+ */
+ if (this->options & NAND_BUSWIDTH_16) {
+ if (readw(this->IO_ADDR_R) != 0xffff)
+ return 1;
+ } else {
+ if (readb(this->IO_ADDR_R) != 0xff)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int nand_read_page(int block, int page, void *dst)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ecc_calc[ECCTOTAL];
+ u_char ecc_code[ECCTOTAL];
+ u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
+ int i;
+ int eccsize = CONFIG_SYS_NAND_ECCSIZE;
+ int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
+ int eccsteps = ECCSTEPS;
+ uint8_t *p = dst;
+ uint32_t data_pos = 0;
+ uint8_t *oob = &oob_data[0] + nand_ecc_pos[0];
+ uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0];
+
+ nand_command(block, page, 0, NAND_CMD_READ0);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ this->ecc.hwctl(mtd, NAND_ECC_READ);
+ nand_command(block, page, data_pos, NAND_CMD_RNDOUT);
+
+ this->read_buf(mtd, p, eccsize);
+
+ nand_command(block, page, oob_pos, NAND_CMD_RNDOUT);
+
+ this->read_buf(mtd, oob, eccbytes);
+ this->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ data_pos += eccsize;
+ oob_pos += eccbytes;
+ oob += eccbytes;
+ }
+
+ /* Pick the ECC bytes out of the oob data */
+ for (i = 0; i < ECCTOTAL; i++)
+ ecc_code[i] = oob_data[nand_ecc_pos[i]];
+
+ eccsteps = ECCSTEPS;
+ p = dst;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ /* No chance to do something with the possible error message
+ * from correct_data(). We just hope that all possible errors
+ * are corrected by this routine.
+ */
+ this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ }
+
+ return 0;
+}
+
+/* nand_init() - initialize data to make nand usable by SPL */
+void nand_init(void)
+{
+ /*
+ * Init board specific nand support
+ */
+ mtd = nand_to_mtd(&nand_chip);
+ nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W =
+ (void __iomem *)CONFIG_SYS_NAND_BASE;
+ board_nand_init(&nand_chip);
+
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, 0);
+
+ /* NAND chip may require reset after power-on */
+ nand_command(0, 0, 0, NAND_CMD_RESET);
+}
+
+/* Unselect after operation */
+void nand_deselect(void)
+{
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, -1);
+}
+
+#include "nand_spl_loaders.c"
diff --git a/drivers/mtd/nand/raw/arasan_nfc.c b/drivers/mtd/nand/raw/arasan_nfc.c
new file mode 100644
index 0000000000..41db9f8bb9
--- /dev/null
+++ b/drivers/mtd/nand/raw/arasan_nfc.c
@@ -0,0 +1,1270 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Arasan NAND Flash Controller Driver
+ *
+ * Copyright (C) 2014 - 2015 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/sys_proto.h>
+#include <nand.h>
+
+struct arasan_nand_info {
+ void __iomem *nand_base;
+ u32 page;
+ bool on_die_ecc_enabled;
+};
+
+struct nand_regs {
+ u32 pkt_reg;
+ u32 memadr_reg1;
+ u32 memadr_reg2;
+ u32 cmd_reg;
+ u32 pgm_reg;
+ u32 intsts_enr;
+ u32 intsig_enr;
+ u32 intsts_reg;
+ u32 rdy_busy;
+ u32 cms_sysadr_reg;
+ u32 flash_sts_reg;
+ u32 tmg_reg;
+ u32 buf_dataport;
+ u32 ecc_reg;
+ u32 ecc_errcnt_reg;
+ u32 ecc_sprcmd_reg;
+ u32 errcnt_1bitreg;
+ u32 errcnt_2bitreg;
+ u32 errcnt_3bitreg;
+ u32 errcnt_4bitreg;
+ u32 dma_sysadr0_reg;
+ u32 dma_bufbdry_reg;
+ u32 cpu_rls_reg;
+ u32 errcnt_5bitreg;
+ u32 errcnt_6bitreg;
+ u32 errcnt_7bitreg;
+ u32 errcnt_8bitreg;
+ u32 data_if_reg;
+};
+
+#define arasan_nand_base ((struct nand_regs __iomem *)ARASAN_NAND_BASEADDR)
+
+struct arasan_nand_command_format {
+ u8 cmd1;
+ u8 cmd2;
+ u8 addr_cycles;
+ u32 pgm;
+};
+
+#define ONDIE_ECC_FEATURE_ADDR 0x90
+#define ENABLE_ONDIE_ECC 0x08
+
+#define ARASAN_PROG_RD_MASK 0x00000001
+#define ARASAN_PROG_BLK_ERS_MASK 0x00000004
+#define ARASAN_PROG_RD_ID_MASK 0x00000040
+#define ARASAN_PROG_RD_STS_MASK 0x00000008
+#define ARASAN_PROG_PG_PROG_MASK 0x00000010
+#define ARASAN_PROG_RD_PARAM_PG_MASK 0x00000080
+#define ARASAN_PROG_RST_MASK 0x00000100
+#define ARASAN_PROG_GET_FTRS_MASK 0x00000200
+#define ARASAN_PROG_SET_FTRS_MASK 0x00000400
+#define ARASAN_PROG_CHNG_ROWADR_END_MASK 0x00400000
+
+#define ARASAN_NAND_CMD_ECC_ON_MASK 0x80000000
+#define ARASAN_NAND_CMD_CMD12_MASK 0xFFFF
+#define ARASAN_NAND_CMD_PG_SIZE_MASK 0x3800000
+#define ARASAN_NAND_CMD_PG_SIZE_SHIFT 23
+#define ARASAN_NAND_CMD_CMD2_SHIFT 8
+#define ARASAN_NAND_CMD_ADDR_CYCL_MASK 0x70000000
+#define ARASAN_NAND_CMD_ADDR_CYCL_SHIFT 28
+
+#define ARASAN_NAND_MEM_ADDR1_PAGE_MASK 0xFFFF0000
+#define ARASAN_NAND_MEM_ADDR1_COL_MASK 0xFFFF
+#define ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT 16
+#define ARASAN_NAND_MEM_ADDR2_PAGE_MASK 0xFF
+#define ARASAN_NAND_MEM_ADDR2_CS_MASK 0xC0000000
+#define ARASAN_NAND_MEM_ADDR2_BCH_MASK 0xE000000
+#define ARASAN_NAND_MEM_ADDR2_BCH_SHIFT 25
+
+#define ARASAN_NAND_INT_STS_ERR_EN_MASK 0x10
+#define ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK 0x08
+#define ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK 0x02
+#define ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK 0x01
+#define ARASAN_NAND_INT_STS_XFR_CMPLT_MASK 0x04
+
+#define ARASAN_NAND_PKT_REG_PKT_CNT_MASK 0xFFF000
+#define ARASAN_NAND_PKT_REG_PKT_SIZE_MASK 0x7FF
+#define ARASAN_NAND_PKT_REG_PKT_CNT_SHFT 12
+
+#define ARASAN_NAND_ROW_ADDR_CYCL_MASK 0x0F
+#define ARASAN_NAND_COL_ADDR_CYCL_MASK 0xF0
+#define ARASAN_NAND_COL_ADDR_CYCL_SHIFT 4
+
+#define ARASAN_NAND_ECC_SIZE_SHIFT 16
+#define ARASAN_NAND_ECC_BCH_SHIFT 27
+
+#define ARASAN_NAND_PKTSIZE_1K 1024
+#define ARASAN_NAND_PKTSIZE_512 512
+
+#define ARASAN_NAND_POLL_TIMEOUT 1000000
+#define ARASAN_NAND_INVALID_ADDR_CYCL 0xFF
+
+#define ERR_ADDR_CYCLE -1
+#define READ_BUFF_SIZE 0x4000
+
+static struct arasan_nand_command_format *curr_cmd;
+
+enum addr_cycles {
+ NAND_ADDR_CYCL_NONE,
+ NAND_ADDR_CYCL_ONE,
+ NAND_ADDR_CYCL_ROW,
+ NAND_ADDR_CYCL_COL,
+ NAND_ADDR_CYCL_BOTH,
+};
+
+static struct arasan_nand_command_format arasan_nand_commands[] = {
+ {NAND_CMD_READ0, NAND_CMD_READSTART, NAND_ADDR_CYCL_BOTH,
+ ARASAN_PROG_RD_MASK},
+ {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, NAND_ADDR_CYCL_COL,
+ ARASAN_PROG_RD_MASK},
+ {NAND_CMD_READID, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE,
+ ARASAN_PROG_RD_ID_MASK},
+ {NAND_CMD_STATUS, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE,
+ ARASAN_PROG_RD_STS_MASK},
+ {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, NAND_ADDR_CYCL_BOTH,
+ ARASAN_PROG_PG_PROG_MASK},
+ {NAND_CMD_RNDIN, NAND_CMD_NONE, NAND_ADDR_CYCL_COL,
+ ARASAN_PROG_CHNG_ROWADR_END_MASK},
+ {NAND_CMD_ERASE1, NAND_CMD_ERASE2, NAND_ADDR_CYCL_ROW,
+ ARASAN_PROG_BLK_ERS_MASK},
+ {NAND_CMD_RESET, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE,
+ ARASAN_PROG_RST_MASK},
+ {NAND_CMD_PARAM, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE,
+ ARASAN_PROG_RD_PARAM_PG_MASK},
+ {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE,
+ ARASAN_PROG_GET_FTRS_MASK},
+ {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE,
+ ARASAN_PROG_SET_FTRS_MASK},
+ {NAND_CMD_NONE, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, 0},
+};
+
+struct arasan_ecc_matrix {
+ u32 pagesize;
+ u32 ecc_codeword_size;
+ u8 eccbits;
+ u8 bch;
+ u8 bchval;
+ u16 eccaddr;
+ u16 eccsize;
+};
+
+static const struct arasan_ecc_matrix ecc_matrix[] = {
+ {512, 512, 1, 0, 0, 0x20D, 0x3},
+ {512, 512, 4, 1, 3, 0x209, 0x7},
+ {512, 512, 8, 1, 2, 0x203, 0xD},
+ /*
+ * 2K byte page
+ */
+ {2048, 512, 1, 0, 0, 0x834, 0xC},
+ {2048, 512, 4, 1, 3, 0x826, 0x1A},
+ {2048, 512, 8, 1, 2, 0x80c, 0x34},
+ {2048, 512, 12, 1, 1, 0x822, 0x4E},
+ {2048, 512, 16, 1, 0, 0x808, 0x68},
+ {2048, 1024, 24, 1, 4, 0x81c, 0x54},
+ /*
+ * 4K byte page
+ */
+ {4096, 512, 1, 0, 0, 0x1068, 0x18},
+ {4096, 512, 4, 1, 3, 0x104c, 0x34},
+ {4096, 512, 8, 1, 2, 0x1018, 0x68},
+ {4096, 512, 12, 1, 1, 0x1044, 0x9C},
+ {4096, 512, 16, 1, 0, 0x1010, 0xD0},
+ {4096, 1024, 24, 1, 4, 0x1038, 0xA8},
+ /*
+ * 8K byte page
+ */
+ {8192, 512, 1, 0, 0, 0x20d0, 0x30},
+ {8192, 512, 4, 1, 3, 0x2098, 0x68},
+ {8192, 512, 8, 1, 2, 0x2030, 0xD0},
+ {8192, 512, 12, 1, 1, 0x2088, 0x138},
+ {8192, 512, 16, 1, 0, 0x2020, 0x1A0},
+ {8192, 1024, 24, 1, 4, 0x2070, 0x150},
+ /*
+ * 16K byte page
+ */
+ {16384, 512, 1, 0, 0, 0x4460, 0x60},
+ {16384, 512, 4, 1, 3, 0x43f0, 0xD0},
+ {16384, 512, 8, 1, 2, 0x4320, 0x1A0},
+ {16384, 512, 12, 1, 1, 0x4250, 0x270},
+ {16384, 512, 16, 1, 0, 0x4180, 0x340},
+ {16384, 1024, 24, 1, 4, 0x4220, 0x2A0}
+};
+
+static struct nand_ecclayout ondie_nand_oob_64 = {
+ .eccbytes = 32,
+
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 56, 57, 58, 59, 60, 61, 62, 63
+ },
+
+ .oobfree = {
+ { .offset = 4, .length = 4 },
+ { .offset = 20, .length = 4 },
+ { .offset = 36, .length = 4 },
+ { .offset = 52, .length = 4 }
+ }
+};
+
+/*
+ * bbt decriptors for chips with on-die ECC and
+ * chips with 64-byte OOB
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+static u8 buf_data[READ_BUFF_SIZE];
+static u32 buf_index;
+
+static struct nand_ecclayout nand_oob;
+
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+
+static void arasan_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+static void arasan_nand_enable_ecc(void)
+{
+ u32 reg_val;
+
+ reg_val = readl(&arasan_nand_base->cmd_reg);
+ reg_val |= ARASAN_NAND_CMD_ECC_ON_MASK;
+
+ writel(reg_val, &arasan_nand_base->cmd_reg);
+}
+
+static u8 arasan_nand_get_addrcycle(struct mtd_info *mtd)
+{
+ u8 addrcycles;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ switch (curr_cmd->addr_cycles) {
+ case NAND_ADDR_CYCL_NONE:
+ addrcycles = 0;
+ break;
+ case NAND_ADDR_CYCL_ONE:
+ addrcycles = 1;
+ break;
+ case NAND_ADDR_CYCL_ROW:
+ addrcycles = chip->onfi_params.addr_cycles &
+ ARASAN_NAND_ROW_ADDR_CYCL_MASK;
+ break;
+ case NAND_ADDR_CYCL_COL:
+ addrcycles = (chip->onfi_params.addr_cycles &
+ ARASAN_NAND_COL_ADDR_CYCL_MASK) >>
+ ARASAN_NAND_COL_ADDR_CYCL_SHIFT;
+ break;
+ case NAND_ADDR_CYCL_BOTH:
+ addrcycles = chip->onfi_params.addr_cycles &
+ ARASAN_NAND_ROW_ADDR_CYCL_MASK;
+ addrcycles += (chip->onfi_params.addr_cycles &
+ ARASAN_NAND_COL_ADDR_CYCL_MASK) >>
+ ARASAN_NAND_COL_ADDR_CYCL_SHIFT;
+ break;
+ default:
+ addrcycles = ARASAN_NAND_INVALID_ADDR_CYCL;
+ break;
+ }
+ return addrcycles;
+}
+
+static int arasan_nand_read_page(struct mtd_info *mtd, u8 *buf, u32 size)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct arasan_nand_info *nand = nand_get_controller_data(chip);
+ u32 reg_val, i, pktsize, pktnum;
+ u32 *bufptr = (u32 *)buf;
+ u32 timeout;
+ u32 rdcount = 0;
+ u8 addr_cycles;
+
+ if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K)
+ pktsize = ARASAN_NAND_PKTSIZE_1K;
+ else
+ pktsize = ARASAN_NAND_PKTSIZE_512;
+
+ if (size % pktsize)
+ pktnum = size/pktsize + 1;
+ else
+ pktnum = size/pktsize;
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ reg_val |= ARASAN_NAND_INT_STS_ERR_EN_MASK |
+ ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK;
+ writel(reg_val, &arasan_nand_base->intsts_enr);
+
+ reg_val = readl(&arasan_nand_base->pkt_reg);
+ reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK |
+ ARASAN_NAND_PKT_REG_PKT_SIZE_MASK);
+ reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) |
+ pktsize;
+ writel(reg_val, &arasan_nand_base->pkt_reg);
+
+ if (!nand->on_die_ecc_enabled) {
+ arasan_nand_enable_ecc();
+ addr_cycles = arasan_nand_get_addrcycle(mtd);
+ if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL)
+ return ERR_ADDR_CYCLE;
+
+ writel((NAND_CMD_RNDOUTSTART << ARASAN_NAND_CMD_CMD2_SHIFT) |
+ NAND_CMD_RNDOUT | (addr_cycles <<
+ ARASAN_NAND_CMD_ADDR_CYCL_SHIFT),
+ &arasan_nand_base->ecc_sprcmd_reg);
+ }
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (rdcount < pktnum) {
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout) {
+ puts("arasan_read_page: timedout:Buff RDY\n");
+ return -ETIMEDOUT;
+ }
+
+ rdcount++;
+
+ if (pktnum == rdcount) {
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK;
+ writel(reg_val, &arasan_nand_base->intsts_enr);
+ } else {
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ }
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ for (i = 0; i < pktsize/4; i++)
+ bufptr[i] = readl(&arasan_nand_base->buf_dataport);
+
+
+ bufptr += pktsize/4;
+
+ if (rdcount >= pktnum)
+ break;
+
+ writel(ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ }
+
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout) {
+ puts("arasan rd_page timedout:Xfer CMPLT\n");
+ return -ETIMEDOUT;
+ }
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ if (!nand->on_die_ecc_enabled) {
+ if (readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK) {
+ printf("arasan rd_page:sbiterror\n");
+ return -1;
+ }
+
+ if (readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_ERR_EN_MASK) {
+ mtd->ecc_stats.failed++;
+ printf("arasan rd_page:multibiterror\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int arasan_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ int status;
+
+ status = arasan_nand_read_page(mtd, buf, (mtd->writesize));
+
+ if (oob_required)
+ chip->ecc.read_oob(mtd, chip, page);
+
+ return status;
+}
+
+static void arasan_nand_fill_tx(const u8 *buf, int len)
+{
+ u32 __iomem *nand = &arasan_nand_base->buf_dataport;
+
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (((unsigned long)buf & 0x1) != 0) {
+ if (len) {
+ writeb(*buf, nand);
+ buf += 1;
+ len--;
+ }
+ }
+
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+ }
+ }
+
+ while (len >= 4) {
+ writel(*(u32 *)buf, nand);
+ buf += 4;
+ len -= 4;
+ }
+
+ if (len) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+
+ if (len)
+ writeb(*buf, nand);
+ }
+}
+
+static int arasan_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int oob_required,
+ int page)
+{
+ u32 reg_val, i, pktsize, pktnum;
+ const u32 *bufptr = (const u32 *)buf;
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+ u32 size = mtd->writesize;
+ u32 rdcount = 0;
+ u8 column_addr_cycles;
+ struct arasan_nand_info *nand = nand_get_controller_data(chip);
+
+ if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K)
+ pktsize = ARASAN_NAND_PKTSIZE_1K;
+ else
+ pktsize = ARASAN_NAND_PKTSIZE_512;
+
+ if (size % pktsize)
+ pktnum = size/pktsize + 1;
+ else
+ pktnum = size/pktsize;
+
+ reg_val = readl(&arasan_nand_base->pkt_reg);
+ reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK |
+ ARASAN_NAND_PKT_REG_PKT_SIZE_MASK);
+ reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | pktsize;
+ writel(reg_val, &arasan_nand_base->pkt_reg);
+
+ if (!nand->on_die_ecc_enabled) {
+ arasan_nand_enable_ecc();
+ column_addr_cycles = (chip->onfi_params.addr_cycles &
+ ARASAN_NAND_COL_ADDR_CYCL_MASK) >>
+ ARASAN_NAND_COL_ADDR_CYCL_SHIFT;
+ writel((NAND_CMD_RNDIN | (column_addr_cycles << 28)),
+ &arasan_nand_base->ecc_sprcmd_reg);
+ }
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (rdcount < pktnum) {
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ puts("arasan_write_page: timedout:Buff RDY\n");
+ return -ETIMEDOUT;
+ }
+
+ rdcount++;
+
+ if (pktnum == rdcount) {
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK;
+ writel(reg_val, &arasan_nand_base->intsts_enr);
+ } else {
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ }
+
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ for (i = 0; i < pktsize/4; i++)
+ writel(bufptr[i], &arasan_nand_base->buf_dataport);
+
+ bufptr += pktsize/4;
+
+ if (rdcount >= pktnum)
+ break;
+
+ writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ }
+
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout) {
+ puts("arasan write_page timedout:Xfer CMPLT\n");
+ return -ETIMEDOUT;
+ }
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ if (oob_required)
+ chip->ecc.write_oob(mtd, chip, nand->page);
+
+ return 0;
+}
+
+static int arasan_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, (mtd->oobsize));
+
+ return 0;
+}
+
+static int arasan_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ const u8 *buf = chip->oob_poi;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ chip->write_buf(mtd, buf, mtd->oobsize);
+
+ return status;
+}
+
+static int arasan_nand_reset(struct arasan_nand_command_format *curr_cmd)
+{
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+ u32 cmd_reg = 0;
+
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ cmd_reg = readl(&arasan_nand_base->cmd_reg);
+ cmd_reg &= ~ARASAN_NAND_CMD_CMD12_MASK;
+
+ cmd_reg |= curr_cmd->cmd1 |
+ (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT);
+ writel(cmd_reg, &arasan_nand_base->cmd_reg);
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout) {
+ printf("ERROR:%s timedout\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ return 0;
+}
+
+static u8 arasan_nand_page(struct mtd_info *mtd)
+{
+ u8 page_val = 0;
+
+ switch (mtd->writesize) {
+ case 512:
+ page_val = 0;
+ break;
+ case 2048:
+ page_val = 1;
+ break;
+ case 4096:
+ page_val = 2;
+ break;
+ case 8192:
+ page_val = 3;
+ break;
+ case 16384:
+ page_val = 4;
+ break;
+ case 1024:
+ page_val = 5;
+ break;
+ default:
+ printf("%s:Pagesize>16K\n", __func__);
+ break;
+ }
+
+ return page_val;
+}
+
+static int arasan_nand_send_wrcmd(struct arasan_nand_command_format *curr_cmd,
+ int column, int page_addr, struct mtd_info *mtd)
+{
+ u32 reg_val, page;
+ u8 page_val, addr_cycles;
+
+ writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->cmd_reg);
+ reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK;
+ reg_val |= curr_cmd->cmd1 |
+ (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT);
+ if (curr_cmd->cmd1 == NAND_CMD_SEQIN) {
+ reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK;
+ page_val = arasan_nand_page(mtd);
+ reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT);
+ }
+
+ reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK;
+ addr_cycles = arasan_nand_get_addrcycle(mtd);
+
+ if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL)
+ return ERR_ADDR_CYCLE;
+
+ reg_val |= (addr_cycles <<
+ ARASAN_NAND_CMD_ADDR_CYCL_SHIFT);
+ writel(reg_val, &arasan_nand_base->cmd_reg);
+
+ if (page_addr == -1)
+ page_addr = 0;
+
+ page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) &
+ ARASAN_NAND_MEM_ADDR1_PAGE_MASK;
+ column &= ARASAN_NAND_MEM_ADDR1_COL_MASK;
+ writel(page|column, &arasan_nand_base->memadr_reg1);
+
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK;
+ reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT);
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK;
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+
+ return 0;
+}
+
+static void arasan_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ u32 reg_val;
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+
+ reg_val = readl(&arasan_nand_base->pkt_reg);
+ reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK |
+ ARASAN_NAND_PKT_REG_PKT_SIZE_MASK);
+
+ reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | len;
+ writel(reg_val, &arasan_nand_base->pkt_reg);
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+
+ if (!timeout)
+ puts("ERROR:arasan_nand_write_buf timedout:Buff RDY\n");
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK;
+ writel(reg_val, &arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ arasan_nand_fill_tx(buf, len);
+
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout)
+ puts("ERROR:arasan_nand_write_buf timedout:Xfer CMPLT\n");
+
+ writel(readl(&arasan_nand_base->intsts_enr) |
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ writel(readl(&arasan_nand_base->intsts_reg) |
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+}
+
+static int arasan_nand_erase(struct arasan_nand_command_format *curr_cmd,
+ int column, int page_addr, struct mtd_info *mtd)
+{
+ u32 reg_val, page;
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+ u8 row_addr_cycles;
+
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->cmd_reg);
+ reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK;
+ reg_val |= curr_cmd->cmd1 |
+ (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT);
+ row_addr_cycles = arasan_nand_get_addrcycle(mtd);
+
+ if (row_addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL)
+ return ERR_ADDR_CYCLE;
+
+ reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK;
+ reg_val |= (row_addr_cycles <<
+ ARASAN_NAND_CMD_ADDR_CYCL_SHIFT);
+
+ writel(reg_val, &arasan_nand_base->cmd_reg);
+
+ page = (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) &
+ ARASAN_NAND_MEM_ADDR1_COL_MASK;
+ column = page_addr & ARASAN_NAND_MEM_ADDR1_COL_MASK;
+ writel(column | (page << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT),
+ &arasan_nand_base->memadr_reg1);
+
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK;
+ reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT);
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK;
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout) {
+ printf("ERROR:%s timedout:Xfer CMPLT\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ return 0;
+}
+
+static int arasan_nand_read_status(struct arasan_nand_command_format *curr_cmd,
+ int column, int page_addr, struct mtd_info *mtd)
+{
+ u32 reg_val;
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+ u8 addr_cycles;
+
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->cmd_reg);
+ reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK;
+ reg_val |= curr_cmd->cmd1 |
+ (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT);
+ addr_cycles = arasan_nand_get_addrcycle(mtd);
+
+ if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL)
+ return ERR_ADDR_CYCLE;
+
+ reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK;
+ reg_val |= (addr_cycles <<
+ ARASAN_NAND_CMD_ADDR_CYCL_SHIFT);
+
+ writel(reg_val, &arasan_nand_base->cmd_reg);
+
+ reg_val = readl(&arasan_nand_base->pkt_reg);
+ reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK |
+ ARASAN_NAND_PKT_REG_PKT_SIZE_MASK);
+ reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | 1;
+ writel(reg_val, &arasan_nand_base->pkt_reg);
+
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK;
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+
+ if (!timeout) {
+ printf("ERROR:%s: timedout:Xfer CMPLT\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ return 0;
+}
+
+static int arasan_nand_send_rdcmd(struct arasan_nand_command_format *curr_cmd,
+ int column, int page_addr, struct mtd_info *mtd)
+{
+ u32 reg_val, addr_cycles, page;
+ u8 page_val;
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+
+ reg_val = readl(&arasan_nand_base->cmd_reg);
+ reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK;
+ reg_val |= curr_cmd->cmd1 |
+ (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT);
+
+ if (curr_cmd->cmd1 == NAND_CMD_RNDOUT ||
+ curr_cmd->cmd1 == NAND_CMD_READ0) {
+ reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK;
+ page_val = arasan_nand_page(mtd);
+ reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT);
+ }
+
+ reg_val &= ~ARASAN_NAND_CMD_ECC_ON_MASK;
+
+ reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK;
+
+ addr_cycles = arasan_nand_get_addrcycle(mtd);
+
+ if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL)
+ return ERR_ADDR_CYCLE;
+
+ reg_val |= (addr_cycles << 28);
+ writel(reg_val, &arasan_nand_base->cmd_reg);
+
+ if (page_addr == -1)
+ page_addr = 0;
+
+ page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) &
+ ARASAN_NAND_MEM_ADDR1_PAGE_MASK;
+ column &= ARASAN_NAND_MEM_ADDR1_COL_MASK;
+ writel(page | column, &arasan_nand_base->memadr_reg1);
+
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK;
+ reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT);
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+
+ reg_val = readl(&arasan_nand_base->memadr_reg2);
+ reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK;
+ writel(reg_val, &arasan_nand_base->memadr_reg2);
+ buf_index = 0;
+
+ return 0;
+}
+
+static void arasan_nand_read_buf(struct mtd_info *mtd, u8 *buf, int size)
+{
+ u32 reg_val, i;
+ u32 *bufptr = (u32 *)buf;
+ u32 timeout = ARASAN_NAND_POLL_TIMEOUT;
+
+ reg_val = readl(&arasan_nand_base->pkt_reg);
+ reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK |
+ ARASAN_NAND_PKT_REG_PKT_SIZE_MASK);
+ reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | size;
+ writel(reg_val, &arasan_nand_base->pkt_reg);
+
+ writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg);
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+
+ if (!timeout)
+ puts("ERROR:arasan_nand_read_buf timedout:Buff RDY\n");
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK;
+ writel(reg_val, &arasan_nand_base->intsts_enr);
+
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK,
+ &arasan_nand_base->intsts_reg);
+
+ buf_index = 0;
+ for (i = 0; i < size / 4; i++)
+ bufptr[i] = readl(&arasan_nand_base->buf_dataport);
+
+ if (size & 0x03)
+ bufptr[i] = readl(&arasan_nand_base->buf_dataport);
+
+ timeout = ARASAN_NAND_POLL_TIMEOUT;
+
+ while (!(readl(&arasan_nand_base->intsts_reg) &
+ ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+
+ if (!timeout)
+ puts("ERROR:arasan_nand_read_buf timedout:Xfer CMPLT\n");
+
+ reg_val = readl(&arasan_nand_base->intsts_enr);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+ reg_val = readl(&arasan_nand_base->intsts_reg);
+ writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_reg);
+}
+
+static u8 arasan_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 size;
+ u8 val;
+ struct nand_onfi_params *p;
+
+ if (buf_index == 0) {
+ p = &chip->onfi_params;
+ if (curr_cmd->cmd1 == NAND_CMD_READID)
+ size = 4;
+ else if (curr_cmd->cmd1 == NAND_CMD_PARAM)
+ size = sizeof(struct nand_onfi_params);
+ else if (curr_cmd->cmd1 == NAND_CMD_RNDOUT)
+ size = le16_to_cpu(p->ext_param_page_length) * 16;
+ else if (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES)
+ size = 4;
+ else if (curr_cmd->cmd1 == NAND_CMD_STATUS)
+ return readb(&arasan_nand_base->flash_sts_reg);
+ else
+ size = 8;
+ chip->read_buf(mtd, &buf_data[0], size);
+ }
+
+ val = *(&buf_data[0] + buf_index);
+ buf_index++;
+
+ return val;
+}
+
+static void arasan_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ u32 i, ret = 0;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct arasan_nand_info *nand = nand_get_controller_data(chip);
+
+ curr_cmd = NULL;
+ writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK,
+ &arasan_nand_base->intsts_enr);
+
+ if ((command == NAND_CMD_READOOB) &&
+ (mtd->writesize > 512)) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Get the command format */
+ for (i = 0; (arasan_nand_commands[i].cmd1 != NAND_CMD_NONE ||
+ arasan_nand_commands[i].cmd2 != NAND_CMD_NONE); i++) {
+ if (command == arasan_nand_commands[i].cmd1) {
+ curr_cmd = &arasan_nand_commands[i];
+ break;
+ }
+ }
+
+ if (curr_cmd == NULL) {
+ printf("Unsupported Command; 0x%x\n", command);
+ return;
+ }
+
+ if (curr_cmd->cmd1 == NAND_CMD_RESET)
+ ret = arasan_nand_reset(curr_cmd);
+
+ if ((curr_cmd->cmd1 == NAND_CMD_READID) ||
+ (curr_cmd->cmd1 == NAND_CMD_PARAM) ||
+ (curr_cmd->cmd1 == NAND_CMD_RNDOUT) ||
+ (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES) ||
+ (curr_cmd->cmd1 == NAND_CMD_READ0))
+ ret = arasan_nand_send_rdcmd(curr_cmd, column, page_addr, mtd);
+
+ if ((curr_cmd->cmd1 == NAND_CMD_SET_FEATURES) ||
+ (curr_cmd->cmd1 == NAND_CMD_SEQIN)) {
+ nand->page = page_addr;
+ ret = arasan_nand_send_wrcmd(curr_cmd, column, page_addr, mtd);
+ }
+
+ if (curr_cmd->cmd1 == NAND_CMD_ERASE1)
+ ret = arasan_nand_erase(curr_cmd, column, page_addr, mtd);
+
+ if (curr_cmd->cmd1 == NAND_CMD_STATUS)
+ ret = arasan_nand_read_status(curr_cmd, column, page_addr, mtd);
+
+ if (ret != 0)
+ printf("ERROR:%s:command:0x%x\n", __func__, curr_cmd->cmd1);
+}
+
+static void arasan_check_ondie(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct arasan_nand_info *nand = nand_get_controller_data(nand_chip);
+ u8 maf_id, dev_id;
+ u8 get_feature[4];
+ u8 set_feature[4] = {ENABLE_ONDIE_ECC, 0x00, 0x00, 0x00};
+ u32 i;
+
+ /* Send the command for reading device ID */
+ nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0, -1);
+
+ /* Read manufacturer and device IDs */
+ maf_id = nand_chip->read_byte(mtd);
+ dev_id = nand_chip->read_byte(mtd);
+
+ if ((maf_id == NAND_MFR_MICRON) &&
+ ((dev_id == 0xf1) || (dev_id == 0xa1) || (dev_id == 0xb1) ||
+ (dev_id == 0xaa) || (dev_id == 0xba) || (dev_id == 0xda) ||
+ (dev_id == 0xca) || (dev_id == 0xac) || (dev_id == 0xbc) ||
+ (dev_id == 0xdc) || (dev_id == 0xcc) || (dev_id == 0xa3) ||
+ (dev_id == 0xb3) || (dev_id == 0xd3) || (dev_id == 0xc3))) {
+ nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+
+ nand_chip->write_buf(mtd, &set_feature[0], 4);
+ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+
+ for (i = 0; i < 4; i++)
+ get_feature[i] = nand_chip->read_byte(mtd);
+
+ if (get_feature[0] & ENABLE_ONDIE_ECC)
+ nand->on_die_ecc_enabled = true;
+ else
+ printf("%s: Unable to enable OnDie ECC\n", __func__);
+
+ /* Use the BBT pattern descriptors */
+ nand_chip->bbt_td = &bbt_main_descr;
+ nand_chip->bbt_md = &bbt_mirror_descr;
+ }
+}
+
+static int arasan_nand_ecc_init(struct mtd_info *mtd)
+{
+ int found = -1;
+ u32 regval, eccpos_start, i, eccaddr;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+
+ for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
+ if ((ecc_matrix[i].pagesize == mtd->writesize) &&
+ (ecc_matrix[i].ecc_codeword_size >=
+ nand_chip->ecc_step_ds)) {
+ if (ecc_matrix[i].eccbits >=
+ nand_chip->ecc_strength_ds) {
+ found = i;
+ break;
+ }
+ found = i;
+ }
+ }
+
+ if (found < 0)
+ return 1;
+
+ eccaddr = mtd->writesize + mtd->oobsize -
+ ecc_matrix[found].eccsize;
+
+ regval = eccaddr |
+ (ecc_matrix[found].eccsize << ARASAN_NAND_ECC_SIZE_SHIFT) |
+ (ecc_matrix[found].bch << ARASAN_NAND_ECC_BCH_SHIFT);
+ writel(regval, &arasan_nand_base->ecc_reg);
+
+ if (ecc_matrix[found].bch) {
+ regval = readl(&arasan_nand_base->memadr_reg2);
+ regval &= ~ARASAN_NAND_MEM_ADDR2_BCH_MASK;
+ regval |= (ecc_matrix[found].bchval <<
+ ARASAN_NAND_MEM_ADDR2_BCH_SHIFT);
+ writel(regval, &arasan_nand_base->memadr_reg2);
+ }
+
+ nand_oob.eccbytes = ecc_matrix[found].eccsize;
+ eccpos_start = mtd->oobsize - nand_oob.eccbytes;
+
+ for (i = 0; i < nand_oob.eccbytes; i++)
+ nand_oob.eccpos[i] = eccpos_start + i;
+
+ nand_oob.oobfree[0].offset = 2;
+ nand_oob.oobfree[0].length = eccpos_start - 2;
+
+ nand_chip->ecc.size = ecc_matrix[found].ecc_codeword_size;
+ nand_chip->ecc.strength = ecc_matrix[found].eccbits;
+ nand_chip->ecc.bytes = ecc_matrix[found].eccsize;
+ nand_chip->ecc.layout = &nand_oob;
+
+ return 0;
+}
+
+static int arasan_nand_init(struct nand_chip *nand_chip, int devnum)
+{
+ struct arasan_nand_info *nand;
+ struct mtd_info *mtd;
+ int err = -1;
+
+ nand = calloc(1, sizeof(struct arasan_nand_info));
+ if (!nand) {
+ printf("%s: failed to allocate\n", __func__);
+ return err;
+ }
+
+ nand->nand_base = arasan_nand_base;
+ mtd = nand_to_mtd(nand_chip);
+ nand_set_controller_data(nand_chip, nand);
+
+ /* Set the driver entry points for MTD */
+ nand_chip->cmdfunc = arasan_nand_cmd_function;
+ nand_chip->select_chip = arasan_nand_select_chip;
+ nand_chip->read_byte = arasan_nand_read_byte;
+
+ /* Buffer read/write routines */
+ nand_chip->read_buf = arasan_nand_read_buf;
+ nand_chip->write_buf = arasan_nand_write_buf;
+ nand_chip->bbt_options = NAND_BBT_USE_FLASH;
+
+ writel(0x0, &arasan_nand_base->cmd_reg);
+ writel(0x0, &arasan_nand_base->pgm_reg);
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ printf("%s: nand_scan_ident failed\n", __func__);
+ goto fail;
+ }
+
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.hwctl = NULL;
+ nand_chip->ecc.read_page = arasan_nand_read_page_hwecc;
+ nand_chip->ecc.write_page = arasan_nand_write_page_hwecc;
+ nand_chip->ecc.read_oob = arasan_nand_read_oob;
+ nand_chip->ecc.write_oob = arasan_nand_write_oob;
+
+ arasan_check_ondie(mtd);
+
+ /*
+ * If on die supported, then give priority to on-die ecc and use
+ * it instead of controller ecc.
+ */
+ if (nand->on_die_ecc_enabled) {
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.size = mtd->writesize;
+ nand_chip->ecc.bytes = 0;
+ nand_chip->ecc.layout = &ondie_nand_oob_64;
+ } else {
+ if (arasan_nand_ecc_init(mtd)) {
+ printf("%s: nand_ecc_init failed\n", __func__);
+ goto fail;
+ }
+ }
+
+ if (nand_scan_tail(mtd)) {
+ printf("%s: nand_scan_tail failed\n", __func__);
+ goto fail;
+ }
+
+ if (nand_register(devnum, mtd)) {
+ printf("Nand Register Fail\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ free(nand);
+ return err;
+}
+
+void board_nand_init(void)
+{
+ struct nand_chip *nand = &nand_chip[0];
+
+ if (arasan_nand_init(nand, 0))
+ puts("NAND init failed\n");
+}
diff --git a/drivers/mtd/nand/raw/atmel_nand.c b/drivers/mtd/nand/raw/atmel_nand.c
new file mode 100644
index 0000000000..a5b76e1aa0
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel_nand.c
@@ -0,0 +1,1511 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2007-2008
+ * Stelian Pop <stelian@popies.net>
+ * Lead Tech Design <www.leadtechdesign.com>
+ *
+ * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * (C) Copyright 2012 ATMEL, Hong Xu
+ */
+
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm/arch/gpio.h>
+
+#include <malloc.h>
+#include <nand.h>
+#include <watchdog.h>
+#include <linux/mtd/nand_ecc.h>
+
+#ifdef CONFIG_ATMEL_NAND_HWECC
+
+/* Register access macros */
+#define ecc_readl(add, reg) \
+ readl(add + ATMEL_ECC_##reg)
+#define ecc_writel(add, reg, value) \
+ writel((value), add + ATMEL_ECC_##reg)
+
+#include "atmel_nand_ecc.h" /* Hardware ECC registers */
+
+#ifdef CONFIG_ATMEL_NAND_HW_PMECC
+
+#ifdef CONFIG_SPL_BUILD
+#undef CONFIG_SYS_NAND_ONFI_DETECTION
+#endif
+
+struct atmel_nand_host {
+ struct pmecc_regs __iomem *pmecc;
+ struct pmecc_errloc_regs __iomem *pmerrloc;
+ void __iomem *pmecc_rom_base;
+
+ u8 pmecc_corr_cap;
+ u16 pmecc_sector_size;
+ u32 pmecc_index_table_offset;
+ u32 pmecc_version;
+
+ int pmecc_bytes_per_sector;
+ int pmecc_sector_number;
+ int pmecc_degree; /* Degree of remainders */
+ int pmecc_cw_len; /* Length of codeword */
+
+ /* lookup table for alpha_to and index_of */
+ void __iomem *pmecc_alpha_to;
+ void __iomem *pmecc_index_of;
+
+ /* data for pmecc computation */
+ int16_t *pmecc_smu;
+ int16_t *pmecc_partial_syn;
+ int16_t *pmecc_si;
+ int16_t *pmecc_lmu; /* polynomal order */
+ int *pmecc_mu;
+ int *pmecc_dmu;
+ int *pmecc_delta;
+};
+
+static struct atmel_nand_host pmecc_host;
+static struct nand_ecclayout atmel_pmecc_oobinfo;
+
+/*
+ * Return number of ecc bytes per sector according to sector size and
+ * correction capability
+ *
+ * Following table shows what at91 PMECC supported:
+ * Correction Capability Sector_512_bytes Sector_1024_bytes
+ * ===================== ================ =================
+ * 2-bits 4-bytes 4-bytes
+ * 4-bits 7-bytes 7-bytes
+ * 8-bits 13-bytes 14-bytes
+ * 12-bits 20-bytes 21-bytes
+ * 24-bits 39-bytes 42-bytes
+ * 32-bits 52-bytes 56-bytes
+ */
+static int pmecc_get_ecc_bytes(int cap, int sector_size)
+{
+ int m = 12 + sector_size / 512;
+ return (m * cap + 7) / 8;
+}
+
+static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
+ int oobsize, int ecc_len)
+{
+ int i;
+
+ layout->eccbytes = ecc_len;
+
+ /* ECC will occupy the last ecc_len bytes continuously */
+ for (i = 0; i < ecc_len; i++)
+ layout->eccpos[i] = oobsize - ecc_len + i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length =
+ oobsize - ecc_len - layout->oobfree[0].offset;
+}
+
+static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
+{
+ int table_size;
+
+ table_size = host->pmecc_sector_size == 512 ?
+ PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024;
+
+ /* the ALPHA lookup table is right behind the INDEX lookup table. */
+ return host->pmecc_rom_base + host->pmecc_index_table_offset +
+ table_size * sizeof(int16_t);
+}
+
+static void pmecc_data_free(struct atmel_nand_host *host)
+{
+ free(host->pmecc_partial_syn);
+ free(host->pmecc_si);
+ free(host->pmecc_lmu);
+ free(host->pmecc_smu);
+ free(host->pmecc_mu);
+ free(host->pmecc_dmu);
+ free(host->pmecc_delta);
+}
+
+static int pmecc_data_alloc(struct atmel_nand_host *host)
+{
+ const int cap = host->pmecc_corr_cap;
+ int size;
+
+ size = (2 * cap + 1) * sizeof(int16_t);
+ host->pmecc_partial_syn = malloc(size);
+ host->pmecc_si = malloc(size);
+ host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t));
+ host->pmecc_smu = malloc((cap + 2) * size);
+
+ size = (cap + 1) * sizeof(int);
+ host->pmecc_mu = malloc(size);
+ host->pmecc_dmu = malloc(size);
+ host->pmecc_delta = malloc(size);
+
+ if (host->pmecc_partial_syn &&
+ host->pmecc_si &&
+ host->pmecc_lmu &&
+ host->pmecc_smu &&
+ host->pmecc_mu &&
+ host->pmecc_dmu &&
+ host->pmecc_delta)
+ return 0;
+
+ /* error happened */
+ pmecc_data_free(host);
+ return -ENOMEM;
+
+}
+
+static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ int i;
+ uint32_t value;
+
+ /* Fill odd syndromes */
+ for (i = 0; i < host->pmecc_corr_cap; i++) {
+ value = pmecc_readl(host->pmecc, rem_port[sector].rem[i / 2]);
+ if (i & 1)
+ value >>= 16;
+ value &= 0xffff;
+ host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
+ }
+}
+
+static void pmecc_substitute(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t *partial_syn = host->pmecc_partial_syn;
+ const int cap = host->pmecc_corr_cap;
+ int16_t *si;
+ int i, j;
+
+ /* si[] is a table that holds the current syndrome value,
+ * an element of that table belongs to the field
+ */
+ si = host->pmecc_si;
+
+ memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
+
+ /* Computation 2t syndromes based on S(x) */
+ /* Odd syndromes */
+ for (i = 1; i < 2 * cap; i += 2) {
+ for (j = 0; j < host->pmecc_degree; j++) {
+ if (partial_syn[i] & (0x1 << j))
+ si[i] = readw(alpha_to + i * j) ^ si[i];
+ }
+ }
+ /* Even syndrome = (Odd syndrome) ** 2 */
+ for (i = 2, j = 1; j <= cap; i = ++j << 1) {
+ if (si[j] == 0) {
+ si[i] = 0;
+ } else {
+ int16_t tmp;
+
+ tmp = readw(index_of + si[j]);
+ tmp = (tmp * 2) % host->pmecc_cw_len;
+ si[i] = readw(alpha_to + tmp);
+ }
+ }
+}
+
+/*
+ * This function defines a Berlekamp iterative procedure for
+ * finding the value of the error location polynomial.
+ * The input is si[], initialize by pmecc_substitute().
+ * The output is smu[][].
+ *
+ * This function is written according to chip datasheet Chapter:
+ * Find the Error Location Polynomial Sigma(x) of Section:
+ * Programmable Multibit ECC Control (PMECC).
+ */
+static void pmecc_get_sigma(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+
+ int16_t *lmu = host->pmecc_lmu;
+ int16_t *si = host->pmecc_si;
+ int *mu = host->pmecc_mu;
+ int *dmu = host->pmecc_dmu; /* Discrepancy */
+ int *delta = host->pmecc_delta; /* Delta order */
+ int cw_len = host->pmecc_cw_len;
+ const int16_t cap = host->pmecc_corr_cap;
+ const int num = 2 * cap + 1;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int i, j, k;
+ uint32_t dmu_0_count, tmp;
+ int16_t *smu = host->pmecc_smu;
+
+ /* index of largest delta */
+ int ro;
+ int largest;
+ int diff;
+
+ /* Init the Sigma(x) */
+ memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu));
+
+ dmu_0_count = 0;
+
+ /* First Row */
+
+ /* Mu */
+ mu[0] = -1;
+
+ smu[0] = 1;
+
+ /* discrepancy set to 1 */
+ dmu[0] = 1;
+ /* polynom order set to 0 */
+ lmu[0] = 0;
+ /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */
+ delta[0] = -1;
+
+ /* Second Row */
+
+ /* Mu */
+ mu[1] = 0;
+ /* Sigma(x) set to 1 */
+ smu[num] = 1;
+
+ /* discrepancy set to S1 */
+ dmu[1] = si[1];
+
+ /* polynom order set to 0 */
+ lmu[1] = 0;
+
+ /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */
+ delta[1] = 0;
+
+ for (i = 1; i <= cap; i++) {
+ mu[i + 1] = i << 1;
+ /* Begin Computing Sigma (Mu+1) and L(mu) */
+ /* check if discrepancy is set to 0 */
+ if (dmu[i] == 0) {
+ dmu_0_count++;
+
+ tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
+ if ((cap - (lmu[i] >> 1) - 1) & 0x1)
+ tmp += 2;
+ else
+ tmp += 1;
+
+ if (dmu_0_count == tmp) {
+ for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+ smu[(cap + 1) * num + j] =
+ smu[i * num + j];
+
+ lmu[cap + 1] = lmu[i];
+ return;
+ }
+
+ /* copy polynom */
+ for (j = 0; j <= lmu[i] >> 1; j++)
+ smu[(i + 1) * num + j] = smu[i * num + j];
+
+ /* copy previous polynom order to the next */
+ lmu[i + 1] = lmu[i];
+ } else {
+ ro = 0;
+ largest = -1;
+ /* find largest delta with dmu != 0 */
+ for (j = 0; j < i; j++) {
+ if ((dmu[j]) && (delta[j] > largest)) {
+ largest = delta[j];
+ ro = j;
+ }
+ }
+
+ /* compute difference */
+ diff = (mu[i] - mu[ro]);
+
+ /* Compute degree of the new smu polynomial */
+ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+ lmu[i + 1] = lmu[i];
+ else
+ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+ /* Init smu[i+1] with 0 */
+ for (k = 0; k < num; k++)
+ smu[(i + 1) * num + k] = 0;
+
+ /* Compute smu[i+1] */
+ for (k = 0; k <= lmu[ro] >> 1; k++) {
+ int16_t a, b, c;
+
+ if (!(smu[ro * num + k] && dmu[i]))
+ continue;
+ a = readw(index_of + dmu[i]);
+ b = readw(index_of + dmu[ro]);
+ c = readw(index_of + smu[ro * num + k]);
+ tmp = a + (cw_len - b) + c;
+ a = readw(alpha_to + tmp % cw_len);
+ smu[(i + 1) * num + (k + diff)] = a;
+ }
+
+ for (k = 0; k <= lmu[i] >> 1; k++)
+ smu[(i + 1) * num + k] ^= smu[i * num + k];
+ }
+
+ /* End Computing Sigma (Mu+1) and L(mu) */
+ /* In either case compute delta */
+ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+ /* Do not compute discrepancy for the last iteration */
+ if (i >= cap)
+ continue;
+
+ for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+ tmp = 2 * (i - 1);
+ if (k == 0) {
+ dmu[i + 1] = si[tmp + 3];
+ } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
+ int16_t a, b, c;
+ a = readw(index_of +
+ smu[(i + 1) * num + k]);
+ b = si[2 * (i - 1) + 3 - k];
+ c = readw(index_of + b);
+ tmp = a + c;
+ tmp %= cw_len;
+ dmu[i + 1] = readw(alpha_to + tmp) ^
+ dmu[i + 1];
+ }
+ }
+ }
+}
+
+static int pmecc_err_location(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ const int cap = host->pmecc_corr_cap;
+ const int num = 2 * cap + 1;
+ int sector_size = host->pmecc_sector_size;
+ int err_nbr = 0; /* number of error */
+ int roots_nbr; /* number of roots */
+ int i;
+ uint32_t val;
+ int16_t *smu = host->pmecc_smu;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ pmecc_writel(host->pmerrloc, eldis, PMERRLOC_DISABLE);
+
+ for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
+ pmecc_writel(host->pmerrloc, sigma[i],
+ smu[(cap + 1) * num + i]);
+ err_nbr++;
+ }
+
+ val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1);
+ if (sector_size == 1024)
+ val |= PMERRLOC_ELCFG_SECTOR_1024;
+
+ pmecc_writel(host->pmerrloc, elcfg, val);
+ pmecc_writel(host->pmerrloc, elen,
+ sector_size * 8 + host->pmecc_degree * cap);
+
+ while (--timeout) {
+ if (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_CALC_DONE)
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ dev_err(host->dev, "atmel_nand : Timeout to calculate PMECC error location\n");
+ return -1;
+ }
+
+ roots_nbr = (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_ERR_NUM_MASK)
+ >> 8;
+ /* Number of roots == degree of smu hence <= cap */
+ if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
+ return err_nbr - 1;
+
+ /* Number of roots does not match the degree of smu
+ * unable to correct error */
+ return -1;
+}
+
+static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
+ int sector_num, int extra_bytes, int err_nbr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ int i = 0;
+ int byte_pos, bit_pos, sector_size, pos;
+ uint32_t tmp;
+ uint8_t err_byte;
+
+ sector_size = host->pmecc_sector_size;
+
+ while (err_nbr) {
+ tmp = pmecc_readl(host->pmerrloc, el[i]) - 1;
+ byte_pos = tmp / 8;
+ bit_pos = tmp % 8;
+
+ if (byte_pos >= (sector_size + extra_bytes))
+ BUG(); /* should never happen */
+
+ if (byte_pos < sector_size) {
+ err_byte = *(buf + byte_pos);
+ *(buf + byte_pos) ^= (1 << bit_pos);
+
+ pos = sector_num * host->pmecc_sector_size + byte_pos;
+ dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+ pos, bit_pos, err_byte, *(buf + byte_pos));
+ } else {
+ /* Bit flip in OOB area */
+ tmp = sector_num * host->pmecc_bytes_per_sector
+ + (byte_pos - sector_size);
+ err_byte = ecc[tmp];
+ ecc[tmp] ^= (1 << bit_pos);
+
+ pos = tmp + nand_chip->ecc.layout->eccpos[0];
+ dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
+ pos, bit_pos, err_byte, ecc[tmp]);
+ }
+
+ i++;
+ err_nbr--;
+ }
+
+ return;
+}
+
+static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
+ u8 *ecc)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ int i, err_nbr, eccbytes;
+ uint8_t *buf_pos;
+
+ /* SAMA5D4 PMECC IP can correct errors for all 0xff page */
+ if (host->pmecc_version >= PMECC_VERSION_SAMA5D4)
+ goto normal_check;
+
+ eccbytes = nand_chip->ecc.bytes;
+ for (i = 0; i < eccbytes; i++)
+ if (ecc[i] != 0xff)
+ goto normal_check;
+ /* Erased page, return OK */
+ return 0;
+
+normal_check:
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ err_nbr = 0;
+ if (pmecc_stat & 0x1) {
+ buf_pos = buf + i * host->pmecc_sector_size;
+
+ pmecc_gen_syndrome(mtd, i);
+ pmecc_substitute(mtd);
+ pmecc_get_sigma(mtd);
+
+ err_nbr = pmecc_err_location(mtd);
+ if (err_nbr == -1) {
+ dev_err(host->dev, "PMECC: Too many errors\n");
+ mtd->ecc_stats.failed++;
+ return -EBADMSG;
+ } else {
+ pmecc_correct_data(mtd, buf_pos, ecc, i,
+ host->pmecc_bytes_per_sector, err_nbr);
+ mtd->ecc_stats.corrected += err_nbr;
+ }
+ }
+ pmecc_stat >>= 1;
+ }
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
+ int eccsize = chip->ecc.size;
+ uint8_t *oob = chip->oob_poi;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t stat;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+ pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg))
+ & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
+
+ chip->read_buf(mtd, buf, eccsize);
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ while (--timeout) {
+ if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ dev_err(host->dev, "atmel_nand : Timeout to read PMECC page\n");
+ return -1;
+ }
+
+ stat = pmecc_readl(host->pmecc, isr);
+ if (stat != 0)
+ if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
+ return -EBADMSG;
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ int i, j;
+ int timeout = PMECC_MAX_TIMEOUT_US;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+
+ pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) |
+ PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA);
+
+ chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
+
+ while (--timeout) {
+ if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY))
+ break;
+ WATCHDOG_RESET();
+ udelay(1);
+ }
+
+ if (!timeout) {
+ dev_err(host->dev, "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n");
+ goto out;
+ }
+
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
+ int pos;
+
+ pos = i * host->pmecc_bytes_per_sector + j;
+ chip->oob_poi[eccpos[pos]] =
+ pmecc_readb(host->pmecc, ecc_port[i].ecc[j]);
+ }
+ }
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+out:
+ return 0;
+}
+
+static void atmel_pmecc_core_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
+ uint32_t val = 0;
+ struct nand_ecclayout *ecc_layout;
+
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE);
+
+ switch (host->pmecc_corr_cap) {
+ case 2:
+ val = PMECC_CFG_BCH_ERR2;
+ break;
+ case 4:
+ val = PMECC_CFG_BCH_ERR4;
+ break;
+ case 8:
+ val = PMECC_CFG_BCH_ERR8;
+ break;
+ case 12:
+ val = PMECC_CFG_BCH_ERR12;
+ break;
+ case 24:
+ val = PMECC_CFG_BCH_ERR24;
+ break;
+ case 32:
+ val = PMECC_CFG_BCH_ERR32;
+ break;
+ }
+
+ if (host->pmecc_sector_size == 512)
+ val |= PMECC_CFG_SECTOR512;
+ else if (host->pmecc_sector_size == 1024)
+ val |= PMECC_CFG_SECTOR1024;
+
+ switch (host->pmecc_sector_number) {
+ case 1:
+ val |= PMECC_CFG_PAGE_1SECTOR;
+ break;
+ case 2:
+ val |= PMECC_CFG_PAGE_2SECTORS;
+ break;
+ case 4:
+ val |= PMECC_CFG_PAGE_4SECTORS;
+ break;
+ case 8:
+ val |= PMECC_CFG_PAGE_8SECTORS;
+ break;
+ }
+
+ val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
+ | PMECC_CFG_AUTO_DISABLE);
+ pmecc_writel(host->pmecc, cfg, val);
+
+ ecc_layout = nand_chip->ecc.layout;
+ pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1);
+ pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]);
+ pmecc_writel(host->pmecc, eaddr,
+ ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
+ /* See datasheet about PMECC Clock Control Register */
+ pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ);
+ pmecc_writel(host->pmecc, idr, 0xff);
+ pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE);
+}
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+/*
+ * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If
+ * pmecc_corr_cap or pmecc_sector_size is 0, then set it as
+ * ONFI ECC parameters.
+ * @host: point to an atmel_nand_host structure.
+ * if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits.
+ * if host->pmecc_sector_size is 0 then set it as the ONFI sector_size.
+ * @chip: point to an nand_chip structure.
+ * @cap: store the ONFI ECC correct bits capbility
+ * @sector_size: in how many bytes that ONFI require to correct @ecc_bits
+ *
+ * Return 0 if success. otherwise return the error code.
+ */
+static int pmecc_choose_ecc(struct atmel_nand_host *host,
+ struct nand_chip *chip,
+ int *cap, int *sector_size)
+{
+ /* Get ECC requirement from ONFI parameters */
+ *cap = *sector_size = 0;
+ if (chip->onfi_version) {
+ *cap = chip->ecc_strength_ds;
+ *sector_size = chip->ecc_step_ds;
+ pr_debug("ONFI params, minimum required ECC: %d bits in %d bytes\n",
+ *cap, *sector_size);
+ }
+
+ if (*cap == 0 && *sector_size == 0) {
+ /* Non-ONFI compliant */
+ dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes\n");
+ *cap = 2;
+ *sector_size = 512;
+ }
+
+ /* If head file doesn't specify then use the one in ONFI parameters */
+ if (host->pmecc_corr_cap == 0) {
+ /* use the most fitable ecc bits (the near bigger one ) */
+ if (*cap <= 2)
+ host->pmecc_corr_cap = 2;
+ else if (*cap <= 4)
+ host->pmecc_corr_cap = 4;
+ else if (*cap <= 8)
+ host->pmecc_corr_cap = 8;
+ else if (*cap <= 12)
+ host->pmecc_corr_cap = 12;
+ else if (*cap <= 24)
+ host->pmecc_corr_cap = 24;
+ else
+#ifdef CONFIG_SAMA5D2
+ host->pmecc_corr_cap = 32;
+#else
+ host->pmecc_corr_cap = 24;
+#endif
+ }
+ if (host->pmecc_sector_size == 0) {
+ /* use the most fitable sector size (the near smaller one ) */
+ if (*sector_size >= 1024)
+ host->pmecc_sector_size = 1024;
+ else if (*sector_size >= 512)
+ host->pmecc_sector_size = 512;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+static uint16_t *pmecc_galois_table;
+static inline int deg(unsigned int poly)
+{
+ /* polynomial degree is the most-significant bit index */
+ return fls(poly) - 1;
+}
+
+static int build_gf_tables(int mm, unsigned int poly,
+ int16_t *index_of, int16_t *alpha_to)
+{
+ unsigned int i, x = 1;
+ const unsigned int k = 1 << deg(poly);
+ unsigned int nn = (1 << mm) - 1;
+
+ /* primitive polynomial must be of degree m */
+ if (k != (1u << mm))
+ return -EINVAL;
+
+ for (i = 0; i < nn; i++) {
+ alpha_to[i] = x;
+ index_of[x] = i;
+ if (i && (x == 1))
+ /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
+ return -EINVAL;
+ x <<= 1;
+ if (x & k)
+ x ^= poly;
+ }
+
+ alpha_to[nn] = 1;
+ index_of[0] = 0;
+
+ return 0;
+}
+
+static uint16_t *create_lookup_table(int sector_size)
+{
+ int degree = (sector_size == 512) ?
+ PMECC_GF_DIMENSION_13 :
+ PMECC_GF_DIMENSION_14;
+ unsigned int poly = (sector_size == 512) ?
+ PMECC_GF_13_PRIMITIVE_POLY :
+ PMECC_GF_14_PRIMITIVE_POLY;
+ int table_size = (sector_size == 512) ?
+ PMECC_INDEX_TABLE_SIZE_512 :
+ PMECC_INDEX_TABLE_SIZE_1024;
+
+ int16_t *addr = kzalloc(2 * table_size * sizeof(uint16_t), GFP_KERNEL);
+ if (addr && build_gf_tables(degree, poly, addr, addr + table_size))
+ return NULL;
+
+ return (uint16_t *)addr;
+}
+#endif
+
+static int atmel_pmecc_nand_init_params(struct nand_chip *nand,
+ struct mtd_info *mtd)
+{
+ struct atmel_nand_host *host;
+ int cap, sector_size;
+
+ host = &pmecc_host;
+ nand_set_controller_data(nand, host);
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+ host->pmecc_corr_cap = host->pmecc_sector_size = 0;
+
+#ifdef CONFIG_PMECC_CAP
+ host->pmecc_corr_cap = CONFIG_PMECC_CAP;
+#endif
+#ifdef CONFIG_PMECC_SECTOR_SIZE
+ host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+#endif
+ /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or
+ * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size
+ * from ONFI.
+ */
+ if (pmecc_choose_ecc(host, nand, &cap, &sector_size)) {
+ dev_err(host->dev, "Required ECC %d bits in %d bytes not supported!\n",
+ cap, sector_size);
+ return -EINVAL;
+ }
+
+ if (cap > host->pmecc_corr_cap)
+ dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n",
+ host->pmecc_corr_cap, cap);
+ if (sector_size < host->pmecc_sector_size)
+ dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n",
+ host->pmecc_sector_size, sector_size);
+#else /* CONFIG_SYS_NAND_ONFI_DETECTION */
+ host->pmecc_corr_cap = CONFIG_PMECC_CAP;
+ host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE;
+#endif
+
+ cap = host->pmecc_corr_cap;
+ sector_size = host->pmecc_sector_size;
+
+ /* TODO: need check whether cap & sector_size is validate */
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+ /*
+ * As pmecc_rom_base is the begin of the gallois field table, So the
+ * index offset just set as 0.
+ */
+ host->pmecc_index_table_offset = 0;
+#else
+ if (host->pmecc_sector_size == 512)
+ host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512;
+ else
+ host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024;
+#endif
+
+ pr_debug("Initialize PMECC params, cap: %d, sector: %d\n",
+ cap, sector_size);
+
+ host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC;
+ host->pmerrloc = (struct pmecc_errloc_regs __iomem *)
+ ATMEL_BASE_PMERRLOC;
+#if defined(NO_GALOIS_TABLE_IN_ROM)
+ pmecc_galois_table = create_lookup_table(host->pmecc_sector_size);
+ if (!pmecc_galois_table) {
+ dev_err(host->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+ host->pmecc_rom_base = (void __iomem *)pmecc_galois_table;
+#else
+ host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM;
+#endif
+
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 2048:
+ case 4096:
+ case 8192:
+ host->pmecc_degree = (sector_size == 512) ?
+ PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;
+ host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
+ host->pmecc_sector_number = mtd->writesize / sector_size;
+ host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
+ cap, sector_size);
+ host->pmecc_alpha_to = pmecc_get_alpha_to(host);
+ host->pmecc_index_of = host->pmecc_rom_base +
+ host->pmecc_index_table_offset;
+
+ nand->ecc.steps = 1;
+ nand->ecc.bytes = host->pmecc_bytes_per_sector *
+ host->pmecc_sector_number;
+
+ if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) {
+ dev_err(host->dev, "too large eccpos entries. max support ecc.bytes is %d\n",
+ MTD_MAX_ECCPOS_ENTRIES_LARGE);
+ return -EINVAL;
+ }
+
+ if (nand->ecc.bytes > mtd->oobsize - PMECC_OOB_RESERVED_BYTES) {
+ dev_err(host->dev, "No room for ECC bytes\n");
+ return -EINVAL;
+ }
+ pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
+ mtd->oobsize,
+ nand->ecc.bytes);
+ nand->ecc.layout = &atmel_pmecc_oobinfo;
+ break;
+ case 512:
+ case 1024:
+ /* TODO */
+ dev_err(host->dev, "Unsupported page size for PMECC, use Software ECC\n");
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ return 0;
+ }
+
+ /* Allocate data for PMECC computation */
+ if (pmecc_data_alloc(host)) {
+ dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n");
+ return -ENOMEM;
+ }
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+ nand->ecc.read_page = atmel_nand_pmecc_read_page;
+ nand->ecc.write_page = atmel_nand_pmecc_write_page;
+ nand->ecc.strength = cap;
+
+ /* Check the PMECC ip version */
+ host->pmecc_version = pmecc_readl(host->pmerrloc, version);
+ dev_dbg(host->dev, "PMECC IP version is: %x\n", host->pmecc_version);
+
+ atmel_pmecc_core_init(mtd);
+
+ return 0;
+}
+
+#else
+
+/* oob layout for large page size
+ * bad block info is on bytes 0 and 1
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_large = {
+ .eccbytes = 4,
+ .eccpos = {60, 61, 62, 63},
+ .oobfree = {
+ {2, 58}
+ },
+};
+
+/* oob layout for small page size
+ * bad block info is on bytes 4 and 5
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_small = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {6, 10}
+ },
+};
+
+/*
+ * Calculate HW ECC
+ *
+ * function called after a write
+ *
+ * mtd: MTD block structure
+ * dat: raw data (unused)
+ * ecc_code: buffer for ECC
+ */
+static int atmel_nand_calculate(struct mtd_info *mtd,
+ const u_char *dat, unsigned char *ecc_code)
+{
+ unsigned int ecc_value;
+
+ /* get the first 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR);
+
+ ecc_code[0] = ecc_value & 0xFF;
+ ecc_code[1] = (ecc_value >> 8) & 0xFF;
+
+ /* get the last 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY;
+
+ ecc_code[2] = ecc_value & 0xFF;
+ ecc_code[3] = (ecc_value >> 8) & 0xFF;
+
+ return 0;
+}
+
+/*
+ * HW ECC read page function
+ *
+ * mtd: mtd info structure
+ * chip: nand chip info structure
+ * buf: buffer to store read data
+ * oob_required: caller expects OOB data read to chip->oob_poi
+ */
+static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ uint8_t *ecc_pos;
+ int stat;
+
+ /* read the page */
+ chip->read_buf(mtd, p, eccsize);
+
+ /* move to ECC position if needed */
+ if (eccpos[0] != 0) {
+ /* This only works on large pages
+ * because the ECC controller waits for
+ * NAND_CMD_RNDOUTSTART after the
+ * NAND_CMD_RNDOUT.
+ * anyway, for small pages, the eccpos[0] == 0
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + eccpos[0], -1);
+ }
+
+ /* the ECC controller needs to read the ECC just after the data */
+ ecc_pos = oob + eccpos[0];
+ chip->read_buf(mtd, ecc_pos, eccbytes);
+
+ /* check if there's an error */
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ /* get back to oob start (end of page) */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+
+ /* read the oob */
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ return 0;
+}
+
+/*
+ * HW ECC Correction
+ *
+ * function called after a read
+ *
+ * mtd: MTD block structure
+ * dat: raw data read from the chip
+ * read_ecc: ECC from the chip (unused)
+ * isnull: unused
+ *
+ * Detect and correct a 1 bit error for a page
+ */
+static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *isnull)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ unsigned int ecc_status;
+ unsigned int ecc_word, ecc_bit;
+
+ /* get the status from the Status Register */
+ ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR);
+
+ /* if there's no error */
+ if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
+ return 0;
+
+ /* get error bit offset (4 bits) */
+ ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR;
+ /* get word address (12 bits) */
+ ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR;
+ ecc_word >>= 4;
+
+ /* if there are multiple errors */
+ if (ecc_status & ATMEL_ECC_MULERR) {
+ /* check if it is a freshly erased block
+ * (filled with 0xff) */
+ if ((ecc_bit == ATMEL_ECC_BITADDR)
+ && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
+ /* the block has just been erased, return OK */
+ return 0;
+ }
+ /* it doesn't seems to be a freshly
+ * erased block.
+ * We can't correct so many errors */
+ dev_warn(host->dev, "atmel_nand : multiple errors detected."
+ " Unable to correct.\n");
+ return -EBADMSG;
+ }
+
+ /* if there's a single bit error : we can correct it */
+ if (ecc_status & ATMEL_ECC_ECCERR) {
+ /* there's nothing much to do here.
+ * the bit error is on the ECC itself.
+ */
+ dev_warn(host->dev, "atmel_nand : one bit error on ECC code."
+ " Nothing to correct\n");
+ return 0;
+ }
+
+ dev_warn(host->dev, "atmel_nand : one bit error on data."
+ " (word offset in the page :"
+ " 0x%x bit offset : 0x%x)\n",
+ ecc_word, ecc_bit);
+ /* correct the error */
+ if (nand_chip->options & NAND_BUSWIDTH_16) {
+ /* 16 bits words */
+ ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
+ } else {
+ /* 8 bits words */
+ dat[ecc_word] ^= (1 << ecc_bit);
+ }
+ dev_warn(host->dev, "atmel_nand : error corrected\n");
+ return 1;
+}
+
+/*
+ * Enable HW ECC : unused on most chips
+ */
+static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
+{
+}
+
+int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd)
+{
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = atmel_nand_calculate;
+ nand->ecc.correct = atmel_nand_correct;
+ nand->ecc.hwctl = atmel_nand_hwctl;
+ nand->ecc.read_page = atmel_nand_read_page;
+ nand->ecc.bytes = 4;
+ nand->ecc.strength = 4;
+
+ if (nand->ecc.mode == NAND_ECC_HW) {
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 512:
+ nand->ecc.layout = &atmel_oobinfo_small;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_528);
+ break;
+ case 1024:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_1056);
+ break;
+ case 2048:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_2112);
+ break;
+ case 4096:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR,
+ ATMEL_ECC_PAGESIZE_4224);
+ break;
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_ATMEL_NAND_HW_PMECC */
+
+#endif /* CONFIG_ATMEL_NAND_HWECC */
+
+static void at91_nand_hwcontrol(struct mtd_info *mtd,
+ int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
+ IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE
+ | CONFIG_SYS_NAND_MASK_CLE);
+
+ if (ctrl & NAND_CLE)
+ IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE;
+ if (ctrl & NAND_ALE)
+ IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE;
+
+#ifdef CONFIG_SYS_NAND_ENABLE_PIN
+ at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN,
+ !(ctrl & NAND_NCE));
+#endif
+ this->IO_ADDR_W = (void *) IO_ADDR_W;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+#ifdef CONFIG_SYS_NAND_READY_PIN
+static int at91_nand_ready(struct mtd_info *mtd)
+{
+ return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN);
+}
+#endif
+
+#ifdef CONFIG_SPL_BUILD
+/* The following code is for SPL */
+static struct mtd_info *mtd;
+static struct nand_chip nand_chip;
+
+static int nand_command(int block, int page, uint32_t offs, u8 cmd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ void (*hwctrl)(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl) = this->cmd_ctrl;
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ if (cmd == NAND_CMD_READOOB) {
+ offs += CONFIG_SYS_NAND_PAGE_SIZE;
+ cmd = NAND_CMD_READ0;
+ }
+
+ hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+
+ if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
+ offs >>= 1;
+
+ hwctrl(mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+ hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE);
+ hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE);
+ hwctrl(mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE);
+#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
+ hwctrl(mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE);
+#endif
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ hwctrl(mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ return 0;
+}
+
+static int nand_is_bad_block(int block)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB);
+
+ if (this->options & NAND_BUSWIDTH_16) {
+ if (readw(this->IO_ADDR_R) != 0xffff)
+ return 1;
+ } else {
+ if (readb(this->IO_ADDR_R) != 0xff)
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SPL_NAND_ECC
+static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
+#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
+ CONFIG_SYS_NAND_ECCSIZE)
+#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
+
+static int nand_read_page(int block, int page, void *dst)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ecc_calc[ECCTOTAL];
+ u_char ecc_code[ECCTOTAL];
+ u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
+ int eccsize = CONFIG_SYS_NAND_ECCSIZE;
+ int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
+ int eccsteps = ECCSTEPS;
+ int i;
+ uint8_t *p = dst;
+ nand_command(block, page, 0, NAND_CMD_READ0);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ if (this->ecc.mode != NAND_ECC_SOFT)
+ this->ecc.hwctl(mtd, NAND_ECC_READ);
+ this->read_buf(mtd, p, eccsize);
+ this->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+ this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
+
+ for (i = 0; i < ECCTOTAL; i++)
+ ecc_code[i] = oob_data[nand_ecc_pos[i]];
+
+ eccsteps = ECCSTEPS;
+ p = dst;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+
+ return 0;
+}
+
+int spl_nand_erase_one(int block, int page)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void (*hwctrl)(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl) = this->cmd_ctrl;
+ int page_addr;
+
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, 0);
+
+ page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ /* Row address */
+ hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+ hwctrl(mtd, ((page_addr >> 8) & 0xff),
+ NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
+ /* One more address cycle for devices > 128MiB */
+ hwctrl(mtd, (page_addr >> 16) & 0x0f,
+ NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+#endif
+ hwctrl(mtd, NAND_CMD_ERASE2, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ nand_deselect();
+
+ return 0;
+}
+#else
+static int nand_read_page(int block, int page, void *dst)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ nand_command(block, page, 0, NAND_CMD_READ0);
+ atmel_nand_pmecc_read_page(mtd, this, dst, 0, page);
+
+ return 0;
+}
+#endif /* CONFIG_SPL_NAND_ECC */
+
+int at91_nand_wait_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ udelay(this->chip_delay);
+
+ return 1;
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+ int ret = 0;
+
+ nand->ecc.mode = NAND_ECC_SOFT;
+#ifdef CONFIG_SYS_NAND_DBW_16
+ nand->options = NAND_BUSWIDTH_16;
+ nand->read_buf = nand_read_buf16;
+#else
+ nand->read_buf = nand_read_buf;
+#endif
+ nand->cmd_ctrl = at91_nand_hwcontrol;
+#ifdef CONFIG_SYS_NAND_READY_PIN
+ nand->dev_ready = at91_nand_ready;
+#else
+ nand->dev_ready = at91_nand_wait_ready;
+#endif
+ nand->chip_delay = 20;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+#endif
+
+#ifdef CONFIG_ATMEL_NAND_HWECC
+#ifdef CONFIG_ATMEL_NAND_HW_PMECC
+ ret = atmel_pmecc_nand_init_params(nand, mtd);
+#endif
+#endif
+
+ return ret;
+}
+
+void nand_init(void)
+{
+ mtd = nand_to_mtd(&nand_chip);
+ mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE;
+ mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE;
+ nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE;
+ nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE;
+ board_nand_init(&nand_chip);
+
+#ifdef CONFIG_SPL_NAND_ECC
+ if (nand_chip.ecc.mode == NAND_ECC_SOFT) {
+ nand_chip.ecc.calculate = nand_calculate_ecc;
+ nand_chip.ecc.correct = nand_correct_data;
+ }
+#endif
+
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, 0);
+}
+
+void nand_deselect(void)
+{
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, -1);
+}
+
+#include "nand_spl_loaders.c"
+
+#else
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
+
+int atmel_nand_chip_init(int devnum, ulong base_addr)
+{
+ int ret;
+ struct nand_chip *nand = &nand_chip[devnum];
+ struct mtd_info *mtd = nand_to_mtd(nand);
+
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
+
+#ifdef CONFIG_NAND_ECC_BCH
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
+ nand->ecc.mode = NAND_ECC_SOFT;
+#endif
+#ifdef CONFIG_SYS_NAND_DBW_16
+ nand->options = NAND_BUSWIDTH_16;
+#endif
+ nand->cmd_ctrl = at91_nand_hwcontrol;
+#ifdef CONFIG_SYS_NAND_READY_PIN
+ nand->dev_ready = at91_nand_ready;
+#endif
+ nand->chip_delay = 75;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+#endif
+
+ ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_ATMEL_NAND_HWECC
+#ifdef CONFIG_ATMEL_NAND_HW_PMECC
+ ret = atmel_pmecc_nand_init_params(nand, mtd);
+#else
+ ret = atmel_hwecc_nand_init_param(nand, mtd);
+#endif
+ if (ret)
+ return ret;
+#endif
+
+ ret = nand_scan_tail(mtd);
+ if (!ret)
+ nand_register(devnum, mtd);
+
+ return ret;
+}
+
+void board_nand_init(void)
+{
+ int i;
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ if (atmel_nand_chip_init(i, base_addr[i]))
+ dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip",
+ i);
+}
+#endif /* CONFIG_SPL_BUILD */
diff --git a/drivers/mtd/nand/raw/atmel_nand_ecc.h b/drivers/mtd/nand/raw/atmel_nand_ecc.h
new file mode 100644
index 0000000000..05eeedb3f8
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel_nand_ecc.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Error Corrected Code Controller (ECC) - System peripherals regsters.
+ * Based on AT91SAM9260 datasheet revision B.
+ */
+
+#ifndef ATMEL_NAND_ECC_H
+#define ATMEL_NAND_ECC_H
+
+#define ATMEL_ECC_CR 0x00 /* Control register */
+#define ATMEL_ECC_RST (1 << 0) /* Reset parity */
+
+#define ATMEL_ECC_MR 0x04 /* Mode register */
+#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */
+#define ATMEL_ECC_PAGESIZE_528 (0)
+#define ATMEL_ECC_PAGESIZE_1056 (1)
+#define ATMEL_ECC_PAGESIZE_2112 (2)
+#define ATMEL_ECC_PAGESIZE_4224 (3)
+
+#define ATMEL_ECC_SR 0x08 /* Status register */
+#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */
+#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
+#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
+
+#define ATMEL_ECC_PR 0x0c /* Parity register */
+#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */
+#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */
+
+#define ATMEL_ECC_NPR 0x10 /* NParity register */
+#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+
+/* Register access macros for PMECC */
+#define pmecc_readl(addr, reg) \
+ readl(&addr->reg)
+
+#define pmecc_readb(addr, reg) \
+ readb(&addr->reg)
+
+#define pmecc_writel(addr, reg, value) \
+ writel((value), &addr->reg)
+
+/* PMECC Register Definitions */
+#define PMECC_MAX_SECTOR_NUM 8
+struct pmecc_regs {
+ u32 cfg; /* 0x00 PMECC Configuration Register */
+ u32 sarea; /* 0x04 PMECC Spare Area Size Register */
+ u32 saddr; /* 0x08 PMECC Start Address Register */
+ u32 eaddr; /* 0x0C PMECC End Address Register */
+ u32 clk; /* 0x10 PMECC Clock Control Register */
+ u32 ctrl; /* 0x14 PMECC Control Register */
+ u32 sr; /* 0x18 PMECC Status Register */
+ u32 ier; /* 0x1C PMECC Interrupt Enable Register */
+ u32 idr; /* 0x20 PMECC Interrupt Disable Register */
+ u32 imr; /* 0x24 PMECC Interrupt Mask Register */
+ u32 isr; /* 0x28 PMECC Interrupt Status Register */
+ u32 reserved0[5]; /* 0x2C-0x3C Reserved */
+
+ /* 0x40 + sector_num * (0x40), Redundancy Registers */
+ struct {
+#ifdef CONFIG_SAMA5D2
+ u8 ecc[56]; /* PMECC Generated Redundancy Byte Per Sector */
+ u32 reserved1[2];
+#else
+ u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */
+ u32 reserved1[5];
+#endif
+ } ecc_port[PMECC_MAX_SECTOR_NUM];
+
+ /* 0x240 + sector_num * (0x40) Remainder Registers */
+ struct {
+#ifdef CONFIG_SAMA5D2
+ u32 rem[16];
+#else
+ u32 rem[12];
+ u32 reserved2[4];
+#endif
+ } rem_port[PMECC_MAX_SECTOR_NUM];
+ u32 reserved3[16]; /* 0x440-0x47C Reserved */
+};
+
+/* For PMECC Configuration Register */
+#define PMECC_CFG_BCH_ERR2 (0 << 0)
+#define PMECC_CFG_BCH_ERR4 (1 << 0)
+#define PMECC_CFG_BCH_ERR8 (2 << 0)
+#define PMECC_CFG_BCH_ERR12 (3 << 0)
+#define PMECC_CFG_BCH_ERR24 (4 << 0)
+#define PMECC_CFG_BCH_ERR32 (5 << 0)
+
+#define PMECC_CFG_SECTOR512 (0 << 4)
+#define PMECC_CFG_SECTOR1024 (1 << 4)
+
+#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
+#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
+#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
+#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
+
+#define PMECC_CFG_READ_OP (0 << 12)
+#define PMECC_CFG_WRITE_OP (1 << 12)
+
+#define PMECC_CFG_SPARE_ENABLE (1 << 16)
+#define PMECC_CFG_SPARE_DISABLE (0 << 16)
+
+#define PMECC_CFG_AUTO_ENABLE (1 << 20)
+#define PMECC_CFG_AUTO_DISABLE (0 << 20)
+
+/* For PMECC Clock Control Register */
+#define PMECC_CLK_133MHZ (2 << 0)
+
+/* For PMECC Control Register */
+#define PMECC_CTRL_RST (1 << 0)
+#define PMECC_CTRL_DATA (1 << 1)
+#define PMECC_CTRL_USER (1 << 2)
+#define PMECC_CTRL_ENABLE (1 << 4)
+#define PMECC_CTRL_DISABLE (1 << 5)
+
+/* For PMECC Status Register */
+#define PMECC_SR_BUSY (1 << 0)
+#define PMECC_SR_ENABLE (1 << 4)
+
+/* PMERRLOC Register Definitions */
+struct pmecc_errloc_regs {
+ u32 elcfg; /* 0x00 Error Location Configuration Register */
+ u32 elprim; /* 0x04 Error Location Primitive Register */
+ u32 elen; /* 0x08 Error Location Enable Register */
+ u32 eldis; /* 0x0C Error Location Disable Register */
+ u32 elsr; /* 0x10 Error Location Status Register */
+ u32 elier; /* 0x14 Error Location Interrupt Enable Register */
+ u32 elidr; /* 0x08 Error Location Interrupt Disable Register */
+ u32 elimr; /* 0x0C Error Location Interrupt Mask Register */
+ u32 elisr; /* 0x20 Error Location Interrupt Status Register */
+ u32 reserved0; /* 0x24 Reserved */
+#ifdef CONFIG_SAMA5D2
+ u32 sigma[33]; /* 0x28-0xA8 Error Location Sigma Registers */
+ u32 el[32]; /* 0xAC-0x128 Error Location Registers */
+
+ /*
+ * 0x12C-0x1FC:
+ * Reserved for SAMA5D2.
+ */
+ u32 reserved1[53];
+#else
+ u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */
+ u32 el[24]; /* 0x8C-0xE8 Error Location Registers */
+ u32 reserved1[5]; /* 0xEC-0xFC Reserved */
+#endif
+
+ /*
+ * SAMA5 chip HSMC registers start here. But for 9X5 chip it is just
+ * reserved.
+ *
+ * Offset 0x00-0xF8:
+ */
+ u32 reserved2[63];
+
+ /*
+ * Offset 0xFC:
+ * PMECC version for AT91SAM9X5, AT91SAM9N12.
+ * HSMC version for SAMA5D3, SAMA5D4. Can refer as PMECC version.
+ */
+ u32 version;
+};
+
+/* For Error Location Configuration Register */
+#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
+
+/* For Error Location Disable Register */
+#define PMERRLOC_DISABLE (1 << 0)
+
+/* For Error Location Interrupt Status Register */
+#ifdef CONFIG_SAMA5D2
+#define PMERRLOC_ERR_NUM_MASK (0x3f << 8)
+#else
+#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
+#endif
+
+#define PMERRLOC_CALC_DONE (1 << 0)
+
+/* PMECC IP version */
+#define PMECC_VERSION_SAMA5D2 0x210
+#define PMECC_VERSION_SAMA5D4 0x113
+#define PMECC_VERSION_SAMA5D3 0x112
+#define PMECC_VERSION_AT91SAM9N12 0x102
+#define PMECC_VERSION_AT91SAM9X5 0x101
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13 13
+#define PMECC_GF_DIMENSION_14 14
+
+/* Primitive Polynomial used by PMECC */
+#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
+#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
+
+#define PMECC_INDEX_TABLE_SIZE_512 0x2000
+#define PMECC_INDEX_TABLE_SIZE_1024 0x4000
+
+#define PMECC_MAX_TIMEOUT_US (100 * 1000)
+
+/* Reserved bytes in oob area */
+#define PMECC_OOB_RESERVED_BYTES 2
+
+#endif
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
new file mode 100644
index 0000000000..e6a84a52b4
--- /dev/null
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -0,0 +1,833 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NAND driver for TI DaVinci based boards.
+ *
+ * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
+ *
+ * Based on Linux DaVinci NAND driver by TI. Original copyright follows:
+ */
+
+/*
+ *
+ * linux/drivers/mtd/nand/raw/nand_davinci.c
+ *
+ * NAND Flash Driver
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * DaVinci board which utilizes the Samsung k9k2g08 part.
+ *
+ Modifications:
+ ver. 1.0: Feb 2005, Vinod/Sudhakar
+ -
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <nand.h>
+#include <asm/ti-common/davinci_nand.h>
+
+/* Definitions for 4-bit hardware ECC */
+#define NAND_TIMEOUT 10240
+#define NAND_ECC_BUSY 0xC
+#define NAND_4BITECC_MASK 0x03FF03FF
+#define EMIF_NANDFSR_ECC_STATE_MASK 0x00000F00
+#define ECC_STATE_NO_ERR 0x0
+#define ECC_STATE_TOO_MANY_ERRS 0x1
+#define ECC_STATE_ERR_CORR_COMP_P 0x2
+#define ECC_STATE_ERR_CORR_COMP_N 0x3
+
+/*
+ * Exploit the little endianness of the ARM to do multi-byte transfers
+ * per device read. This can perform over twice as quickly as individual
+ * byte transfers when buffer alignment is conducive.
+ *
+ * NOTE: This only works if the NAND is not connected to the 2 LSBs of
+ * the address bus. On Davinci EVM platforms this has always been true.
+ */
+static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ const u32 *nand = chip->IO_ADDR_R;
+
+ /* Make sure that buf is 32 bit aligned */
+ if (((int)buf & 0x3) != 0) {
+ if (((int)buf & 0x1) != 0) {
+ if (len) {
+ *buf = readb(nand);
+ buf += 1;
+ len--;
+ }
+ }
+
+ if (((int)buf & 0x3) != 0) {
+ if (len >= 2) {
+ *(u16 *)buf = readw(nand);
+ buf += 2;
+ len -= 2;
+ }
+ }
+ }
+
+ /* copy aligned data */
+ while (len >= 4) {
+ *(u32 *)buf = __raw_readl(nand);
+ buf += 4;
+ len -= 4;
+ }
+
+ /* mop up any remaining bytes */
+ if (len) {
+ if (len >= 2) {
+ *(u16 *)buf = readw(nand);
+ buf += 2;
+ len -= 2;
+ }
+
+ if (len)
+ *buf = readb(nand);
+ }
+}
+
+static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ const u32 *nand = chip->IO_ADDR_W;
+
+ /* Make sure that buf is 32 bit aligned */
+ if (((int)buf & 0x3) != 0) {
+ if (((int)buf & 0x1) != 0) {
+ if (len) {
+ writeb(*buf, nand);
+ buf += 1;
+ len--;
+ }
+ }
+
+ if (((int)buf & 0x3) != 0) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+ }
+ }
+
+ /* copy aligned data */
+ while (len >= 4) {
+ __raw_writel(*(u32 *)buf, nand);
+ buf += 4;
+ len -= 4;
+ }
+
+ /* mop up any remaining bytes */
+ if (len) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+
+ if (len)
+ writeb(*buf, nand);
+ }
+}
+
+static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ IO_ADDR_W &= ~(MASK_ALE|MASK_CLE);
+
+ if (ctrl & NAND_CLE)
+ IO_ADDR_W |= MASK_CLE;
+ if (ctrl & NAND_ALE)
+ IO_ADDR_W |= MASK_ALE;
+ this->IO_ADDR_W = (void __iomem *) IO_ADDR_W;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, IO_ADDR_W);
+}
+
+#ifdef CONFIG_SYS_NAND_HW_ECC
+
+static u_int32_t nand_davinci_readecc(struct mtd_info *mtd)
+{
+ u_int32_t ecc = 0;
+
+ ecc = __raw_readl(&(davinci_emif_regs->nandfecc[
+ CONFIG_SYS_NAND_CS - 2]));
+
+ return ecc;
+}
+
+static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ u_int32_t val;
+
+ /* reading the ECC result register resets the ECC calculation */
+ nand_davinci_readecc(mtd);
+
+ val = __raw_readl(&davinci_emif_regs->nandfcr);
+ val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS);
+ val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS);
+ __raw_writel(val, &davinci_emif_regs->nandfcr);
+}
+
+static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ u_int32_t tmp;
+
+ tmp = nand_davinci_readecc(mtd);
+
+ /* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits
+ * and shifting. RESERVED bits are 31 to 28 and 15 to 12. */
+ tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4);
+
+ /* Invert so that erased block ECC is correct */
+ tmp = ~tmp;
+
+ *ecc_code++ = tmp;
+ *ecc_code++ = tmp >> 8;
+ *ecc_code++ = tmp >> 16;
+
+ /* NOTE: the above code matches mainline Linux:
+ * .PQR.stu ==> ~PQRstu
+ *
+ * MontaVista/TI kernels encode those bytes differently, use
+ * complicated (and allegedly sometimes-wrong) correction code,
+ * and usually shipped with U-Boot that uses software ECC:
+ * .PQR.stu ==> PsQRtu
+ *
+ * If you need MV/TI compatible NAND I/O in U-Boot, it should
+ * be possible to (a) change the mangling above, (b) reverse
+ * that mangling in nand_davinci_correct_data() below.
+ */
+
+ return 0;
+}
+
+static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) |
+ (read_ecc[2] << 16);
+ u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) |
+ (calc_ecc[2] << 16);
+ u_int32_t diff = ecc_calc ^ ecc_nand;
+
+ if (diff) {
+ if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
+ /* Correctable error */
+ if ((diff >> (12 + 3)) < this->ecc.size) {
+ uint8_t find_bit = 1 << ((diff >> 12) & 7);
+ uint32_t find_byte = diff >> (12 + 3);
+
+ dat[find_byte] ^= find_bit;
+ pr_debug("Correcting single "
+ "bit ECC error at offset: %d, bit: "
+ "%d\n", find_byte, find_bit);
+ return 1;
+ } else {
+ return -EBADMSG;
+ }
+ } else if (!(diff & (diff - 1))) {
+ /* Single bit ECC error in the ECC itself,
+ nothing to fix */
+ pr_debug("Single bit ECC error in " "ECC.\n");
+ return 1;
+ } else {
+ /* Uncorrectable error */
+ pr_debug("ECC UNCORRECTED_ERROR 1\n");
+ return -EBADMSG;
+ }
+ }
+ return 0;
+}
+#endif /* CONFIG_SYS_NAND_HW_ECC */
+
+#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST
+static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = {
+#if defined(CONFIG_SYS_NAND_PAGE_2K)
+ .eccbytes = 40,
+#ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ },
+ .oobfree = {
+ {2, 4}, {16, 6}, {32, 6}, {48, 6},
+ },
+#else
+ .eccpos = {
+ 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63,
+ },
+ .oobfree = {
+ {.offset = 2, .length = 22, },
+ },
+#endif /* #ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC */
+#elif defined(CONFIG_SYS_NAND_PAGE_4K)
+ .eccbytes = 80,
+ .eccpos = {
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ },
+ .oobfree = {
+ {.offset = 2, .length = 46, },
+ },
+#endif
+};
+
+#if defined CONFIG_KEYSTONE_RBL_NAND
+static struct nand_ecclayout nand_keystone_rbl_4bit_layout_oobfirst = {
+#if defined(CONFIG_SYS_NAND_PAGE_2K)
+ .eccbytes = 40,
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ },
+ .oobfree = {
+ {.offset = 2, .length = 4, },
+ {.offset = 16, .length = 6, },
+ {.offset = 32, .length = 6, },
+ {.offset = 48, .length = 6, },
+ },
+#elif defined(CONFIG_SYS_NAND_PAGE_4K)
+ .eccbytes = 80,
+ .eccpos = {
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ },
+ .oobfree = {
+ {.offset = 2, .length = 4, },
+ {.offset = 16, .length = 6, },
+ {.offset = 32, .length = 6, },
+ {.offset = 48, .length = 6, },
+ {.offset = 64, .length = 6, },
+ {.offset = 80, .length = 6, },
+ {.offset = 96, .length = 6, },
+ {.offset = 112, .length = 6, },
+ },
+#endif
+};
+
+#ifdef CONFIG_SYS_NAND_PAGE_2K
+#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 11
+#elif defined(CONFIG_SYS_NAND_PAGE_4K)
+#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 12
+#endif
+
+/**
+ * nand_davinci_write_page - write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ * @raw: use _raw version of write_page
+ */
+static int nand_davinci_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, int data_len,
+ const uint8_t *buf, int oob_required,
+ int page, int raw)
+{
+ int status;
+ int ret = 0;
+ struct nand_ecclayout *saved_ecc_layout;
+
+ /* save current ECC layout and assign Keystone RBL ECC layout */
+ if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
+ saved_ecc_layout = chip->ecc.layout;
+ chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst;
+ mtd->oobavail = chip->ecc.layout->oobavail;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (unlikely(raw)) {
+ status = chip->ecc.write_page_raw(mtd, chip, buf,
+ oob_required, page);
+ } else {
+ status = chip->ecc.write_page(mtd, chip, buf,
+ oob_required, page);
+ }
+
+ if (status < 0) {
+ ret = status;
+ goto err;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ if (status & NAND_STATUS_FAIL) {
+ ret = -EIO;
+ goto err;
+ }
+
+err:
+ /* restore ECC layout */
+ if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
+ chip->ecc.layout = saved_ecc_layout;
+ mtd->oobavail = saved_ecc_layout->oobavail;
+ }
+
+ return ret;
+}
+
+/**
+ * nand_davinci_read_page_hwecc - hardware ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
+ */
+static int nand_davinci_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint32_t *eccpos;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ struct nand_ecclayout *saved_ecc_layout = chip->ecc.layout;
+
+ /* save current ECC layout and assign Keystone RBL ECC layout */
+ if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
+ chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst;
+ mtd->oobavail = chip->ecc.layout->oobavail;
+ }
+
+ eccpos = chip->ecc.layout->eccpos;
+
+ /* Read the OOB area first */
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+
+ /* restore ECC layout */
+ if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
+ chip->ecc.layout = saved_ecc_layout;
+ mtd->oobavail = saved_ecc_layout->oobavail;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_KEYSTONE_RBL_NAND */
+
+static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ u32 val;
+
+ switch (mode) {
+ case NAND_ECC_WRITE:
+ case NAND_ECC_READ:
+ /*
+ * Start a new ECC calculation for reading or writing 512 bytes
+ * of data.
+ */
+ val = __raw_readl(&davinci_emif_regs->nandfcr);
+ val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK;
+ val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS);
+ val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS);
+ val |= DAVINCI_NANDFCR_4BIT_ECC_START;
+ __raw_writel(val, &davinci_emif_regs->nandfcr);
+ break;
+ case NAND_ECC_READSYN:
+ val = __raw_readl(&davinci_emif_regs->nand4bitecc[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4])
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ ecc[i] = __raw_readl(&davinci_emif_regs->nand4bitecc[i]) &
+ NAND_4BITECC_MASK;
+ }
+
+ return 0;
+}
+
+static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd,
+ const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ unsigned int hw_4ecc[4];
+ unsigned int i;
+
+ nand_davinci_4bit_readecc(mtd, hw_4ecc);
+
+ /*Convert 10 bit ecc value to 8 bit */
+ for (i = 0; i < 2; i++) {
+ unsigned int hw_ecc_low = hw_4ecc[i * 2];
+ unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1];
+
+ /* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */
+ *ecc_code++ = hw_ecc_low & 0xFF;
+
+ /*
+ * Take 2 bits as LSB bits from val1 (count1=0) or val5
+ * (count1=1) and 6 bits from val2 (count1=0) or
+ * val5 (count1=1)
+ */
+ *ecc_code++ =
+ ((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC);
+
+ /*
+ * Take 4 bits from val2 (count1=0) or val5 (count1=1) and
+ * 4 bits from val3 (count1=0) or val6 (count1=1)
+ */
+ *ecc_code++ =
+ ((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0);
+
+ /*
+ * Take 6 bits from val3(count1=0) or val6 (count1=1) and
+ * 2 bits from val4 (count1=0) or val7 (count1=1)
+ */
+ *ecc_code++ =
+ ((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0);
+
+ /* Take 8 bits from val4 (count1=0) or val7 (count1=1) */
+ *ecc_code++ = (hw_ecc_hi >> 18) & 0xFF;
+ }
+
+ return 0;
+}
+
+static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ int i;
+ unsigned int hw_4ecc[4];
+ unsigned int iserror;
+ unsigned short *ecc16;
+ unsigned int numerrors, erroraddress, errorvalue;
+ u32 val;
+
+ /*
+ * Check for an ECC where all bytes are 0xFF. If this is the case, we
+ * will assume we are looking at an erased page and we should ignore
+ * the ECC.
+ */
+ for (i = 0; i < 10; i++) {
+ if (read_ecc[i] != 0xFF)
+ break;
+ }
+ if (i == 10)
+ return 0;
+
+ /* Convert 8 bit in to 10 bit */
+ ecc16 = (unsigned short *)&read_ecc[0];
+
+ /*
+ * Write the parity values in the NAND Flash 4-bit ECC Load register.
+ * Write each parity value one at a time starting from 4bit_ecc_val8
+ * to 4bit_ecc_val1.
+ */
+
+ /*Take 2 bits from 8th byte and 8 bits from 9th byte */
+ __raw_writel(((ecc16[4]) >> 6) & 0x3FF,
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 4 bits from 7th byte and 6 bits from 8th byte */
+ __raw_writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0),
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 6 bits from 6th byte and 4 bits from 7th byte */
+ __raw_writel((ecc16[3] >> 2) & 0x3FF,
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 8 bits from 5th byte and 2 bits from 6th byte */
+ __raw_writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300),
+ &davinci_emif_regs->nand4biteccload);
+
+ /*Take 2 bits from 3rd byte and 8 bits from 4th byte */
+ __raw_writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC),
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */
+ __raw_writel(((ecc16[1]) >> 4) & 0x3FF,
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 6 bits from 1st byte and 4 bits from 2nd byte */
+ __raw_writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0),
+ &davinci_emif_regs->nand4biteccload);
+
+ /* Take 10 bits from 0th and 1st bytes */
+ __raw_writel((ecc16[0]) & 0x3FF,
+ &davinci_emif_regs->nand4biteccload);
+
+ /*
+ * Perform a dummy read to the EMIF Revision Code and Status register.
+ * This is required to ensure time for syndrome calculation after
+ * writing the ECC values in previous step.
+ */
+
+ val = __raw_readl(&davinci_emif_regs->nandfsr);
+
+ /*
+ * Read the syndrome from the NAND Flash 4-Bit ECC 1-4 registers.
+ * A syndrome value of 0 means no bit errors. If the syndrome is
+ * non-zero then go further otherwise return.
+ */
+ nand_davinci_4bit_readecc(mtd, hw_4ecc);
+
+ if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3]))
+ return 0;
+
+ /*
+ * Clear any previous address calculation by doing a dummy read of an
+ * error address register.
+ */
+ val = __raw_readl(&davinci_emif_regs->nanderradd1);
+
+ /*
+ * Set the addr_calc_st bit(bit no 13) in the NAND Flash Control
+ * register to 1.
+ */
+ __raw_writel(DAVINCI_NANDFCR_4BIT_CALC_START,
+ &davinci_emif_regs->nandfcr);
+
+ /*
+ * Wait for the corr_state field (bits 8 to 11) in the
+ * NAND Flash Status register to be not equal to 0x0, 0x1, 0x2, or 0x3.
+ * Otherwise ECC calculation has not even begun and the next loop might
+ * fail because of a false positive!
+ */
+ i = NAND_TIMEOUT;
+ do {
+ val = __raw_readl(&davinci_emif_regs->nandfsr);
+ val &= 0xc00;
+ i--;
+ } while ((i > 0) && !val);
+
+ /*
+ * Wait for the corr_state field (bits 8 to 11) in the
+ * NAND Flash Status register to be equal to 0x0, 0x1, 0x2, or 0x3.
+ */
+ i = NAND_TIMEOUT;
+ do {
+ val = __raw_readl(&davinci_emif_regs->nandfsr);
+ val &= 0xc00;
+ i--;
+ } while ((i > 0) && val);
+
+ iserror = __raw_readl(&davinci_emif_regs->nandfsr);
+ iserror &= EMIF_NANDFSR_ECC_STATE_MASK;
+ iserror = iserror >> 8;
+
+ /*
+ * ECC_STATE_TOO_MANY_ERRS (0x1) means errors cannot be
+ * corrected (five or more errors). The number of errors
+ * calculated (err_num field) differs from the number of errors
+ * searched. ECC_STATE_ERR_CORR_COMP_P (0x2) means error
+ * correction complete (errors on bit 8 or 9).
+ * ECC_STATE_ERR_CORR_COMP_N (0x3) means error correction
+ * complete (error exists).
+ */
+
+ if (iserror == ECC_STATE_NO_ERR) {
+ val = __raw_readl(&davinci_emif_regs->nanderrval1);
+ return 0;
+ } else if (iserror == ECC_STATE_TOO_MANY_ERRS) {
+ val = __raw_readl(&davinci_emif_regs->nanderrval1);
+ return -EBADMSG;
+ }
+
+ numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16)
+ & 0x3) + 1;
+
+ /* Read the error address, error value and correct */
+ for (i = 0; i < numerrors; i++) {
+ if (i > 1) {
+ erroraddress =
+ ((__raw_readl(&davinci_emif_regs->nanderradd2) >>
+ (16 * (i & 1))) & 0x3FF);
+ erroraddress = ((512 + 7) - erroraddress);
+ errorvalue =
+ ((__raw_readl(&davinci_emif_regs->nanderrval2) >>
+ (16 * (i & 1))) & 0xFF);
+ } else {
+ erroraddress =
+ ((__raw_readl(&davinci_emif_regs->nanderradd1) >>
+ (16 * (i & 1))) & 0x3FF);
+ erroraddress = ((512 + 7) - erroraddress);
+ errorvalue =
+ ((__raw_readl(&davinci_emif_regs->nanderrval1) >>
+ (16 * (i & 1))) & 0xFF);
+ }
+ /* xor the corrupt data with error value */
+ if (erroraddress < 512)
+ dat[erroraddress] ^= errorvalue;
+ }
+
+ return numerrors;
+}
+#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */
+
+static int nand_davinci_dev_ready(struct mtd_info *mtd)
+{
+ return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1;
+}
+
+static void nand_flash_init(void)
+{
+ /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS!
+ * Instead, have your board_init() set EMIF timings, based on its
+ * knowledge of the clocks and what devices are hooked up ... and
+ * don't even do that unless no UBL handled it.
+ */
+#ifdef CONFIG_SOC_DM644X
+ u_int32_t acfg1 = 0x3ffffffc;
+
+ /*------------------------------------------------------------------*
+ * NAND FLASH CHIP TIMEOUT @ 459 MHz *
+ * *
+ * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz *
+ * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns *
+ * *
+ *------------------------------------------------------------------*/
+ acfg1 = 0
+ | (0 << 31) /* selectStrobe */
+ | (0 << 30) /* extWait */
+ | (1 << 26) /* writeSetup 10 ns */
+ | (3 << 20) /* writeStrobe 40 ns */
+ | (1 << 17) /* writeHold 10 ns */
+ | (1 << 13) /* readSetup 10 ns */
+ | (5 << 7) /* readStrobe 60 ns */
+ | (1 << 4) /* readHold 10 ns */
+ | (3 << 2) /* turnAround ?? ns */
+ | (0 << 0) /* asyncSize 8-bit bus */
+ ;
+
+ __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */
+
+ /* NAND flash on CS2 */
+ __raw_writel(0x00000101, &davinci_emif_regs->nandfcr);
+#endif
+}
+
+void davinci_nand_init(struct nand_chip *nand)
+{
+#if defined CONFIG_KEYSTONE_RBL_NAND
+ int i;
+ struct nand_ecclayout *layout;
+
+ layout = &nand_keystone_rbl_4bit_layout_oobfirst;
+ layout->oobavail = 0;
+ for (i = 0; layout->oobfree[i].length &&
+ i < ARRAY_SIZE(layout->oobfree); i++)
+ layout->oobavail += layout->oobfree[i].length;
+
+ nand->write_page = nand_davinci_write_page;
+ nand->ecc.read_page = nand_davinci_read_page_hwecc;
+#endif
+ nand->chip_delay = 0;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+#endif
+#ifdef CONFIG_SYS_NAND_NO_SUBPAGE_WRITE
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+#endif
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
+ nand->options |= NAND_BUSWIDTH_16;
+#endif
+#ifdef CONFIG_SYS_NAND_HW_ECC
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
+ nand->ecc.calculate = nand_davinci_calculate_ecc;
+ nand->ecc.correct = nand_davinci_correct_data;
+ nand->ecc.hwctl = nand_davinci_enable_hwecc;
+#else
+ nand->ecc.mode = NAND_ECC_SOFT;
+#endif /* CONFIG_SYS_NAND_HW_ECC */
+#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST
+ nand->ecc.mode = NAND_ECC_HW_OOB_FIRST;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 10;
+ nand->ecc.strength = 4;
+ nand->ecc.calculate = nand_davinci_4bit_calculate_ecc;
+ nand->ecc.correct = nand_davinci_4bit_correct_data;
+ nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc;
+ nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst;
+#endif
+ /* Set address of hardware control function */
+ nand->cmd_ctrl = nand_davinci_hwcontrol;
+
+ nand->read_buf = nand_davinci_read_buf;
+ nand->write_buf = nand_davinci_write_buf;
+
+ nand->dev_ready = nand_davinci_dev_ready;
+
+ nand_flash_init();
+}
+
+int board_nand_init(struct nand_chip *chip) __attribute__((weak));
+
+int board_nand_init(struct nand_chip *chip)
+{
+ davinci_nand_init(chip);
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
new file mode 100644
index 0000000000..d1cac063f4
--- /dev/null
+++ b/drivers/mtd/nand/raw/denali.c
@@ -0,0 +1,1371 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Panasonic Corporation
+ * Copyright (C) 2013-2014, Altera Corporation <www.altera.com>
+ * Copyright (C) 2009-2010, Intel Corporation and its suppliers.
+ */
+
+#include <dm.h>
+#include <nand.h>
+#include <linux/bitfield.h>
+#include <linux/dma-direction.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+
+#include "denali.h"
+
+static dma_addr_t dma_map_single(void *dev, void *ptr, size_t size,
+ enum dma_data_direction dir)
+{
+ unsigned long addr = (unsigned long)ptr;
+
+ size = ALIGN(size, ARCH_DMA_MINALIGN);
+
+ if (dir == DMA_FROM_DEVICE)
+ invalidate_dcache_range(addr, addr + size);
+ else
+ flush_dcache_range(addr, addr + size);
+
+ return addr;
+}
+
+static void dma_unmap_single(void *dev, dma_addr_t addr, size_t size,
+ enum dma_data_direction dir)
+{
+ size = ALIGN(size, ARCH_DMA_MINALIGN);
+
+ if (dir != DMA_TO_DEVICE)
+ invalidate_dcache_range(addr, addr + size);
+}
+
+static int dma_mapping_error(void *dev, dma_addr_t addr)
+{
+ return 0;
+}
+
+#define DENALI_NAND_NAME "denali-nand"
+
+/* for Indexed Addressing */
+#define DENALI_INDEXED_CTRL 0x00
+#define DENALI_INDEXED_DATA 0x10
+
+#define DENALI_MAP00 (0 << 26) /* direct access to buffer */
+#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
+#define DENALI_MAP10 (2 << 26) /* high-level control plane */
+#define DENALI_MAP11 (3 << 26) /* direct controller access */
+
+/* MAP11 access cycle type */
+#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */
+#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
+#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
+
+/* MAP10 commands */
+#define DENALI_ERASE 0x01
+
+#define DENALI_BANK(denali) ((denali)->active_bank << 24)
+
+#define DENALI_INVALID_BANK -1
+#define DENALI_NR_BANKS 4
+
+/*
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
+ * clk_x is an integral multiple N of the core clk. The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
+ * to the largest value to make it work with any possible configuration.
+ */
+#define DENALI_CLK_X_MULT 6
+
+static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
+}
+
+/*
+ * Direct Addressing - the slave address forms the control information (command
+ * type, bank, block, and page address). The slave data is the actual data to
+ * be transferred. This mode requires 28 bits of address region allocated.
+ */
+static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr)
+{
+ return ioread32(denali->host + addr);
+}
+
+static void denali_direct_write(struct denali_nand_info *denali, u32 addr,
+ u32 data)
+{
+ iowrite32(data, denali->host + addr);
+}
+
+/*
+ * Indexed Addressing - address translation module intervenes in passing the
+ * control information. This mode reduces the required address range. The
+ * control information and transferred data are latched by the registers in
+ * the translation module.
+ */
+static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr)
+{
+ iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
+ return ioread32(denali->host + DENALI_INDEXED_DATA);
+}
+
+static void denali_indexed_write(struct denali_nand_info *denali, u32 addr,
+ u32 data)
+{
+ iowrite32(addr, denali->host + DENALI_INDEXED_CTRL);
+ iowrite32(data, denali->host + DENALI_INDEXED_DATA);
+}
+
+/*
+ * Use the configuration feature register to determine the maximum number of
+ * banks that the hardware supports.
+ */
+static void denali_detect_max_banks(struct denali_nand_info *denali)
+{
+ uint32_t features = ioread32(denali->reg + FEATURES);
+
+ denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features);
+
+ /* the encoding changed from rev 5.0 to 5.1 */
+ if (denali->revision < 0x0501)
+ denali->max_banks <<= 1;
+}
+
+static void __maybe_unused denali_enable_irq(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(U32_MAX, denali->reg + INTR_EN(i));
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
+}
+
+static void __maybe_unused denali_disable_irq(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ iowrite32(0, denali->reg + INTR_EN(i));
+ iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
+}
+
+static void denali_clear_irq(struct denali_nand_info *denali,
+ int bank, uint32_t irq_status)
+{
+ /* write one to clear bits */
+ iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
+}
+
+static void denali_clear_irq_all(struct denali_nand_info *denali)
+{
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++)
+ denali_clear_irq(denali, i, U32_MAX);
+}
+
+static void __denali_check_irq(struct denali_nand_info *denali)
+{
+ uint32_t irq_status;
+ int i;
+
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
+ irq_status = ioread32(denali->reg + INTR_STATUS(i));
+ denali_clear_irq(denali, i, irq_status);
+
+ if (i != denali->active_bank)
+ continue;
+
+ denali->irq_status |= irq_status;
+ }
+}
+
+static void denali_reset_irq(struct denali_nand_info *denali)
+{
+ denali->irq_status = 0;
+ denali->irq_mask = 0;
+}
+
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+ uint32_t irq_mask)
+{
+ unsigned long time_left = 1000000;
+
+ while (time_left) {
+ __denali_check_irq(denali);
+
+ if (irq_mask & denali->irq_status)
+ return denali->irq_status;
+ udelay(1);
+ time_left--;
+ }
+
+ if (!time_left) {
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+ irq_mask);
+ return 0;
+ }
+
+ return denali->irq_status;
+}
+
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
+{
+ __denali_check_irq(denali);
+
+ return denali->irq_status;
+}
+
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = denali->host_read(denali, addr);
+}
+
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ int i;
+
+ for (i = 0; i < len; i++)
+ denali->host_write(denali, addr, buf[i]);
+}
+
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ uint16_t *buf16 = (uint16_t *)buf;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = denali->host_read(denali, addr);
+}
+
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali);
+ const uint16_t *buf16 = (const uint16_t *)buf;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ denali->host_write(denali, addr, buf16[i]);
+}
+
+static uint8_t denali_read_byte(struct mtd_info *mtd)
+{
+ uint8_t byte;
+
+ denali_read_buf(mtd, &byte, 1);
+
+ return byte;
+}
+
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ denali_write_buf(mtd, &byte, 1);
+}
+
+static uint16_t denali_read_word(struct mtd_info *mtd)
+{
+ uint16_t word;
+
+ denali_read_buf16(mtd, (uint8_t *)&word, 2);
+
+ return word;
+}
+
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t type;
+
+ if (ctrl & NAND_CLE)
+ type = DENALI_MAP11_CMD;
+ else if (ctrl & NAND_ALE)
+ type = DENALI_MAP11_ADDR;
+ else
+ return;
+
+ /*
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
+ * irq_status must be cleared here to catch the R/B# interrupt later.
+ */
+ if (ctrl & NAND_CTRL_CHANGE)
+ denali_reset_irq(denali);
+
+ denali->host_write(denali, DENALI_BANK(denali) | type, dat);
+}
+
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
+}
+
+static int denali_check_erased_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ unsigned long uncor_ecc_flags,
+ unsigned int max_bitflips)
+{
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int i, ret, stat;
+
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+ chip->ecc.total);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ecc_steps; i++) {
+ if (!(uncor_ecc_flags & BIT(i)))
+ continue;
+
+ stat = nand_check_erased_ecc_chunk(buf, ecc_size,
+ ecc_code, ecc_bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+
+ buf += ecc_size;
+ ecc_code += ecc_bytes;
+ }
+
+ return max_bitflips;
+}
+
+static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int bank = denali->active_bank;
+ uint32_t ecc_cor;
+ unsigned int max_bitflips;
+
+ ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
+ ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
+
+ if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
+ /*
+ * This flag is set when uncorrectable error occurs at least in
+ * one ECC sector. We can not know "how many sectors", or
+ * "which sector(s)". We need erase-page check for all sectors.
+ */
+ *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
+ return 0;
+ }
+
+ max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor);
+
+ /*
+ * The register holds the maximum of per-sector corrected bitflips.
+ * This is suitable for the return value of the ->read_page() callback.
+ * Unfortunately, we can not know the total number of corrected bits in
+ * the page. Increase the stats by max_bitflips. (compromised solution)
+ */
+ mtd->ecc_stats.corrected += max_bitflips;
+
+ return max_bitflips;
+}
+
+static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+ struct denali_nand_info *denali,
+ unsigned long *uncor_ecc_flags, uint8_t *buf)
+{
+ unsigned int ecc_size = denali->nand.ecc.size;
+ unsigned int bitflips = 0;
+ unsigned int max_bitflips = 0;
+ uint32_t err_addr, err_cor_info;
+ unsigned int err_byte, err_sector, err_device;
+ uint8_t err_cor_value;
+ unsigned int prev_sector = 0;
+ uint32_t irq_status;
+
+ denali_reset_irq(denali);
+
+ do {
+ err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
+ err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr);
+ err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr);
+
+ err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
+ err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE,
+ err_cor_info);
+ err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE,
+ err_cor_info);
+
+ /* reset the bitflip counter when crossing ECC sector */
+ if (err_sector != prev_sector)
+ bitflips = 0;
+
+ if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) {
+ /*
+ * Check later if this is a real ECC error, or
+ * an erased sector.
+ */
+ *uncor_ecc_flags |= BIT(err_sector);
+ } else if (err_byte < ecc_size) {
+ /*
+ * If err_byte is larger than ecc_size, means error
+ * happened in OOB, so we ignore it. It's no need for
+ * us to correct it err_device is represented the NAND
+ * error bits are happened in if there are more than
+ * one NAND connected.
+ */
+ int offset;
+ unsigned int flips_in_byte;
+
+ offset = (err_sector * ecc_size + err_byte) *
+ denali->devs_per_cs + err_device;
+
+ /* correct the ECC error */
+ flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
+ buf[offset] ^= err_cor_value;
+ mtd->ecc_stats.corrected += flips_in_byte;
+ bitflips += flips_in_byte;
+
+ max_bitflips = max(max_bitflips, bitflips);
+ }
+
+ prev_sector = err_sector;
+ } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR));
+
+ /*
+ * Once handle all ECC errors, controller will trigger an
+ * ECC_TRANSACTION_DONE interrupt.
+ */
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+ return -EIO;
+
+ return max_bitflips;
+}
+
+static void denali_setup_dma64(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
+{
+ uint32_t mode;
+ const int page_count = 1;
+
+ mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
+
+ /* DMA is a three step process */
+
+ /*
+ * 1. setup transfer type, interrupt when complete,
+ * burst len = 64 bytes, the number of pages
+ */
+ denali->host_write(denali, mode,
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
+
+ /* 2. set memory low address */
+ denali->host_write(denali, mode, lower_32_bits(dma_addr));
+
+ /* 3. set memory high address */
+ denali->host_write(denali, mode, upper_32_bits(dma_addr));
+}
+
+static void denali_setup_dma32(struct denali_nand_info *denali,
+ dma_addr_t dma_addr, int page, int write)
+{
+ uint32_t mode;
+ const int page_count = 1;
+
+ mode = DENALI_MAP10 | DENALI_BANK(denali);
+
+ /* DMA is a four step process */
+
+ /* 1. setup transfer type and # of pages */
+ denali->host_write(denali, mode | page,
+ 0x2000 | (write << 8) | page_count);
+
+ /* 2. set memory high address bits 23:8 */
+ denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
+
+ /* 3. set memory low address bits 23:8 */
+ denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
+
+ /* 4. interrupt when complete, burst len = 64 bytes */
+ denali->host_write(denali, mode | 0x14000, 0x2400);
+}
+
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw)
+{
+ u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
+ uint32_t *buf32 = (uint32_t *)buf;
+ uint32_t irq_status, ecc_err_mask;
+ int i;
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ else
+ ecc_err_mask = INTR__ECC_ERR;
+
+ denali_reset_irq(denali);
+
+ for (i = 0; i < size / 4; i++)
+ *buf32++ = denali->host_read(denali, addr);
+
+ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+ if (!(irq_status & INTR__PAGE_XFER_INC))
+ return -EIO;
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+ const void *buf, size_t size, int page, int raw)
+{
+ u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page;
+ const uint32_t *buf32 = (uint32_t *)buf;
+ uint32_t irq_status;
+ int i;
+
+ denali_reset_irq(denali);
+
+ for (i = 0; i < size / 4; i++)
+ denali->host_write(denali, addr, *buf32++);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+ if (!(irq_status & INTR__PROGRAM_COMP))
+ return -EIO;
+
+ return 0;
+}
+
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ if (write)
+ return denali_pio_write(denali, buf, size, page, raw);
+ else
+ return denali_pio_read(denali, buf, size, page, raw);
+}
+
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ dma_addr_t dma_addr;
+ uint32_t irq_mask, irq_status, ecc_err_mask;
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ int ret = 0;
+
+ dma_addr = dma_map_single(denali->dev, buf, size, dir);
+ if (dma_mapping_error(denali->dev, dma_addr)) {
+ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
+ }
+
+ if (write) {
+ /*
+ * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
+ * We can use INTR__DMA_CMD_COMP instead. This flag is asserted
+ * when the page program is completed.
+ */
+ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+ ecc_err_mask = 0;
+ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
+ } else {
+ irq_mask = INTR__DMA_CMD_COMP;
+ ecc_err_mask = INTR__ECC_ERR;
+ }
+
+ iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE);
+
+ denali_reset_irq(denali);
+ denali->setup_dma(denali, dma_addr, page, write);
+
+ irq_status = denali_wait_for_irq(denali, irq_mask);
+ if (!(irq_status & INTR__DMA_CMD_COMP))
+ ret = -EIO;
+ else if (irq_status & ecc_err_mask)
+ ret = -EBADMSG;
+
+ iowrite32(0, denali->reg + DMA_ENABLE);
+
+ dma_unmap_single(denali->dev, dma_addr, size, dir);
+
+ if (irq_status & INTR__ERASED_PAGE)
+ memset(buf, 0xff, size);
+
+ return ret;
+}
+
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+ size_t size, int page, int raw, int write)
+{
+ iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE);
+ iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0,
+ denali->reg + TRANSFER_SPARE_REG);
+
+ if (denali->dma_avail)
+ return denali_dma_xfer(denali, buf, size, page, raw, write);
+ else
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
+}
+
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int write)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0;
+ unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT;
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ uint8_t *bufpoi = chip->oob_poi;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /* BBM at the beginning of the OOB area */
+ chip->cmdfunc(mtd, start_cmd, writesize, page);
+ if (write)
+ chip->write_buf(mtd, bufpoi, oob_skip);
+ else
+ chip->read_buf(mtd, bufpoi, oob_skip);
+ bufpoi += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ chip->cmdfunc(mtd, rnd_cmd, pos, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (bufpoi - chip->oob_poi);
+ chip->cmdfunc(mtd, rnd_cmd, size - len, -1);
+ if (write)
+ chip->write_buf(mtd, bufpoi, len);
+ else
+ chip->read_buf(mtd, bufpoi, len);
+}
+
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int ret, i, pos, len;
+
+ ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0);
+ if (ret)
+ return ret;
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(buf, tmp_buf + pos, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(buf, tmp_buf + writesize + oob_skip,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(oob, tmp_buf + writesize, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(oob, tmp_buf + pos, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(oob, tmp_buf + writesize + oob_skip,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(oob, tmp_buf + size - len, len);
+ }
+
+ return 0;
+}
+
+static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ denali_oob_xfer(mtd, chip, page, 0);
+
+ return 0;
+}
+
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int status;
+
+ denali_reset_irq(denali);
+
+ denali_oob_xfer(mtd, chip, page, 1);
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ unsigned long uncor_ecc_flags = 0;
+ int stat = 0;
+ int ret;
+
+ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
+ if (ret && ret != -EBADMSG)
+ return ret;
+
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+ stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+ else if (ret == -EBADMSG)
+ stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
+
+ if (stat < 0)
+ return stat;
+
+ if (uncor_ecc_flags) {
+ ret = denali_read_oob(mtd, chip, page);
+ if (ret)
+ return ret;
+
+ stat = denali_check_erased_page(mtd, chip, buf,
+ uncor_ecc_flags, stat);
+ }
+
+ return stat;
+}
+
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ int writesize = mtd->writesize;
+ int oobsize = mtd->oobsize;
+ int ecc_steps = chip->ecc.steps;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ void *tmp_buf = denali->buf;
+ int oob_skip = denali->oob_skip_bytes;
+ size_t size = writesize + oobsize;
+ int i, pos, len;
+
+ /*
+ * Fill the buffer with 0xff first except the full page transfer.
+ * This simplifies the logic.
+ */
+ if (!buf || !oob_required)
+ memset(tmp_buf, 0xff, size);
+
+ /* Arrange the buffer for syndrome payload/ecc layout */
+ if (buf) {
+ for (i = 0; i < ecc_steps; i++) {
+ pos = i * (ecc_size + ecc_bytes);
+ len = ecc_size;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, buf, len);
+ buf += len;
+ if (len < ecc_size) {
+ len = ecc_size - len;
+ memcpy(tmp_buf + writesize + oob_skip, buf,
+ len);
+ buf += len;
+ }
+ }
+ }
+
+ if (oob_required) {
+ const uint8_t *oob = chip->oob_poi;
+
+ /* BBM at the beginning of the OOB area */
+ memcpy(tmp_buf + writesize, oob, oob_skip);
+ oob += oob_skip;
+
+ /* OOB ECC */
+ for (i = 0; i < ecc_steps; i++) {
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
+ len = ecc_bytes;
+
+ if (pos >= writesize)
+ pos += oob_skip;
+ else if (pos + len > writesize)
+ len = writesize - pos;
+
+ memcpy(tmp_buf + pos, oob, len);
+ oob += len;
+ if (len < ecc_bytes) {
+ len = ecc_bytes - len;
+ memcpy(tmp_buf + writesize + oob_skip, oob,
+ len);
+ oob += len;
+ }
+ }
+
+ /* OOB free */
+ len = oobsize - (oob - chip->oob_poi);
+ memcpy(tmp_buf + size - len, oob, len);
+ }
+
+ return denali_data_xfer(denali, tmp_buf, size, page, 1, 1);
+}
+
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ return denali_data_xfer(denali, (void *)buf, mtd->writesize,
+ page, 0, 1);
+}
+
+static void denali_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+ denali->active_bank = chip;
+}
+
+static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
+
+ /* R/B# pin transitioned from low to high? */
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
+}
+
+static int denali_erase(struct mtd_info *mtd, int page)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ uint32_t irq_status;
+
+ denali_reset_irq(denali);
+
+ denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
+ DENALI_ERASE);
+
+ /* wait for erase to complete or failure to occur */
+ irq_status = denali_wait_for_irq(denali,
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
+
+ return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
+}
+
+static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
+ const struct nand_data_interface *conf)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ const struct nand_sdr_timings *timings;
+ unsigned long t_clk;
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
+ int addr_2_data_mask;
+ uint32_t tmp;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ /* clk_x period in picoseconds */
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+ if (!t_clk)
+ return -EINVAL;
+
+ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ /* tREA -> ACC_CLKS */
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+ tmp = ioread32(denali->reg + ACC_CLKS);
+ tmp &= ~ACC_CLKS__VALUE;
+ tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks);
+ iowrite32(tmp, denali->reg + ACC_CLKS);
+
+ /* tRWH -> RE_2_WE */
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_WE);
+ tmp &= ~RE_2_WE__VALUE;
+ tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we);
+ iowrite32(tmp, denali->reg + RE_2_WE);
+
+ /* tRHZ -> RE_2_RE */
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+ tmp = ioread32(denali->reg + RE_2_RE);
+ tmp &= ~RE_2_RE__VALUE;
+ tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re);
+ iowrite32(tmp, denali->reg + RE_2_RE);
+
+ /*
+ * tCCS, tWHR -> WE_2_RE
+ *
+ * With WE_2_RE properly set, the Denali controller automatically takes
+ * care of the delay; the driver need not set NAND_WAIT_TCCS.
+ */
+ we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),
+ t_clk);
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
+
+ tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+ tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re);
+ iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
+
+ /* tADL -> ADDR_2_DATA */
+
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ if (denali->revision < 0x0501)
+ addr_2_data_mask >>= 1;
+
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+ tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
+ tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+ tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data);
+ iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
+
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+ t_clk);
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
+ tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi);
+ iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
+
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
+ t_clk);
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
+ t_clk);
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
+ tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo);
+ iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
+
+ /* tCS, tCEA -> CS_SETUP_CNT */
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+ 0);
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+ tmp = ioread32(denali->reg + CS_SETUP_CNT);
+ tmp &= ~CS_SETUP_CNT__VALUE;
+ tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup);
+ iowrite32(tmp, denali->reg + CS_SETUP_CNT);
+
+ return 0;
+}
+
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+ u32 irq_status;
+ int i;
+
+ for (i = 0; i < denali->max_banks; i++) {
+ denali->active_bank = i;
+
+ denali_reset_irq(denali);
+
+ iowrite32(DEVICE_RESET__BANK(i),
+ denali->reg + DEVICE_RESET);
+
+ irq_status = denali_wait_for_irq(denali,
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+ if (!(irq_status & INTR__INT_ACT))
+ break;
+ }
+
+ dev_dbg(denali->dev, "%d chips connected\n", i);
+ denali->max_banks = i;
+}
+
+static void denali_hw_init(struct denali_nand_info *denali)
+{
+ /*
+ * The REVISION register may not be reliable. Platforms are allowed to
+ * override it.
+ */
+ if (!denali->revision)
+ denali->revision = swab16(ioread32(denali->reg + REVISION));
+
+ /*
+ * tell driver how many bit controller will skip before writing
+ * ECC code in OOB. This is normally used for bad block marker
+ */
+ denali->oob_skip_bytes = CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES;
+ iowrite32(denali->oob_skip_bytes, denali->reg + SPARE_AREA_SKIP_BYTES);
+ denali_detect_max_banks(denali);
+ iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
+ iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
+
+ iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
+}
+
+int denali_calc_ecc_bytes(int step_size, int strength)
+{
+ /* BCH code. Denali requires ecc.bytes to be multiple of 2 */
+ return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2;
+}
+EXPORT_SYMBOL(denali_calc_ecc_bytes);
+
+static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip,
+ struct denali_nand_info *denali)
+{
+ int oobavail = mtd->oobsize - denali->oob_skip_bytes;
+ int ret;
+
+ /*
+ * If .size and .strength are already set (usually by DT),
+ * check if they are supported by this controller.
+ */
+ if (chip->ecc.size && chip->ecc.strength)
+ return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail);
+
+ /*
+ * We want .size and .strength closest to the chip's requirement
+ * unless NAND_ECC_MAXIMIZE is requested.
+ */
+ if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+ ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail);
+ if (!ret)
+ return 0;
+ }
+
+ /* Max ECC strength is the last thing we can do */
+ return nand_maximize_ecc(chip, denali->ecc_caps, oobavail);
+}
+
+static struct nand_ecclayout nand_oob;
+
+static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = denali->oob_skip_bytes;
+ oobregion->length = chip->ecc.total;
+
+ return 0;
+}
+
+static int denali_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
+ oobregion->length = mtd->oobsize - oobregion->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
+ .ecc = denali_ooblayout_ecc,
+ .free = denali_ooblayout_free,
+};
+
+static int denali_multidev_fixup(struct denali_nand_info *denali)
+{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
+ /*
+ * Support for multi device:
+ * When the IP configuration is x16 capable and two x8 chips are
+ * connected in parallel, DEVICES_CONNECTED should be set to 2.
+ * In this case, the core framework knows nothing about this fact,
+ * so we should tell it the _logical_ pagesize and anything necessary.
+ */
+ denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED);
+
+ /*
+ * On some SoCs, DEVICES_CONNECTED is not auto-detected.
+ * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
+ */
+ if (denali->devs_per_cs == 0) {
+ denali->devs_per_cs = 1;
+ iowrite32(1, denali->reg + DEVICES_CONNECTED);
+ }
+
+ if (denali->devs_per_cs == 1)
+ return 0;
+
+ if (denali->devs_per_cs != 2) {
+ dev_err(denali->dev, "unsupported number of devices %d\n",
+ denali->devs_per_cs);
+ return -EINVAL;
+ }
+
+ /* 2 chips in parallel */
+ mtd->size <<= 1;
+ mtd->erasesize <<= 1;
+ mtd->writesize <<= 1;
+ mtd->oobsize <<= 1;
+ chip->chipsize <<= 1;
+ chip->page_shift += 1;
+ chip->phys_erase_shift += 1;
+ chip->bbt_erase_shift += 1;
+ chip->chip_shift += 1;
+ chip->pagemask <<= 1;
+ chip->ecc.size <<= 1;
+ chip->ecc.bytes <<= 1;
+ chip->ecc.strength <<= 1;
+ denali->oob_skip_bytes <<= 1;
+
+ return 0;
+}
+
+int denali_init(struct denali_nand_info *denali)
+{
+ struct nand_chip *chip = &denali->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ u32 features = ioread32(denali->reg + FEATURES);
+ int ret;
+
+ denali_hw_init(denali);
+
+ denali_clear_irq_all(denali);
+
+ denali_reset_banks(denali);
+
+ denali->active_bank = DENALI_INVALID_BANK;
+
+ chip->flash_node = dev_of_offset(denali->dev);
+ /* Fallback to the default name if DT did not give "label" property */
+ if (!mtd->name)
+ mtd->name = "denali-nand";
+
+ chip->select_chip = denali_select_chip;
+ chip->read_byte = denali_read_byte;
+ chip->write_byte = denali_write_byte;
+ chip->read_word = denali_read_word;
+ chip->cmd_ctrl = denali_cmd_ctrl;
+ chip->dev_ready = denali_dev_ready;
+ chip->waitfunc = denali_waitfunc;
+
+ if (features & FEATURES__INDEX_ADDR) {
+ denali->host_read = denali_indexed_read;
+ denali->host_write = denali_indexed_write;
+ } else {
+ denali->host_read = denali_direct_read;
+ denali->host_write = denali_direct_write;
+ }
+
+ /* clk rate info is needed for setup_data_interface */
+ if (denali->clk_x_rate)
+ chip->setup_data_interface = denali_setup_data_interface;
+
+ ret = nand_scan_ident(mtd, denali->max_banks, NULL);
+ if (ret)
+ return ret;
+
+ if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
+ denali->dma_avail = 1;
+
+ if (denali->dma_avail) {
+ chip->buf_align = ARCH_DMA_MINALIGN;
+ if (denali->caps & DENALI_CAP_DMA_64BIT)
+ denali->setup_dma = denali_setup_dma64;
+ else
+ denali->setup_dma = denali_setup_dma32;
+ } else {
+ chip->buf_align = 4;
+ }
+
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+ chip->bbt_options |= NAND_BBT_NO_OOB;
+ denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
+
+ /* no subpage writes on denali */
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ ret = denali_ecc_setup(mtd, chip, denali);
+ if (ret) {
+ dev_err(denali->dev, "Failed to setup ECC settings.\n");
+ return ret;
+ }
+
+ dev_dbg(denali->dev,
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
+
+ iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) |
+ FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength),
+ denali->reg + ECC_CORRECTION);
+ iowrite32(mtd->erasesize / mtd->writesize,
+ denali->reg + PAGES_PER_BLOCK);
+ iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
+ denali->reg + DEVICE_WIDTH);
+ iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG,
+ denali->reg + TWO_ROW_ADDR_CYCLES);
+ iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
+ iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
+
+ iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
+ iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
+ iowrite32(mtd->writesize / chip->ecc.size,
+ denali->reg + CFG_NUM_DATA_BLOCKS);
+
+ mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
+
+ nand_oob.eccbytes = denali->nand.ecc.bytes;
+ denali->nand.ecc.layout = &nand_oob;
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->read_buf = denali_read_buf16;
+ chip->write_buf = denali_write_buf16;
+ } else {
+ chip->read_buf = denali_read_buf;
+ chip->write_buf = denali_write_buf;
+ }
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
+ chip->ecc.read_page = denali_read_page;
+ chip->ecc.read_page_raw = denali_read_page_raw;
+ chip->ecc.write_page = denali_write_page;
+ chip->ecc.write_page_raw = denali_write_page_raw;
+ chip->ecc.read_oob = denali_read_oob;
+ chip->ecc.write_oob = denali_write_oob;
+ chip->erase = denali_erase;
+
+ ret = denali_multidev_fixup(denali);
+ if (ret)
+ return ret;
+
+ /*
+ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
+ * use devm_kmalloc() because the memory allocated by devm_ does not
+ * guarantee DMA-safe alignment.
+ */
+ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!denali->buf)
+ return -ENOMEM;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto free_buf;
+
+ ret = nand_register(0, mtd);
+ if (ret) {
+ dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
+ goto free_buf;
+ }
+ return 0;
+
+free_buf:
+ kfree(denali->buf);
+
+ return ret;
+}
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
new file mode 100644
index 0000000000..9b797beffa
--- /dev/null
+++ b/drivers/mtd/nand/raw/denali.h
@@ -0,0 +1,325 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2013-2014 Altera Corporation <www.altera.com>
+ * Copyright (C) 2009-2010, Intel Corporation and its suppliers.
+ */
+
+#ifndef __DENALI_H__
+#define __DENALI_H__
+
+#include <linux/bitops.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/types.h>
+
+#define DEVICE_RESET 0x0
+#define DEVICE_RESET__BANK(bank) BIT(bank)
+
+#define TRANSFER_SPARE_REG 0x10
+#define TRANSFER_SPARE_REG__FLAG BIT(0)
+
+#define LOAD_WAIT_CNT 0x20
+#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0)
+
+#define PROGRAM_WAIT_CNT 0x30
+#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0)
+
+#define ERASE_WAIT_CNT 0x40
+#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0)
+
+#define INT_MON_CYCCNT 0x50
+#define INT_MON_CYCCNT__VALUE GENMASK(15, 0)
+
+#define RB_PIN_ENABLED 0x60
+#define RB_PIN_ENABLED__BANK(bank) BIT(bank)
+
+#define MULTIPLANE_OPERATION 0x70
+#define MULTIPLANE_OPERATION__FLAG BIT(0)
+
+#define MULTIPLANE_READ_ENABLE 0x80
+#define MULTIPLANE_READ_ENABLE__FLAG BIT(0)
+
+#define COPYBACK_DISABLE 0x90
+#define COPYBACK_DISABLE__FLAG BIT(0)
+
+#define CACHE_WRITE_ENABLE 0xa0
+#define CACHE_WRITE_ENABLE__FLAG BIT(0)
+
+#define CACHE_READ_ENABLE 0xb0
+#define CACHE_READ_ENABLE__FLAG BIT(0)
+
+#define PREFETCH_MODE 0xc0
+#define PREFETCH_MODE__PREFETCH_EN BIT(0)
+#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4)
+
+#define CHIP_ENABLE_DONT_CARE 0xd0
+#define CHIP_EN_DONT_CARE__FLAG BIT(0)
+
+#define ECC_ENABLE 0xe0
+#define ECC_ENABLE__FLAG BIT(0)
+
+#define GLOBAL_INT_ENABLE 0xf0
+#define GLOBAL_INT_EN_FLAG BIT(0)
+
+#define TWHR2_AND_WE_2_RE 0x100
+#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8)
+
+#define TCWAW_AND_ADDR_2_DATA 0x110
+/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */
+#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0)
+#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8)
+
+#define RE_2_WE 0x120
+#define RE_2_WE__VALUE GENMASK(5, 0)
+
+#define ACC_CLKS 0x130
+#define ACC_CLKS__VALUE GENMASK(3, 0)
+
+#define NUMBER_OF_PLANES 0x140
+#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0)
+
+#define PAGES_PER_BLOCK 0x150
+#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0)
+
+#define DEVICE_WIDTH 0x160
+#define DEVICE_WIDTH__VALUE GENMASK(1, 0)
+
+#define DEVICE_MAIN_AREA_SIZE 0x170
+#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0)
+
+#define DEVICE_SPARE_AREA_SIZE 0x180
+#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0)
+
+#define TWO_ROW_ADDR_CYCLES 0x190
+#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0)
+
+#define MULTIPLANE_ADDR_RESTRICT 0x1a0
+#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0)
+
+#define ECC_CORRECTION 0x1b0
+#define ECC_CORRECTION__VALUE GENMASK(4, 0)
+#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16)
+
+#define READ_MODE 0x1c0
+#define READ_MODE__VALUE GENMASK(3, 0)
+
+#define WRITE_MODE 0x1d0
+#define WRITE_MODE__VALUE GENMASK(3, 0)
+
+#define COPYBACK_MODE 0x1e0
+#define COPYBACK_MODE__VALUE GENMASK(3, 0)
+
+#define RDWR_EN_LO_CNT 0x1f0
+#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0)
+
+#define RDWR_EN_HI_CNT 0x200
+#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0)
+
+#define MAX_RD_DELAY 0x210
+#define MAX_RD_DELAY__VALUE GENMASK(3, 0)
+
+#define CS_SETUP_CNT 0x220
+#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
+#define CS_SETUP_CNT__TWB GENMASK(17, 12)
+
+#define SPARE_AREA_SKIP_BYTES 0x230
+#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
+
+#define SPARE_AREA_MARKER 0x240
+#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0)
+
+#define DEVICES_CONNECTED 0x250
+#define DEVICES_CONNECTED__VALUE GENMASK(2, 0)
+
+#define DIE_MASK 0x260
+#define DIE_MASK__VALUE GENMASK(7, 0)
+
+#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
+#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0)
+
+#define WRITE_PROTECT 0x280
+#define WRITE_PROTECT__FLAG BIT(0)
+
+#define RE_2_RE 0x290
+#define RE_2_RE__VALUE GENMASK(5, 0)
+
+#define MANUFACTURER_ID 0x300
+#define MANUFACTURER_ID__VALUE GENMASK(7, 0)
+
+#define DEVICE_ID 0x310
+#define DEVICE_ID__VALUE GENMASK(7, 0)
+
+#define DEVICE_PARAM_0 0x320
+#define DEVICE_PARAM_0__VALUE GENMASK(7, 0)
+
+#define DEVICE_PARAM_1 0x330
+#define DEVICE_PARAM_1__VALUE GENMASK(7, 0)
+
+#define DEVICE_PARAM_2 0x340
+#define DEVICE_PARAM_2__VALUE GENMASK(7, 0)
+
+#define LOGICAL_PAGE_DATA_SIZE 0x350
+#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0)
+
+#define LOGICAL_PAGE_SPARE_SIZE 0x360
+#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0)
+
+#define REVISION 0x370
+#define REVISION__VALUE GENMASK(15, 0)
+
+#define ONFI_DEVICE_FEATURES 0x380
+#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0)
+
+#define ONFI_OPTIONAL_COMMANDS 0x390
+#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0)
+
+#define ONFI_TIMING_MODE 0x3a0
+#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0)
+
+#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
+#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0)
+
+#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
+#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0)
+#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8)
+
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0)
+
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0)
+
+#define FEATURES 0x3f0
+#define FEATURES__N_BANKS GENMASK(1, 0)
+#define FEATURES__ECC_MAX_ERR GENMASK(5, 2)
+#define FEATURES__DMA BIT(6)
+#define FEATURES__CMD_DMA BIT(7)
+#define FEATURES__PARTITION BIT(8)
+#define FEATURES__XDMA_SIDEBAND BIT(9)
+#define FEATURES__GPREG BIT(10)
+#define FEATURES__INDEX_ADDR BIT(11)
+
+#define TRANSFER_MODE 0x400
+#define TRANSFER_MODE__VALUE GENMASK(1, 0)
+
+#define INTR_STATUS(bank) (0x410 + (bank) * 0x50)
+#define INTR_EN(bank) (0x420 + (bank) * 0x50)
+/* bit[1:0] is used differently depending on IP version */
+#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */
+#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */
+#define INTR__ECC_ERR BIT(1) /* old IP */
+#define INTR__DMA_CMD_COMP BIT(2)
+#define INTR__TIME_OUT BIT(3)
+#define INTR__PROGRAM_FAIL BIT(4)
+#define INTR__ERASE_FAIL BIT(5)
+#define INTR__LOAD_COMP BIT(6)
+#define INTR__PROGRAM_COMP BIT(7)
+#define INTR__ERASE_COMP BIT(8)
+#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9)
+#define INTR__LOCKED_BLK BIT(10)
+#define INTR__UNSUP_CMD BIT(11)
+#define INTR__INT_ACT BIT(12)
+#define INTR__RST_COMP BIT(13)
+#define INTR__PIPE_CMD_ERR BIT(14)
+#define INTR__PAGE_XFER_INC BIT(15)
+#define INTR__ERASED_PAGE BIT(16)
+
+#define PAGE_CNT(bank) (0x430 + (bank) * 0x50)
+#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50)
+#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50)
+
+#define ECC_THRESHOLD 0x600
+#define ECC_THRESHOLD__VALUE GENMASK(9, 0)
+
+#define ECC_ERROR_BLOCK_ADDRESS 0x610
+#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0)
+
+#define ECC_ERROR_PAGE_ADDRESS 0x620
+#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0)
+#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12)
+
+#define ECC_ERROR_ADDRESS 0x630
+#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0)
+#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12)
+
+#define ERR_CORRECTION_INFO 0x640
+#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0)
+#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8)
+#define ERR_CORRECTION_INFO__UNCOR BIT(14)
+#define ERR_CORRECTION_INFO__LAST_ERR BIT(15)
+
+#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
+#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
+#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0)
+#define ECC_COR_INFO__UNCOR_ERR BIT(7)
+
+#define CFG_DATA_BLOCK_SIZE 0x6b0
+
+#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0
+
+#define CFG_NUM_DATA_BLOCKS 0x6d0
+
+#define CFG_META_DATA_SIZE 0x6e0
+
+#define DMA_ENABLE 0x700
+#define DMA_ENABLE__FLAG BIT(0)
+
+#define IGNORE_ECC_DONE 0x710
+#define IGNORE_ECC_DONE__FLAG BIT(0)
+
+#define DMA_INTR 0x720
+#define DMA_INTR_EN 0x730
+#define DMA_INTR__TARGET_ERROR BIT(0)
+#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1)
+#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2)
+#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3)
+#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4)
+#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5)
+
+#define TARGET_ERR_ADDR_LO 0x740
+#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0)
+
+#define TARGET_ERR_ADDR_HI 0x750
+#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0)
+
+#define CHNL_ACTIVE 0x760
+#define CHNL_ACTIVE__CHANNEL0 BIT(0)
+#define CHNL_ACTIVE__CHANNEL1 BIT(1)
+#define CHNL_ACTIVE__CHANNEL2 BIT(2)
+#define CHNL_ACTIVE__CHANNEL3 BIT(3)
+
+struct udevice;
+
+struct denali_nand_info {
+ struct nand_chip nand;
+ unsigned long clk_x_rate; /* bus interface clock rate */
+ int active_bank; /* currently selected bank */
+ struct udevice *dev;
+ uint32_t page;
+ void __iomem *reg; /* Register Interface */
+ void __iomem *host; /* Host Data/Command Interface */
+ u32 irq_mask; /* interrupts we are waiting for */
+ u32 irq_status; /* interrupts that have happened */
+ int irq;
+ void *buf; /* for syndrome layout conversion */
+ dma_addr_t dma_addr;
+ int dma_avail; /* can support DMA? */
+ int devs_per_cs; /* devices connected in parallel */
+ int oob_skip_bytes; /* number of bytes reserved for BBM */
+ int max_banks;
+ unsigned int revision; /* IP revision */
+ unsigned int caps; /* IP capability (or quirk) */
+ const struct nand_ecc_caps *ecc_caps;
+ u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
+ void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
+ void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
+ int page, int write);
+};
+
+#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
+#define DENALI_CAP_DMA_64BIT BIT(1)
+
+int denali_calc_ecc_bytes(int step_size, int strength);
+int denali_init(struct denali_nand_info *denali);
+
+#endif /* __DENALI_H__ */
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
new file mode 100644
index 0000000000..65a7797f0f
--- /dev/null
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+
+#include "denali.h"
+
+struct denali_dt_data {
+ unsigned int revision;
+ unsigned int caps;
+ const struct nand_ecc_caps *ecc_caps;
+};
+
+NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
+ 512, 8, 15);
+static const struct denali_dt_data denali_socfpga_data = {
+ .caps = DENALI_CAP_HW_ECC_FIXUP,
+ .ecc_caps = &denali_socfpga_ecc_caps,
+};
+
+NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
+ 1024, 8, 16, 24);
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .ecc_caps = &denali_uniphier_v5a_ecc_caps,
+};
+
+NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
+ 1024, 8, 16);
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+ .revision = 0x0501,
+ .caps = DENALI_CAP_HW_ECC_FIXUP |
+ DENALI_CAP_DMA_64BIT,
+ .ecc_caps = &denali_uniphier_v5b_ecc_caps,
+};
+
+static const struct udevice_id denali_nand_dt_ids[] = {
+ {
+ .compatible = "altr,socfpga-denali-nand",
+ .data = (unsigned long)&denali_socfpga_data,
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5a",
+ .data = (unsigned long)&denali_uniphier_v5a_data,
+ },
+ {
+ .compatible = "socionext,uniphier-denali-nand-v5b",
+ .data = (unsigned long)&denali_uniphier_v5b_data,
+ },
+ { /* sentinel */ }
+};
+
+static int denali_dt_probe(struct udevice *dev)
+{
+ struct denali_nand_info *denali = dev_get_priv(dev);
+ const struct denali_dt_data *data;
+ struct clk clk;
+ struct resource res;
+ int ret;
+
+ data = (void *)dev_get_driver_data(dev);
+ if (data) {
+ denali->revision = data->revision;
+ denali->caps = data->caps;
+ denali->ecc_caps = data->ecc_caps;
+ }
+
+ denali->dev = dev;
+
+ ret = dev_read_resource_byname(dev, "denali_reg", &res);
+ if (ret)
+ return ret;
+
+ denali->reg = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = dev_read_resource_byname(dev, "nand_data", &res);
+ if (ret)
+ return ret;
+
+ denali->host = devm_ioremap(dev, res.start, resource_size(&res));
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ denali->clk_x_rate = clk_get_rate(&clk);
+
+ return denali_init(denali);
+}
+
+U_BOOT_DRIVER(denali_nand_dt) = {
+ .name = "denali-nand-dt",
+ .id = UCLASS_MISC,
+ .of_match = denali_nand_dt_ids,
+ .probe = denali_dt_probe,
+ .priv_auto_alloc_size = sizeof(struct denali_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MISC,
+ DM_GET_DRIVER(denali_nand_dt),
+ &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize Denali NAND controller. (error %d)\n",
+ ret);
+}
diff --git a/drivers/mtd/nand/raw/denali_spl.c b/drivers/mtd/nand/raw/denali_spl.c
new file mode 100644
index 0000000000..dbaba3cab2
--- /dev/null
+++ b/drivers/mtd/nand/raw/denali_spl.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Panasonic Corporation
+ * Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <linux/mtd/rawnand.h>
+#include "denali.h"
+
+#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
+#define DENALI_MAP10 (2 << 26) /* high-level control plane */
+
+#define INDEX_CTRL_REG 0x0
+#define INDEX_DATA_REG 0x10
+
+#define SPARE_ACCESS 0x41
+#define MAIN_ACCESS 0x42
+#define PIPELINE_ACCESS 0x2000
+
+#define BANK(x) ((x) << 24)
+
+static void __iomem *denali_flash_mem =
+ (void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
+static void __iomem *denali_flash_reg =
+ (void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
+
+static const int flash_bank;
+static int page_size, oob_size, pages_per_block;
+
+static void index_addr(uint32_t address, uint32_t data)
+{
+ writel(address, denali_flash_mem + INDEX_CTRL_REG);
+ writel(data, denali_flash_mem + INDEX_DATA_REG);
+}
+
+static int wait_for_irq(uint32_t irq_mask)
+{
+ unsigned long timeout = 1000000;
+ uint32_t intr_status;
+
+ do {
+ intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
+
+ if (intr_status & INTR__ECC_UNCOR_ERR) {
+ debug("Uncorrected ECC detected\n");
+ return -EBADMSG;
+ }
+
+ if (intr_status & irq_mask)
+ break;
+
+ udelay(1);
+ timeout--;
+ } while (timeout);
+
+ if (!timeout) {
+ debug("Timeout with interrupt status %08x\n", intr_status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void read_data_from_flash_mem(uint8_t *buf, int len)
+{
+ int i;
+ uint32_t *buf32;
+
+ /* transfer the data from the flash */
+ buf32 = (uint32_t *)buf;
+
+ /*
+ * Let's take care of unaligned access although it rarely happens.
+ * Avoid put_unaligned() for the normal use cases since it leads to
+ * a bit performance regression.
+ */
+ if ((unsigned long)buf32 % 4) {
+ for (i = 0; i < len / 4; i++)
+ put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG),
+ buf32++);
+ } else {
+ for (i = 0; i < len / 4; i++)
+ *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
+ }
+
+ if (len % 4) {
+ u32 tmp;
+
+ tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG));
+ buf = (uint8_t *)buf32;
+ for (i = 0; i < len % 4; i++) {
+ *buf++ = tmp;
+ tmp >>= 8;
+ }
+ }
+}
+
+int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
+{
+ uint32_t addr, cmd;
+ static uint32_t page_count = 1;
+
+ writel(ecc_en, denali_flash_reg + ECC_ENABLE);
+
+ /* clear all bits of intr_status. */
+ writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
+
+ addr = BANK(flash_bank) | page;
+
+ /* setup the acccess type */
+ cmd = DENALI_MAP10 | addr;
+ index_addr(cmd, access_type);
+
+ /* setup the pipeline command */
+ index_addr(cmd, PIPELINE_ACCESS | page_count);
+
+ cmd = DENALI_MAP01 | addr;
+ writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
+
+ return wait_for_irq(INTR__LOAD_COMP);
+}
+
+static int nand_read_oob(void *buf, int page)
+{
+ int ret;
+
+ ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
+ if (ret < 0)
+ return ret;
+
+ read_data_from_flash_mem(buf, oob_size);
+
+ return 0;
+}
+
+static int nand_read_page(void *buf, int page)
+{
+ int ret;
+
+ ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
+ if (ret < 0)
+ return ret;
+
+ read_data_from_flash_mem(buf, page_size);
+
+ return 0;
+}
+
+static int nand_block_isbad(void *buf, int block)
+{
+ int ret;
+
+ ret = nand_read_oob(buf, block * pages_per_block);
+ if (ret < 0)
+ return ret;
+
+ return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff;
+}
+
+/* nand_init() - initialize data to make nand usable by SPL */
+void nand_init(void)
+{
+ /* access to main area */
+ writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
+
+ /*
+ * These registers are expected to be already set by the hardware
+ * or earlier boot code. So we read these values out.
+ */
+ page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
+ oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
+ pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+ int block, page, column, readlen;
+ int ret;
+ int force_bad_block_check = 1;
+
+ page = offs / page_size;
+ column = offs % page_size;
+
+ block = page / pages_per_block;
+ page = page % pages_per_block;
+
+ while (size) {
+ if (force_bad_block_check || page == 0) {
+ ret = nand_block_isbad(dst, block);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ block++;
+ continue;
+ }
+ }
+
+ force_bad_block_check = 0;
+
+ ret = nand_read_page(dst, block * pages_per_block + page);
+ if (ret < 0)
+ return ret;
+
+ readlen = min(page_size - column, (int)size);
+
+ if (unlikely(column)) {
+ /* Partial page read */
+ memmove(dst, dst + column, readlen);
+ column = 0;
+ }
+
+ size -= readlen;
+ dst += readlen;
+ page++;
+ if (page == pages_per_block) {
+ block++;
+ page = 0;
+ }
+ }
+
+ return 0;
+}
+
+void nand_deselect(void) {}
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
new file mode 100644
index 0000000000..263d46ec8f
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -0,0 +1,810 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Freescale Enhanced Local Bus Controller FCM NAND driver
+ *
+ * Copyright (c) 2006-2008 Freescale Semiconductor
+ *
+ * Authors: Nick Spence <nick.spence@freescale.com>,
+ * Scott Wood <scottwood@freescale.com>
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <nand.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#include <asm/io.h>
+#include <linux/errno.h>
+
+#ifdef VERBOSE_DEBUG
+#define DEBUG_ELBC
+#define vdbg(format, arg...) printf("DEBUG: " format, ##arg)
+#else
+#define vdbg(format, arg...) do {} while (0)
+#endif
+
+/* Can't use plain old DEBUG because the linux mtd
+ * headers define it as a macro.
+ */
+#ifdef DEBUG_ELBC
+#define dbg(format, arg...) printf("DEBUG: " format, ##arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#define MAX_BANKS 8
+#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
+
+#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
+
+struct fsl_elbc_ctrl;
+
+/* mtd information per set */
+
+struct fsl_elbc_mtd {
+ struct nand_chip chip;
+ struct fsl_elbc_ctrl *ctrl;
+
+ struct device *dev;
+ int bank; /* Chip select bank number */
+ u8 __iomem *vbase; /* Chip select base virtual address */
+ int page_size; /* NAND page size (0=512, 1=2048) */
+ unsigned int fmr; /* FCM Flash Mode Register value */
+};
+
+/* overview of the fsl elbc controller */
+
+struct fsl_elbc_ctrl {
+ struct nand_hw_control controller;
+ struct fsl_elbc_mtd *chips[MAX_BANKS];
+
+ /* device info */
+ fsl_lbc_t *regs;
+ u8 __iomem *addr; /* Address of assigned FCM buffer */
+ unsigned int page; /* Last page written to / read from */
+ unsigned int read_bytes; /* Number of bytes read during command */
+ unsigned int column; /* Saved column from SEQIN */
+ unsigned int index; /* Pointer to next byte to 'read' */
+ unsigned int status; /* status read from LTESR after last op */
+ unsigned int mdr; /* UPM/FCM Data Register value */
+ unsigned int use_mdr; /* Non zero if the MDR is to be set */
+ unsigned int oob; /* Non zero if operating on OOB data */
+};
+
+/* These map to the positions used by the FCM hardware ECC generator */
+
+/* Small Page FLASH with FMR[ECCM] = 0 */
+static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
+ .eccbytes = 3,
+ .eccpos = {6, 7, 8},
+ .oobfree = { {0, 5}, {9, 7} },
+};
+
+/* Small Page FLASH with FMR[ECCM] = 1 */
+static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
+ .eccbytes = 3,
+ .eccpos = {8, 9, 10},
+ .oobfree = { {0, 5}, {6, 2}, {11, 5} },
+};
+
+/* Large Page FLASH with FMR[ECCM] = 0 */
+static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
+ .eccbytes = 12,
+ .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
+ .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
+};
+
+/* Large Page FLASH with FMR[ECCM] = 1 */
+static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
+ .eccbytes = 12,
+ .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
+ .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
+};
+
+/*
+ * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset
+ * 1, so we have to adjust bad block pattern. This pattern should be used for
+ * x8 chips only. So far hardware does not support x16 chips anyway.
+ */
+static u8 scan_ff_pattern[] = { 0xff, };
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 1,
+ .pattern = scan_ff_pattern,
+};
+
+/*
+ * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
+ * interfere with ECC positions, that's why we implement our own descriptors.
+ * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 11,
+ .len = 4,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 11,
+ .len = 4,
+ .veroffs = 15,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/*=================================*/
+
+/*
+ * Set up the FCM hardware block and page address fields, and the fcm
+ * structure addr field to point to the correct FCM buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ fsl_lbc_t *lbc = ctrl->regs;
+ int buf_num;
+
+ ctrl->page = page_addr;
+
+ if (priv->page_size) {
+ out_be32(&lbc->fbar, page_addr >> 6);
+ out_be32(&lbc->fpar,
+ ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
+ (oob ? FPAR_LP_MS : 0) | column);
+ buf_num = (page_addr & 1) << 2;
+ } else {
+ out_be32(&lbc->fbar, page_addr >> 5);
+ out_be32(&lbc->fpar,
+ ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
+ (oob ? FPAR_SP_MS : 0) | column);
+ buf_num = page_addr & 7;
+ }
+
+ ctrl->addr = priv->vbase + buf_num * 1024;
+ ctrl->index = column;
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ ctrl->index += priv->page_size ? 2048 : 512;
+
+ vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
+ "index %x, pes %d ps %d\n",
+ buf_num, ctrl->addr, priv->vbase, ctrl->index,
+ chip->phys_erase_shift, chip->page_shift);
+}
+
+/*
+ * execute FCM command and wait for it to complete
+ */
+static int fsl_elbc_run_command(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ fsl_lbc_t *lbc = ctrl->regs;
+ u32 timeo = (CONFIG_SYS_HZ * 10) / 1000;
+ u32 time_start;
+ u32 ltesr;
+
+ /* Setup the FMR[OP] to execute without write protection */
+ out_be32(&lbc->fmr, priv->fmr | 3);
+ if (ctrl->use_mdr)
+ out_be32(&lbc->mdr, ctrl->mdr);
+
+ vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
+ in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
+ vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x "
+ "fbcr=%08x bank=%d\n",
+ in_be32(&lbc->fbar), in_be32(&lbc->fpar),
+ in_be32(&lbc->fbcr), priv->bank);
+
+ /* execute special operation */
+ out_be32(&lbc->lsor, priv->bank);
+
+ /* wait for FCM complete flag or timeout */
+ time_start = get_timer(0);
+
+ ltesr = 0;
+ while (get_timer(time_start) < timeo) {
+ ltesr = in_be32(&lbc->ltesr);
+ if (ltesr & LTESR_CC)
+ break;
+ }
+
+ ctrl->status = ltesr & LTESR_NAND_MASK;
+ out_be32(&lbc->ltesr, ctrl->status);
+ out_be32(&lbc->lteatr, 0);
+
+ /* store mdr value in case it was needed */
+ if (ctrl->use_mdr)
+ ctrl->mdr = in_be32(&lbc->mdr);
+
+ ctrl->use_mdr = 0;
+
+ vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n",
+ ctrl->status, ctrl->mdr, in_be32(&lbc->fmr));
+
+ /* returns 0 on success otherwise non-zero) */
+ return ctrl->status == LTESR_CC ? 0 : -EIO;
+}
+
+static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
+{
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ fsl_lbc_t *lbc = ctrl->regs;
+
+ if (priv->page_size) {
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT));
+
+ out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+ } else {
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT));
+
+ if (oob)
+ out_be32(&lbc->fcr,
+ NAND_CMD_READOOB << FCR_CMD0_SHIFT);
+ else
+ out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
+ }
+}
+
+/* cmdfunc send commands to the FCM */
+static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ fsl_lbc_t *lbc = ctrl->regs;
+
+ ctrl->use_mdr = 0;
+
+ /* clear the read buffer */
+ ctrl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ ctrl->index = 0;
+
+ switch (command) {
+ /* READ0 and READ1 read the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ1:
+ column += 256;
+
+ /* fall-through */
+ case NAND_CMD_READ0:
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+
+ out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
+ set_addr(mtd, 0, page_addr, 0);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+ ctrl->index += column;
+
+ fsl_elbc_do_read(chip, 0);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+
+ out_be32(&lbc->fbcr, mtd->oobsize - column);
+ set_addr(mtd, column, page_addr, 1);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+ fsl_elbc_do_read(chip, 1);
+ fsl_elbc_run_command(mtd);
+
+ return;
+
+ /* READID must read all 5 possible bytes while CEB is active */
+ case NAND_CMD_READID:
+ case NAND_CMD_PARAM:
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command);
+
+ out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_UA << FIR_OP1_SHIFT) |
+ (FIR_OP_RBW << FIR_OP2_SHIFT));
+ out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
+ /*
+ * although currently it's 8 bytes for READID, we always read
+ * the maximum 256 bytes(for PARAM)
+ */
+ out_be32(&lbc->fbcr, 256);
+ ctrl->read_bytes = 256;
+ ctrl->use_mdr = 1;
+ ctrl->mdr = column;
+ set_addr(mtd, 0, 0, 0);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
+ "page_addr: 0x%x.\n", page_addr);
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
+
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_PA << FIR_OP1_SHIFT) |
+ (FIR_OP_CM1 << FIR_OP2_SHIFT));
+
+ out_be32(&lbc->fcr,
+ (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
+
+ out_be32(&lbc->fbcr, 0);
+ ctrl->read_bytes = 0;
+
+ fsl_elbc_run_command(mtd);
+ return;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN: {
+ u32 fcr;
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
+ "page_addr: 0x%x, column: 0x%x.\n",
+ page_addr, column);
+
+ ctrl->column = column;
+ ctrl->oob = 0;
+
+ if (priv->page_size) {
+ fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |
+ (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
+
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_WB << FIR_OP3_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP4_SHIFT));
+ } else {
+ fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
+ (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
+
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CM2 << FIR_OP1_SHIFT) |
+ (FIR_OP_CA << FIR_OP2_SHIFT) |
+ (FIR_OP_PA << FIR_OP3_SHIFT) |
+ (FIR_OP_WB << FIR_OP4_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP5_SHIFT));
+
+ if (column >= mtd->writesize) {
+ /* OOB area --> READOOB */
+ column -= mtd->writesize;
+ fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
+ ctrl->oob = 1;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
+ } else {
+ /* Second 256 bytes --> READ1 */
+ fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT;
+ }
+ }
+
+ out_be32(&lbc->fcr, fcr);
+ set_addr(mtd, column, page_addr, ctrl->oob);
+ return;
+ }
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG: {
+ vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
+ "writing %d bytes.\n", ctrl->index);
+
+ /* if the write did not start at 0 or is not a full page
+ * then set the exact length, otherwise use a full page
+ * write so the HW generates the ECC.
+ */
+ if (ctrl->oob || ctrl->column != 0 ||
+ ctrl->index != mtd->writesize + mtd->oobsize)
+ out_be32(&lbc->fbcr, ctrl->index);
+ else
+ out_be32(&lbc->fbcr, 0);
+
+ fsl_elbc_run_command(mtd);
+
+ return;
+ }
+
+ /* CMD_STATUS must read the status byte while CEB is active */
+ /* Note - it does not wait for the ready line */
+ case NAND_CMD_STATUS:
+ out_be32(&lbc->fir,
+ (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_RBW << FIR_OP1_SHIFT));
+ out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
+ out_be32(&lbc->fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_elbc_run_command(mtd);
+
+ /* The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
+ return;
+
+ /* RESET without waiting for the ready line */
+ case NAND_CMD_RESET:
+ dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
+ out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
+ out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
+ fsl_elbc_run_command(mtd);
+ return;
+
+ default:
+ printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
+ command);
+ }
+}
+
+static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
+{
+ /* The hardware does not seem to support multiple
+ * chips per bank.
+ */
+}
+
+/*
+ * Write buf to the FCM Controller Data Buffer
+ */
+static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+ if (len <= 0) {
+ printf("write_buf of %d bytes", len);
+ ctrl->status = 0;
+ return;
+ }
+
+ if ((unsigned int)len > bufsize - ctrl->index) {
+ printf("write_buf beyond end of buffer "
+ "(%d requested, %u available)\n",
+ len, bufsize - ctrl->index);
+ len = bufsize - ctrl->index;
+ }
+
+ memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+ /*
+ * This is workaround for the weird elbc hangs during nand write,
+ * Scott Wood says: "...perhaps difference in how long it takes a
+ * write to make it through the localbus compared to a write to IMMR
+ * is causing problems, and sync isn't helping for some reason."
+ * Reading back the last byte helps though.
+ */
+ in_8(&ctrl->addr[ctrl->index] + len - 1);
+
+ ctrl->index += len;
+}
+
+/*
+ * read a byte from either the FCM hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+ /* If there are still bytes in the FCM, then use the next byte. */
+ if (ctrl->index < ctrl->read_bytes)
+ return in_8(&ctrl->addr[ctrl->index++]);
+
+ printf("read_byte beyond end of buffer\n");
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the FCM Controller Data Buffer
+ */
+static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ int avail;
+
+ if (len < 0)
+ return;
+
+ avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
+ memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
+ ctrl->index += avail;
+
+ if (len > avail)
+ printf("read_buf beyond end of buffer "
+ "(%d requested, %d available)\n",
+ len, avail);
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+ fsl_lbc_t *lbc = ctrl->regs;
+
+ if (ctrl->status != LTESR_CC)
+ return NAND_STATUS_FAIL;
+
+ /* Use READ_STATUS command, but wait for the device to be ready */
+ ctrl->use_mdr = 0;
+ out_be32(&lbc->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_RBW << FIR_OP1_SHIFT));
+ out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
+ out_be32(&lbc->fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_elbc_run_command(mtd);
+
+ if (ctrl->status != LTESR_CC)
+ return NAND_STATUS_FAIL;
+
+ /* The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
+ return fsl_elbc_read_byte(mtd);
+}
+
+static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ fsl_elbc_read_buf(mtd, buf, mtd->writesize);
+ fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
+ mtd->ecc_stats.failed++;
+
+ return 0;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ fsl_elbc_write_buf(mtd, buf, mtd->writesize);
+ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static struct fsl_elbc_ctrl *elbc_ctrl;
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, uint32_t data_len,
+ const uint8_t *buf, int oob_required, int page)
+{
+ fsl_elbc_write_buf(mtd, buf, mtd->writesize);
+ fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static void fsl_elbc_ctrl_init(void)
+{
+ elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL);
+ if (!elbc_ctrl)
+ return;
+
+ elbc_ctrl->regs = LBC_BASE_ADDR;
+
+ /* clear event registers */
+ out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK);
+ out_be32(&elbc_ctrl->regs->lteatr, 0);
+
+ /* Enable interrupts for any detected events */
+ out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK);
+
+ elbc_ctrl->read_bytes = 0;
+ elbc_ctrl->index = 0;
+ elbc_ctrl->addr = NULL;
+}
+
+static int fsl_elbc_chip_init(int devnum, u8 *addr)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ struct fsl_elbc_mtd *priv;
+ uint32_t br = 0, or = 0;
+ int ret;
+
+ if (!elbc_ctrl) {
+ fsl_elbc_ctrl_init();
+ if (!elbc_ctrl)
+ return -1;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ctrl = elbc_ctrl;
+ priv->vbase = addr;
+
+ /* Find which chip select it is connected to. It'd be nice
+ * if we could pass more than one datum to the NAND driver...
+ */
+ for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
+ phys_addr_t phys_addr = virt_to_phys(addr);
+
+ br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br);
+ or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+
+ if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM &&
+ (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr))
+ break;
+ }
+
+ if (priv->bank >= MAX_BANKS) {
+ printf("fsl_elbc_nand: address did not match any "
+ "chip selects\n");
+ kfree(priv);
+ return -ENODEV;
+ }
+
+ nand = &priv->chip;
+ mtd = nand_to_mtd(nand);
+
+ elbc_ctrl->chips[priv->bank] = priv;
+
+ /* fill in nand_chip structure */
+ /* set up function call table */
+ nand->read_byte = fsl_elbc_read_byte;
+ nand->write_buf = fsl_elbc_write_buf;
+ nand->read_buf = fsl_elbc_read_buf;
+ nand->select_chip = fsl_elbc_select_chip;
+ nand->cmdfunc = fsl_elbc_cmdfunc;
+ nand->waitfunc = fsl_elbc_wait;
+
+ /* set up nand options */
+ nand->bbt_td = &bbt_main_descr;
+ nand->bbt_md = &bbt_mirror_descr;
+
+ /* set up nand options */
+ nand->options = NAND_NO_SUBPAGE_WRITE;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
+
+ nand->controller = &elbc_ctrl->controller;
+ nand_set_controller_data(nand, priv);
+
+ nand->ecc.read_page = fsl_elbc_read_page;
+ nand->ecc.write_page = fsl_elbc_write_page;
+ nand->ecc.write_subpage = fsl_elbc_write_subpage;
+
+ priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
+
+ /* If CS Base Register selects full hardware ECC then use it */
+ if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
+ nand->ecc.mode = NAND_ECC_HW;
+
+ nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
+ &fsl_elbc_oob_sp_eccm1 :
+ &fsl_elbc_oob_sp_eccm0;
+
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 3;
+ nand->ecc.steps = 1;
+ nand->ecc.strength = 1;
+ } else {
+ /* otherwise fall back to software ECC */
+#if defined(CONFIG_NAND_ECC_BCH)
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
+ nand->ecc.mode = NAND_ECC_SOFT;
+#endif
+ }
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ /* Large-page-specific setup */
+ if (mtd->writesize == 2048) {
+ setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+ OR_FCM_PGS);
+ in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+
+ priv->page_size = 1;
+ nand->badblock_pattern = &largepage_memorybased;
+
+ /*
+ * Hardware expects small page has ECCM0, large page has
+ * ECCM1 when booting from NAND, and we follow that even
+ * when not booting from NAND.
+ */
+ priv->fmr |= FMR_ECCM;
+
+ /* adjust ecc setup if needed */
+ if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
+ nand->ecc.steps = 4;
+ nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
+ &fsl_elbc_oob_lp_eccm1 :
+ &fsl_elbc_oob_lp_eccm0;
+ }
+ } else if (mtd->writesize == 512) {
+ clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+ OR_FCM_PGS);
+ in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+ } else {
+ return -ENODEV;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(devnum, mtd);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] =
+ CONFIG_SYS_NAND_BASE_LIST;
+
+void board_nand_init(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ fsl_elbc_chip_init(i, (u8 *)base_address[i]);
+}
diff --git a/drivers/mtd/nand/raw/fsl_elbc_spl.c b/drivers/mtd/nand/raw/fsl_elbc_spl.c
new file mode 100644
index 0000000000..30c3308940
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_elbc_spl.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * Copyright (c) 2008 Freescale Semiconductor, Inc.
+ * Author: Scott Wood <scottwood@freescale.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/fsl_lbc.h>
+#include <nand.h>
+
+#define WINDOW_SIZE 8192
+
+static void nand_wait(void)
+{
+ fsl_lbc_t *regs = LBC_BASE_ADDR;
+
+ for (;;) {
+ uint32_t status = in_be32(&regs->ltesr);
+
+ if (status == 1)
+ return;
+
+ if (status & 1) {
+ puts("read failed (ltesr)\n");
+ for (;;);
+ }
+ }
+}
+
+#ifdef CONFIG_TPL_BUILD
+int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
+#else
+static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
+#endif
+{
+ fsl_lbc_t *regs = LBC_BASE_ADDR;
+ uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
+ const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS;
+ const int block_shift = large ? 17 : 14;
+ const int block_size = 1 << block_shift;
+ const int page_size = large ? 2048 : 512;
+ const int bad_marker = large ? page_size + 0 : page_size + 5;
+ int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2;
+ int pos = 0;
+ char *dst = vdst;
+
+ if (offs & (block_size - 1)) {
+ puts("bad offset\n");
+ for (;;);
+ }
+
+ if (large) {
+ fmr |= FMR_ECCM;
+ out_be32(&regs->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+ out_be32(&regs->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT));
+ } else {
+ out_be32(&regs->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
+ out_be32(&regs->fir,
+ (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT));
+ }
+
+ out_be32(&regs->fbcr, 0);
+ clrsetbits_be32(&regs->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
+
+ while (pos < uboot_size) {
+ int i = 0;
+ out_be32(&regs->fbar, offs >> block_shift);
+
+ do {
+ int j;
+ unsigned int page_offs = (offs & (block_size - 1)) << 1;
+
+ out_be32(&regs->ltesr, ~0);
+ out_be32(&regs->lteatr, 0);
+ out_be32(&regs->fpar, page_offs);
+ out_be32(&regs->fmr, fmr);
+ out_be32(&regs->lsor, 0);
+ nand_wait();
+
+ page_offs %= WINDOW_SIZE;
+
+ /*
+ * If either of the first two pages are marked bad,
+ * continue to the next block.
+ */
+ if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) {
+ puts("skipping\n");
+ offs = (offs + block_size) & ~(block_size - 1);
+ pos &= ~(block_size - 1);
+ break;
+ }
+
+ for (j = 0; j < page_size; j++)
+ dst[pos + j] = buf[page_offs + j];
+
+ pos += page_size;
+ offs += page_size;
+ } while ((offs & (block_size - 1)) && (pos < uboot_size));
+ }
+
+ return 0;
+}
+
+/*
+ * Defines a static function nand_load_image() here, because non-static makes
+ * the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes)
+ */
+#ifndef CONFIG_TPL_BUILD
+#define nand_spl_load_image(offs, uboot_size, vdst) \
+ nand_load_image(offs, uboot_size, vdst)
+#endif
+
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+ /*
+ * Load U-Boot image from NAND into RAM
+ */
+ nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (void *)CONFIG_SYS_NAND_U_BOOT_DST);
+
+#ifdef CONFIG_NAND_ENV_DST
+ nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST);
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
+#endif
+#endif
+
+#ifdef CONFIG_SPL_FLUSH_IMAGE
+ /*
+ * Clean d-cache and invalidate i-cache, to
+ * make sure that no stale data is executed.
+ */
+ flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
+#endif
+
+ puts("transfering control\n");
+ /*
+ * Jump to U-Boot image
+ */
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ (*uboot)();
+}
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
new file mode 100644
index 0000000000..29f30d8ccc
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -0,0 +1,1064 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Integrated Flash Controller NAND Machine Driver
+ *
+ * Copyright (c) 2012 Freescale Semiconductor, Inc
+ *
+ * Authors: Dipen Dudhat <Dipen.Dudhat@freescale.com>
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <nand.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <fsl_ifc.h>
+
+#ifndef CONFIG_SYS_FSL_IFC_BANK_COUNT
+#define CONFIG_SYS_FSL_IFC_BANK_COUNT 4
+#endif
+
+#define MAX_BANKS CONFIG_SYS_FSL_IFC_BANK_COUNT
+#define ERR_BYTE 0xFF /* Value returned for read bytes
+ when read failed */
+
+struct fsl_ifc_ctrl;
+
+/* mtd information per set */
+struct fsl_ifc_mtd {
+ struct nand_chip chip;
+ struct fsl_ifc_ctrl *ctrl;
+
+ struct device *dev;
+ int bank; /* Chip select bank number */
+ unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */
+ u8 __iomem *vbase; /* Chip select base virtual address */
+};
+
+/* overview of the fsl ifc controller */
+struct fsl_ifc_ctrl {
+ struct nand_hw_control controller;
+ struct fsl_ifc_mtd *chips[MAX_BANKS];
+
+ /* device info */
+ struct fsl_ifc regs;
+ void __iomem *addr; /* Address of assigned IFC buffer */
+ unsigned int page; /* Last page written to / read from */
+ unsigned int read_bytes; /* Number of bytes read during command */
+ unsigned int column; /* Saved column from SEQIN */
+ unsigned int index; /* Pointer to next byte to 'read' */
+ unsigned int status; /* status read from NEESR after last op */
+ unsigned int oob; /* Non zero if operating on OOB data */
+ unsigned int eccread; /* Non zero for a full-page ECC read */
+};
+
+static struct fsl_ifc_ctrl *ifc_ctrl;
+
+/* 512-byte page with 4-bit ECC, 8-bit */
+static struct nand_ecclayout oob_512_8bit_ecc4 = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobfree = { {0, 5}, {6, 2} },
+};
+
+/* 512-byte page with 4-bit ECC, 16-bit */
+static struct nand_ecclayout oob_512_16bit_ecc4 = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobfree = { {2, 6}, },
+};
+
+/* 2048-byte page size with 4-bit ECC */
+static struct nand_ecclayout oob_2048_ecc4 = {
+ .eccbytes = 32,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ },
+ .oobfree = { {2, 6}, {40, 24} },
+};
+
+/* 4096-byte page size with 4-bit ECC */
+static struct nand_ecclayout oob_4096_ecc4 = {
+ .eccbytes = 64,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ },
+ .oobfree = { {2, 6}, {72, 56} },
+};
+
+/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */
+static struct nand_ecclayout oob_4096_ecc8 = {
+ .eccbytes = 128,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ },
+ .oobfree = { {2, 6}, {136, 82} },
+};
+
+/* 8192-byte page size with 4-bit ECC */
+static struct nand_ecclayout oob_8192_ecc4 = {
+ .eccbytes = 128,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ },
+ .oobfree = { {2, 6}, {136, 208} },
+};
+
+/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */
+static struct nand_ecclayout oob_8192_ecc8 = {
+ .eccbytes = 256,
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263,
+ },
+ .oobfree = { {2, 6}, {264, 80} },
+};
+
+/*
+ * Generic flash bbt descriptors
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 2, /* 0 on 8-bit small page */
+ .len = 4,
+ .veroffs = 6,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+/*
+ * Set up the IFC hardware block and page address fields, and the ifc nand
+ * structure addr field to point to the correct IFC buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime *ifc = ctrl->regs.rregs;
+ int buf_num;
+
+ ctrl->page = page_addr;
+
+ /* Program ROW0/COL0 */
+ ifc_out32(&ifc->ifc_nand.row0, page_addr);
+ ifc_out32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column);
+
+ buf_num = page_addr & priv->bufnum_mask;
+
+ ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2);
+ ctrl->index = column;
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ ctrl->index += mtd->writesize;
+}
+
+/* returns nonzero if entire page is blank */
+static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
+ u32 eccstat, unsigned int bufnum)
+{
+ return (eccstat >> ((3 - bufnum % 4) * 8)) & 15;
+}
+
+/*
+ * execute IFC NAND command and wait for it to complete
+ */
+static int fsl_ifc_run_command(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime *ifc = ctrl->regs.rregs;
+ u32 timeo = (CONFIG_SYS_HZ * 10) / 1000;
+ u32 time_start;
+ u32 eccstat;
+ int i;
+
+ /* set the chip select for NAND Transaction */
+ ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT);
+
+ /* start read/write seq */
+ ifc_out32(&ifc->ifc_nand.nandseq_strt,
+ IFC_NAND_SEQ_STRT_FIR_STRT);
+
+ /* wait for NAND Machine complete flag or timeout */
+ time_start = get_timer(0);
+
+ while (get_timer(time_start) < timeo) {
+ ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
+
+ if (ctrl->status & IFC_NAND_EVTER_STAT_OPC)
+ break;
+ }
+
+ ifc_out32(&ifc->ifc_nand.nand_evter_stat, ctrl->status);
+
+ if (ctrl->status & IFC_NAND_EVTER_STAT_FTOER)
+ printf("%s: Flash Time Out Error\n", __func__);
+ if (ctrl->status & IFC_NAND_EVTER_STAT_WPER)
+ printf("%s: Write Protect Error\n", __func__);
+
+ if (ctrl->eccread) {
+ int errors;
+ int bufnum = ctrl->page & priv->bufnum_mask;
+ int sector_start = bufnum * chip->ecc.steps;
+ int sector_end = sector_start + chip->ecc.steps - 1;
+ u32 *eccstat_regs;
+
+ eccstat_regs = ifc->ifc_nand.nand_eccstat;
+ eccstat = ifc_in32(&eccstat_regs[sector_start / 4]);
+
+ for (i = sector_start; i <= sector_end; i++) {
+ if ((i != sector_start) && !(i % 4))
+ eccstat = ifc_in32(&eccstat_regs[i / 4]);
+
+ errors = check_read_ecc(mtd, ctrl, eccstat, i);
+
+ if (errors == 15) {
+ /*
+ * Uncorrectable error.
+ * We'll check for blank pages later.
+ *
+ * We disable ECCER reporting due to erratum
+ * IFC-A002770 -- so report it now if we
+ * see an uncorrectable error in ECCSTAT.
+ */
+ ctrl->status |= IFC_NAND_EVTER_STAT_ECCER;
+ continue;
+ }
+
+ mtd->ecc_stats.corrected += errors;
+ }
+
+ ctrl->eccread = 0;
+ }
+
+ /* returns 0 on success otherwise non-zero) */
+ return ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO;
+}
+
+static void fsl_ifc_do_read(struct nand_chip *chip,
+ int oob,
+ struct mtd_info *mtd)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime *ifc = ctrl->regs.rregs;
+
+ /* Program FIR/IFC_NAND_FCR0 for Small/Large page */
+ if (mtd->writesize > 512) {
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
+
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT));
+ } else {
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT));
+
+ if (oob)
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT);
+ else
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT);
+ }
+}
+
+/* cmdfunc send commands to the IFC NAND Machine */
+static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime *ifc = ctrl->regs.rregs;
+
+ /* clear the read buffer */
+ ctrl->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ ctrl->index = 0;
+
+ switch (command) {
+ /* READ0 read the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ0: {
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
+ set_addr(mtd, 0, page_addr, 0);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+ ctrl->index += column;
+
+ if (chip->ecc.mode == NAND_ECC_HW)
+ ctrl->eccread = 1;
+
+ fsl_ifc_do_read(chip, 0, mtd);
+ fsl_ifc_run_command(mtd);
+ return;
+ }
+
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column);
+ set_addr(mtd, column, page_addr, 1);
+
+ ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+ fsl_ifc_do_read(chip, 1, mtd);
+ fsl_ifc_run_command(mtd);
+
+ return;
+
+ /* READID must read all possible bytes while CEB is active */
+ case NAND_CMD_READID:
+ case NAND_CMD_PARAM: {
+ int timing = IFC_FIR_OP_RB;
+ if (command == NAND_CMD_PARAM)
+ timing = IFC_FIR_OP_RBCD;
+
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (timing << IFC_NAND_FIR0_OP2_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ command << IFC_NAND_FCR0_CMD0_SHIFT);
+ ifc_out32(&ifc->ifc_nand.row3, column);
+
+ /*
+ * although currently it's 8 bytes for READID, we always read
+ * the maximum 256 bytes(for PARAM)
+ */
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 256);
+ ctrl->read_bytes = 256;
+
+ set_addr(mtd, 0, 0, 0);
+ fsl_ifc_run_command(mtd);
+ return;
+ }
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT));
+
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT));
+
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
+ ctrl->read_bytes = 0;
+ fsl_ifc_run_command(mtd);
+ return;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN: {
+ u32 nand_fcr0;
+ ctrl->column = column;
+ ctrl->oob = 0;
+
+ if (mtd->writesize > 512) {
+ nand_fcr0 =
+ (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) |
+ (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);
+
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_WBCD <<
+ IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fir1,
+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_RDSTAT <<
+ IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT));
+ } else {
+ nand_fcr0 = ((NAND_CMD_PAGEPROG <<
+ IFC_NAND_FCR0_CMD1_SHIFT) |
+ (NAND_CMD_SEQIN <<
+ IFC_NAND_FCR0_CMD2_SHIFT) |
+ (NAND_CMD_STATUS <<
+ IFC_NAND_FCR0_CMD3_SHIFT));
+
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fir1,
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_RDSTAT <<
+ IFC_NAND_FIR1_OP7_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT));
+
+ if (column >= mtd->writesize)
+ nand_fcr0 |=
+ NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT;
+ else
+ nand_fcr0 |=
+ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT;
+ }
+
+ if (column >= mtd->writesize) {
+ /* OOB area --> READOOB */
+ column -= mtd->writesize;
+ ctrl->oob = 1;
+ }
+ ifc_out32(&ifc->ifc_nand.nand_fcr0, nand_fcr0);
+ set_addr(mtd, column, page_addr, ctrl->oob);
+ return;
+ }
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG:
+ if (ctrl->oob)
+ ifc_out32(&ifc->ifc_nand.nand_fbcr,
+ ctrl->index - ctrl->column);
+ else
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
+
+ fsl_ifc_run_command(mtd);
+ return;
+
+ case NAND_CMD_STATUS:
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT);
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ /*
+ * The chip always seems to report that it is
+ * write-protected, even when it is not.
+ */
+ if (chip->options & NAND_BUSWIDTH_16)
+ ifc_out16(ctrl->addr,
+ ifc_in16(ctrl->addr) | NAND_STATUS_WP);
+ else
+ out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
+ return;
+
+ case NAND_CMD_RESET:
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT);
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT);
+ fsl_ifc_run_command(mtd);
+ return;
+
+ default:
+ printf("%s: error, unsupported command 0x%x.\n",
+ __func__, command);
+ }
+}
+
+/*
+ * Write buf to the IFC NAND Controller Data Buffer
+ */
+static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+ if (len <= 0) {
+ printf("%s of %d bytes", __func__, len);
+ ctrl->status = 0;
+ return;
+ }
+
+ if ((unsigned int)len > bufsize - ctrl->index) {
+ printf("%s beyond end of buffer "
+ "(%d requested, %u available)\n",
+ __func__, len, bufsize - ctrl->index);
+ len = bufsize - ctrl->index;
+ }
+
+ memcpy_toio(ctrl->addr + ctrl->index, buf, len);
+ ctrl->index += len;
+}
+
+/*
+ * read a byte from either the IFC hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fsl_ifc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ unsigned int offset;
+
+ /*
+ * If there are still bytes in the IFC buffer, then use the
+ * next byte.
+ */
+ if (ctrl->index < ctrl->read_bytes) {
+ offset = ctrl->index++;
+ return in_8(ctrl->addr + offset);
+ }
+
+ printf("%s beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read two bytes from the IFC hardware buffer
+ * read function for 16-bit buswith
+ */
+static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ uint16_t data;
+
+ /*
+ * If there are still bytes in the IFC buffer, then use the
+ * next byte.
+ */
+ if (ctrl->index < ctrl->read_bytes) {
+ data = ifc_in16(ctrl->addr + ctrl->index);
+ ctrl->index += 2;
+ return (uint8_t)data;
+ }
+
+ printf("%s beyond end of buffer\n", __func__);
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the IFC Controller Data Buffer
+ */
+static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ int avail;
+
+ if (len < 0)
+ return;
+
+ avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
+ memcpy_fromio(buf, ctrl->addr + ctrl->index, avail);
+ ctrl->index += avail;
+
+ if (len > avail)
+ printf("%s beyond end of buffer "
+ "(%d requested, %d available)\n",
+ __func__, len, avail);
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+ struct fsl_ifc_runtime *ifc = ctrl->regs.rregs;
+ u32 nand_fsr;
+ int status;
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ return NAND_STATUS_FAIL;
+
+ /* Use READ_STATUS command, but wait for the device to be ready */
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS <<
+ IFC_NAND_FCR0_CMD0_SHIFT);
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 1);
+ set_addr(mtd, 0, 0, 0);
+ ctrl->read_bytes = 1;
+
+ fsl_ifc_run_command(mtd);
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ return NAND_STATUS_FAIL;
+
+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
+ status = nand_fsr >> 24;
+
+ /* Chip sometimes reporting write protect even when it's not */
+ return status | NAND_STATUS_WP;
+}
+
+/*
+ * The controller does not check for bitflips in erased pages,
+ * therefore software must check instead.
+ */
+static int
+check_erased_page(struct nand_chip *chip, u8 *buf, struct mtd_info *mtd)
+{
+ u8 *ecc = chip->oob_poi;
+ const int ecc_size = chip->ecc.bytes;
+ const int pkt_size = chip->ecc.size;
+ int i, res, bitflips;
+
+ /* IFC starts ecc bytes at offset 8 in the spare area. */
+ ecc += 8;
+ bitflips = 0;
+ for (i = 0; i < chip->ecc.steps; i++) {
+ res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size,
+ NULL, 0, chip->ecc.strength);
+
+ if (res < 0) {
+ printf("fsl-ifc: NAND Flash ECC Uncorrectable Error\n");
+ mtd->ecc_stats.failed++;
+ } else if (res > 0) {
+ mtd->ecc_stats.corrected += res;
+ }
+ bitflips = max(res, bitflips);
+ buf += pkt_size;
+ ecc += ecc_size;
+ }
+
+ return bitflips;
+}
+
+static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
+ struct fsl_ifc_ctrl *ctrl = priv->ctrl;
+
+ fsl_ifc_read_buf(mtd, buf, mtd->writesize);
+ fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (ctrl->status & IFC_NAND_EVTER_STAT_ECCER)
+ return check_erased_page(chip, buf, mtd);
+
+ if (ctrl->status != IFC_NAND_EVTER_STAT_OPC)
+ mtd->ecc_stats.failed++;
+
+ return 0;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ fsl_ifc_write_buf(mtd, buf, mtd->writesize);
+ fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static void fsl_ifc_ctrl_init(void)
+{
+ uint32_t ver = 0;
+ ifc_ctrl = kzalloc(sizeof(*ifc_ctrl), GFP_KERNEL);
+ if (!ifc_ctrl)
+ return;
+
+ ifc_ctrl->regs.gregs = IFC_FCM_BASE_ADDR;
+
+ ver = ifc_in32(&ifc_ctrl->regs.gregs->ifc_rev);
+ if (ver >= FSL_IFC_V2_0_0)
+ ifc_ctrl->regs.rregs =
+ (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET;
+ else
+ ifc_ctrl->regs.rregs =
+ (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET;
+
+ /* clear event registers */
+ ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_stat, ~0U);
+ ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.pgrdcmpl_evt_stat, ~0U);
+
+ /* Enable error and event for any detected errors */
+ ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_en,
+ IFC_NAND_EVTER_EN_OPC_EN |
+ IFC_NAND_EVTER_EN_PGRDCMPL_EN |
+ IFC_NAND_EVTER_EN_FTOER_EN |
+ IFC_NAND_EVTER_EN_WPER_EN);
+
+ ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.ncfgr, 0x0);
+}
+
+static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+static int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv, uint32_t ver)
+{
+ struct fsl_ifc_runtime *ifc = ifc_ctrl->regs.rregs;
+ uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0;
+ uint32_t ncfgr = 0;
+ u32 timeo = (CONFIG_SYS_HZ * 10) / 1000;
+ u32 time_start;
+
+ if (ver > FSL_IFC_V1_1_0) {
+ ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr);
+ ifc_out32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN);
+
+ /* wait for SRAM_INIT bit to be clear or timeout */
+ time_start = get_timer(0);
+ while (get_timer(time_start) < timeo) {
+ ifc_ctrl->status =
+ ifc_in32(&ifc->ifc_nand.nand_evter_stat);
+
+ if (!(ifc_ctrl->status & IFC_NAND_SRAM_INIT_EN))
+ return 0;
+ }
+ printf("fsl-ifc: Failed to Initialise SRAM\n");
+ return 1;
+ }
+
+ cs = priv->bank;
+
+ /* Save CSOR and CSOR_ext */
+ csor = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor);
+ csor_ext = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext);
+
+ /* chage PageSize 8K and SpareSize 1K*/
+ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
+ ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor_8k);
+ ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, 0x0000400);
+
+ /* READID */
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT);
+ ifc_out32(&ifc->ifc_nand.row3, 0x0);
+
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 0x0);
+
+ /* Program ROW0/COL0 */
+ ifc_out32(&ifc->ifc_nand.row0, 0x0);
+ ifc_out32(&ifc->ifc_nand.col0, 0x0);
+
+ /* set the chip select for NAND Transaction */
+ ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT);
+
+ /* start read seq */
+ ifc_out32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT);
+
+ time_start = get_timer(0);
+
+ while (get_timer(time_start) < timeo) {
+ ifc_ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
+
+ if (ifc_ctrl->status & IFC_NAND_EVTER_STAT_OPC)
+ break;
+ }
+
+ if (ifc_ctrl->status != IFC_NAND_EVTER_STAT_OPC) {
+ printf("fsl-ifc: Failed to Initialise SRAM\n");
+ return 1;
+ }
+
+ ifc_out32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status);
+
+ /* Restore CSOR and CSOR_ext */
+ ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor);
+ ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, csor_ext);
+
+ return 0;
+}
+
+static int fsl_ifc_chip_init(int devnum, u8 *addr)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ struct fsl_ifc_mtd *priv;
+ struct nand_ecclayout *layout;
+ struct fsl_ifc_fcm *gregs = NULL;
+ uint32_t cspr = 0, csor = 0, ver = 0;
+ int ret = 0;
+
+ if (!ifc_ctrl) {
+ fsl_ifc_ctrl_init();
+ if (!ifc_ctrl)
+ return -1;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ctrl = ifc_ctrl;
+ priv->vbase = addr;
+ gregs = ifc_ctrl->regs.gregs;
+
+ /* Find which chip select it is connected to.
+ */
+ for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
+ phys_addr_t phys_addr = virt_to_phys(addr);
+
+ cspr = ifc_in32(&gregs->cspr_cs[priv->bank].cspr);
+ csor = ifc_in32(&gregs->csor_cs[priv->bank].csor);
+
+ if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND &&
+ (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr))
+ break;
+ }
+
+ if (priv->bank >= MAX_BANKS) {
+ printf("%s: address did not match any "
+ "chip selects\n", __func__);
+ kfree(priv);
+ return -ENODEV;
+ }
+
+ nand = &priv->chip;
+ mtd = nand_to_mtd(nand);
+
+ ifc_ctrl->chips[priv->bank] = priv;
+
+ /* fill in nand_chip structure */
+ /* set up function call table */
+
+ nand->write_buf = fsl_ifc_write_buf;
+ nand->read_buf = fsl_ifc_read_buf;
+ nand->select_chip = fsl_ifc_select_chip;
+ nand->cmdfunc = fsl_ifc_cmdfunc;
+ nand->waitfunc = fsl_ifc_wait;
+
+ /* set up nand options */
+ nand->bbt_td = &bbt_main_descr;
+ nand->bbt_md = &bbt_mirror_descr;
+
+ /* set up nand options */
+ nand->options = NAND_NO_SUBPAGE_WRITE;
+ nand->bbt_options = NAND_BBT_USE_FLASH;
+
+ if (cspr & CSPR_PORT_SIZE_16) {
+ nand->read_byte = fsl_ifc_read_byte16;
+ nand->options |= NAND_BUSWIDTH_16;
+ } else {
+ nand->read_byte = fsl_ifc_read_byte;
+ }
+
+ nand->controller = &ifc_ctrl->controller;
+ nand_set_controller_data(nand, priv);
+
+ nand->ecc.read_page = fsl_ifc_read_page;
+ nand->ecc.write_page = fsl_ifc_write_page;
+
+ /* Hardware generates ECC per 512 Bytes */
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 8;
+
+ switch (csor & CSOR_NAND_PGS_MASK) {
+ case CSOR_NAND_PGS_512:
+ if (nand->options & NAND_BUSWIDTH_16) {
+ layout = &oob_512_16bit_ecc4;
+ } else {
+ layout = &oob_512_8bit_ecc4;
+
+ /* Avoid conflict with bad block marker */
+ bbt_main_descr.offs = 0;
+ bbt_mirror_descr.offs = 0;
+ }
+
+ nand->ecc.strength = 4;
+ priv->bufnum_mask = 15;
+ break;
+
+ case CSOR_NAND_PGS_2K:
+ layout = &oob_2048_ecc4;
+ nand->ecc.strength = 4;
+ priv->bufnum_mask = 3;
+ break;
+
+ case CSOR_NAND_PGS_4K:
+ if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
+ CSOR_NAND_ECC_MODE_4) {
+ layout = &oob_4096_ecc4;
+ nand->ecc.strength = 4;
+ } else {
+ layout = &oob_4096_ecc8;
+ nand->ecc.strength = 8;
+ nand->ecc.bytes = 16;
+ }
+
+ priv->bufnum_mask = 1;
+ break;
+
+ case CSOR_NAND_PGS_8K:
+ if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
+ CSOR_NAND_ECC_MODE_4) {
+ layout = &oob_8192_ecc4;
+ nand->ecc.strength = 4;
+ } else {
+ layout = &oob_8192_ecc8;
+ nand->ecc.strength = 8;
+ nand->ecc.bytes = 16;
+ }
+
+ priv->bufnum_mask = 0;
+ break;
+
+
+ default:
+ printf("ifc nand: bad csor %#x: bad page size\n", csor);
+ return -ENODEV;
+ }
+
+ /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
+ if (csor & CSOR_NAND_ECC_DEC_EN) {
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = layout;
+ } else {
+ nand->ecc.mode = NAND_ECC_SOFT;
+ }
+
+ ver = ifc_in32(&gregs->ifc_rev);
+ if (ver >= FSL_IFC_V1_1_0)
+ ret = fsl_ifc_sram_init(priv, ver);
+ if (ret)
+ return ret;
+
+ if (ver >= FSL_IFC_V2_0_0)
+ priv->bufnum_mask = (priv->bufnum_mask * 2) + 1;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(devnum, mtd);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] =
+ CONFIG_SYS_NAND_BASE_LIST;
+
+void board_nand_init(void)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ fsl_ifc_chip_init(i, (u8 *)base_address[i]);
+}
diff --git a/drivers/mtd/nand/raw/fsl_ifc_spl.c b/drivers/mtd/nand/raw/fsl_ifc_spl.c
new file mode 100644
index 0000000000..7137eb4108
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_ifc_spl.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NAND boot for Freescale Integrated Flash Controller, NAND FCM
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Author: Dipen Dudhat <dipen.dudhat@freescale.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <fsl_ifc.h>
+#include <linux/mtd/rawnand.h>
+#ifdef CONFIG_CHAIN_OF_TRUST
+#include <fsl_validate.h>
+#endif
+
+static inline int is_blank(uchar *addr, int page_size)
+{
+ int i;
+
+ for (i = 0; i < page_size; i++) {
+ if (__raw_readb(&addr[i]) != 0xff)
+ return 0;
+ }
+
+ /*
+ * For the SPL, don't worry about uncorrectable errors
+ * where the main area is all FFs but shouldn't be.
+ */
+ return 1;
+}
+
+/* returns nonzero if entire page is blank */
+static inline int check_read_ecc(uchar *buf, u32 *eccstat,
+ unsigned int bufnum, int page_size)
+{
+ u32 reg = eccstat[bufnum / 4];
+ int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf;
+
+ if (errors == 0xf) { /* uncorrectable */
+ /* Blank pages fail hw ECC checks */
+ if (is_blank(buf, page_size))
+ return 1;
+
+ puts("ecc error\n");
+ for (;;)
+ ;
+ }
+
+ return 0;
+}
+
+static inline struct fsl_ifc_runtime *runtime_regs_address(void)
+{
+ struct fsl_ifc regs = {(void *)CONFIG_SYS_IFC_ADDR, NULL};
+ int ver = 0;
+
+ ver = ifc_in32(&regs.gregs->ifc_rev);
+ if (ver >= FSL_IFC_V2_0_0)
+ regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET;
+ else
+ regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET;
+
+ return regs.rregs;
+}
+
+static inline void nand_wait(uchar *buf, int bufnum, int page_size)
+{
+ struct fsl_ifc_runtime *ifc = runtime_regs_address();
+ u32 status;
+ u32 eccstat[8];
+ int bufperpage = page_size / 512;
+ int bufnum_end, i;
+
+ bufnum *= bufperpage;
+ bufnum_end = bufnum + bufperpage - 1;
+
+ do {
+ status = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
+ } while (!(status & IFC_NAND_EVTER_STAT_OPC));
+
+ if (status & IFC_NAND_EVTER_STAT_FTOER) {
+ puts("flash time out error\n");
+ for (;;)
+ ;
+ }
+
+ for (i = bufnum / 4; i <= bufnum_end / 4; i++)
+ eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]);
+
+ for (i = bufnum; i <= bufnum_end; i++) {
+ if (check_read_ecc(buf, eccstat, i, page_size))
+ break;
+ }
+
+ ifc_out32(&ifc->ifc_nand.nand_evter_stat, status);
+}
+
+static inline int bad_block(uchar *marker, int port_size)
+{
+ if (port_size == 8)
+ return __raw_readb(marker) != 0xff;
+ else
+ return __raw_readw((u16 *)marker) != 0xffff;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
+{
+ struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR;
+ struct fsl_ifc_runtime *ifc = NULL;
+ uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
+ int page_size;
+ int port_size;
+ int pages_per_blk;
+ int blk_size;
+ int bad_marker = 0;
+ int bufnum_mask, bufnum, ver = 0;
+
+ int csor, cspr;
+ int pos = 0;
+ int j = 0;
+
+ int sram_addr;
+ int pg_no;
+ uchar *dst = vdst;
+
+ ifc = runtime_regs_address();
+
+ /* Get NAND Flash configuration */
+ csor = CONFIG_SYS_NAND_CSOR;
+ cspr = CONFIG_SYS_NAND_CSPR;
+
+ port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8;
+
+ if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) {
+ page_size = 8192;
+ bufnum_mask = 0x0;
+ } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) {
+ page_size = 4096;
+ bufnum_mask = 0x1;
+ } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) {
+ page_size = 2048;
+ bufnum_mask = 0x3;
+ } else {
+ page_size = 512;
+ bufnum_mask = 0xf;
+
+ if (port_size == 8)
+ bad_marker = 5;
+ }
+
+ ver = ifc_in32(&gregs->ifc_rev);
+ if (ver >= FSL_IFC_V2_0_0)
+ bufnum_mask = (bufnum_mask * 2) + 1;
+
+ pages_per_blk =
+ 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT);
+
+ blk_size = pages_per_blk * page_size;
+
+ /* Open Full SRAM mapping for spare are access */
+ ifc_out32(&ifc->ifc_nand.ncfgr, 0x0);
+
+ /* Clear Boot events */
+ ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff);
+
+ /* Program FIR/FCR for Large/Small page */
+ if (page_size > 512) {
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
+
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT));
+ } else {
+ ifc_out32(&ifc->ifc_nand.nand_fir0,
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT));
+ ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
+
+ ifc_out32(&ifc->ifc_nand.nand_fcr0,
+ NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT);
+ }
+
+ /* Program FBCR = 0 for full page read */
+ ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
+
+ /* Read and copy u-boot on SDRAM from NAND device, In parallel
+ * check for Bad block if found skip it and read continue to
+ * next Block
+ */
+ while (pos < uboot_size) {
+ int i = 0;
+ do {
+ pg_no = offs / page_size;
+ bufnum = pg_no & bufnum_mask;
+ sram_addr = bufnum * page_size * 2;
+
+ ifc_out32(&ifc->ifc_nand.row0, pg_no);
+ ifc_out32(&ifc->ifc_nand.col0, 0);
+ /* start read */
+ ifc_out32(&ifc->ifc_nand.nandseq_strt,
+ IFC_NAND_SEQ_STRT_FIR_STRT);
+
+ /* wait for read to complete */
+ nand_wait(&buf[sram_addr], bufnum, page_size);
+
+ /*
+ * If either of the first two pages are marked bad,
+ * continue to the next block.
+ */
+ if (i++ < 2 &&
+ bad_block(&buf[sram_addr + page_size + bad_marker],
+ port_size)) {
+ puts("skipping\n");
+ offs = (offs + blk_size) & ~(blk_size - 1);
+ pos &= ~(blk_size - 1);
+ break;
+ }
+
+ for (j = 0; j < page_size; j++)
+ dst[pos + j] = __raw_readb(&buf[sram_addr + j]);
+
+ pos += page_size;
+ offs += page_size;
+ } while ((offs & (blk_size - 1)) && (pos < uboot_size));
+ }
+
+ return 0;
+}
+
+/*
+ * Main entrypoint for NAND Boot. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+ /*
+ * Load U-Boot image from NAND into RAM
+ */
+ nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (uchar *)CONFIG_SYS_NAND_U_BOOT_DST);
+
+#ifdef CONFIG_NAND_ENV_DST
+ nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+ (uchar *)CONFIG_NAND_ENV_DST);
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+ (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
+#endif
+#endif
+ /*
+ * Jump to U-Boot image
+ */
+#ifdef CONFIG_SPL_FLUSH_IMAGE
+ /*
+ * Clean d-cache and invalidate i-cache, to
+ * make sure that no stale data is executed.
+ */
+ flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
+#endif
+
+#ifdef CONFIG_CHAIN_OF_TRUST
+ /*
+ * U-Boot header is appended at end of U-boot image, so
+ * calculate U-boot header address using U-boot header size.
+ */
+#define CONFIG_U_BOOT_HDR_ADDR \
+ ((CONFIG_SYS_NAND_U_BOOT_START + \
+ CONFIG_SYS_NAND_U_BOOT_SIZE) - \
+ CONFIG_U_BOOT_HDR_SIZE)
+ spl_validate_uboot(CONFIG_U_BOOT_HDR_ADDR,
+ CONFIG_SYS_NAND_U_BOOT_START);
+ /*
+ * In case of failure in validation, spl_validate_uboot would
+ * not return back in case of Production environment with ITS=1.
+ * Thus U-Boot will not start.
+ * In Development environment (ITS=0 and SB_EN=1), the function
+ * may return back in case of non-fatal failures.
+ */
+#endif
+
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ uboot();
+}
+
+#ifndef CONFIG_SPL_NAND_INIT
+void nand_init(void)
+{
+}
+
+void nand_deselect(void)
+{
+}
+#endif
diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c
new file mode 100644
index 0000000000..dfbdbca3ae
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsl_upm.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FSL UPM NAND driver
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc.
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/fsl_upm.h>
+#include <nand.h>
+
+static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset)
+{
+ clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset);
+ (void)in_be32(upm->mxmr);
+}
+
+static void fsl_upm_end_pattern(struct fsl_upm *upm)
+{
+ clrbits_be32(upm->mxmr, MxMR_OP_RUNP);
+
+ while (in_be32(upm->mxmr) & MxMR_OP_RUNP)
+ eieio();
+}
+
+static void fsl_upm_run_pattern(struct fsl_upm *upm, int width,
+ void __iomem *io_addr, u32 mar)
+{
+ out_be32(upm->mar, mar);
+ (void)in_be32(upm->mar);
+ switch (width) {
+ case 8:
+ out_8(io_addr, 0x0);
+ break;
+ case 16:
+ out_be16(io_addr, 0x0);
+ break;
+ case 32:
+ out_be32(io_addr, 0x0);
+ break;
+ }
+}
+
+static void fun_wait(struct fsl_upm_nand *fun)
+{
+ if (fun->dev_ready) {
+ while (!fun->dev_ready(fun->chip_nr))
+ debug("unexpected busy state\n");
+ } else {
+ /*
+ * If the R/B pin is not connected,
+ * a short delay is necessary.
+ */
+ udelay(1);
+ }
+}
+
+#if CONFIG_SYS_NAND_MAX_CHIPS > 1
+static void fun_select_chip(struct mtd_info *mtd, int chip_nr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_upm_nand *fun = nand_get_controller_data(chip);
+
+ if (chip_nr >= 0) {
+ fun->chip_nr = chip_nr;
+ chip->IO_ADDR_R = chip->IO_ADDR_W =
+ fun->upm.io_addr + fun->chip_offset * chip_nr;
+ } else if (chip_nr == -1) {
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+ }
+}
+#endif
+
+static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_upm_nand *fun = nand_get_controller_data(chip);
+ void __iomem *io_addr;
+ u32 mar;
+
+ if (!(ctrl & fun->last_ctrl)) {
+ fsl_upm_end_pattern(&fun->upm);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
+ }
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_ALE)
+ fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
+ else if (ctrl & NAND_CLE)
+ fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
+ }
+
+ mar = cmd << (32 - fun->width);
+ io_addr = fun->upm.io_addr;
+#if CONFIG_SYS_NAND_MAX_CHIPS > 1
+ if (fun->chip_nr > 0) {
+ io_addr += fun->chip_offset * fun->chip_nr;
+ if (fun->upm_mar_chip_offset)
+ mar |= fun->upm_mar_chip_offset * fun->chip_nr;
+ }
+#endif
+ fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar);
+
+ /*
+ * Some boards/chips needs this. At least the MPC8360E-RDK
+ * needs it. Probably weird chip, because I don't see any
+ * need for this on MPC8555E + Samsung K9F1G08U0A. Usually
+ * here are 0-2 unexpected busy states per block read.
+ */
+ if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
+ fun_wait(fun);
+}
+
+static u8 upm_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ return in_8(chip->IO_ADDR_R);
+}
+
+static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_upm_nand *fun = nand_get_controller_data(chip);
+
+ for (i = 0; i < len; i++) {
+ out_8(chip->IO_ADDR_W, buf[i]);
+ if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
+ fun_wait(fun);
+ }
+
+ if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
+ fun_wait(fun);
+}
+
+static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ for (i = 0; i < len; i++)
+ buf[i] = in_8(chip->IO_ADDR_R);
+}
+
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_upm_nand *fun = nand_get_controller_data(chip);
+
+ return fun->dev_ready(fun->chip_nr);
+}
+
+int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun)
+{
+ if (fun->width != 8 && fun->width != 16 && fun->width != 32)
+ return -ENOSYS;
+
+ fun->last_ctrl = NAND_CLE;
+
+ nand_set_controller_data(chip, fun);
+ chip->chip_delay = fun->chip_delay;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ chip->cmd_ctrl = fun_cmd_ctrl;
+#if CONFIG_SYS_NAND_MAX_CHIPS > 1
+ chip->select_chip = fun_select_chip;
+#endif
+ chip->read_byte = upm_nand_read_byte;
+ chip->read_buf = upm_nand_read_buf;
+ chip->write_buf = upm_nand_write_buf;
+ if (fun->dev_ready)
+ chip->dev_ready = nand_dev_ready;
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
new file mode 100644
index 0000000000..1f4c74f0f6
--- /dev/null
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2010
+ * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com.
+ *
+ * (C) Copyright 2012
+ * Amit Virdi, ST Microelectronics, amit.virdi@st.com.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/fsmc_nand.h>
+#include <asm/arch/hardware.h>
+
+static u32 fsmc_version;
+static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *)
+ CONFIG_SYS_FSMC_BASE;
+
+/*
+ * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of
+ * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can
+ * correct 1 bit in 512 bytes
+ */
+
+static struct nand_ecclayout fsmc_ecc4_lp_layout = {
+ .eccbytes = 104,
+ .eccpos = { 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14,
+ 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30,
+ 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46,
+ 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62,
+ 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78,
+ 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94,
+ 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110,
+ 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126
+ },
+ .oobfree = {
+ {.offset = 15, .length = 3},
+ {.offset = 31, .length = 3},
+ {.offset = 47, .length = 3},
+ {.offset = 63, .length = 3},
+ {.offset = 79, .length = 3},
+ {.offset = 95, .length = 3},
+ {.offset = 111, .length = 3},
+ {.offset = 127, .length = 1}
+ }
+};
+
+/*
+ * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
+ * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
+ * bytes are free for use.
+ */
+static struct nand_ecclayout fsmc_ecc4_224_layout = {
+ .eccbytes = 104,
+ .eccpos = { 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14,
+ 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30,
+ 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46,
+ 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62,
+ 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78,
+ 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94,
+ 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110,
+ 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126
+ },
+ .oobfree = {
+ {.offset = 15, .length = 3},
+ {.offset = 31, .length = 3},
+ {.offset = 47, .length = 3},
+ {.offset = 63, .length = 3},
+ {.offset = 79, .length = 3},
+ {.offset = 95, .length = 3},
+ {.offset = 111, .length = 3},
+ {.offset = 127, .length = 97}
+ }
+};
+
+/*
+ * ECC placement definitions in oobfree type format
+ * There are 13 bytes of ecc for every 512 byte block and it has to be read
+ * consecutively and immediately after the 512 byte data block for hardware to
+ * generate the error bit offsets in 512 byte data
+ * Managing the ecc bytes in the following way makes it easier for software to
+ * read ecc bytes consecutive to data bytes. This way is similar to
+ * oobfree structure maintained already in u-boot nand driver
+ */
+static struct fsmc_eccplace fsmc_eccpl_lp = {
+ .eccplace = {
+ {.offset = 2, .length = 13},
+ {.offset = 18, .length = 13},
+ {.offset = 34, .length = 13},
+ {.offset = 50, .length = 13},
+ {.offset = 66, .length = 13},
+ {.offset = 82, .length = 13},
+ {.offset = 98, .length = 13},
+ {.offset = 114, .length = 13}
+ }
+};
+
+static struct nand_ecclayout fsmc_ecc4_sp_layout = {
+ .eccbytes = 13,
+ .eccpos = { 0, 1, 2, 3, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14
+ },
+ .oobfree = {
+ {.offset = 15, .length = 1},
+ }
+};
+
+static struct fsmc_eccplace fsmc_eccpl_sp = {
+ .eccplace = {
+ {.offset = 0, .length = 4},
+ {.offset = 6, .length = 9}
+ }
+};
+
+static struct nand_ecclayout fsmc_ecc1_layout = {
+ .eccbytes = 24,
+ .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
+ 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
+ .oobfree = {
+ {.offset = 8, .length = 8},
+ {.offset = 24, .length = 8},
+ {.offset = 40, .length = 8},
+ {.offset = 56, .length = 8},
+ {.offset = 72, .length = 8},
+ {.offset = 88, .length = 8},
+ {.offset = 104, .length = 8},
+ {.offset = 120, .length = 8}
+ }
+};
+
+/* Count the number of 0's in buff upto a max of max_bits */
+static int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+ int k, written_bits = 0;
+
+ for (k = 0; k < size; k++) {
+ written_bits += hweight8(~buff[k]);
+ if (written_bits > max_bits)
+ break;
+ }
+
+ return written_bits;
+}
+
+static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ ulong IO_ADDR_W;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ IO_ADDR_W = (ulong)this->IO_ADDR_W;
+
+ IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE);
+ if (ctrl & NAND_CLE)
+ IO_ADDR_W |= CONFIG_SYS_NAND_CLE;
+ if (ctrl & NAND_ALE)
+ IO_ADDR_W |= CONFIG_SYS_NAND_ALE;
+
+ if (ctrl & NAND_NCE) {
+ writel(readl(&fsmc_regs_p->pc) |
+ FSMC_ENABLE, &fsmc_regs_p->pc);
+ } else {
+ writel(readl(&fsmc_regs_p->pc) &
+ ~FSMC_ENABLE, &fsmc_regs_p->pc);
+ }
+ this->IO_ADDR_W = (void *)IO_ADDR_W;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ /* The calculated ecc is actually the correction index in data */
+ u32 err_idx[8];
+ u32 num_err, i;
+ u32 ecc1, ecc2, ecc3, ecc4;
+
+ num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF;
+
+ if (likely(num_err == 0))
+ return 0;
+
+ if (unlikely(num_err > 8)) {
+ /*
+ * This is a temporary erase check. A newly erased page read
+ * would result in an ecc error because the oob data is also
+ * erased to FF and the calculated ecc for an FF data is not
+ * FF..FF.
+ * This is a workaround to skip performing correction in case
+ * data is FF..FF
+ *
+ * Logic:
+ * For every page, each bit written as 0 is counted until these
+ * number of bits are greater than 8 (the maximum correction
+ * capability of FSMC for each 512 + 13 bytes)
+ */
+
+ int bits_ecc = count_written_bits(read_ecc, 13, 8);
+ int bits_data = count_written_bits(dat, 512, 8);
+
+ if ((bits_ecc + bits_data) <= 8) {
+ if (bits_data)
+ memset(dat, 0xff, 512);
+ return bits_data + bits_ecc;
+ }
+
+ return -EBADMSG;
+ }
+
+ ecc1 = readl(&fsmc_regs_p->ecc1);
+ ecc2 = readl(&fsmc_regs_p->ecc2);
+ ecc3 = readl(&fsmc_regs_p->ecc3);
+ ecc4 = readl(&fsmc_regs_p->sts);
+
+ err_idx[0] = (ecc1 >> 0) & 0x1FFF;
+ err_idx[1] = (ecc1 >> 13) & 0x1FFF;
+ err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
+ err_idx[3] = (ecc2 >> 7) & 0x1FFF;
+ err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
+ err_idx[5] = (ecc3 >> 1) & 0x1FFF;
+ err_idx[6] = (ecc3 >> 14) & 0x1FFF;
+ err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
+
+ i = 0;
+ while (i < num_err) {
+ err_idx[i] ^= 3;
+
+ if (err_idx[i] < 512 * 8)
+ __change_bit(err_idx[i], dat);
+
+ i++;
+ }
+
+ return num_err;
+}
+
+static int fsmc_read_hwecc(struct mtd_info *mtd,
+ const u_char *data, u_char *ecc)
+{
+ u_int ecc_tmp;
+ int timeout = CONFIG_SYS_HZ;
+ ulong start;
+
+ switch (fsmc_version) {
+ case FSMC_VER8:
+ start = get_timer(0);
+ while (get_timer(start) < timeout) {
+ /*
+ * Busy waiting for ecc computation
+ * to finish for 512 bytes
+ */
+ if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY)
+ break;
+ }
+
+ ecc_tmp = readl(&fsmc_regs_p->ecc1);
+ ecc[0] = (u_char) (ecc_tmp >> 0);
+ ecc[1] = (u_char) (ecc_tmp >> 8);
+ ecc[2] = (u_char) (ecc_tmp >> 16);
+ ecc[3] = (u_char) (ecc_tmp >> 24);
+
+ ecc_tmp = readl(&fsmc_regs_p->ecc2);
+ ecc[4] = (u_char) (ecc_tmp >> 0);
+ ecc[5] = (u_char) (ecc_tmp >> 8);
+ ecc[6] = (u_char) (ecc_tmp >> 16);
+ ecc[7] = (u_char) (ecc_tmp >> 24);
+
+ ecc_tmp = readl(&fsmc_regs_p->ecc3);
+ ecc[8] = (u_char) (ecc_tmp >> 0);
+ ecc[9] = (u_char) (ecc_tmp >> 8);
+ ecc[10] = (u_char) (ecc_tmp >> 16);
+ ecc[11] = (u_char) (ecc_tmp >> 24);
+
+ ecc_tmp = readl(&fsmc_regs_p->sts);
+ ecc[12] = (u_char) (ecc_tmp >> 16);
+ break;
+
+ default:
+ ecc_tmp = readl(&fsmc_regs_p->ecc1);
+ ecc[0] = (u_char) (ecc_tmp >> 0);
+ ecc[1] = (u_char) (ecc_tmp >> 8);
+ ecc[2] = (u_char) (ecc_tmp >> 16);
+ break;
+ }
+
+ return 0;
+}
+
+void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256,
+ &fsmc_regs_p->pc);
+ writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN,
+ &fsmc_regs_p->pc);
+ writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN,
+ &fsmc_regs_p->pc);
+}
+
+/*
+ * fsmc_read_page_hwecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * This routine is needed for fsmc verison 8 as reading from NAND chip has to be
+ * performed in a strict sequence as follows:
+ * data(512 byte) -> ecc(13 byte)
+ * After this read, fsmc hardware generates and reports error data bits(upto a
+ * max of 8 bits)
+ */
+static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ struct fsmc_eccplace *fsmc_eccpl;
+ int i, j, s, stat, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ int off, len, group = 0;
+ uint8_t oob[13] __attribute__ ((aligned (2)));
+
+ /* Differentiate between small and large page ecc place definitions */
+ if (mtd->writesize == 512)
+ fsmc_eccpl = &fsmc_eccpl_sp;
+ else
+ fsmc_eccpl = &fsmc_eccpl_lp;
+
+ for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+
+ for (j = 0; j < eccbytes;) {
+ off = fsmc_eccpl->eccplace[group].offset;
+ len = fsmc_eccpl->eccplace[group].length;
+ group++;
+
+ /*
+ * length is intentionally kept a higher multiple of 2
+ * to read at least 13 bytes even in case of 16 bit NAND
+ * devices
+ */
+ if (chip->options & NAND_BUSWIDTH_16)
+ len = roundup(len, 2);
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
+ chip->read_buf(mtd, oob + j, len);
+ j += len;
+ }
+
+ memcpy(&ecc_code[i], oob, 13);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i],
+ &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+
+ return 0;
+}
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ * fsmc_nand_switch_ecc - switch the ECC operation between different engines
+ *
+ * @eccstrength - the number of bits that could be corrected
+ * (1 - HW, 4 - SW BCH4)
+ */
+int fsmc_nand_switch_ecc(uint32_t eccstrength)
+{
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ int err;
+
+ /*
+ * This functions is only called on SPEAr600 platforms, supporting
+ * 1 bit HW ECC. The BCH8 HW ECC (FSMC_VER8) from the ST-Ericsson
+ * Nomadik SoC is currently supporting this fsmc_nand_switch_ecc()
+ * function, as it doesn't need to switch to a different ECC layout.
+ */
+ mtd = get_nand_dev_by_index(nand_curr_device);
+ nand = mtd_to_nand(mtd);
+
+ /* Setup the ecc configurations again */
+ if (eccstrength == 1) {
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
+ nand->ecc.layout = &fsmc_ecc1_layout;
+ nand->ecc.calculate = fsmc_read_hwecc;
+ nand->ecc.correct = nand_correct_data;
+ } else if (eccstrength == 4) {
+ /*
+ * .calculate .correct and .bytes will be set in
+ * nand_scan_tail()
+ */
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+ nand->ecc.strength = 4;
+ nand->ecc.layout = NULL;
+ } else {
+ printf("Error: ECC strength %d not supported!\n", eccstrength);
+ }
+
+ /* Update NAND handling after ECC mode switch */
+ err = nand_scan_tail(mtd);
+
+ return err;
+}
+#endif /* CONFIG_SPL_BUILD */
+
+int fsmc_nand_init(struct nand_chip *nand)
+{
+ static int chip_nr;
+ struct mtd_info *mtd;
+ u32 peripid2 = readl(&fsmc_regs_p->peripid2);
+
+ fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) &
+ FSMC_REVISION_MSK;
+
+ writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl);
+
+#if defined(CONFIG_SYS_FSMC_NAND_16BIT)
+ writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON,
+ &fsmc_regs_p->pc);
+#elif defined(CONFIG_SYS_FSMC_NAND_8BIT)
+ writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON,
+ &fsmc_regs_p->pc);
+#else
+#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT
+#endif
+ writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1,
+ &fsmc_regs_p->pc);
+ writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
+ &fsmc_regs_p->comm);
+ writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
+ &fsmc_regs_p->attrib);
+
+ nand->options = 0;
+#if defined(CONFIG_SYS_FSMC_NAND_16BIT)
+ nand->options |= NAND_BUSWIDTH_16;
+#endif
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = 512;
+ nand->ecc.calculate = fsmc_read_hwecc;
+ nand->ecc.hwctl = fsmc_enable_hwecc;
+ nand->cmd_ctrl = fsmc_nand_hwcontrol;
+ nand->IO_ADDR_R = nand->IO_ADDR_W =
+ (void __iomem *)CONFIG_SYS_NAND_BASE;
+ nand->badblockbits = 7;
+
+ mtd = nand_to_mtd(nand);
+
+ switch (fsmc_version) {
+ case FSMC_VER8:
+ nand->ecc.bytes = 13;
+ nand->ecc.strength = 8;
+ nand->ecc.correct = fsmc_bch8_correct_data;
+ nand->ecc.read_page = fsmc_read_page_hwecc;
+ if (mtd->writesize == 512)
+ nand->ecc.layout = &fsmc_ecc4_sp_layout;
+ else {
+ if (mtd->oobsize == 224)
+ nand->ecc.layout = &fsmc_ecc4_224_layout;
+ else
+ nand->ecc.layout = &fsmc_ecc4_lp_layout;
+ }
+
+ break;
+ default:
+ nand->ecc.bytes = 3;
+ nand->ecc.strength = 1;
+ nand->ecc.layout = &fsmc_ecc1_layout;
+ nand->ecc.correct = nand_correct_data;
+ break;
+ }
+
+ /* Detect NAND chips */
+ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL))
+ return -ENXIO;
+
+ if (nand_scan_tail(mtd))
+ return -ENXIO;
+
+ if (nand_register(chip_nr++, mtd))
+ return -ENXIO;
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/kb9202_nand.c b/drivers/mtd/nand/raw/kb9202_nand.c
new file mode 100644
index 0000000000..0f68f1cd86
--- /dev/null
+++ b/drivers/mtd/nand/raw/kb9202_nand.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2006
+ * KwikByte <kb9200_dev@kwikbyte.com>
+ *
+ * (C) Copyright 2009
+ * Matthias Kaehlcke <matthias@kaehlcke.net>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/AT91RM9200.h>
+#include <asm/arch/hardware.h>
+
+#include <nand.h>
+
+/*
+ * hardware specific access to control-lines
+ */
+
+#define MASK_ALE (1 << 22) /* our ALE is A22 */
+#define MASK_CLE (1 << 21) /* our CLE is A21 */
+
+#define KB9202_NAND_NCE (1 << 28) /* EN* on D28 */
+#define KB9202_NAND_BUSY (1 << 29) /* RB* on D29 */
+
+#define KB9202_SMC2_NWS (1 << 2)
+#define KB9202_SMC2_TDF (1 << 8)
+#define KB9202_SMC2_RWSETUP (1 << 24)
+#define KB9202_SMC2_RWHOLD (1 << 29)
+
+/*
+ * Board-specific function to access device control signals
+ */
+static void kb9202_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
+
+ /* clear ALE and CLE bits */
+ IO_ADDR_W &= ~(MASK_ALE | MASK_CLE);
+
+ if (ctrl & NAND_CLE)
+ IO_ADDR_W |= MASK_CLE;
+
+ if (ctrl & NAND_ALE)
+ IO_ADDR_W |= MASK_ALE;
+
+ this->IO_ADDR_W = (void *) IO_ADDR_W;
+
+ if (ctrl & NAND_NCE)
+ writel(KB9202_NAND_NCE, AT91C_PIOC_CODR);
+ else
+ writel(KB9202_NAND_NCE, AT91C_PIOC_SODR);
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+
+/*
+ * Board-specific function to access the device ready signal.
+ */
+static int kb9202_nand_ready(struct mtd_info *mtd)
+{
+ return readl(AT91C_PIOC_PDSR) & KB9202_NAND_BUSY;
+}
+
+
+/*
+ * Board-specific NAND init. Copied from include/linux/mtd/nand.h for reference.
+ *
+ * struct nand_chip - NAND Private Flash Chip Data
+ * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
+ * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
+ * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
+ * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
+ * If set to NULL no access to ready/busy is available and the ready/busy information
+ * is read from the chip status register
+ * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
+ * be provided if a hardware ECC is available
+ * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
+ * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
+ * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
+ * special functionality. See the defines for further explanation
+*/
+/*
+ * This routine initializes controller and GPIOs.
+ */
+int board_nand_init(struct nand_chip *nand)
+{
+ unsigned int value;
+
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->cmd_ctrl = kb9202_nand_hwcontrol;
+ nand->dev_ready = kb9202_nand_ready;
+
+ /* in case running outside of bootloader */
+ writel(1 << AT91C_ID_PIOC, AT91C_PMC_PCER);
+
+ /* setup nand flash access (allow ample margin) */
+ /* 4 wait states, 1 setup, 1 hold, 1 float for 8-bit device */
+ writel(AT91C_SMC2_WSEN | KB9202_SMC2_NWS | KB9202_SMC2_TDF |
+ AT91C_SMC2_DBW_8 | KB9202_SMC2_RWSETUP | KB9202_SMC2_RWHOLD,
+ AT91C_SMC_CSR3);
+
+ /* enable internal NAND controller */
+ value = readl(AT91C_EBI_CSA);
+ value |= AT91C_EBI_CS3A_SMC_SmartMedia;
+ writel(value, AT91C_EBI_CSA);
+
+ /* enable SMOE/SMWE */
+ writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_ASR);
+ writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_PDR);
+ writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_OER);
+
+ /* set NCE to high */
+ writel(KB9202_NAND_NCE, AT91C_PIOC_SODR);
+
+ /* disable output on pin connected to the busy line of the NAND */
+ writel(KB9202_NAND_BUSY, AT91C_PIOC_ODR);
+
+ /* enable the PIO to control NCE and BUSY */
+ writel(KB9202_NAND_NCE | KB9202_NAND_BUSY, AT91C_PIOC_PER);
+
+ /* enable output for NCE */
+ writel(KB9202_NAND_NCE, AT91C_PIOC_OER);
+
+ return (0);
+}
diff --git a/drivers/mtd/nand/raw/kirkwood_nand.c b/drivers/mtd/nand/raw/kirkwood_nand.c
new file mode 100644
index 0000000000..0757fa840b
--- /dev/null
+++ b/drivers/mtd/nand/raw/kirkwood_nand.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Prafulla Wadaskar <prafulla@marvell.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/mpp.h>
+#include <nand.h>
+
+/* NAND Flash Soc registers */
+struct kwnandf_registers {
+ u32 rd_params; /* 0x10418 */
+ u32 wr_param; /* 0x1041c */
+ u8 pad[0x10470 - 0x1041c - 4];
+ u32 ctrl; /* 0x10470 */
+};
+
+static struct kwnandf_registers *nf_reg =
+ (struct kwnandf_registers *)KW_NANDF_BASE;
+
+static u32 nand_mpp_backup[9] = { 0 };
+
+/*
+ * hardware specific access to control-lines/bits
+ */
+#define NAND_ACTCEBOOT_BIT 0x02
+
+static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_chip *nc = mtd_to_nand(mtd);
+ u32 offs;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ offs = (1 << 0); /* Commands with A[1:0] == 01 */
+ else if (ctrl & NAND_ALE)
+ offs = (1 << 1); /* Addresses with A[1:0] == 10 */
+ else
+ return;
+
+ writeb(cmd, nc->IO_ADDR_W + offs);
+}
+
+void kw_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ u32 data;
+ static const u32 nand_config[] = {
+ MPP0_NF_IO2,
+ MPP1_NF_IO3,
+ MPP2_NF_IO4,
+ MPP3_NF_IO5,
+ MPP4_NF_IO6,
+ MPP5_NF_IO7,
+ MPP18_NF_IO0,
+ MPP19_NF_IO1,
+ 0
+ };
+
+ if (chip >= 0)
+ kirkwood_mpp_conf(nand_config, nand_mpp_backup);
+ else
+ kirkwood_mpp_conf(nand_mpp_backup, NULL);
+
+ data = readl(&nf_reg->ctrl);
+ data |= NAND_ACTCEBOOT_BIT;
+ writel(data, &nf_reg->ctrl);
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+ nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
+#if defined(CONFIG_SYS_NAND_NO_SUBPAGE_WRITE)
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+#endif
+#if defined(CONFIG_NAND_ECC_BCH)
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
+ nand->ecc.mode = NAND_ECC_SOFT;
+#endif
+ nand->cmd_ctrl = kw_nand_hwcontrol;
+ nand->chip_delay = 40;
+ nand->select_chip = kw_nand_select_chip;
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/kmeter1_nand.c b/drivers/mtd/nand/raw/kmeter1_nand.c
new file mode 100644
index 0000000000..7103300060
--- /dev/null
+++ b/drivers/mtd/nand/raw/kmeter1_nand.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009
+ * Heiko Schocher, DENX Software Engineering, hs@denx.de
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+
+#define CONFIG_NAND_MODE_REG (void *)(CONFIG_SYS_NAND_BASE + 0x20000)
+#define CONFIG_NAND_DATA_REG (void *)(CONFIG_SYS_NAND_BASE + 0x30000)
+
+#define read_mode() in_8(CONFIG_NAND_MODE_REG)
+#define write_mode(val) out_8(CONFIG_NAND_MODE_REG, val)
+#define read_data() in_8(CONFIG_NAND_DATA_REG)
+#define write_data(val) out_8(CONFIG_NAND_DATA_REG, val)
+
+#define KPN_RDY2 (1 << 7)
+#define KPN_RDY1 (1 << 6)
+#define KPN_WPN (1 << 4)
+#define KPN_CE2N (1 << 3)
+#define KPN_CE1N (1 << 2)
+#define KPN_ALE (1 << 1)
+#define KPN_CLE (1 << 0)
+
+#define KPN_DEFAULT_CHIP_DELAY 50
+
+static int kpn_chip_ready(void)
+{
+ if (read_mode() & KPN_RDY1)
+ return 1;
+
+ return 0;
+}
+
+static void kpn_wait_rdy(void)
+{
+ int cnt = 1000000;
+
+ while (--cnt && !kpn_chip_ready())
+ udelay(1);
+
+ if (!cnt)
+ printf ("timeout while waiting for RDY\n");
+}
+
+static void kpn_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ u8 reg_val = read_mode();
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ reg_val = reg_val & ~(KPN_ALE + KPN_CLE);
+
+ if (ctrl & NAND_CLE)
+ reg_val = reg_val | KPN_CLE;
+ if (ctrl & NAND_ALE)
+ reg_val = reg_val | KPN_ALE;
+ if (ctrl & NAND_NCE)
+ reg_val = reg_val & ~KPN_CE1N;
+ else
+ reg_val = reg_val | KPN_CE1N;
+
+ write_mode(reg_val);
+ }
+ if (cmd != NAND_CMD_NONE)
+ write_data(cmd);
+
+ /* wait until flash is ready */
+ kpn_wait_rdy();
+}
+
+static u_char kpn_nand_read_byte(struct mtd_info *mtd)
+{
+ return read_data();
+}
+
+static void kpn_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ write_data(buf[i]);
+ kpn_wait_rdy();
+ }
+}
+
+static void kpn_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = read_data();
+}
+
+static int kpn_nand_dev_ready(struct mtd_info *mtd)
+{
+ kpn_wait_rdy();
+
+ return 1;
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+#if defined(CONFIG_NAND_ECC_BCH)
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
+ nand->ecc.mode = NAND_ECC_SOFT;
+#endif
+
+ /* Reference hardware control function */
+ nand->cmd_ctrl = kpn_nand_hwcontrol;
+ nand->read_byte = kpn_nand_read_byte;
+ nand->write_buf = kpn_nand_write_buf;
+ nand->read_buf = kpn_nand_read_buf;
+ nand->dev_ready = kpn_nand_dev_ready;
+ nand->chip_delay = KPN_DEFAULT_CHIP_DELAY;
+
+ /* reset mode register */
+ write_mode(KPN_CE1N + KPN_CE2N + KPN_WPN);
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c
new file mode 100644
index 0000000000..5d4ffea608
--- /dev/null
+++ b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c
@@ -0,0 +1,761 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LPC32xx MLC NAND flash controller driver
+ *
+ * (C) Copyright 2014 3ADEV <http://3adev.com>
+ * Written by Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * NOTE:
+ *
+ * The MLC NAND flash controller provides hardware Reed-Solomon ECC
+ * covering in- and out-of-band data together. Therefore, in- and out-
+ * of-band data must be written together in order to have a valid ECC.
+ *
+ * Consequently, pages with meaningful in-band data are written with
+ * blank (all-ones) out-of-band data and a valid ECC, and any later
+ * out-of-band data write will void the ECC.
+ *
+ * Therefore, code which reads such late-written out-of-band data
+ * should not rely on the ECC validity.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <nand.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/sys_proto.h>
+
+/*
+ * MLC NAND controller registers.
+ */
+struct lpc32xx_nand_mlc_registers {
+ u8 buff[32768]; /* controller's serial data buffer */
+ u8 data[32768]; /* NAND's raw data buffer */
+ u32 cmd;
+ u32 addr;
+ u32 ecc_enc_reg;
+ u32 ecc_dec_reg;
+ u32 ecc_auto_enc_reg;
+ u32 ecc_auto_dec_reg;
+ u32 rpr;
+ u32 wpr;
+ u32 rubp;
+ u32 robp;
+ u32 sw_wp_add_low;
+ u32 sw_wp_add_hig;
+ u32 icr;
+ u32 time_reg;
+ u32 irq_mr;
+ u32 irq_sr;
+ u32 lock_pr;
+ u32 isr;
+ u32 ceh;
+};
+
+/* LOCK_PR register defines */
+#define LOCK_PR_UNLOCK_KEY 0x0000A25E /* Magic unlock value */
+
+/* ICR defines */
+#define ICR_LARGE_BLOCKS 0x00000004 /* configure for 2KB blocks */
+#define ICR_ADDR4 0x00000002 /* configure for 4-word addrs */
+
+/* CEH defines */
+#define CEH_NORMAL_CE 0x00000001 /* do not force CE ON */
+
+/* ISR register defines */
+#define ISR_NAND_READY 0x00000001
+#define ISR_CONTROLLER_READY 0x00000002
+#define ISR_ECC_READY 0x00000004
+#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1)
+#define ISR_DECODER_FAILURE 0x00000040
+#define ISR_DECODER_ERROR 0x00000008
+
+/* time-out for NAND chip / controller loops, in us */
+#define LPC32X_NAND_TIMEOUT 5000
+
+/*
+ * There is a single instance of the NAND MLC controller
+ */
+
+static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers
+ = (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE;
+
+#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o)
+
+/**
+ * OOB data in each small page are 6 'free' then 10 ECC bytes.
+ * To make things easier, when reading large pages, the four pages'
+ * 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer,
+ * while the the four ECC bytes are groupe in its last 40 bytes.
+ *
+ * The struct below represents how free vs ecc oob bytes are stored
+ * in the buffer.
+ *
+ * Note: the OOB bytes contain the bad block marker at offsets 0 and 1.
+ */
+
+struct lpc32xx_oob {
+ struct {
+ uint8_t free_oob_bytes[6];
+ } free[4];
+ struct {
+ uint8_t ecc_oob_bytes[10];
+ } ecc[4];
+};
+
+/*
+ * Initialize the controller
+ */
+
+static void lpc32xx_nand_init(void)
+{
+ unsigned int clk;
+
+ /* Configure controller for no software write protection, x8 bus
+ width, large block device, and 4 address words */
+
+ /* unlock controller registers with magic key */
+ writel(LOCK_PR_UNLOCK_KEY,
+ &lpc32xx_nand_mlc_registers->lock_pr);
+
+ /* enable large blocks and large NANDs */
+ writel(ICR_LARGE_BLOCKS | ICR_ADDR4,
+ &lpc32xx_nand_mlc_registers->icr);
+
+ /* Make sure MLC interrupts are disabled */
+ writel(0, &lpc32xx_nand_mlc_registers->irq_mr);
+
+ /* Normal chip enable operation */
+ writel(CEH_NORMAL_CE,
+ &lpc32xx_nand_mlc_registers->ceh);
+
+ /* Setup NAND timing */
+ clk = get_hclk_clk_rate();
+
+ writel(
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA, 0x07, 16) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH, 0x0F, 12) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW, 0x0F, 8) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH, 0x0F, 4) |
+ clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW, 0x0F, 0),
+ &lpc32xx_nand_mlc_registers->time_reg);
+}
+
+#if !defined(CONFIG_SPL_BUILD)
+
+/**
+ * lpc32xx_cmd_ctrl - write command to either cmd or data register
+ */
+
+static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd);
+ else if (ctrl & NAND_ALE)
+ writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr);
+}
+
+/**
+ * lpc32xx_read_byte - read a byte from the NAND
+ * @mtd: MTD device structure
+ */
+
+static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
+{
+ return readb(&lpc32xx_nand_mlc_registers->data);
+}
+
+/**
+ * lpc32xx_dev_ready - test if NAND device (actually controller) is ready
+ * @mtd: MTD device structure
+ * @mode: mode to set the ECC HW to.
+ */
+
+static int lpc32xx_dev_ready(struct mtd_info *mtd)
+{
+ /* means *controller* ready for us */
+ int status = readl(&lpc32xx_nand_mlc_registers->isr);
+ return status & ISR_CONTROLLER_READY;
+}
+
+/**
+ * ECC layout -- this is needed whatever ECC mode we are using.
+ * In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes.
+ * To make U-Boot's life easier, we pack 'useable' OOB at the
+ * front and R/S ECC at the back.
+ */
+
+static struct nand_ecclayout lpc32xx_largepage_ecclayout = {
+ .eccbytes = 40,
+ .eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ },
+ .oobfree = {
+ /* bytes 0 and 1 are used for the bad block marker */
+ {
+ .offset = 2,
+ .length = 22
+ },
+ }
+};
+
+/**
+ * lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Use large block Auto Decode Read Mode(1) as described in User Manual
+ * section 8.6.2.1.
+ *
+ * The initial Read Mode and Read Start commands are sent by the caller.
+ *
+ * ECC will be false if out-of-band data has been updated since in-band
+ * data was initially written.
+ */
+
+static int lpc32xx_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required,
+ int page)
+{
+ unsigned int i, status, timeout, err, max_bitflips = 0;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ /* go through all four small pages */
+ for (i = 0; i < 4; i++) {
+ /* start auto decode (reads 528 NAND bytes) */
+ writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+ /* wait for controller to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_CONTROLLER_READY)
+ break;
+ udelay(1);
+ }
+ /* if decoder failed, return failure */
+ if (status & ISR_DECODER_FAILURE)
+ return -1;
+ /* keep count of maximum bitflips performed */
+ if (status & ISR_DECODER_ERROR) {
+ err = ISR_DECODER_ERRORS(status);
+ if (err > max_bitflips)
+ max_bitflips = err;
+ }
+ /* copy first 512 bytes into buffer */
+ memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512);
+ /* copy next 6 bytes at front of OOB buffer */
+ memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+ /* copy last 10 bytes (R/S ECC) at back of OOB buffer */
+ memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
+ }
+ return max_bitflips;
+}
+
+/**
+ * lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Read NAND directly; can read pages with invalid ECC.
+ */
+
+static int lpc32xx_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required,
+ int page)
+{
+ unsigned int i, status, timeout;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ /* when we get here we've already had the Read Mode(1) */
+
+ /* go through all four small pages */
+ for (i = 0; i < 4; i++) {
+ /* wait for NAND to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_NAND_READY)
+ break;
+ udelay(1);
+ }
+ /* if NAND stalled, return failure */
+ if (!(status & ISR_NAND_READY))
+ return -1;
+ /* copy first 512 bytes into buffer */
+ memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512);
+ /* copy next 6 bytes at front of OOB buffer */
+ memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6);
+ /* copy last 10 bytes (R/S ECC) at back of OOB buffer */
+ memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10);
+ }
+ return 0;
+}
+
+/**
+ * lpc32xx_read_oob - read out-of-band data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ *
+ * Read out-of-band data. User Manual section 8.6.4 suggests using Read
+ * Mode(3) which the controller will turn into a Read Mode(1) internally
+ * but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0)
+ * directly.
+ *
+ * ECC covers in- and out-of-band data and was written when out-of-band
+ * data was blank. Therefore, if the out-of-band being read here is not
+ * blank, then the ECC will be false and the read will return bitflips,
+ * even in case of ECC failure where we will return 5 bitflips. The
+ * caller should be prepared to handle this.
+ */
+
+static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ unsigned int i, status, timeout, err, max_bitflips = 0;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ /* No command was sent before calling read_oob() so send one */
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ /* go through all four small pages */
+ for (i = 0; i < 4; i++) {
+ /* start auto decode (reads 528 NAND bytes) */
+ writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+ /* wait for controller to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_CONTROLLER_READY)
+ break;
+ udelay(1);
+ }
+ /* if decoder failure, count 'one too many' bitflips */
+ if (status & ISR_DECODER_FAILURE)
+ max_bitflips = 5;
+ /* keep count of maximum bitflips performed */
+ if (status & ISR_DECODER_ERROR) {
+ err = ISR_DECODER_ERRORS(status);
+ if (err > max_bitflips)
+ max_bitflips = err;
+ }
+ /* set read pointer to OOB area */
+ writel(0, &lpc32xx_nand_mlc_registers->robp);
+ /* copy next 6 bytes at front of OOB buffer */
+ memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+ /* copy next 10 bytes (R/S ECC) at back of OOB buffer */
+ memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
+ }
+ return max_bitflips;
+}
+
+/**
+ * lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ *
+ * Use large block Auto Encode as per User Manual section 8.6.4.
+ *
+ * The initial Write Serial Input and final Auto Program commands are
+ * sent by the caller.
+ */
+
+static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
+ int page)
+{
+ unsigned int i, status, timeout;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ /* when we get here we've already had the SEQIN */
+ for (i = 0; i < 4; i++) {
+ /* start encode (expects 518 writes to buff) */
+ writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg);
+ /* copy first 512 bytes from buffer */
+ memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
+ /* copy next 6 bytes from OOB buffer -- excluding ECC */
+ memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
+ /* wait for ECC to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_ECC_READY)
+ break;
+ udelay(1);
+ }
+ /* if ECC stalled, return failure */
+ if (!(status & ISR_ECC_READY))
+ return -1;
+ /* Trigger auto encode (writes 528 bytes to NAND) */
+ writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg);
+ /* wait for controller to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_CONTROLLER_READY)
+ break;
+ udelay(1);
+ }
+ /* if controller stalled, return error */
+ if (!(status & ISR_CONTROLLER_READY))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Use large block write but without encode.
+ *
+ * The initial Write Serial Input and final Auto Program commands are
+ * sent by the caller.
+ *
+ * This function will write the full out-of-band data, including the
+ * ECC area. Therefore, it can write pages with valid *or* invalid ECC.
+ */
+
+static int lpc32xx_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
+ int page)
+{
+ unsigned int i;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ /* when we get here we've already had the Read Mode(1) */
+ for (i = 0; i < 4; i++) {
+ /* copy first 512 bytes from buffer */
+ memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
+ /* copy next 6 bytes into OOB buffer -- excluding ECC */
+ memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
+ /* copy next 10 bytes into OOB buffer -- that is 'ECC' */
+ memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10);
+ }
+ return 0;
+}
+
+/**
+ * lpc32xx_write_oob - write out-of-band data
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ *
+ * Since ECC covers in- and out-of-band data, writing out-of-band data
+ * with ECC will render the page ECC wrong -- or, if the page was blank,
+ * then it will produce a good ECC but a later in-band data write will
+ * render it wrong.
+ *
+ * Therefore, do not compute or write any ECC, and always return success.
+ *
+ * This implies that we do four writes, since non-ECC out-of-band data
+ * are not contiguous in a large page.
+ */
+
+static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ /* update oob on all 4 subpages in sequence */
+ unsigned int i, status, timeout;
+ struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
+
+ for (i = 0; i < 4; i++) {
+ /* start data input */
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page);
+ /* copy 6 non-ECC out-of-band bytes directly into NAND */
+ memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6);
+ /* program page */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ /* wait for NAND to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_NAND_READY)
+ break;
+ udelay(1);
+ }
+ /* if NAND stalled, return error */
+ if (!(status & ISR_NAND_READY))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * lpc32xx_waitfunc - wait until a command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ *
+ * Wait for controller and FLASH to both be ready.
+ */
+
+static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ int status;
+ unsigned int timeout;
+ /* wait until both controller and NAND are ready */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
+ == (ISR_CONTROLLER_READY || ISR_NAND_READY))
+ break;
+ udelay(1);
+ }
+ /* if controller or NAND stalled, return error */
+ if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
+ != (ISR_CONTROLLER_READY || ISR_NAND_READY))
+ return -1;
+ /* write NAND status command */
+ writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd);
+ /* read back status and return it */
+ return readb(&lpc32xx_nand_mlc_registers->data);
+}
+
+/*
+ * We are self-initializing, so we need our own chip struct
+ */
+
+static struct nand_chip lpc32xx_chip;
+
+/*
+ * Initialize the controller
+ */
+
+void board_nand_init(void)
+{
+ struct mtd_info *mtd = nand_to_mtd(&lpc32xx_chip);
+ int ret;
+
+ /* Set all BOARDSPECIFIC (actually core-specific) fields */
+
+ lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff;
+ lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff;
+ lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl;
+ /* do not set init_size: nand_base.c will read sizes from chip */
+ lpc32xx_chip.dev_ready = lpc32xx_dev_ready;
+ /* do not set setup_read_retry: this is NAND-chip-specific */
+ /* do not set chip_delay: we have dev_ready defined. */
+ lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE;
+
+ /* Set needed ECC fields */
+
+ lpc32xx_chip.ecc.mode = NAND_ECC_HW;
+ lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout;
+ lpc32xx_chip.ecc.size = 512;
+ lpc32xx_chip.ecc.bytes = 10;
+ lpc32xx_chip.ecc.strength = 4;
+ lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc;
+ lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw;
+ lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc;
+ lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw;
+ lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob;
+ lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob;
+ lpc32xx_chip.waitfunc = lpc32xx_waitfunc;
+
+ lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */
+
+ /* BBT options: read from last two pages */
+ lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK
+ | NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE
+ | NAND_BBT_WRITE;
+
+ /* Initialize NAND interface */
+ lpc32xx_nand_init();
+
+ /* identify chip */
+ ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL);
+ if (ret) {
+ pr_err("nand_scan_ident returned %i", ret);
+ return;
+ }
+
+ /* finish scanning the chip */
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ pr_err("nand_scan_tail returned %i", ret);
+ return;
+ }
+
+ /* chip is good, register it */
+ ret = nand_register(0, mtd);
+ if (ret)
+ pr_err("nand_register returned %i", ret);
+}
+
+#else /* defined(CONFIG_SPL_BUILD) */
+
+void nand_init(void)
+{
+ /* enable NAND controller */
+ lpc32xx_mlc_nand_init();
+ /* initialize NAND controller */
+ lpc32xx_nand_init();
+}
+
+void nand_deselect(void)
+{
+ /* nothing to do, but SPL requires this function */
+}
+
+static int read_single_page(uint8_t *dest, int page,
+ struct lpc32xx_oob *oob)
+{
+ int status, i, timeout, err, max_bitflips = 0;
+
+ /* enter read mode */
+ writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd);
+ /* send column (lsb then MSB) and page (lsb to MSB) */
+ writel(0, &lpc32xx_nand_mlc_registers->addr);
+ writel(0, &lpc32xx_nand_mlc_registers->addr);
+ writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr);
+ writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr);
+ writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr);
+ /* start reading */
+ writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd);
+
+ /* large page auto decode read */
+ for (i = 0; i < 4; i++) {
+ /* start auto decode (reads 528 NAND bytes) */
+ writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
+ /* wait for controller to return to ready state */
+ for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
+ status = readl(&lpc32xx_nand_mlc_registers->isr);
+ if (status & ISR_CONTROLLER_READY)
+ break;
+ udelay(1);
+ }
+ /* if controller stalled, return error */
+ if (!(status & ISR_CONTROLLER_READY))
+ return -1;
+ /* if decoder failure, return error */
+ if (status & ISR_DECODER_FAILURE)
+ return -1;
+ /* keep count of maximum bitflips performed */
+ if (status & ISR_DECODER_ERROR) {
+ err = ISR_DECODER_ERRORS(status);
+ if (err > max_bitflips)
+ max_bitflips = err;
+ }
+ /* copy first 512 bytes into buffer */
+ memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512);
+ /* copy next 6 bytes bytes into OOB buffer */
+ memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
+ }
+ return max_bitflips;
+}
+
+/*
+ * Load U-Boot signed image.
+ * This loads an image from NAND, skipping bad blocks.
+ * A block is declared bad if at least one of its readable pages has
+ * a bad block marker in its OOB at position 0.
+ * If all pages ion a block are unreadable, the block is considered
+ * bad (i.e., assumed not to be part of the image) and skipped.
+ *
+ * IMPORTANT NOTE:
+ *
+ * If the first block of the image is fully unreadable, it will be
+ * ignored and skipped as if it had been marked bad. If it was not
+ * actually marked bad at the time of writing the image, the resulting
+ * image loaded will lack a header and magic number. It could thus be
+ * considered as a raw, headerless, image and SPL might erroneously
+ * jump into it.
+ *
+ * In order to avoid this risk, LPC32XX-based boards which use this
+ * driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE.
+ */
+
+#define BYTES_PER_PAGE 2048
+#define PAGES_PER_BLOCK 64
+#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK)
+#define PAGES_PER_CHIP_MAX 524288
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+ int bytes_left = size;
+ int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE);
+ int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK);
+ int block = 0;
+ int page = offs / BYTES_PER_PAGE;
+ /* perform reads block by block */
+ while (blocks_left) {
+ /* compute first page number to read */
+ void *block_page_dst = dst;
+ /* read at most one block, possibly less */
+ int block_bytes_left = bytes_left;
+ if (block_bytes_left > BYTES_PER_BLOCK)
+ block_bytes_left = BYTES_PER_BLOCK;
+ /* keep track of good, failed, and "bad" pages */
+ int block_pages_good = 0;
+ int block_pages_bad = 0;
+ int block_pages_err = 0;
+ /* we shall read a full block of pages, maybe less */
+ int block_pages_left = pages_left;
+ if (block_pages_left > PAGES_PER_BLOCK)
+ block_pages_left = PAGES_PER_BLOCK;
+ int block_pages = block_pages_left;
+ int block_page = page;
+ /* while pages are left and the block is not known as bad */
+ while ((block_pages > 0) && (block_pages_bad == 0)) {
+ /* we will read OOB, too, for bad block markers */
+ struct lpc32xx_oob oob;
+ /* read page */
+ int res = read_single_page(block_page_dst, block_page,
+ &oob);
+ /* count readable pages */
+ if (res >= 0) {
+ /* this page is good */
+ block_pages_good++;
+ /* this page is bad */
+ if ((oob.free[0].free_oob_bytes[0] != 0xff)
+ | (oob.free[0].free_oob_bytes[1] != 0xff))
+ block_pages_bad++;
+ } else
+ /* count errors */
+ block_pages_err++;
+ /* we're done with this page */
+ block_page++;
+ block_page_dst += BYTES_PER_PAGE;
+ if (block_pages)
+ block_pages--;
+ }
+ /* a fully unreadable block is considered bad */
+ if (block_pages_good == 0)
+ block_pages_bad = block_pages_err;
+ /* errors are fatal only in good blocks */
+ if ((block_pages_err > 0) && (block_pages_bad == 0))
+ return -1;
+ /* we keep reads only of good blocks */
+ if (block_pages_bad == 0) {
+ dst += block_bytes_left;
+ bytes_left -= block_bytes_left;
+ pages_left -= block_pages_left;
+ blocks_left--;
+ }
+ /* good or bad, we're done with this block */
+ block++;
+ page += PAGES_PER_BLOCK;
+ }
+
+ /* report success */
+ return 0;
+}
+
+#endif /* CONFIG_SPL_BUILD */
diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_slc.c b/drivers/mtd/nand/raw/lpc32xx_nand_slc.c
new file mode 100644
index 0000000000..99f6e15f4e
--- /dev/null
+++ b/drivers/mtd/nand/raw/lpc32xx_nand_slc.c
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LPC32xx SLC NAND flash controller driver
+ *
+ * (C) Copyright 2015 Vladimir Zapolskiy <vz@mleia.com>
+ *
+ * Hardware ECC support original source code
+ * Copyright (C) 2008 by NXP Semiconductors
+ * Author: Kevin Wells
+ *
+ * Copyright (c) 2015 Tyco Fire Protection Products.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/arch/config.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/cpu.h>
+
+#if defined(CONFIG_DMA_LPC32XX) && defined(CONFIG_SPL_BUILD)
+#warning "DMA support in SPL image is not tested"
+#endif
+
+struct lpc32xx_nand_slc_regs {
+ u32 data;
+ u32 addr;
+ u32 cmd;
+ u32 stop;
+ u32 ctrl;
+ u32 cfg;
+ u32 stat;
+ u32 int_stat;
+ u32 ien;
+ u32 isr;
+ u32 icr;
+ u32 tac;
+ u32 tc;
+ u32 ecc;
+ u32 dma_data;
+};
+
+/* CFG register */
+#define CFG_CE_LOW (1 << 5)
+#define CFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */
+#define CFG_ECC_EN (1 << 3) /* ECC enable bit */
+#define CFG_DMA_BURST (1 << 2) /* DMA burst bit */
+#define CFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */
+
+/* CTRL register */
+#define CTRL_SW_RESET (1 << 2)
+#define CTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */
+#define CTRL_DMA_START (1 << 0) /* Start DMA channel bit */
+
+/* STAT register */
+#define STAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */
+#define STAT_NAND_READY (1 << 0)
+
+/* INT_STAT register */
+#define INT_STAT_TC (1 << 1)
+#define INT_STAT_RDY (1 << 0)
+
+/* TAC register bits, be aware of overflows */
+#define TAC_W_RDY(n) (max_t(uint32_t, (n), 0xF) << 28)
+#define TAC_W_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 24)
+#define TAC_W_HOLD(n) (max_t(uint32_t, (n), 0xF) << 20)
+#define TAC_W_SETUP(n) (max_t(uint32_t, (n), 0xF) << 16)
+#define TAC_R_RDY(n) (max_t(uint32_t, (n), 0xF) << 12)
+#define TAC_R_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 8)
+#define TAC_R_HOLD(n) (max_t(uint32_t, (n), 0xF) << 4)
+#define TAC_R_SETUP(n) (max_t(uint32_t, (n), 0xF) << 0)
+
+/* NAND ECC Layout for small page NAND devices
+ * Note: For large page devices, the default layouts are used. */
+static struct nand_ecclayout lpc32xx_nand_oob_16 = {
+ .eccbytes = 6,
+ .eccpos = {10, 11, 12, 13, 14, 15},
+ .oobfree = {
+ {.offset = 0,
+ . length = 4},
+ {.offset = 6,
+ . length = 4}
+ }
+};
+
+#if defined(CONFIG_DMA_LPC32XX)
+#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE)
+
+/*
+ * DMA Descriptors
+ * For Large Block: 17 descriptors = ((16 Data and ECC Read) + 1 Spare Area)
+ * For Small Block: 5 descriptors = ((4 Data and ECC Read) + 1 Spare Area)
+ */
+static struct lpc32xx_dmac_ll dmalist[ECCSTEPS * 2 + 1];
+static u32 ecc_buffer[8]; /* MAX ECC size */
+static unsigned int dmachan = (unsigned int)-1; /* Invalid channel */
+
+/*
+ * Helper macro for the DMA client (i.e. NAND SLC):
+ * - to write the next DMA linked list item address
+ * (see arch/include/asm/arch-lpc32xx/dma.h).
+ * - to assign the DMA data register to DMA source or destination address.
+ * - to assign the ECC register to DMA source or destination address.
+ */
+#define lpc32xx_dmac_next_lli(x) ((u32)x)
+#define lpc32xx_dmac_set_dma_data() ((u32)&lpc32xx_nand_slc_regs->dma_data)
+#define lpc32xx_dmac_set_ecc() ((u32)&lpc32xx_nand_slc_regs->ecc)
+#endif
+
+static struct lpc32xx_nand_slc_regs __iomem *lpc32xx_nand_slc_regs
+ = (struct lpc32xx_nand_slc_regs __iomem *)SLC_NAND_BASE;
+
+static void lpc32xx_nand_init(void)
+{
+ uint32_t hclk = get_hclk_clk_rate();
+
+ /* Reset SLC NAND controller */
+ writel(CTRL_SW_RESET, &lpc32xx_nand_slc_regs->ctrl);
+
+ /* 8-bit bus, no DMA, no ECC, ordinary CE signal */
+ writel(0, &lpc32xx_nand_slc_regs->cfg);
+
+ /* Interrupts disabled and cleared */
+ writel(0, &lpc32xx_nand_slc_regs->ien);
+ writel(INT_STAT_TC | INT_STAT_RDY,
+ &lpc32xx_nand_slc_regs->icr);
+
+ /* Configure NAND flash timings */
+ writel(TAC_W_RDY(CONFIG_LPC32XX_NAND_SLC_WDR_CLKS) |
+ TAC_W_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_WWIDTH) |
+ TAC_W_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_WHOLD) |
+ TAC_W_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_WSETUP) |
+ TAC_R_RDY(CONFIG_LPC32XX_NAND_SLC_RDR_CLKS) |
+ TAC_R_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_RWIDTH) |
+ TAC_R_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_RHOLD) |
+ TAC_R_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_RSETUP),
+ &lpc32xx_nand_slc_regs->tac);
+}
+
+static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd,
+ int cmd, unsigned int ctrl)
+{
+ debug("ctrl: 0x%08x, cmd: 0x%08x\n", ctrl, cmd);
+
+ if (ctrl & NAND_NCE)
+ setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW);
+ else
+ clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->cmd);
+ else if (ctrl & NAND_ALE)
+ writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->addr);
+}
+
+static int lpc32xx_nand_dev_ready(struct mtd_info *mtd)
+{
+ return readl(&lpc32xx_nand_slc_regs->stat) & STAT_NAND_READY;
+}
+
+#if defined(CONFIG_DMA_LPC32XX)
+/*
+ * Prepares DMA descriptors for NAND RD/WR operations
+ * If the size is < 256 Bytes then it is assumed to be
+ * an OOB transfer
+ */
+static void lpc32xx_nand_dma_configure(struct nand_chip *chip,
+ const u8 *buffer, int size,
+ int read)
+{
+ u32 i, dmasrc, ctrl, ecc_ctrl, oob_ctrl, dmadst;
+ struct lpc32xx_dmac_ll *dmalist_cur;
+ struct lpc32xx_dmac_ll *dmalist_cur_ecc;
+
+ /*
+ * CTRL descriptor entry for reading ECC
+ * Copy Multiple times to sync DMA with Flash Controller
+ */
+ ecc_ctrl = 0x5 |
+ DMAC_CHAN_SRC_BURST_1 |
+ DMAC_CHAN_DEST_BURST_1 |
+ DMAC_CHAN_SRC_WIDTH_32 |
+ DMAC_CHAN_DEST_WIDTH_32 |
+ DMAC_CHAN_DEST_AHB1;
+
+ /* CTRL descriptor entry for reading/writing Data */
+ ctrl = (CONFIG_SYS_NAND_ECCSIZE / 4) |
+ DMAC_CHAN_SRC_BURST_4 |
+ DMAC_CHAN_DEST_BURST_4 |
+ DMAC_CHAN_SRC_WIDTH_32 |
+ DMAC_CHAN_DEST_WIDTH_32 |
+ DMAC_CHAN_DEST_AHB1;
+
+ /* CTRL descriptor entry for reading/writing Spare Area */
+ oob_ctrl = (CONFIG_SYS_NAND_OOBSIZE / 4) |
+ DMAC_CHAN_SRC_BURST_4 |
+ DMAC_CHAN_DEST_BURST_4 |
+ DMAC_CHAN_SRC_WIDTH_32 |
+ DMAC_CHAN_DEST_WIDTH_32 |
+ DMAC_CHAN_DEST_AHB1;
+
+ if (read) {
+ dmasrc = lpc32xx_dmac_set_dma_data();
+ dmadst = (u32)buffer;
+ ctrl |= DMAC_CHAN_DEST_AUTOINC;
+ } else {
+ dmadst = lpc32xx_dmac_set_dma_data();
+ dmasrc = (u32)buffer;
+ ctrl |= DMAC_CHAN_SRC_AUTOINC;
+ }
+
+ /*
+ * Write Operation Sequence for Small Block NAND
+ * ----------------------------------------------------------
+ * 1. X'fer 256 bytes of data from Memory to Flash.
+ * 2. Copy generated ECC data from Register to Spare Area
+ * 3. X'fer next 256 bytes of data from Memory to Flash.
+ * 4. Copy generated ECC data from Register to Spare Area.
+ * 5. X'fer 16 byets of Spare area from Memory to Flash.
+ * Read Operation Sequence for Small Block NAND
+ * ----------------------------------------------------------
+ * 1. X'fer 256 bytes of data from Flash to Memory.
+ * 2. Copy generated ECC data from Register to ECC calc Buffer.
+ * 3. X'fer next 256 bytes of data from Flash to Memory.
+ * 4. Copy generated ECC data from Register to ECC calc Buffer.
+ * 5. X'fer 16 bytes of Spare area from Flash to Memory.
+ * Write Operation Sequence for Large Block NAND
+ * ----------------------------------------------------------
+ * 1. Steps(1-4) of Write Operations repeate for four times
+ * which generates 16 DMA descriptors to X'fer 2048 bytes of
+ * data & 32 bytes of ECC data.
+ * 2. X'fer 64 bytes of Spare area from Memory to Flash.
+ * Read Operation Sequence for Large Block NAND
+ * ----------------------------------------------------------
+ * 1. Steps(1-4) of Read Operations repeate for four times
+ * which generates 16 DMA descriptors to X'fer 2048 bytes of
+ * data & 32 bytes of ECC data.
+ * 2. X'fer 64 bytes of Spare area from Flash to Memory.
+ */
+
+ for (i = 0; i < size/CONFIG_SYS_NAND_ECCSIZE; i++) {
+ dmalist_cur = &dmalist[i * 2];
+ dmalist_cur_ecc = &dmalist[(i * 2) + 1];
+
+ dmalist_cur->dma_src = (read ? (dmasrc) : (dmasrc + (i*256)));
+ dmalist_cur->dma_dest = (read ? (dmadst + (i*256)) : dmadst);
+ dmalist_cur->next_lli = lpc32xx_dmac_next_lli(dmalist_cur_ecc);
+ dmalist_cur->next_ctrl = ctrl;
+
+ dmalist_cur_ecc->dma_src = lpc32xx_dmac_set_ecc();
+ dmalist_cur_ecc->dma_dest = (u32)&ecc_buffer[i];
+ dmalist_cur_ecc->next_lli =
+ lpc32xx_dmac_next_lli(&dmalist[(i * 2) + 2]);
+ dmalist_cur_ecc->next_ctrl = ecc_ctrl;
+ }
+
+ if (i) { /* Data only transfer */
+ dmalist_cur_ecc = &dmalist[(i * 2) - 1];
+ dmalist_cur_ecc->next_lli = 0;
+ dmalist_cur_ecc->next_ctrl |= DMAC_CHAN_INT_TC_EN;
+ return;
+ }
+
+ /* OOB only transfer */
+ if (read) {
+ dmasrc = lpc32xx_dmac_set_dma_data();
+ dmadst = (u32)buffer;
+ oob_ctrl |= DMAC_CHAN_DEST_AUTOINC;
+ } else {
+ dmadst = lpc32xx_dmac_set_dma_data();
+ dmasrc = (u32)buffer;
+ oob_ctrl |= DMAC_CHAN_SRC_AUTOINC;
+ }
+
+ /* Read/ Write Spare Area Data To/From Flash */
+ dmalist_cur = &dmalist[i * 2];
+ dmalist_cur->dma_src = dmasrc;
+ dmalist_cur->dma_dest = dmadst;
+ dmalist_cur->next_lli = 0;
+ dmalist_cur->next_ctrl = (oob_ctrl | DMAC_CHAN_INT_TC_EN);
+}
+
+static void lpc32xx_nand_xfer(struct mtd_info *mtd, const u8 *buf,
+ int len, int read)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 config;
+ int ret;
+
+ /* DMA Channel Configuration */
+ config = (read ? DMAC_CHAN_FLOW_D_P2M : DMAC_CHAN_FLOW_D_M2P) |
+ (read ? DMAC_DEST_PERIP(0) : DMAC_DEST_PERIP(DMA_PERID_NAND1)) |
+ (read ? DMAC_SRC_PERIP(DMA_PERID_NAND1) : DMAC_SRC_PERIP(0)) |
+ DMAC_CHAN_ENABLE;
+
+ /* Prepare DMA descriptors */
+ lpc32xx_nand_dma_configure(chip, buf, len, read);
+
+ /* Setup SLC controller and start transfer */
+ if (read)
+ setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR);
+ else /* NAND_ECC_WRITE */
+ clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR);
+ setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_BURST);
+
+ /* Write length for new transfers */
+ if (!((readl(&lpc32xx_nand_slc_regs->stat) & STAT_DMA_FIFO) |
+ readl(&lpc32xx_nand_slc_regs->tc))) {
+ int tmp = (len != mtd->oobsize) ? mtd->oobsize : 0;
+ writel(len + tmp, &lpc32xx_nand_slc_regs->tc);
+ }
+
+ setbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START);
+
+ /* Start DMA transfers */
+ ret = lpc32xx_dma_start_xfer(dmachan, dmalist, config);
+ if (unlikely(ret < 0))
+ BUG();
+
+
+ /* Wait for NAND to be ready */
+ while (!lpc32xx_nand_dev_ready(mtd))
+ ;
+
+ /* Wait till DMA transfer is DONE */
+ if (lpc32xx_dma_wait_status(dmachan))
+ pr_err("NAND DMA transfer error!\r\n");
+
+ /* Stop DMA & HW ECC */
+ clrbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START);
+ clrbits_le32(&lpc32xx_nand_slc_regs->cfg,
+ CFG_DMA_DIR | CFG_DMA_BURST | CFG_ECC_EN | CFG_DMA_ECC);
+}
+
+static u32 slc_ecc_copy_to_buffer(u8 *spare, const u32 *ecc, int count)
+{
+ int i;
+ for (i = 0; i < (count * CONFIG_SYS_NAND_ECCBYTES);
+ i += CONFIG_SYS_NAND_ECCBYTES) {
+ u32 ce = ecc[i / CONFIG_SYS_NAND_ECCBYTES];
+ ce = ~(ce << 2) & 0xFFFFFF;
+ spare[i+2] = (u8)(ce & 0xFF); ce >>= 8;
+ spare[i+1] = (u8)(ce & 0xFF); ce >>= 8;
+ spare[i] = (u8)(ce & 0xFF);
+ }
+ return 0;
+}
+
+static int lpc32xx_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ return slc_ecc_copy_to_buffer(ecc_code, ecc_buffer, ECCSTEPS);
+}
+
+/*
+ * Enables and prepares SLC NAND controller
+ * for doing data transfers with H/W ECC enabled.
+ */
+static void lpc32xx_hwecc_enable(struct mtd_info *mtd, int mode)
+{
+ /* Clear ECC */
+ writel(CTRL_ECC_CLEAR, &lpc32xx_nand_slc_regs->ctrl);
+
+ /* Setup SLC controller for H/W ECC operations */
+ setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_ECC_EN | CFG_DMA_ECC);
+}
+
+/*
+ * lpc32xx_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * mtd: MTD block structure
+ * dat: raw data read from the chip
+ * read_ecc: ECC from the chip
+ * calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+int lpc32xx_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ unsigned int i;
+ int ret1, ret2 = 0;
+ u_char *r = read_ecc;
+ u_char *c = calc_ecc;
+ u16 data_offset = 0;
+
+ for (i = 0 ; i < ECCSTEPS ; i++) {
+ r += CONFIG_SYS_NAND_ECCBYTES;
+ c += CONFIG_SYS_NAND_ECCBYTES;
+ data_offset += CONFIG_SYS_NAND_ECCSIZE;
+
+ ret1 = nand_correct_data(mtd, dat + data_offset, r, c);
+ if (ret1 < 0)
+ return -EBADMSG;
+ else
+ ret2 += ret1;
+ }
+
+ return ret2;
+}
+#endif
+
+#if defined(CONFIG_DMA_LPC32XX)
+static void lpc32xx_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ lpc32xx_nand_xfer(mtd, buf, len, 1);
+}
+#else
+static void lpc32xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ while (len-- > 0)
+ *buf++ = readl(&lpc32xx_nand_slc_regs->data);
+}
+#endif
+
+static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
+{
+ return readl(&lpc32xx_nand_slc_regs->data);
+}
+
+#if defined(CONFIG_DMA_LPC32XX)
+static void lpc32xx_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ lpc32xx_nand_xfer(mtd, buf, len, 0);
+}
+#else
+static void lpc32xx_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ while (len-- > 0)
+ writel(*buf++, &lpc32xx_nand_slc_regs->data);
+}
+#endif
+
+static void lpc32xx_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ writel(byte, &lpc32xx_nand_slc_regs->data);
+}
+
+#if defined(CONFIG_DMA_LPC32XX)
+/* Reuse the logic from "nand_read_page_hwecc()" */
+static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i;
+ int stat;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned int max_bitflips = 0;
+
+ /*
+ * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes
+ * and section 9.7, the NAND SLC & DMA allowed single DMA transaction
+ * of a page size using DMA controller scatter/gather mode through
+ * linked list; the ECC read is done without any software intervention.
+ */
+
+ lpc32xx_hwecc_enable(mtd, NAND_ECC_READ);
+ lpc32xx_dma_read_buf(mtd, p, chip->ecc.size * chip->ecc.steps);
+ lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]);
+ lpc32xx_dma_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[0], &ecc_calc[0]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+
+ return max_bitflips;
+}
+
+/* Reuse the logic from "nand_write_page_hwecc()" */
+static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ /*
+ * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes
+ * and section 9.7, the NAND SLC & DMA allowed single DMA transaction
+ * of a page size using DMA controller scatter/gather mode through
+ * linked list; the ECC read is done without any software intervention.
+ */
+
+ lpc32xx_hwecc_enable(mtd, NAND_ECC_WRITE);
+ lpc32xx_dma_write_buf(mtd, p, chip->ecc.size * chip->ecc.steps);
+ lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ lpc32xx_dma_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+#endif
+
+/*
+ * LPC32xx has only one SLC NAND controller, don't utilize
+ * CONFIG_SYS_NAND_SELF_INIT to be able to reuse this function
+ * both in SPL NAND and U-Boot images.
+ */
+int board_nand_init(struct nand_chip *lpc32xx_chip)
+{
+#if defined(CONFIG_DMA_LPC32XX)
+ int ret;
+
+ /* Acquire a channel for our use */
+ ret = lpc32xx_dma_get_channel();
+ if (unlikely(ret < 0)) {
+ pr_info("Unable to get free DMA channel for NAND transfers\n");
+ return -1;
+ }
+ dmachan = (unsigned int)ret;
+#endif
+
+ lpc32xx_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
+ lpc32xx_chip->dev_ready = lpc32xx_nand_dev_ready;
+
+ /*
+ * The implementation of these functions is quite common, but
+ * they MUST be defined, because access to data register
+ * is strictly 32-bit aligned.
+ */
+ lpc32xx_chip->read_byte = lpc32xx_read_byte;
+ lpc32xx_chip->write_byte = lpc32xx_write_byte;
+
+#if defined(CONFIG_DMA_LPC32XX)
+ /* Hardware ECC calculation is supported when DMA driver is selected */
+ lpc32xx_chip->ecc.mode = NAND_ECC_HW;
+
+ lpc32xx_chip->read_buf = lpc32xx_dma_read_buf;
+ lpc32xx_chip->write_buf = lpc32xx_dma_write_buf;
+
+ lpc32xx_chip->ecc.calculate = lpc32xx_ecc_calculate;
+ lpc32xx_chip->ecc.correct = lpc32xx_correct_data;
+ lpc32xx_chip->ecc.hwctl = lpc32xx_hwecc_enable;
+ lpc32xx_chip->chip_delay = 2000;
+
+ lpc32xx_chip->ecc.read_page = lpc32xx_read_page_hwecc;
+ lpc32xx_chip->ecc.write_page = lpc32xx_write_page_hwecc;
+ lpc32xx_chip->options |= NAND_NO_SUBPAGE_WRITE;
+#else
+ /*
+ * Hardware ECC calculation is not supported by the driver,
+ * because it requires DMA support, see LPC32x0 User Manual,
+ * note after SLC_ECC register description (UM10326, p.198)
+ */
+ lpc32xx_chip->ecc.mode = NAND_ECC_SOFT;
+
+ /*
+ * The implementation of these functions is quite common, but
+ * they MUST be defined, because access to data register
+ * is strictly 32-bit aligned.
+ */
+ lpc32xx_chip->read_buf = lpc32xx_read_buf;
+ lpc32xx_chip->write_buf = lpc32xx_write_buf;
+#endif
+
+ /*
+ * These values are predefined
+ * for both small and large page NAND flash devices.
+ */
+ lpc32xx_chip->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
+ lpc32xx_chip->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
+ lpc32xx_chip->ecc.strength = 1;
+
+ if (CONFIG_SYS_NAND_PAGE_SIZE != NAND_LARGE_BLOCK_PAGE_SIZE)
+ lpc32xx_chip->ecc.layout = &lpc32xx_nand_oob_16;
+
+#if defined(CONFIG_SYS_NAND_USE_FLASH_BBT)
+ lpc32xx_chip->bbt_options |= NAND_BBT_USE_FLASH;
+#endif
+
+ /* Initialize NAND interface */
+ lpc32xx_nand_init();
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
new file mode 100644
index 0000000000..cf97e0f74f
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -0,0 +1,1307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc.
+ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
+ * Copyright 2009 Ilya Yanok, <yanok@emcraft.com>
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \
+ defined(CONFIG_MX51) || defined(CONFIG_MX53)
+#include <asm/arch/imx-regs.h>
+#endif
+#include "mxc_nand.h"
+
+#define DRIVER_NAME "mxc_nand"
+
+struct mxc_nand_host {
+ struct nand_chip *nand;
+
+ struct mxc_nand_regs __iomem *regs;
+#ifdef MXC_NFC_V3_2
+ struct mxc_nand_ip_regs __iomem *ip_regs;
+#endif
+ int spare_only;
+ int status_request;
+ int pagesize_2k;
+ int clk_act;
+ uint16_t col_addr;
+ unsigned int page_addr;
+};
+
+static struct mxc_nand_host mxc_host;
+static struct mxc_nand_host *host = &mxc_host;
+
+/* Define delays in microsec for NAND device operations */
+#define TROP_US_DELAY 2000
+/* Macros to get byte and bit positions of ECC */
+#define COLPOS(x) ((x) >> 3)
+#define BITPOS(x) ((x) & 0xf)
+
+/* Define single bit Error positions in Main & Spare area */
+#define MAIN_SINGLEBIT_ERROR 0x4
+#define SPARE_SINGLEBIT_ERROR 0x1
+
+/* OOB placement block for use with hardware ecc generation */
+#if defined(MXC_NFC_V1)
+#ifndef CONFIG_SYS_NAND_LARGEPAGE
+static struct nand_ecclayout nand_hw_eccoob = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobfree = { {0, 5}, {11, 5}, }
+};
+#else
+static struct nand_ecclayout nand_hw_eccoob2k = {
+ .eccbytes = 20,
+ .eccpos = {
+ 6, 7, 8, 9, 10,
+ 22, 23, 24, 25, 26,
+ 38, 39, 40, 41, 42,
+ 54, 55, 56, 57, 58,
+ },
+ .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} },
+};
+#endif
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+#ifndef CONFIG_SYS_NAND_LARGEPAGE
+static struct nand_ecclayout nand_hw_eccoob = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobfree = { {2, 5} }
+};
+#else
+static struct nand_ecclayout nand_hw_eccoob2k = {
+ .eccbytes = 36,
+ .eccpos = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ },
+ .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} },
+};
+#endif
+#endif
+
+static int is_16bit_nand(void)
+{
+#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT)
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size)
+{
+ uint32_t *d = dest;
+
+ size >>= 2;
+ while (size--)
+ __raw_writel(__raw_readl(source++), d++);
+ return dest;
+}
+
+/*
+ * This function polls the NANDFC to wait for the basic operation to
+ * complete by checking the INT bit.
+ */
+static void wait_op_done(struct mxc_nand_host *host, int max_retries,
+ uint16_t param)
+{
+ uint32_t tmp;
+
+ while (max_retries-- > 0) {
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ tmp = readnfc(&host->regs->config2);
+ if (tmp & NFC_V1_V2_CONFIG2_INT) {
+ tmp &= ~NFC_V1_V2_CONFIG2_INT;
+ writenfc(tmp, &host->regs->config2);
+#elif defined(MXC_NFC_V3_2)
+ tmp = readnfc(&host->ip_regs->ipc);
+ if (tmp & NFC_V3_IPC_INT) {
+ tmp &= ~NFC_V3_IPC_INT;
+ writenfc(tmp, &host->ip_regs->ipc);
+#endif
+ break;
+ }
+ udelay(1);
+ }
+ if (max_retries < 0) {
+ pr_debug("%s(%d): INT not set\n",
+ __func__, param);
+ }
+}
+
+/*
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ */
+static void send_cmd(struct mxc_nand_host *host, uint16_t cmd)
+{
+ pr_debug("send_cmd(host, 0x%x)\n", cmd);
+
+ writenfc(cmd, &host->regs->flash_cmd);
+ writenfc(NFC_CMD, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, cmd);
+}
+
+/*
+ * This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command.
+ */
+static void send_addr(struct mxc_nand_host *host, uint16_t addr)
+{
+ pr_debug("send_addr(host, 0x%x)\n", addr);
+
+ writenfc(addr, &host->regs->flash_addr);
+ writenfc(NFC_ADDR, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, addr);
+}
+
+/*
+ * This function requests the NANDFC to initiate the transfer
+ * of data currently in the NANDFC RAM buffer to the NAND device.
+ */
+static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,
+ int spare_only)
+{
+ if (spare_only)
+ pr_debug("send_prog_page (%d)\n", spare_only);
+
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
+ int i;
+ /*
+ * The controller copies the 64 bytes of spare data from
+ * the first 16 bytes of each of the 4 64 byte spare buffers.
+ * Copy the contiguous data starting in spare_area[0] to
+ * the four spare area buffers.
+ */
+ for (i = 1; i < 4; i++) {
+ void __iomem *src = &host->regs->spare_area[0][i * 16];
+ void __iomem *dst = &host->regs->spare_area[i][0];
+
+ mxc_nand_memcpy32(dst, src, 16);
+ }
+ }
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(buf_id, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ tmp |= NFC_V3_CONFIG1_RBA(buf_id);
+ writenfc(tmp, &host->regs->config1);
+#endif
+
+ /* Configure spare or page+spare access */
+ if (!host->pagesize_2k) {
+ uint32_t config1 = readnfc(&host->regs->config1);
+ if (spare_only)
+ config1 |= NFC_CONFIG1_SP_EN;
+ else
+ config1 &= ~NFC_CONFIG1_SP_EN;
+ writenfc(config1, &host->regs->config1);
+ }
+
+ writenfc(NFC_INPUT, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, spare_only);
+}
+
+/*
+ * Requests NANDFC to initiate the transfer of data from the
+ * NAND device into in the NANDFC ram buffer.
+ */
+static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id,
+ int spare_only)
+{
+ pr_debug("send_read_page (%d)\n", spare_only);
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(buf_id, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ tmp |= NFC_V3_CONFIG1_RBA(buf_id);
+ writenfc(tmp, &host->regs->config1);
+#endif
+
+ /* Configure spare or page+spare access */
+ if (!host->pagesize_2k) {
+ uint32_t config1 = readnfc(&host->regs->config1);
+ if (spare_only)
+ config1 |= NFC_CONFIG1_SP_EN;
+ else
+ config1 &= ~NFC_CONFIG1_SP_EN;
+ writenfc(config1, &host->regs->config1);
+ }
+
+ writenfc(NFC_OUTPUT, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, spare_only);
+
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
+ int i;
+
+ /*
+ * The controller copies the 64 bytes of spare data to
+ * the first 16 bytes of each of the 4 spare buffers.
+ * Make the data contiguous starting in spare_area[0].
+ */
+ for (i = 1; i < 4; i++) {
+ void __iomem *src = &host->regs->spare_area[i][0];
+ void __iomem *dst = &host->regs->spare_area[0][i * 16];
+
+ mxc_nand_memcpy32(dst, src, 16);
+ }
+ }
+}
+
+/* Request the NANDFC to perform a read of the NAND device ID. */
+static void send_read_id(struct mxc_nand_host *host)
+{
+ uint32_t tmp;
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ /* NANDFC buffer 0 is used for device ID output */
+ writenfc(0x0, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(tmp, &host->regs->config1);
+#endif
+
+ /* Read ID into main buffer */
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_CONFIG1_SP_EN;
+ writenfc(tmp, &host->regs->config1);
+
+ writenfc(NFC_ID, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, 0);
+}
+
+/*
+ * This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status.
+ */
+static uint16_t get_dev_status(struct mxc_nand_host *host)
+{
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ void __iomem *main_buf = host->regs->main_area[1];
+ uint32_t store;
+#endif
+ uint32_t ret, tmp;
+ /* Issue status request to NAND device */
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ /* store the main area1 first word, later do recovery */
+ store = readl(main_buf);
+ /* NANDFC buffer 1 is used for device status */
+ writenfc(1, &host->regs->buf_addr);
+#endif
+
+ /* Read status into main buffer */
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_CONFIG1_SP_EN;
+ writenfc(tmp, &host->regs->config1);
+
+ writenfc(NFC_STATUS, &host->regs->operation);
+
+ /* Wait for operation to complete */
+ wait_op_done(host, TROP_US_DELAY, 0);
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ /*
+ * Status is placed in first word of main buffer
+ * get status, then recovery area 1 data
+ */
+ ret = readw(main_buf);
+ writel(store, main_buf);
+#elif defined(MXC_NFC_V3_2)
+ ret = readnfc(&host->regs->config1) >> 16;
+#endif
+
+ return ret;
+}
+
+/* This function is used by upper layer to checks if device is ready */
+static int mxc_nand_dev_ready(struct mtd_info *mtd)
+{
+ /*
+ * NFC handles R/B internally. Therefore, this function
+ * always returns status as ready.
+ */
+ return 1;
+}
+
+static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ uint16_t tmp = readnfc(&host->regs->config1);
+
+ if (on)
+ tmp |= NFC_V1_V2_CONFIG1_ECC_EN;
+ else
+ tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+ writenfc(tmp, &host->regs->config1);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->ip_regs->config2);
+
+ if (on)
+ tmp |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ tmp &= ~NFC_V3_CONFIG2_ECC_EN;
+ writenfc(tmp, &host->ip_regs->config2);
+#endif
+}
+
+#ifdef CONFIG_MXC_NAND_HWECC
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ /*
+ * If HW ECC is enabled, we turn it on during init. There is
+ * no need to enable again here.
+ */
+}
+
+#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ uint8_t *buf = chip->oob_poi;
+ int length = mtd->oobsize;
+ int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *bufpoi = buf;
+ int i, toread;
+
+ pr_debug("%s: Reading OOB area of page %u to oob %p\n",
+ __func__, page, buf);
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page);
+ for (i = 0; i < chip->ecc.steps; i++) {
+ toread = min_t(int, length, chip->ecc.prepad);
+ if (toread) {
+ chip->read_buf(mtd, bufpoi, toread);
+ bufpoi += toread;
+ length -= toread;
+ }
+ bufpoi += chip->ecc.bytes;
+ host->col_addr += chip->ecc.bytes;
+ length -= chip->ecc.bytes;
+
+ toread = min_t(int, length, chip->ecc.postpad);
+ if (toread) {
+ chip->read_buf(mtd, bufpoi, toread);
+ bufpoi += toread;
+ length -= toread;
+ }
+ }
+ if (length > 0)
+ chip->read_buf(mtd, bufpoi, length);
+
+ _mxc_nand_enable_hwecc(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_READOOB,
+ mtd->writesize + chip->ecc.prepad, page);
+ bufpoi = buf + chip->ecc.prepad;
+ length = mtd->oobsize - chip->ecc.prepad;
+ for (i = 0; i < chip->ecc.steps; i++) {
+ toread = min_t(int, length, chip->ecc.bytes);
+ chip->read_buf(mtd, bufpoi, toread);
+ bufpoi += eccpitch;
+ length -= eccpitch;
+ host->col_addr += chip->ecc.postpad + chip->ecc.prepad;
+ }
+ _mxc_nand_enable_hwecc(mtd, 1);
+ return 1;
+}
+
+static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf,
+ int oob_required,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size;
+ int n;
+
+ _mxc_nand_enable_hwecc(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+
+ for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) {
+ host->col_addr = n * eccsize;
+ chip->read_buf(mtd, buf, eccsize);
+ buf += eccsize;
+
+ host->col_addr = mtd->writesize + n * eccpitch;
+ if (chip->ecc.prepad) {
+ chip->read_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->read_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size)
+ chip->read_buf(mtd, oob, size);
+ _mxc_nand_enable_hwecc(mtd, 1);
+
+ return 0;
+}
+
+static int mxc_nand_read_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf,
+ int oob_required,
+ int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int n, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ pr_debug("Reading page %u to buf %p oob %p\n",
+ page, buf, oob);
+
+ /* first read the data area and the available portion of OOB */
+ for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) {
+ int stat;
+
+ host->col_addr = n * eccsize;
+
+ chip->read_buf(mtd, p, eccsize);
+
+ host->col_addr = mtd->writesize + n * eccpitch;
+
+ if (chip->ecc.prepad) {
+ chip->read_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ n = mtd->oobsize - (oob - chip->oob_poi);
+ if (n)
+ chip->read_buf(mtd, oob, n);
+
+ /* Then switch ECC off and read the OOB area to get the ECC code */
+ _mxc_nand_enable_hwecc(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page);
+ eccsteps = chip->ecc.steps;
+ oob = chip->oob_poi + chip->ecc.prepad;
+ for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) {
+ host->col_addr = mtd->writesize +
+ n * eccpitch +
+ chip->ecc.prepad;
+ chip->read_buf(mtd, oob, eccbytes);
+ oob += eccbytes + chip->ecc.postpad;
+ }
+ _mxc_nand_enable_hwecc(mtd, 1);
+ return 0;
+}
+
+static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int length = mtd->oobsize;
+ int i, len, status, steps = chip->ecc.steps;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ for (i = 0; i < steps; i++) {
+ len = min_t(int, length, eccpitch);
+
+ chip->write_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ length -= len;
+ host->col_addr += chip->ecc.prepad + chip->ecc.postpad;
+ }
+ if (length > 0)
+ chip->write_buf(mtd, bufpoi, length);
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size;
+ int n;
+
+ for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) {
+ host->col_addr = n * eccsize;
+ chip->write_buf(mtd, buf, eccsize);
+ buf += eccsize;
+
+ host->col_addr = mtd->writesize + n * eccpitch;
+
+ if (chip->ecc.prepad) {
+ chip->write_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ host->col_addr += eccbytes;
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size)
+ chip->write_buf(mtd, oob, size);
+ return 0;
+}
+
+static int mxc_nand_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
+ int i, n, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ for (i = n = 0;
+ eccsteps;
+ n++, eccsteps--, i += eccbytes, p += eccsize) {
+ host->col_addr = n * eccsize;
+
+ chip->write_buf(mtd, p, eccsize);
+
+ host->col_addr = mtd->writesize + n * eccpitch;
+
+ if (chip->ecc.prepad) {
+ chip->write_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->write_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->write_buf(mtd, oob, i);
+ return 0;
+}
+
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint32_t ecc_status = readl(&host->regs->ecc_status_result);
+ int subpages = mtd->writesize / nand_chip->subpagesize;
+ int pg2blk_shift = nand_chip->phys_erase_shift -
+ nand_chip->page_shift;
+
+ do {
+ if ((ecc_status & 0xf) > 4) {
+ static int last_bad = -1;
+
+ if (last_bad != host->page_addr >> pg2blk_shift) {
+ last_bad = host->page_addr >> pg2blk_shift;
+ printk(KERN_DEBUG
+ "MXC_NAND: HWECC uncorrectable ECC error"
+ " in block %u page %u subpage %d\n",
+ last_bad, host->page_addr,
+ mtd->writesize / nand_chip->subpagesize
+ - subpages);
+ }
+ return -EBADMSG;
+ }
+ ecc_status >>= 4;
+ subpages--;
+ } while (subpages > 0);
+
+ return 0;
+}
+#else
+#define mxc_nand_read_page_syndrome NULL
+#define mxc_nand_read_page_raw_syndrome NULL
+#define mxc_nand_read_oob_syndrome NULL
+#define mxc_nand_write_page_syndrome NULL
+#define mxc_nand_write_page_raw_syndrome NULL
+#define mxc_nand_write_oob_syndrome NULL
+
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ /*
+ * 1-Bit errors are automatically corrected in HW. No need for
+ * additional correction. 2-Bit errors cannot be corrected by
+ * HW ECC, so we need to return failure
+ */
+ uint16_t ecc_status = readnfc(&host->regs->ecc_status_result);
+
+ if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
+ pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+#endif
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ return 0;
+}
+#endif
+
+static u_char mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint8_t ret = 0;
+ uint16_t col;
+ uint16_t __iomem *main_buf =
+ (uint16_t __iomem *)host->regs->main_area[0];
+ uint16_t __iomem *spare_buf =
+ (uint16_t __iomem *)host->regs->spare_area[0];
+ union {
+ uint16_t word;
+ uint8_t bytes[2];
+ } nfc_word;
+
+ /* Check for status request */
+ if (host->status_request)
+ return get_dev_status(host) & 0xFF;
+
+ /* Get column for 16-bit access */
+ col = host->col_addr >> 1;
+
+ /* If we are accessing the spare region */
+ if (host->spare_only)
+ nfc_word.word = readw(&spare_buf[col]);
+ else
+ nfc_word.word = readw(&main_buf[col]);
+
+ /* Pick upper/lower byte of word from RAM buffer */
+ ret = nfc_word.bytes[host->col_addr & 0x1];
+
+ /* Update saved column address */
+ if (nand_chip->options & NAND_BUSWIDTH_16)
+ host->col_addr += 2;
+ else
+ host->col_addr++;
+
+ return ret;
+}
+
+static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ uint16_t col, ret;
+ uint16_t __iomem *p;
+
+ pr_debug("mxc_nand_read_word(col = %d)\n", host->col_addr);
+
+ col = host->col_addr;
+ /* Adjust saved column address */
+ if (col < mtd->writesize && host->spare_only)
+ col += mtd->writesize;
+
+ if (col < mtd->writesize) {
+ p = (uint16_t __iomem *)(host->regs->main_area[0] +
+ (col >> 1));
+ } else {
+ p = (uint16_t __iomem *)(host->regs->spare_area[0] +
+ ((col - mtd->writesize) >> 1));
+ }
+
+ if (col & 1) {
+ union {
+ uint16_t word;
+ uint8_t bytes[2];
+ } nfc_word[3];
+
+ nfc_word[0].word = readw(p);
+ nfc_word[1].word = readw(p + 1);
+
+ nfc_word[2].bytes[0] = nfc_word[0].bytes[1];
+ nfc_word[2].bytes[1] = nfc_word[1].bytes[0];
+
+ ret = nfc_word[2].word;
+ } else {
+ ret = readw(p);
+ }
+
+ /* Update saved column address */
+ host->col_addr = col + 2;
+
+ return ret;
+}
+
+/*
+ * Write data of length len to buffer buf. The data to be
+ * written on NAND Flash is first copied to RAMbuffer. After the Data Input
+ * Operation by the NFC, the data is written to NAND Flash
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int n, col, i = 0;
+
+ pr_debug("mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr,
+ len);
+
+ col = host->col_addr;
+
+ /* Adjust saved column address */
+ if (col < mtd->writesize && host->spare_only)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ pr_debug("%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n);
+
+ while (n > 0) {
+ void __iomem *p;
+
+ if (col < mtd->writesize) {
+ p = host->regs->main_area[0] + (col & ~3);
+ } else {
+ p = host->regs->spare_area[0] -
+ mtd->writesize + (col & ~3);
+ }
+
+ pr_debug("%s:%d: p = %p\n", __func__,
+ __LINE__, p);
+
+ if (((col | (unsigned long)&buf[i]) & 3) || n < 4) {
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } nfc_word;
+
+ nfc_word.word = readl(p);
+ nfc_word.bytes[col & 3] = buf[i++];
+ n--;
+ col++;
+
+ writel(nfc_word.word, p);
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+
+ pr_debug("%s:%d: n = %d, m = %d, i = %d, col = %d\n",
+ __func__, __LINE__, n, m, i, col);
+
+ mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m);
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ host->col_addr = col;
+}
+
+/*
+ * Read the data buffer from the NAND Flash. To read the data from NAND
+ * Flash first the data output cycle is initiated by the NFC, which copies
+ * the data to RAMbuffer. This data of length len is then copied to buffer buf.
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+ int n, col, i = 0;
+
+ pr_debug("mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr,
+ len);
+
+ col = host->col_addr;
+
+ /* Adjust saved column address */
+ if (col < mtd->writesize && host->spare_only)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ while (n > 0) {
+ void __iomem *p;
+
+ if (col < mtd->writesize) {
+ p = host->regs->main_area[0] + (col & ~3);
+ } else {
+ p = host->regs->spare_area[0] -
+ mtd->writesize + (col & ~3);
+ }
+
+ if (((col | (int)&buf[i]) & 3) || n < 4) {
+ union {
+ uint32_t word;
+ uint8_t bytes[4];
+ } nfc_word;
+
+ nfc_word.word = readl(p);
+ buf[i++] = nfc_word.bytes[col & 3];
+ n--;
+ col++;
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+ mxc_nand_memcpy32((uint32_t *)&buf[i], p, m);
+
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ host->col_addr = col;
+}
+
+/*
+ * This function is used by upper layer for select and
+ * deselect of the NAND chip
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ switch (chip) {
+ case -1:
+ /* TODO: Disable the NFC clock */
+ if (host->clk_act)
+ host->clk_act = 0;
+ break;
+ case 0:
+ /* TODO: Enable the NFC clock */
+ if (!host->clk_act)
+ host->clk_act = 1;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ */
+void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+
+ pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+
+ /* Reset command state information */
+ host->status_request = false;
+
+ /* Command pre-processing step */
+ switch (command) {
+
+ case NAND_CMD_STATUS:
+ host->col_addr = 0;
+ host->status_request = true;
+ break;
+
+ case NAND_CMD_READ0:
+ host->page_addr = page_addr;
+ host->col_addr = column;
+ host->spare_only = false;
+ break;
+
+ case NAND_CMD_READOOB:
+ host->col_addr = column;
+ host->spare_only = true;
+ if (host->pagesize_2k)
+ command = NAND_CMD_READ0; /* only READ0 is valid */
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column >= mtd->writesize) {
+ /*
+ * before sending SEQIN command for partial write,
+ * we need read one page out. FSL NFC does not support
+ * partial write. It always sends out 512+ecc+512+ecc
+ * for large page nand flash. But for small page nand
+ * flash, it does support SPARE ONLY operation.
+ */
+ if (host->pagesize_2k) {
+ /* call ourself to read a page */
+ mxc_nand_command(mtd, NAND_CMD_READ0, 0,
+ page_addr);
+ }
+
+ host->col_addr = column - mtd->writesize;
+ host->spare_only = true;
+
+ /* Set program pointer to spare region */
+ if (!host->pagesize_2k)
+ send_cmd(host, NAND_CMD_READOOB);
+ } else {
+ host->spare_only = false;
+ host->col_addr = column;
+
+ /* Set program pointer to page start */
+ if (!host->pagesize_2k)
+ send_cmd(host, NAND_CMD_READ0);
+ }
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ send_prog_page(host, 0, host->spare_only);
+
+ if (host->pagesize_2k && is_mxc_nfc_1()) {
+ /* data in 4 areas */
+ send_prog_page(host, 1, host->spare_only);
+ send_prog_page(host, 2, host->spare_only);
+ send_prog_page(host, 3, host->spare_only);
+ }
+
+ break;
+ }
+
+ /* Write out the command to the device. */
+ send_cmd(host, command);
+
+ /* Write out column address, if necessary */
+ if (column != -1) {
+ /*
+ * MXC NANDFC can only perform full page+spare or
+ * spare-only read/write. When the upper layers perform
+ * a read/write buffer operation, we will use the saved
+ * column address to index into the full page.
+ */
+ send_addr(host, 0);
+ if (host->pagesize_2k)
+ /* another col addr cycle for 2k page */
+ send_addr(host, 0);
+ }
+
+ /* Write out page address, if necessary */
+ if (page_addr != -1) {
+ u32 page_mask = nand_chip->pagemask;
+ do {
+ send_addr(host, page_addr & 0xFF);
+ page_addr >>= 8;
+ page_mask >>= 8;
+ } while (page_mask);
+ }
+
+ /* Command post-processing step */
+ switch (command) {
+
+ case NAND_CMD_RESET:
+ break;
+
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ if (host->pagesize_2k) {
+ /* send read confirm command */
+ send_cmd(host, NAND_CMD_READSTART);
+ /* read for each AREA */
+ send_read_page(host, 0, host->spare_only);
+ if (is_mxc_nfc_1()) {
+ send_read_page(host, 1, host->spare_only);
+ send_read_page(host, 2, host->spare_only);
+ send_read_page(host, 3, host->spare_only);
+ }
+ } else {
+ send_read_page(host, 0, host->spare_only);
+ }
+ break;
+
+ case NAND_CMD_READID:
+ host->col_addr = 0;
+ send_read_id(host);
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ break;
+
+ case NAND_CMD_STATUS:
+ break;
+
+ case NAND_CMD_ERASE2:
+ break;
+ }
+}
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+
+#endif
+
+int board_nand_init(struct nand_chip *this)
+{
+ struct mtd_info *mtd;
+#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+ uint32_t tmp;
+#endif
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ this->bbt_options |= NAND_BBT_USE_FLASH;
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+#endif
+
+ /* structures must be linked */
+ mtd = &this->mtd;
+ host->nand = this;
+
+ /* 5 us command delay time */
+ this->chip_delay = 5;
+
+ nand_set_controller_data(this, host);
+ this->dev_ready = mxc_nand_dev_ready;
+ this->cmdfunc = mxc_nand_command;
+ this->select_chip = mxc_nand_select_chip;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+
+ host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
+#ifdef MXC_NFC_V3_2
+ host->ip_regs =
+ (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE;
+#endif
+ host->clk_act = 1;
+
+#ifdef CONFIG_MXC_NAND_HWECC
+ this->ecc.calculate = mxc_nand_calculate_ecc;
+ this->ecc.hwctl = mxc_nand_enable_hwecc;
+ this->ecc.correct = mxc_nand_correct_data;
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
+ this->ecc.mode = NAND_ECC_HW_SYNDROME;
+ this->ecc.read_page = mxc_nand_read_page_syndrome;
+ this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome;
+ this->ecc.read_oob = mxc_nand_read_oob_syndrome;
+ this->ecc.write_page = mxc_nand_write_page_syndrome;
+ this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome;
+ this->ecc.write_oob = mxc_nand_write_oob_syndrome;
+ this->ecc.bytes = 9;
+ this->ecc.prepad = 7;
+ } else {
+ this->ecc.mode = NAND_ECC_HW;
+ }
+
+ if (is_mxc_nfc_1())
+ this->ecc.strength = 1;
+ else
+ this->ecc.strength = 4;
+
+ host->pagesize_2k = 0;
+
+ this->ecc.size = 512;
+ _mxc_nand_enable_hwecc(mtd, 1);
+#else
+ this->ecc.layout = &nand_soft_eccoob;
+ this->ecc.mode = NAND_ECC_SOFT;
+ _mxc_nand_enable_hwecc(mtd, 0);
+#endif
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* NAND bus width determines access functions used by upper layer */
+ if (is_16bit_nand())
+ this->options |= NAND_BUSWIDTH_16;
+
+#ifdef CONFIG_SYS_NAND_LARGEPAGE
+ host->pagesize_2k = 1;
+ this->ecc.layout = &nand_hw_eccoob2k;
+#else
+ host->pagesize_2k = 0;
+ this->ecc.layout = &nand_hw_eccoob;
+#endif
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#ifdef MXC_NFC_V2_1
+ tmp = readnfc(&host->regs->config1);
+ tmp |= NFC_V2_CONFIG1_ONE_CYCLE;
+ tmp |= NFC_V2_CONFIG1_ECC_MODE_4;
+ writenfc(tmp, &host->regs->config1);
+ if (host->pagesize_2k)
+ writenfc(64/2, &host->regs->spare_area_size);
+ else
+ writenfc(16/2, &host->regs->spare_area_size);
+#endif
+
+ /*
+ * preset operation
+ * Unlock the internal RAM Buffer
+ */
+ writenfc(0x2, &host->regs->config);
+
+ /* Blocks to be unlocked */
+ writenfc(0x0, &host->regs->unlockstart_blkaddr);
+ /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the
+ * unlockend_blkaddr, but the magic 0x4000 does not always work
+ * when writing more than some 32 megabytes (on 2k page nands)
+ * However 0xFFFF doesn't seem to have this kind
+ * of limitation (tried it back and forth several times).
+ * The linux kernel driver sets this to 0xFFFF for the v2 controller
+ * only, but probably this was not tested there for v1.
+ * The very same limitation seems to apply to this kernel driver.
+ * This might be NAND chip specific and the i.MX31 datasheet is
+ * extremely vague about the semantics of this register.
+ */
+ writenfc(0xFFFF, &host->regs->unlockend_blkaddr);
+
+ /* Unlock Block Command for given address range */
+ writenfc(0x4, &host->regs->wrprot);
+#elif defined(MXC_NFC_V3_2)
+ writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1);
+ writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc);
+
+ /* Unlock the internal RAM Buffer */
+ writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
+ &host->ip_regs->wrprot);
+
+ /* Blocks to be unlocked */
+ for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++)
+ writenfc(0x0 | 0xFFFF << 16,
+ &host->ip_regs->wrprot_unlock_blkaddr[tmp]);
+
+ writenfc(0, &host->ip_regs->ipc);
+
+ tmp = readnfc(&host->ip_regs->config2);
+ tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK |
+ NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK);
+ tmp |= NFC_V3_CONFIG2_ONE_CYCLE;
+
+ if (host->pagesize_2k) {
+ tmp |= NFC_V3_CONFIG2_SPAS(64/2);
+ tmp |= NFC_V3_CONFIG2_PS_2048;
+ } else {
+ tmp |= NFC_V3_CONFIG2_SPAS(16/2);
+ tmp |= NFC_V3_CONFIG2_PS_512;
+ }
+
+ writenfc(tmp, &host->ip_regs->config2);
+
+ tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+
+ if (!(this->options & NAND_BUSWIDTH_16))
+ tmp |= NFC_V3_CONFIG3_FW8;
+
+ writenfc(tmp, &host->ip_regs->config3);
+
+ writenfc(0, &host->ip_regs->delay_line);
+#endif
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/mxc_nand.h b/drivers/mtd/nand/raw/mxc_nand.h
new file mode 100644
index 0000000000..1c7f3a2e22
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxc_nand.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (c) 2009 Magnus Lilja <lilja.magnus@gmail.com>
+ */
+
+#ifndef __MXC_NAND_H
+#define __MXC_NAND_H
+
+/*
+ * Register map and bit definitions for the Freescale NAND Flash Controller
+ * present in various i.MX devices.
+ *
+ * MX31 and MX27 have version 1, which has:
+ * 4 512-byte main buffers and
+ * 4 16-byte spare buffers
+ * to support up to 2K byte pagesize nand.
+ * Reading or writing a 2K page requires 4 FDI/FDO cycles.
+ *
+ * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which
+ * have:
+ * 8 512-byte main buffers and
+ * 8 64-byte spare buffers
+ * to support up to 4K byte pagesize nand.
+ * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle.
+ * Also some of registers are moved and/or changed meaning as seen below.
+ */
+#if defined(CONFIG_MX27) || defined(CONFIG_MX31)
+#define MXC_NFC_V1
+#define is_mxc_nfc_1() 1
+#define is_mxc_nfc_21() 0
+#define is_mxc_nfc_32() 0
+#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
+#define MXC_NFC_V2_1
+#define is_mxc_nfc_1() 0
+#define is_mxc_nfc_21() 1
+#define is_mxc_nfc_32() 0
+#elif defined(CONFIG_MX51) || defined(CONFIG_MX53)
+#define MXC_NFC_V3
+#define MXC_NFC_V3_2
+#define is_mxc_nfc_1() 0
+#define is_mxc_nfc_21() 0
+#define is_mxc_nfc_32() 1
+#else
+#error "MXC NFC implementation not supported"
+#endif
+#define is_mxc_nfc_3() is_mxc_nfc_32()
+
+#if defined(MXC_NFC_V1)
+#define NAND_MXC_NR_BUFS 4
+#define NAND_MXC_SPARE_BUF_SIZE 16
+#define NAND_MXC_REG_OFFSET 0xe00
+#define NAND_MXC_2K_MULTI_CYCLE
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+#define NAND_MXC_NR_BUFS 8
+#define NAND_MXC_SPARE_BUF_SIZE 64
+#define NAND_MXC_REG_OFFSET 0x1e00
+#endif
+
+struct mxc_nand_regs {
+ u8 main_area[NAND_MXC_NR_BUFS][0x200];
+ u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE];
+ /*
+ * reserved size is offset of nfc registers
+ * minus total main and spare sizes
+ */
+ u8 reserved1[NAND_MXC_REG_OFFSET
+ - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)];
+#if defined(MXC_NFC_V1)
+ u16 buf_size;
+ u16 reserved2;
+ u16 buf_addr;
+ u16 flash_addr;
+ u16 flash_cmd;
+ u16 config;
+ u16 ecc_status_result;
+ u16 rsltmain_area;
+ u16 rsltspare_area;
+ u16 wrprot;
+ u16 unlockstart_blkaddr;
+ u16 unlockend_blkaddr;
+ u16 nf_wrprst;
+ u16 config1;
+ u16 config2;
+#elif defined(MXC_NFC_V2_1)
+ u16 reserved2[2];
+ u16 buf_addr;
+ u16 flash_addr;
+ u16 flash_cmd;
+ u16 config;
+ u32 ecc_status_result;
+ u16 spare_area_size;
+ u16 wrprot;
+ u16 reserved3[2];
+ u16 nf_wrprst;
+ u16 config1;
+ u16 config2;
+ u16 reserved4;
+ u16 unlockstart_blkaddr;
+ u16 unlockend_blkaddr;
+ u16 unlockstart_blkaddr1;
+ u16 unlockend_blkaddr1;
+ u16 unlockstart_blkaddr2;
+ u16 unlockend_blkaddr2;
+ u16 unlockstart_blkaddr3;
+ u16 unlockend_blkaddr3;
+#elif defined(MXC_NFC_V3_2)
+ u32 flash_cmd;
+ u32 flash_addr[12];
+ u32 config1;
+ u32 ecc_status_result;
+ u32 status_sum;
+ u32 launch;
+#endif
+};
+
+#ifdef MXC_NFC_V3_2
+struct mxc_nand_ip_regs {
+ u32 wrprot;
+ u32 wrprot_unlock_blkaddr[8];
+ u32 config2;
+ u32 config3;
+ u32 ipc;
+ u32 err_addr;
+ u32 delay_line;
+};
+#endif
+
+/* Set FCMD to 1, rest to 0 for Command operation */
+#define NFC_CMD 0x1
+
+/* Set FADD to 1, rest to 0 for Address operation */
+#define NFC_ADDR 0x2
+
+/* Set FDI to 1, rest to 0 for Input operation */
+#define NFC_INPUT 0x4
+
+/* Set FDO to 001, rest to 0 for Data Output operation */
+#define NFC_OUTPUT 0x8
+
+/* Set FDO to 010, rest to 0 for Read ID operation */
+#define NFC_ID 0x10
+
+/* Set FDO to 100, rest to 0 for Read Status operation */
+#define NFC_STATUS 0x20
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define NFC_CONFIG1_SP_EN (1 << 2)
+#define NFC_CONFIG1_RST (1 << 6)
+#define NFC_CONFIG1_CE (1 << 7)
+#elif defined(MXC_NFC_V3_2)
+#define NFC_CONFIG1_SP_EN (1 << 0)
+#define NFC_CONFIG1_CE (1 << 1)
+#define NFC_CONFIG1_RST (1 << 2)
+#endif
+#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
+#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
+#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
+#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
+#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
+#define NFC_V2_CONFIG1_FP_INT (1 << 11)
+#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4)
+#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4)
+
+#define NFC_V1_V2_CONFIG2_INT (1 << 15)
+#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0)
+#define NFC_V3_CONFIG2_PS_512 (0 << 0)
+#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
+#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
+#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7)
+#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7)
+#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9)
+#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
+#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16)
+#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
+#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24)
+#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
+
+#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8 (1 << 3)
+#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
+
+#define NFC_V3_WRPROT_UNLOCK (1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
+
+#define NFC_V3_IPC_CREQ (1 << 0)
+#define NFC_V3_IPC_INT (1 << 31)
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define operation config2
+#define readnfc readw
+#define writenfc writew
+#elif defined(MXC_NFC_V3_2)
+#define operation launch
+#define readnfc readl
+#define writenfc writel
+#endif
+
+#endif /* __MXC_NAND_H */
diff --git a/drivers/mtd/nand/raw/mxc_nand_spl.c b/drivers/mtd/nand/raw/mxc_nand_spl.c
new file mode 100644
index 0000000000..6c03db8428
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxc_nand_spl.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009
+ * Magnus Lilja <lilja.magnus@gmail.com>
+ *
+ * (C) Copyright 2008
+ * Maxim Artamonov, <scn1874 at yandex.ru>
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr at denx.de.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/io.h>
+#include "mxc_nand.h"
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR;
+#elif defined(MXC_NFC_V3_2)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI;
+static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR;
+#endif
+
+static void nfc_wait_ready(void)
+{
+ uint32_t tmp;
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT))
+ ;
+
+ /* Reset interrupt flag */
+ tmp = readnfc(&nfc->config2);
+ tmp &= ~NFC_V1_V2_CONFIG2_INT;
+ writenfc(tmp, &nfc->config2);
+#elif defined(MXC_NFC_V3_2)
+ while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT))
+ ;
+
+ /* Reset interrupt flag */
+ tmp = readnfc(&nfc_ip->ipc);
+ tmp &= ~NFC_V3_IPC_INT;
+ writenfc(tmp, &nfc_ip->ipc);
+#endif
+}
+
+static void nfc_nand_init(void)
+{
+#if defined(MXC_NFC_V3_2)
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int tmp;
+
+ tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK |
+ NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) |
+ NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) |
+ NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN |
+ NFC_V3_CONFIG2_ONE_CYCLE;
+ if (CONFIG_SYS_NAND_PAGE_SIZE == 4096)
+ tmp |= NFC_V3_CONFIG2_PS_4096;
+ else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048)
+ tmp |= NFC_V3_CONFIG2_PS_2048;
+ else if (CONFIG_SYS_NAND_PAGE_SIZE == 512)
+ tmp |= NFC_V3_CONFIG2_PS_512;
+ /*
+ * if spare size is larger that 16 bytes per 512 byte hunk
+ * then use 8 symbol correction instead of 4
+ */
+ if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+ tmp |= NFC_V3_CONFIG2_ECC_MODE_8;
+ else
+ tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8;
+ writenfc(tmp, &nfc_ip->config2);
+
+ tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
+ tmp |= NFC_V3_CONFIG3_FW8;
+#endif
+ writenfc(tmp, &nfc_ip->config3);
+
+ writenfc(0, &nfc_ip->delay_line);
+#elif defined(MXC_NFC_V2_1)
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int config1;
+
+ writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size);
+
+ /* unlocking RAM Buff */
+ writenfc(0x2, &nfc->config);
+
+ /* hardware ECC checking and correct */
+ config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN |
+ NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE |
+ NFC_V2_CONFIG1_FP_INT;
+ /*
+ * if spare size is larger that 16 bytes per 512 byte hunk
+ * then use 8 symbol correction instead of 4
+ */
+ if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+ config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4;
+ else
+ config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
+ writenfc(config1, &nfc->config1);
+#elif defined(MXC_NFC_V1)
+ /* unlocking RAM Buff */
+ writenfc(0x2, &nfc->config);
+
+ /* hardware ECC checking and correct */
+ writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK,
+ &nfc->config1);
+#endif
+}
+
+static void nfc_nand_command(unsigned short command)
+{
+ writenfc(command, &nfc->flash_cmd);
+ writenfc(NFC_CMD, &nfc->operation);
+ nfc_wait_ready();
+}
+
+static void nfc_nand_address(unsigned short address)
+{
+ writenfc(address, &nfc->flash_addr);
+ writenfc(NFC_ADDR, &nfc->operation);
+ nfc_wait_ready();
+}
+
+static void nfc_nand_page_address(unsigned int page_address)
+{
+ unsigned int page_count;
+
+ nfc_nand_address(0x00);
+
+ /* code only for large page flash */
+ if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+ nfc_nand_address(0x00);
+
+ page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE;
+
+ if (page_address <= page_count) {
+ page_count--; /* transform 0x01000000 to 0x00ffffff */
+ do {
+ nfc_nand_address(page_address & 0xff);
+ page_address = page_address >> 8;
+ page_count = page_count >> 8;
+ } while (page_count);
+ }
+
+ nfc_nand_address(0x00);
+}
+
+static void nfc_nand_data_output(void)
+{
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+ int i;
+#endif
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ int config1 = readnfc(&nfc->config1);
+ config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(config1, &nfc->config1);
+#endif
+ writenfc(NFC_OUTPUT, &nfc->operation);
+ nfc_wait_ready();
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+ /*
+ * This NAND controller requires multiple input commands
+ * for pages larger than 512 bytes.
+ */
+ for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) {
+ writenfc(i, &nfc->buf_addr);
+ writenfc(NFC_OUTPUT, &nfc->operation);
+ nfc_wait_ready();
+ }
+#endif
+}
+
+static int nfc_nand_check_ecc(void)
+{
+#if defined(MXC_NFC_V1)
+ u16 ecc_status = readw(&nfc->ecc_status_result);
+ return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2;
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+ u32 ecc_status = readl(&nfc->ecc_status_result);
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4;
+ int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+
+ do {
+ if ((ecc_status & 0xf) > err_limit)
+ return 1;
+ ecc_status >>= 4;
+ } while (--subpages);
+
+ return 0;
+#endif
+}
+
+static void nfc_nand_read_page(unsigned int page_address)
+{
+ /* read in first 0 buffer */
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ int config1 = readnfc(&nfc->config1);
+ config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(config1, &nfc->config1);
+#endif
+ nfc_nand_command(NAND_CMD_READ0);
+ nfc_nand_page_address(page_address);
+
+ if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+ nfc_nand_command(NAND_CMD_READSTART);
+
+ nfc_nand_data_output(); /* fill the main buffer 0 */
+}
+
+static int nfc_read_page(unsigned int page_address, unsigned char *buf)
+{
+ int i;
+ u32 *src;
+ u32 *dst;
+
+ nfc_nand_read_page(page_address);
+
+ if (nfc_nand_check_ecc())
+ return -EBADMSG;
+
+ src = (u32 *)&nfc->main_area[0][0];
+ dst = (u32 *)buf;
+
+ /* main copy loop from NAND-buffer to SDRAM memory */
+ for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) {
+ writel(readl(src), dst);
+ src++;
+ dst++;
+ }
+
+ return 0;
+}
+
+static int is_badblock(int pagenumber)
+{
+ int page = pagenumber;
+ u32 badblock;
+ u32 *src;
+
+ /* Check the first two pages for bad block markers */
+ for (page = pagenumber; page < pagenumber + 2; page++) {
+ nfc_nand_read_page(page);
+
+ src = (u32 *)&nfc->spare_area[0][0];
+
+ /*
+ * IMPORTANT NOTE: The nand flash controller uses a non-
+ * standard layout for large page devices. This can
+ * affect the position of the bad block marker.
+ */
+ /* Get the bad block marker */
+ badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]);
+ badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4);
+ badblock &= 0xff;
+
+ /* bad block marker verify */
+ if (badblock != 0xff)
+ return 1; /* potential bad block */
+ }
+
+ return 0;
+}
+
+int nand_spl_load_image(uint32_t from, unsigned int size, void *buf)
+{
+ int i;
+ unsigned int page;
+ unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
+ CONFIG_SYS_NAND_PAGE_SIZE;
+
+ nfc_nand_init();
+
+ /* Convert to page number */
+ page = from / CONFIG_SYS_NAND_PAGE_SIZE;
+ i = 0;
+
+ size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE);
+ while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) {
+ if (nfc_read_page(page, buf) < 0)
+ return -1;
+
+ page++;
+ i++;
+ buf = buf + CONFIG_SYS_NAND_PAGE_SIZE;
+
+ /*
+ * Check if we have crossed a block boundary, and if so
+ * check for bad block.
+ */
+ if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) {
+ /*
+ * Yes, new block. See if this block is good. If not,
+ * loop until we find a good block.
+ */
+ while (is_badblock(page)) {
+ page = page + CONFIG_SYS_NAND_PAGE_COUNT;
+ /* Check i we've reached the end of flash. */
+ if (page >= maxpages)
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#ifndef CONFIG_SPL_FRAMEWORK
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+
+ /*
+ * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must
+ * be aligned to full pages
+ */
+ if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) {
+ /* Copy from NAND successful, start U-Boot */
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ uboot();
+ } else {
+ /* Unrecoverable error when copying from NAND */
+ hang();
+ }
+}
+#endif
+
+void nand_init(void) {}
+void nand_deselect(void) {}
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
new file mode 100644
index 0000000000..e3341812a2
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -0,0 +1,1302 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Freescale i.MX28 NAND flash driver
+ *
+ * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
+ * on behalf of DENX Software Engineering GmbH
+ *
+ * Based on code from LTIB:
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+#include <malloc.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/mach-imx/regs-bch.h>
+#include <asm/mach-imx/regs-gpmi.h>
+#include <asm/arch/sys_proto.h>
+#include "mxs_nand.h"
+
+#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
+
+#if (defined(CONFIG_MX6) || defined(CONFIG_MX7))
+#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2
+#else
+#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0
+#endif
+#define MXS_NAND_METADATA_SIZE 10
+#define MXS_NAND_BITS_PER_ECC_LEVEL 13
+
+#if !defined(CONFIG_SYS_CACHELINE_SIZE) || CONFIG_SYS_CACHELINE_SIZE < 32
+#define MXS_NAND_COMMAND_BUFFER_SIZE 32
+#else
+#define MXS_NAND_COMMAND_BUFFER_SIZE CONFIG_SYS_CACHELINE_SIZE
+#endif
+
+#define MXS_NAND_BCH_TIMEOUT 10000
+
+struct nand_ecclayout fake_ecc_layout;
+
+/*
+ * Cache management functions
+ */
+#ifndef CONFIG_SYS_DCACHE_OFF
+static void mxs_nand_flush_data_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->data_buf;
+
+ flush_dcache_range(addr, addr + info->data_buf_size);
+}
+
+static void mxs_nand_inval_data_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->data_buf;
+
+ invalidate_dcache_range(addr, addr + info->data_buf_size);
+}
+
+static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info)
+{
+ uint32_t addr = (uint32_t)info->cmd_buf;
+
+ flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE);
+}
+#else
+static inline void mxs_nand_flush_data_buf(struct mxs_nand_info *info) {}
+static inline void mxs_nand_inval_data_buf(struct mxs_nand_info *info) {}
+static inline void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) {}
+#endif
+
+static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
+{
+ struct mxs_dma_desc *desc;
+
+ if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) {
+ printf("MXS NAND: Too many DMA descriptors requested\n");
+ return NULL;
+ }
+
+ desc = info->desc[info->desc_index];
+ info->desc_index++;
+
+ return desc;
+}
+
+static void mxs_nand_return_dma_descs(struct mxs_nand_info *info)
+{
+ int i;
+ struct mxs_dma_desc *desc;
+
+ for (i = 0; i < info->desc_index; i++) {
+ desc = info->desc[i];
+ memset(desc, 0, sizeof(struct mxs_dma_desc));
+ desc->address = (dma_addr_t)desc;
+ }
+
+ info->desc_index = 0;
+}
+
+static uint32_t mxs_nand_aux_status_offset(void)
+{
+ return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
+}
+
+static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo,
+ uint32_t page_data_size)
+{
+ uint32_t chunk_data_size_in_bits = geo->ecc_chunk_size * 8;
+ uint32_t chunk_ecc_size_in_bits = geo->ecc_strength * geo->gf_len;
+ uint32_t chunk_total_size_in_bits;
+ uint32_t block_mark_chunk_number;
+ uint32_t block_mark_chunk_bit_offset;
+ uint32_t block_mark_bit_offset;
+
+ chunk_total_size_in_bits =
+ chunk_data_size_in_bits + chunk_ecc_size_in_bits;
+
+ /* Compute the bit offset of the block mark within the physical page. */
+ block_mark_bit_offset = page_data_size * 8;
+
+ /* Subtract the metadata bits. */
+ block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8;
+
+ /*
+ * Compute the chunk number (starting at zero) in which the block mark
+ * appears.
+ */
+ block_mark_chunk_number =
+ block_mark_bit_offset / chunk_total_size_in_bits;
+
+ /*
+ * Compute the bit offset of the block mark within its chunk, and
+ * validate it.
+ */
+ block_mark_chunk_bit_offset = block_mark_bit_offset -
+ (block_mark_chunk_number * chunk_total_size_in_bits);
+
+ if (block_mark_chunk_bit_offset > chunk_data_size_in_bits)
+ return -EINVAL;
+
+ /*
+ * Now that we know the chunk number in which the block mark appears,
+ * we can subtract all the ECC bits that appear before it.
+ */
+ block_mark_bit_offset -=
+ block_mark_chunk_number * chunk_ecc_size_in_bits;
+
+ geo->block_mark_byte_offset = block_mark_bit_offset >> 3;
+ geo->block_mark_bit_offset = block_mark_bit_offset & 0x7;
+
+ return 0;
+}
+
+static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo,
+ struct mtd_info *mtd,
+ unsigned int ecc_strength,
+ unsigned int ecc_step)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+ switch (ecc_step) {
+ case SZ_512:
+ geo->gf_len = 13;
+ break;
+ case SZ_1K:
+ geo->gf_len = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ geo->ecc_chunk_size = ecc_step;
+ geo->ecc_strength = round_up(ecc_strength, 2);
+
+ /* Keep the C >= O */
+ if (geo->ecc_chunk_size < mtd->oobsize)
+ return -EINVAL;
+
+ if (geo->ecc_strength > nand_info->max_ecc_strength_supported)
+ return -EINVAL;
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ return 0;
+}
+
+static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo,
+ struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+ /* The default for the length of Galois Field. */
+ geo->gf_len = 13;
+
+ /* The default for chunk size. */
+ geo->ecc_chunk_size = 512;
+
+ if (geo->ecc_chunk_size < mtd->oobsize) {
+ geo->gf_len = 14;
+ geo->ecc_chunk_size *= 2;
+ }
+
+ if (mtd->oobsize > geo->ecc_chunk_size) {
+ printf("Not support the NAND chips whose oob size is larger then %d bytes!\n",
+ geo->ecc_chunk_size);
+ return -EINVAL;
+ }
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ /*
+ * Determine the ECC layout with the formula:
+ * ECC bits per chunk = (total page spare data bits) /
+ * (bits per ECC level) / (chunks per page)
+ * where:
+ * total page spare data bits =
+ * (page oob size - meta data size) * (bits per byte)
+ */
+ geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8)
+ / (geo->gf_len * geo->ecc_chunk_count);
+
+ geo->ecc_strength = min(round_down(geo->ecc_strength, 2),
+ nand_info->max_ecc_strength_supported);
+
+ return 0;
+}
+
+/*
+ * Wait for BCH complete IRQ and clear the IRQ
+ */
+static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
+{
+ int timeout = MXS_NAND_BCH_TIMEOUT;
+ int ret;
+
+ ret = mxs_wait_mask_set(&nand_info->bch_regs->hw_bch_ctrl_reg,
+ BCH_CTRL_COMPLETE_IRQ, timeout);
+
+ writel(BCH_CTRL_COMPLETE_IRQ, &nand_info->bch_regs->hw_bch_ctrl_clr);
+
+ return ret;
+}
+
+/*
+ * This is the function that we install in the cmd_ctrl function pointer of the
+ * owning struct nand_chip. The only functions in the reference implementation
+ * that use these functions pointers are cmdfunc and select_chip.
+ *
+ * In this driver, we implement our own select_chip, so this function will only
+ * be called by the reference implementation's cmdfunc. For this reason, we can
+ * ignore the chip enable bit and concentrate only on sending bytes to the NAND
+ * Flash.
+ */
+static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct mxs_dma_desc *d;
+ uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ int ret;
+
+ /*
+ * If this condition is true, something is _VERY_ wrong in MTD
+ * subsystem!
+ */
+ if (nand_info->cmd_queue_len == MXS_NAND_COMMAND_BUFFER_SIZE) {
+ printf("MXS NAND: Command queue too long\n");
+ return;
+ }
+
+ /*
+ * Every operation begins with a command byte and a series of zero or
+ * more address bytes. These are distinguished by either the Address
+ * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
+ * asserted. When MTD is ready to execute the command, it will
+ * deasert both latch enables.
+ *
+ * Rather than run a separate DMA operation for every single byte, we
+ * queue them up and run a single DMA operation for the entire series
+ * of command and data bytes.
+ */
+ if (ctrl & (NAND_ALE | NAND_CLE)) {
+ if (data != NAND_CMD_NONE)
+ nand_info->cmd_buf[nand_info->cmd_queue_len++] = data;
+ return;
+ }
+
+ /*
+ * If control arrives here, MTD has deasserted both the ALE and CLE,
+ * which means it's ready to run an operation. Check if we have any
+ * bytes to send.
+ */
+ if (nand_info->cmd_queue_len == 0)
+ return;
+
+ /* Compile the DMA descriptor -- a descriptor that sends command. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM |
+ MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET);
+
+ d->cmd.address = (dma_addr_t)nand_info->cmd_buf;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WRITE |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_CLE |
+ GPMI_CTRL0_ADDRESS_INCREMENT |
+ nand_info->cmd_queue_len;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Flush caches */
+ mxs_nand_flush_cmd_buf(nand_info);
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel);
+ if (ret)
+ printf("MXS NAND: Error sending command\n");
+
+ mxs_nand_return_dma_descs(nand_info);
+
+ /* Reset the command queue. */
+ nand_info->cmd_queue_len = 0;
+}
+
+/*
+ * Test if the NAND flash is ready.
+ */
+static int mxs_nand_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ uint32_t tmp;
+
+ tmp = readl(&nand_info->gpmi_regs->hw_gpmi_stat);
+ tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip);
+
+ return tmp & 1;
+}
+
+/*
+ * Select the NAND chip.
+ */
+static void mxs_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+ nand_info->cur_chip = chip;
+}
+
+/*
+ * Handle block mark swapping.
+ *
+ * Note that, when this function is called, it doesn't know whether it's
+ * swapping the block mark, or swapping it *back* -- but it doesn't matter
+ * because the the operation is the same.
+ */
+static void mxs_nand_swap_block_mark(struct bch_geometry *geo,
+ uint8_t *data_buf, uint8_t *oob_buf)
+{
+ uint32_t bit_offset = geo->block_mark_bit_offset;
+ uint32_t buf_offset = geo->block_mark_byte_offset;
+
+ uint32_t src;
+ uint32_t dst;
+
+ /*
+ * Get the byte from the data area that overlays the block mark. Since
+ * the ECC engine applies its own view to the bits in the page, the
+ * physical block mark won't (in general) appear on a byte boundary in
+ * the data.
+ */
+ src = data_buf[buf_offset] >> bit_offset;
+ src |= data_buf[buf_offset + 1] << (8 - bit_offset);
+
+ dst = oob_buf[0];
+
+ oob_buf[0] = src;
+
+ data_buf[buf_offset] &= ~(0xff << bit_offset);
+ data_buf[buf_offset + 1] &= 0xff << bit_offset;
+
+ data_buf[buf_offset] |= dst << bit_offset;
+ data_buf[buf_offset + 1] |= dst >> (8 - bit_offset);
+}
+
+/*
+ * Read data from NAND.
+ */
+static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct mxs_dma_desc *d;
+ uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ int ret;
+
+ if (length > NAND_MAX_PAGESIZE) {
+ printf("MXS NAND: DMA buffer too big\n");
+ return;
+ }
+
+ if (!buf) {
+ printf("MXS NAND: DMA buffer is NULL\n");
+ return;
+ }
+
+ /* Compile the DMA descriptor - a descriptor that reads data. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
+ (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ (length << MXS_DMA_DESC_BYTES_OFFSET);
+
+ d->cmd.address = (dma_addr_t)nand_info->data_buf;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_READ |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA |
+ length;
+
+ mxs_dma_desc_append(channel, d);
+
+ /*
+ * A DMA descriptor that waits for the command to end and the chip to
+ * become ready.
+ *
+ * I think we actually should *not* be waiting for the chip to become
+ * ready because, after all, we don't care. I think the original code
+ * did that and no one has re-thought it yet.
+ */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM |
+ MXS_DMA_DESC_WAIT4END | (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+ d->cmd.address = 0;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel);
+ if (ret) {
+ printf("MXS NAND: DMA read error\n");
+ goto rtn;
+ }
+
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
+ memcpy(buf, nand_info->data_buf, length);
+
+rtn:
+ mxs_nand_return_dma_descs(nand_info);
+}
+
+/*
+ * Write data to NAND.
+ */
+static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int length)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct mxs_dma_desc *d;
+ uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ int ret;
+
+ if (length > NAND_MAX_PAGESIZE) {
+ printf("MXS NAND: DMA buffer too big\n");
+ return;
+ }
+
+ if (!buf) {
+ printf("MXS NAND: DMA buffer is NULL\n");
+ return;
+ }
+
+ memcpy(nand_info->data_buf, buf, length);
+
+ /* Compile the DMA descriptor - a descriptor that writes data. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
+ (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
+ (length << MXS_DMA_DESC_BYTES_OFFSET);
+
+ d->cmd.address = (dma_addr_t)nand_info->data_buf;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WRITE |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA |
+ length;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Flush caches */
+ mxs_nand_flush_data_buf(nand_info);
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel);
+ if (ret)
+ printf("MXS NAND: DMA write error\n");
+
+ mxs_nand_return_dma_descs(nand_info);
+}
+
+/*
+ * Read a single byte from NAND.
+ */
+static uint8_t mxs_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t buf;
+ mxs_nand_read_buf(mtd, &buf, 1);
+ return buf;
+}
+
+/*
+ * Read a page from NAND.
+ */
+static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
+ struct mxs_dma_desc *d;
+ uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t corrected = 0, failed = 0;
+ uint8_t *status;
+ int i, ret;
+
+ /* Compile the DMA descriptor - wait for ready. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
+ MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
+ (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+ d->cmd.address = 0;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Compile the DMA descriptor - enable the BCH block and read. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
+ MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+ d->cmd.address = 0;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_READ |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA |
+ (mtd->writesize + mtd->oobsize);
+ d->cmd.pio_words[1] = 0;
+ d->cmd.pio_words[2] =
+ GPMI_ECCCTRL_ENABLE_ECC |
+ GPMI_ECCCTRL_ECC_CMD_DECODE |
+ GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
+ d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize;
+ d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Compile the DMA descriptor - disable the BCH block. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
+ MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
+ (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+ d->cmd.address = 0;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA |
+ (mtd->writesize + mtd->oobsize);
+ d->cmd.pio_words[1] = 0;
+ d->cmd.pio_words[2] = 0;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_DEC_SEM;
+
+ d->cmd.address = 0;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel);
+ if (ret) {
+ printf("MXS NAND: DMA read error\n");
+ goto rtn;
+ }
+
+ ret = mxs_nand_wait_for_bch_complete(nand_info);
+ if (ret) {
+ printf("MXS NAND: BCH read timeout\n");
+ goto rtn;
+ }
+
+ /* Invalidate caches */
+ mxs_nand_inval_data_buf(nand_info);
+
+ /* Read DMA completed, now do the mark swapping. */
+ mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf);
+
+ /* Loop over status bytes, accumulating ECC status. */
+ status = nand_info->oob_buf + mxs_nand_aux_status_offset();
+ for (i = 0; i < geo->ecc_chunk_count; i++) {
+ if (status[i] == 0x00)
+ continue;
+
+ if (status[i] == 0xff)
+ continue;
+
+ if (status[i] == 0xfe) {
+ failed++;
+ continue;
+ }
+
+ corrected += status[i];
+ }
+
+ /* Propagate ECC status to the owning MTD. */
+ mtd->ecc_stats.failed += failed;
+ mtd->ecc_stats.corrected += corrected;
+
+ /*
+ * It's time to deliver the OOB bytes. See mxs_nand_ecc_read_oob() for
+ * details about our policy for delivering the OOB.
+ *
+ * We fill the caller's buffer with set bits, and then copy the block
+ * mark to the caller's buffer. Note that, if block mark swapping was
+ * necessary, it has already been done, so we can rely on the first
+ * byte of the auxiliary buffer to contain the block mark.
+ */
+ memset(nand->oob_poi, 0xff, mtd->oobsize);
+
+ nand->oob_poi[0] = nand_info->oob_buf[0];
+
+ memcpy(buf, nand_info->data_buf, mtd->writesize);
+
+rtn:
+ mxs_nand_return_dma_descs(nand_info);
+
+ return ret;
+}
+
+/*
+ * Write a page to NAND.
+ */
+static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
+ struct mxs_dma_desc *d;
+ uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ int ret;
+
+ memcpy(nand_info->data_buf, buf, mtd->writesize);
+ memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize);
+
+ /* Handle block mark swapping. */
+ mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf);
+
+ /* Compile the DMA descriptor - write data. */
+ d = mxs_nand_get_dma_desc(nand_info);
+ d->cmd.data =
+ MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+ MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
+ (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+
+ d->cmd.address = 0;
+
+ d->cmd.pio_words[0] =
+ GPMI_CTRL0_COMMAND_MODE_WRITE |
+ GPMI_CTRL0_WORD_LENGTH |
+ (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ GPMI_CTRL0_ADDRESS_NAND_DATA;
+ d->cmd.pio_words[1] = 0;
+ d->cmd.pio_words[2] =
+ GPMI_ECCCTRL_ENABLE_ECC |
+ GPMI_ECCCTRL_ECC_CMD_ENCODE |
+ GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
+ d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize);
+ d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+
+ mxs_dma_desc_append(channel, d);
+
+ /* Flush caches */
+ mxs_nand_flush_data_buf(nand_info);
+
+ /* Execute the DMA chain. */
+ ret = mxs_dma_go(channel);
+ if (ret) {
+ printf("MXS NAND: DMA write error\n");
+ goto rtn;
+ }
+
+ ret = mxs_nand_wait_for_bch_complete(nand_info);
+ if (ret) {
+ printf("MXS NAND: BCH write timeout\n");
+ goto rtn;
+ }
+
+rtn:
+ mxs_nand_return_dma_descs(nand_info);
+ return 0;
+}
+
+/*
+ * Read OOB from NAND.
+ *
+ * This function is a veneer that replaces the function originally installed by
+ * the NAND Flash MTD code.
+ */
+static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ int ret;
+
+ if (ops->mode == MTD_OPS_RAW)
+ nand_info->raw_oob_mode = 1;
+ else
+ nand_info->raw_oob_mode = 0;
+
+ ret = nand_info->hooked_read_oob(mtd, from, ops);
+
+ nand_info->raw_oob_mode = 0;
+
+ return ret;
+}
+
+/*
+ * Write OOB to NAND.
+ *
+ * This function is a veneer that replaces the function originally installed by
+ * the NAND Flash MTD code.
+ */
+static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ int ret;
+
+ if (ops->mode == MTD_OPS_RAW)
+ nand_info->raw_oob_mode = 1;
+ else
+ nand_info->raw_oob_mode = 0;
+
+ ret = nand_info->hooked_write_oob(mtd, to, ops);
+
+ nand_info->raw_oob_mode = 0;
+
+ return ret;
+}
+
+/*
+ * Mark a block bad in NAND.
+ *
+ * This function is a veneer that replaces the function originally installed by
+ * the NAND Flash MTD code.
+ */
+static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ int ret;
+
+ nand_info->marking_block_bad = 1;
+
+ ret = nand_info->hooked_block_markbad(mtd, ofs);
+
+ nand_info->marking_block_bad = 0;
+
+ return ret;
+}
+
+/*
+ * There are several places in this driver where we have to handle the OOB and
+ * block marks. This is the function where things are the most complicated, so
+ * this is where we try to explain it all. All the other places refer back to
+ * here.
+ *
+ * These are the rules, in order of decreasing importance:
+ *
+ * 1) Nothing the caller does can be allowed to imperil the block mark, so all
+ * write operations take measures to protect it.
+ *
+ * 2) In read operations, the first byte of the OOB we return must reflect the
+ * true state of the block mark, no matter where that block mark appears in
+ * the physical page.
+ *
+ * 3) ECC-based read operations return an OOB full of set bits (since we never
+ * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
+ * return).
+ *
+ * 4) "Raw" read operations return a direct view of the physical bytes in the
+ * page, using the conventional definition of which bytes are data and which
+ * are OOB. This gives the caller a way to see the actual, physical bytes
+ * in the page, without the distortions applied by our ECC engine.
+ *
+ * What we do for this specific read operation depends on whether we're doing
+ * "raw" read, or an ECC-based read.
+ *
+ * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
+ * easy. When reading a page, for example, the NAND Flash MTD code calls our
+ * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
+ * ECC-based or raw view of the page is implicit in which function it calls
+ * (there is a similar pair of ECC-based/raw functions for writing).
+ *
+ * Since MTD assumes the OOB is not covered by ECC, there is no pair of
+ * ECC-based/raw functions for reading or or writing the OOB. The fact that the
+ * caller wants an ECC-based or raw view of the page is not propagated down to
+ * this driver.
+ *
+ * Since our OOB *is* covered by ECC, we need this information. So, we hook the
+ * ecc.read_oob and ecc.write_oob function pointers in the owning
+ * struct mtd_info with our own functions. These hook functions set the
+ * raw_oob_mode field so that, when control finally arrives here, we'll know
+ * what to do.
+ */
+static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page)
+{
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+ /*
+ * First, fill in the OOB buffer. If we're doing a raw read, we need to
+ * get the bytes from the physical page. If we're not doing a raw read,
+ * we need to fill the buffer with set bits.
+ */
+ if (nand_info->raw_oob_mode) {
+ /*
+ * If control arrives here, we're doing a "raw" read. Send the
+ * command to read the conventional OOB and read it.
+ */
+ nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ nand->read_buf(mtd, nand->oob_poi, mtd->oobsize);
+ } else {
+ /*
+ * If control arrives here, we're not doing a "raw" read. Fill
+ * the OOB buffer with set bits and correct the block mark.
+ */
+ memset(nand->oob_poi, 0xff, mtd->oobsize);
+
+ nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ mxs_nand_read_buf(mtd, nand->oob_poi, 1);
+ }
+
+ return 0;
+
+}
+
+/*
+ * Write OOB data to NAND.
+ */
+static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page)
+{
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ uint8_t block_mark = 0;
+
+ /*
+ * There are fundamental incompatibilities between the i.MX GPMI NFC and
+ * the NAND Flash MTD model that make it essentially impossible to write
+ * the out-of-band bytes.
+ *
+ * We permit *ONE* exception. If the *intent* of writing the OOB is to
+ * mark a block bad, we can do that.
+ */
+
+ if (!nand_info->marking_block_bad) {
+ printf("NXS NAND: Writing OOB isn't supported\n");
+ return -EIO;
+ }
+
+ /* Write the block mark. */
+ nand->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ nand->write_buf(mtd, &block_mark, 1);
+ nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ /* Check if it worked. */
+ if (nand->waitfunc(mtd, nand) & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Claims all blocks are good.
+ *
+ * In principle, this function is *only* called when the NAND Flash MTD system
+ * isn't allowed to keep an in-memory bad block table, so it is forced to ask
+ * the driver for bad block information.
+ *
+ * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
+ * this function is *only* called when we take it away.
+ *
+ * Thus, this function is only called when we want *all* blocks to look good,
+ * so it *always* return success.
+ */
+static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ return 0;
+}
+
+static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+ if (chip->ecc.strength > 0 && chip->ecc.size > 0)
+ return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
+ chip->ecc.strength, chip->ecc.size);
+
+ if (nand_info->use_minimum_ecc ||
+ mxs_nand_calc_ecc_layout(geo, mtd)) {
+ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+
+ return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
+ chip->ecc_strength_ds, chip->ecc_step_ds);
+ }
+
+ return 0;
+}
+
+/*
+ * At this point, the physical NAND Flash chips have been identified and
+ * counted, so we know the physical geometry. This enables us to make some
+ * important configuration decisions.
+ *
+ * The return value of this function propagates directly back to this driver's
+ * board_nand_init(). Anything other than zero will cause this driver to
+ * tear everything down and declare failure.
+ */
+int mxs_nand_setup_ecc(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
+ struct mxs_bch_regs *bch_regs = nand_info->bch_regs;
+ uint32_t tmp;
+ int ret;
+
+ ret = mxs_nand_set_geometry(mtd, geo);
+ if (ret)
+ return ret;
+
+ mxs_nand_calc_mark_offset(geo, mtd->writesize);
+
+ /* Configure BCH and set NFC geometry */
+ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
+
+ /* Configure layout 0 */
+ tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+ tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
+ tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET;
+ tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= (geo->gf_len == 14 ? 1 : 0) <<
+ BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
+ writel(tmp, &bch_regs->hw_bch_flash0layout0);
+
+ tmp = (mtd->writesize + mtd->oobsize)
+ << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
+ tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET;
+ tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= (geo->gf_len == 14 ? 1 : 0) <<
+ BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
+ writel(tmp, &bch_regs->hw_bch_flash0layout1);
+
+ /* Set *all* chip selects to use layout 0 */
+ writel(0, &bch_regs->hw_bch_layoutselect);
+
+ /* Enable BCH complete interrupt */
+ writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set);
+
+ /* Hook some operations at the MTD level. */
+ if (mtd->_read_oob != mxs_nand_hook_read_oob) {
+ nand_info->hooked_read_oob = mtd->_read_oob;
+ mtd->_read_oob = mxs_nand_hook_read_oob;
+ }
+
+ if (mtd->_write_oob != mxs_nand_hook_write_oob) {
+ nand_info->hooked_write_oob = mtd->_write_oob;
+ mtd->_write_oob = mxs_nand_hook_write_oob;
+ }
+
+ if (mtd->_block_markbad != mxs_nand_hook_block_markbad) {
+ nand_info->hooked_block_markbad = mtd->_block_markbad;
+ mtd->_block_markbad = mxs_nand_hook_block_markbad;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate DMA buffers
+ */
+int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)
+{
+ uint8_t *buf;
+ const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE;
+
+ nand_info->data_buf_size = roundup(size, MXS_DMA_ALIGNMENT);
+
+ /* DMA buffers */
+ buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size);
+ if (!buf) {
+ printf("MXS NAND: Error allocating DMA buffers\n");
+ return -ENOMEM;
+ }
+
+ memset(buf, 0, nand_info->data_buf_size);
+
+ nand_info->data_buf = buf;
+ nand_info->oob_buf = buf + NAND_MAX_PAGESIZE;
+ /* Command buffers */
+ nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT,
+ MXS_NAND_COMMAND_BUFFER_SIZE);
+ if (!nand_info->cmd_buf) {
+ free(buf);
+ printf("MXS NAND: Error allocating command buffers\n");
+ return -ENOMEM;
+ }
+ memset(nand_info->cmd_buf, 0, MXS_NAND_COMMAND_BUFFER_SIZE);
+ nand_info->cmd_queue_len = 0;
+
+ return 0;
+}
+
+/*
+ * Initializes the NFC hardware.
+ */
+int mxs_nand_init_dma(struct mxs_nand_info *info)
+{
+ int i = 0, j, ret = 0;
+
+ info->desc = malloc(sizeof(struct mxs_dma_desc *) *
+ MXS_NAND_DMA_DESCRIPTOR_COUNT);
+ if (!info->desc) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ /* Allocate the DMA descriptors. */
+ for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) {
+ info->desc[i] = mxs_dma_desc_alloc();
+ if (!info->desc[i]) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+ }
+
+ /* Init the DMA controller. */
+ mxs_dma_init();
+ for (j = MXS_DMA_CHANNEL_AHB_APBH_GPMI0;
+ j <= MXS_DMA_CHANNEL_AHB_APBH_GPMI7; j++) {
+ ret = mxs_dma_init_channel(j);
+ if (ret)
+ goto err3;
+ }
+
+ /* Reset the GPMI block. */
+ mxs_reset_block(&info->gpmi_regs->hw_gpmi_ctrl0_reg);
+ mxs_reset_block(&info->bch_regs->hw_bch_ctrl_reg);
+
+ /*
+ * Choose NAND mode, set IRQ polarity, disable write protection and
+ * select BCH ECC.
+ */
+ clrsetbits_le32(&info->gpmi_regs->hw_gpmi_ctrl1,
+ GPMI_CTRL1_GPMI_MODE,
+ GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET |
+ GPMI_CTRL1_BCH_MODE);
+
+ return 0;
+
+err3:
+ for (--j; j >= MXS_DMA_CHANNEL_AHB_APBH_GPMI0; j--)
+ mxs_dma_release(j);
+err2:
+ for (--i; i >= 0; i--)
+ mxs_dma_desc_free(info->desc[i]);
+ free(info->desc);
+err1:
+ if (ret == -ENOMEM)
+ printf("MXS NAND: Unable to allocate DMA descriptors\n");
+ return ret;
+}
+
+int mxs_nand_init_spl(struct nand_chip *nand)
+{
+ struct mxs_nand_info *nand_info;
+ int err;
+
+ nand_info = malloc(sizeof(struct mxs_nand_info));
+ if (!nand_info) {
+ printf("MXS NAND: Failed to allocate private data\n");
+ return -ENOMEM;
+ }
+ memset(nand_info, 0, sizeof(struct mxs_nand_info));
+
+ nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
+ nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+ err = mxs_nand_alloc_buffers(nand_info);
+ if (err)
+ return err;
+
+ err = mxs_nand_init_dma(nand_info);
+ if (err)
+ return err;
+
+ nand_set_controller_data(nand, nand_info);
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->cmd_ctrl = mxs_nand_cmd_ctrl;
+ nand->dev_ready = mxs_nand_device_ready;
+ nand->select_chip = mxs_nand_select_chip;
+
+ nand->read_byte = mxs_nand_read_byte;
+ nand->read_buf = mxs_nand_read_buf;
+
+ nand->ecc.read_page = mxs_nand_ecc_read_page;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.bytes = 9;
+ nand->ecc.size = 512;
+ nand->ecc.strength = 8;
+
+ return 0;
+}
+
+int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ int err;
+
+ nand = &nand_info->chip;
+ mtd = nand_to_mtd(nand);
+ err = mxs_nand_alloc_buffers(nand_info);
+ if (err)
+ return err;
+
+ err = mxs_nand_init_dma(nand_info);
+ if (err)
+ goto err_free_buffers;
+
+ memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout));
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+#endif
+
+ nand_set_controller_data(nand, nand_info);
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ if (nand_info->dev)
+ nand->flash_node = dev_of_offset(nand_info->dev);
+
+ nand->cmd_ctrl = mxs_nand_cmd_ctrl;
+
+ nand->dev_ready = mxs_nand_device_ready;
+ nand->select_chip = mxs_nand_select_chip;
+ nand->block_bad = mxs_nand_block_bad;
+
+ nand->read_byte = mxs_nand_read_byte;
+
+ nand->read_buf = mxs_nand_read_buf;
+ nand->write_buf = mxs_nand_write_buf;
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL))
+ goto err_free_buffers;
+
+ if (mxs_nand_setup_ecc(mtd))
+ goto err_free_buffers;
+
+ nand->ecc.read_page = mxs_nand_ecc_read_page;
+ nand->ecc.write_page = mxs_nand_ecc_write_page;
+ nand->ecc.read_oob = mxs_nand_ecc_read_oob;
+ nand->ecc.write_oob = mxs_nand_ecc_write_oob;
+
+ nand->ecc.layout = &fake_ecc_layout;
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size;
+ nand->ecc.strength = nand_info->bch_geometry.ecc_strength;
+
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto err_free_buffers;
+
+ err = nand_register(0, mtd);
+ if (err)
+ goto err_free_buffers;
+
+ return 0;
+
+err_free_buffers:
+ free(nand_info->data_buf);
+ free(nand_info->cmd_buf);
+
+ return err;
+}
+
+#ifndef CONFIG_NAND_MXS_DT
+void board_nand_init(void)
+{
+ struct mxs_nand_info *nand_info;
+
+ nand_info = malloc(sizeof(struct mxs_nand_info));
+ if (!nand_info) {
+ printf("MXS NAND: Failed to allocate private data\n");
+ return;
+ }
+ memset(nand_info, 0, sizeof(struct mxs_nand_info));
+
+ nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
+ nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+
+ /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */
+ if (is_mx6sx() || is_mx7())
+ nand_info->max_ecc_strength_supported = 62;
+ else
+ nand_info->max_ecc_strength_supported = 40;
+
+#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC
+ nand_info->use_minimum_ecc = true;
+#endif
+
+ if (mxs_nand_init_ctrl(nand_info) < 0)
+ goto err;
+
+ return;
+
+err:
+ free(nand_info);
+}
+#endif
diff --git a/drivers/mtd/nand/raw/mxs_nand.h b/drivers/mtd/nand/raw/mxs_nand.h
new file mode 100644
index 0000000000..4bd65cded9
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxs_nand.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NXP GPMI NAND flash driver
+ *
+ * Copyright (C) 2018 Toradex
+ * Authors:
+ * Stefan Agner <stefan.agner@toradex.com>
+ */
+
+#include <linux/mtd/mtd.h>
+#include <asm/cache.h>
+#include <nand.h>
+#include <asm/mach-imx/dma.h>
+
+/**
+ * @gf_len: The length of Galois Field. (e.g., 13 or 14)
+ * @ecc_strength: A number that describes the strength of the ECC
+ * algorithm.
+ * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
+ * the first chunk in the page includes both data and
+ * metadata, so it's a bit larger than this value.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @block_mark_byte_offset: The byte offset in the ECC-based page view at
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
+ */
+struct bch_geometry {
+ unsigned int gf_len;
+ unsigned int ecc_strength;
+ unsigned int ecc_chunk_size;
+ unsigned int ecc_chunk_count;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
+};
+
+struct mxs_nand_info {
+ struct nand_chip chip;
+ struct udevice *dev;
+ unsigned int max_ecc_strength_supported;
+ bool use_minimum_ecc;
+ int cur_chip;
+
+ uint32_t cmd_queue_len;
+ uint32_t data_buf_size;
+ struct bch_geometry bch_geometry;
+
+ uint8_t *cmd_buf;
+ uint8_t *data_buf;
+ uint8_t *oob_buf;
+
+ uint8_t marking_block_bad;
+ uint8_t raw_oob_mode;
+
+ struct mxs_gpmi_regs *gpmi_regs;
+ struct mxs_bch_regs *bch_regs;
+
+ /* Functions with altered behaviour */
+ int (*hooked_read_oob)(struct mtd_info *mtd,
+ loff_t from, struct mtd_oob_ops *ops);
+ int (*hooked_write_oob)(struct mtd_info *mtd,
+ loff_t to, struct mtd_oob_ops *ops);
+ int (*hooked_block_markbad)(struct mtd_info *mtd,
+ loff_t ofs);
+
+ /* DMA descriptors */
+ struct mxs_dma_desc **desc;
+ uint32_t desc_index;
+};
+
+int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
+int mxs_nand_init_spl(struct nand_chip *nand);
+int mxs_nand_setup_ecc(struct mtd_info *mtd);
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c
new file mode 100644
index 0000000000..44dec5dedf
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxs_nand_dt.c
@@ -0,0 +1,94 @@
+/*
+ * NXP GPMI NAND flash driver (DT initialization)
+ *
+ * Copyright (C) 2018 Toradex
+ * Authors:
+ * Stefan Agner <stefan.agner@toradex.com>
+ *
+ * Based on denali_dt.c
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+
+#include "mxs_nand.h"
+
+struct mxs_nand_dt_data {
+ unsigned int max_ecc_strength_supported;
+};
+
+static const struct mxs_nand_dt_data mxs_nand_imx6q_data = {
+ .max_ecc_strength_supported = 40,
+};
+
+static const struct mxs_nand_dt_data mxs_nand_imx7d_data = {
+ .max_ecc_strength_supported = 62,
+};
+
+static const struct udevice_id mxs_nand_dt_ids[] = {
+ {
+ .compatible = "fsl,imx6q-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx6q_data,
+ },
+ {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx7d_data,
+ },
+ { /* sentinel */ }
+};
+
+static int mxs_nand_dt_probe(struct udevice *dev)
+{
+ struct mxs_nand_info *info = dev_get_priv(dev);
+ const struct mxs_nand_dt_data *data;
+ struct resource res;
+ int ret;
+
+ data = (void *)dev_get_driver_data(dev);
+ if (data)
+ info->max_ecc_strength_supported = data->max_ecc_strength_supported;
+
+ info->dev = dev;
+
+ ret = dev_read_resource_byname(dev, "gpmi-nand", &res);
+ if (ret)
+ return ret;
+
+ info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res));
+
+
+ ret = dev_read_resource_byname(dev, "bch", &res);
+ if (ret)
+ return ret;
+
+ info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res));
+
+ info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
+
+ return mxs_nand_init_ctrl(info);
+}
+
+U_BOOT_DRIVER(mxs_nand_dt) = {
+ .name = "mxs-nand-dt",
+ .id = UCLASS_MTD,
+ .of_match = mxs_nand_dt_ids,
+ .probe = mxs_nand_dt_probe,
+ .priv_auto_alloc_size = sizeof(struct mxs_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_GET_DRIVER(mxs_nand_dt),
+ &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize MXS NAND controller. (error %d)\n",
+ ret);
+}
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
new file mode 100644
index 0000000000..2d7bbe83cc
--- /dev/null
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014 Gateworks Corporation
+ * Author: Tim Harvey <tharvey@gateworks.com>
+ */
+#include <common.h>
+#include <nand.h>
+#include <malloc.h>
+#include "mxs_nand.h"
+
+static struct mtd_info *mtd;
+static struct nand_chip nand_chip;
+
+static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 timeo, time_start;
+
+ /* write out the command to the device */
+ chip->cmd_ctrl(mtd, command, NAND_CLE);
+
+ /* Serially input address */
+ if (column != -1) {
+ chip->cmd_ctrl(mtd, column, NAND_ALE);
+ chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
+ chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
+ /* One more address cycle for devices > 128MiB */
+ if (chip->chipsize > (128 << 20))
+ chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
+
+ if (command == NAND_CMD_READ0) {
+ chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
+ }
+
+ /* wait for nand ready */
+ ndelay(100);
+ timeo = (CONFIG_SYS_HZ * 20) / 1000;
+ time_start = get_timer(0);
+ while (get_timer(time_start) < timeo) {
+ if (chip->dev_ready(mtd))
+ break;
+ }
+}
+
+#if defined (CONFIG_SPL_NAND_IDENT)
+
+/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
+static int mxs_flash_full_ident(struct mtd_info *mtd)
+{
+ int nand_maf_id, nand_dev_id;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_flash_dev *type;
+
+ type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL);
+
+ if (IS_ERR(type)) {
+ chip->select_chip(mtd, -1);
+ return PTR_ERR(type);
+ }
+
+ return 0;
+}
+
+#else
+
+/* Trying to detect the NAND flash using ONFi only */
+static int mxs_flash_onfi_ident(struct mtd_info *mtd)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int i;
+ u8 mfg_id, dev_id;
+ u8 id_data[8];
+ struct nand_onfi_params *p = &chip->onfi_params;
+
+ /* Reset the chip */
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Send the command for reading device ID */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+ mfg_id = chip->read_byte(mtd);
+ dev_id = chip->read_byte(mtd);
+
+ /* Try again to make sure */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ for (i = 0; i < 8; i++)
+ id_data[i] = chip->read_byte(mtd);
+ if (id_data[0] != mfg_id || id_data[1] != dev_id) {
+ printf("second ID read did not match");
+ return -1;
+ }
+ debug("0x%02x:0x%02x ", mfg_id, dev_id);
+
+ /* read ONFI */
+ chip->onfi_version = 0;
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+ if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+ chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') {
+ return -2;
+ }
+
+ /* we have ONFI, probe it */
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+ chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+ mtd->name = p->model;
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+ mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+ chip->chipsize = le32_to_cpu(p->blocks_per_lun);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ /* Calculate the address shift from the page size */
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
+ /* Convert chipsize to number of pages per chip -1 */
+ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+ chip->badblockbits = 8;
+
+ debug("erasesize=%d (>>%d)\n", mtd->erasesize, chip->phys_erase_shift);
+ debug("writesize=%d (>>%d)\n", mtd->writesize, chip->page_shift);
+ debug("oobsize=%d\n", mtd->oobsize);
+ debug("chipsize=%lld\n", chip->chipsize);
+
+ return 0;
+}
+
+#endif /* CONFIG_SPL_NAND_IDENT */
+
+static int mxs_flash_ident(struct mtd_info *mtd)
+{
+ int ret;
+#if defined (CONFIG_SPL_NAND_IDENT)
+ ret = mxs_flash_full_ident(mtd);
+#else
+ ret = mxs_flash_onfi_ident(mtd);
+#endif
+ return ret;
+}
+
+static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+ ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
+ if (ret < 0) {
+ printf("read_page failed %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned int block = offs >> chip->phys_erase_shift;
+ unsigned int page = offs >> chip->page_shift;
+
+ debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
+ page);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ memset(chip->oob_poi, 0, mtd->oobsize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return chip->oob_poi[0] != 0xff;
+}
+
+/* setup mtd and nand structs and init mxs_nand driver */
+static int mxs_nand_init(void)
+{
+ /* return if already initalized */
+ if (nand_chip.numchips)
+ return 0;
+
+ /* init mxs nand driver */
+ mxs_nand_init_spl(&nand_chip);
+ mtd = nand_to_mtd(&nand_chip);
+ /* set mtd functions */
+ nand_chip.cmdfunc = mxs_nand_command;
+ nand_chip.numchips = 1;
+
+ /* identify flash device */
+ if (mxs_flash_ident(mtd)) {
+ printf("Failed to identify\n");
+ return -1;
+ }
+
+ /* allocate and initialize buffers */
+ nand_chip.buffers = memalign(ARCH_DMA_MINALIGN,
+ sizeof(*nand_chip.buffers));
+ nand_chip.oob_poi = nand_chip.buffers->databuf + mtd->writesize;
+ /* setup flash layout (does not scan as we override that) */
+ mtd->size = nand_chip.chipsize;
+ nand_chip.scan_bbt(mtd);
+
+ return 0;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
+{
+ struct nand_chip *chip;
+ unsigned int page;
+ unsigned int nand_page_per_block;
+ unsigned int sz = 0;
+
+ if (mxs_nand_init())
+ return -ENODEV;
+ chip = mtd_to_nand(mtd);
+ page = offs >> chip->page_shift;
+ nand_page_per_block = mtd->erasesize / mtd->writesize;
+
+ debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page);
+
+ size = roundup(size, mtd->writesize);
+ while (sz < size) {
+ if (mxs_read_page_ecc(mtd, buf, page) < 0)
+ return -1;
+ sz += mtd->writesize;
+ offs += mtd->writesize;
+ page++;
+ buf += mtd->writesize;
+
+ /*
+ * Check if we have crossed a block boundary, and if so
+ * check for bad block.
+ */
+ if (!(page % nand_page_per_block)) {
+ /*
+ * Yes, new block. See if this block is good. If not,
+ * loop until we find a good block.
+ */
+ while (is_badblock(mtd, offs, 1)) {
+ page = page + nand_page_per_block;
+ /* Check i we've reached the end of flash. */
+ if (page >= mtd->size >> chip->page_shift)
+ return -ENOMEM;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int nand_default_bbt(struct mtd_info *mtd)
+{
+ return 0;
+}
+
+void nand_init(void)
+{
+}
+
+void nand_deselect(void)
+{
+}
+
diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c
new file mode 100644
index 0000000000..bca51ffbf2
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2005
+ * 2N Telekomunikace, a.s. <www.2n.cz>
+ * Ladislav Michl <michl@2n.cz>
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <errno.h>
+#include <linux/mtd/concat.h>
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+int nand_curr_device = -1;
+
+static struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
+
+#ifndef CONFIG_SYS_NAND_SELF_INIT
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
+#endif
+
+static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
+
+static unsigned long total_nand_size; /* in kiB */
+
+struct mtd_info *get_nand_dev_by_index(int dev)
+{
+ if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev] ||
+ !nand_info[dev]->name)
+ return NULL;
+
+ return nand_info[dev];
+}
+
+int nand_mtd_to_devnum(struct mtd_info *mtd)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
+ if (mtd && get_nand_dev_by_index(i) == mtd)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+/* Register an initialized NAND mtd device with the U-Boot NAND command. */
+int nand_register(int devnum, struct mtd_info *mtd)
+{
+ if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE)
+ return -EINVAL;
+
+ nand_info[devnum] = mtd;
+
+ sprintf(dev_name[devnum], "nand%d", devnum);
+ mtd->name = dev_name[devnum];
+
+#ifdef CONFIG_MTD_DEVICE
+ /*
+ * Add MTD device so that we can reference it later
+ * via the mtdcore infrastructure (e.g. ubi).
+ */
+ add_mtd_device(mtd);
+#endif
+
+ total_nand_size += mtd->size / 1024;
+
+ if (nand_curr_device == -1)
+ nand_curr_device = devnum;
+
+ return 0;
+}
+
+#ifndef CONFIG_SYS_NAND_SELF_INIT
+static void nand_init_chip(int i)
+{
+ struct nand_chip *nand = &nand_chip[i];
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ ulong base_addr = base_address[i];
+ int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
+
+ if (maxchips < 1)
+ maxchips = 1;
+
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
+
+ if (board_nand_init(nand))
+ return;
+
+ if (nand_scan(mtd, maxchips))
+ return;
+
+ nand_register(i, mtd);
+}
+#endif
+
+#ifdef CONFIG_MTD_CONCAT
+static void create_mtd_concat(void)
+{
+ struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE];
+ int nand_devices_found = 0;
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
+ struct mtd_info *mtd = get_nand_dev_by_index(i);
+ if (mtd != NULL) {
+ nand_info_list[nand_devices_found] = mtd;
+ nand_devices_found++;
+ }
+ }
+ if (nand_devices_found > 1) {
+ struct mtd_info *mtd;
+ char c_mtd_name[16];
+
+ /*
+ * We detected multiple devices. Concatenate them together.
+ */
+ sprintf(c_mtd_name, "nand%d", nand_devices_found);
+ mtd = mtd_concat_create(nand_info_list, nand_devices_found,
+ c_mtd_name);
+
+ if (mtd == NULL)
+ return;
+
+ nand_register(nand_devices_found, mtd);
+ }
+
+ return;
+}
+#else
+static void create_mtd_concat(void)
+{
+}
+#endif
+
+unsigned long nand_size(void)
+{
+ return total_nand_size;
+}
+
+void nand_init(void)
+{
+ static int initialized;
+
+ /*
+ * Avoid initializing NAND Flash multiple times,
+ * otherwise it will calculate a wrong total size.
+ */
+ if (initialized)
+ return;
+ initialized = 1;
+
+#ifdef CONFIG_SYS_NAND_SELF_INIT
+ board_nand_init();
+#else
+ int i;
+
+ for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+ nand_init_chip(i);
+#endif
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+ /*
+ * Select the chip in the board/cpu specific driver
+ */
+ board_nand_select_device(mtd_to_nand(get_nand_dev_by_index(nand_curr_device)),
+ nand_curr_device);
+#endif
+
+ create_mtd_concat();
+}
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
new file mode 100644
index 0000000000..92daebe120
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -0,0 +1,4619 @@
+/*
+ * Overview:
+ * This is the generic MTD driver for NAND flash devices. It should be
+ * capable of working with almost all NAND chips currently available.
+ *
+ * Additional technical information is available on
+ * http://www.linux-mtd.infradead.org/doc/nand.html
+ *
+ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002-2006 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Credits:
+ * David Woodhouse for adding multichip support
+ *
+ * Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ * rework for 2K page size chips
+ *
+ * TODO:
+ * Enable cached programming for 2k page size chips
+ * Check, if mtd->ecctype should be set to MTD_ECC_HW
+ * if we have HW ECC support.
+ * BBT table is not serialized, has to be fixed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <common.h>
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+#include <fdtdec.h>
+#endif
+#include <malloc.h>
+#include <watchdog.h>
+#include <linux/err.h>
+#include <linux/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand_bch.h>
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+#include <asm/io.h>
+#include <linux/errno.h>
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_ecclayout nand_oob_8 = {
+ .eccbytes = 3,
+ .eccpos = {0, 1, 2},
+ .oobfree = {
+ {.offset = 3,
+ .length = 2},
+ {.offset = 6,
+ .length = 2} }
+};
+
+static struct nand_ecclayout nand_oob_16 = {
+ .eccbytes = 6,
+ .eccpos = {0, 1, 2, 3, 6, 7},
+ .oobfree = {
+ {.offset = 8,
+ . length = 8} }
+};
+
+static struct nand_ecclayout nand_oob_64 = {
+ .eccbytes = 24,
+ .eccpos = {
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+ {.offset = 2,
+ .length = 38} }
+};
+
+static struct nand_ecclayout nand_oob_128 = {
+ .eccbytes = 48,
+ .eccpos = {
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127},
+ .oobfree = {
+ {.offset = 2,
+ .length = 78} }
+};
+
+static int nand_get_device(struct mtd_info *mtd, int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+/*
+ * For devices which display every fart in the system on a separate LED. Is
+ * compiled away when LED support is disabled.
+ */
+DEFINE_LED_TRIGGER(nand_led_trigger);
+
+static int check_offs_len(struct mtd_info *mtd,
+ loff_t ofs, uint64_t len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+
+ /* Start address must align on block boundary */
+ if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) {
+ pr_debug("%s: unaligned address\n", __func__);
+ ret = -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (len & ((1ULL << chip->phys_erase_shift) - 1)) {
+ pr_debug("%s: length not block aligned\n", __func__);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * nand_release_device - [GENERIC] release chip
+ * @mtd: MTD device structure
+ *
+ * Release chip lock and wake up anyone waiting on the device.
+ */
+static void nand_release_device(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* De-select the NAND device */
+ chip->select_chip(mtd, -1);
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 8bit buswidth
+ */
+uint8_t nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readb(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth with endianness conversion.
+ *
+ */
+static uint8_t nand_read_byte16(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd: MTD device structure
+ *
+ * Default read function for 16bit buswidth without endianness conversion.
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return readw(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd: MTD device structure
+ * @chipnr: chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ switch (chipnr) {
+ case -1:
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+ break;
+ case 0:
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write single byte to chip
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0]
+ */
+static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ chip->write_buf(mtd, &byte, 1);
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0] on a 16-bit wide chip.
+ */
+static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint16_t word = byte;
+
+ /*
+ * It's not entirely clear what should happen to I/O[15:8] when writing
+ * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads:
+ *
+ * When the host supports a 16-bit bus width, only data is
+ * transferred at the 16-bit width. All address and command line
+ * transfers shall use only the lower 8-bits of the data bus. During
+ * command transfers, the host may place any value on the upper
+ * 8-bits of the data bus. During address transfers, the host shall
+ * set the upper 8-bits of the data bus to 00h.
+ *
+ * One user of the write_byte callback is nand_onfi_set_features. The
+ * four parameters are specified to be written to I/O[7:0], but this is
+ * neither an address nor a command transfer. Let's assume a 0 on the
+ * upper I/O lines is OK.
+ */
+ chip->write_buf(mtd, (uint8_t *)&word, 2);
+}
+
+static void iowrite8_rep(void *addr, const uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ writeb(buf[i], addr);
+}
+static void ioread8_rep(void *addr, uint8_t *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = readb(addr);
+}
+
+static void ioread16_rep(void *addr, void *buf, int len)
+{
+ int i;
+ u16 *p = (u16 *) buf;
+
+ for (i = 0; i < len; i++)
+ p[i] = readw(addr);
+}
+
+static void iowrite16_rep(void *addr, void *buf, int len)
+{
+ int i;
+ u16 *p = (u16 *) buf;
+
+ for (i = 0; i < len; i++)
+ writew(p[i], addr);
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 8bit buswidth.
+ */
+void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ iowrite8_rep(chip->IO_ADDR_W, buf, len);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * Default read function for 8bit buswidth.
+ */
+void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ ioread8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ * Default write function for 16bit buswidth.
+ */
+void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ * Default read function for 16bit buswidth.
+ */
+void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 *p = (u16 *) buf;
+
+ ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * Check, if the block is bad.
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ int page, res = 0, i = 0;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 bad;
+
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ ofs += mtd->erasesize - mtd->writesize;
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+ do {
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB,
+ chip->badblockpos & 0xFE, page);
+ bad = cpu_to_le16(chip->read_word(mtd));
+ if (chip->badblockpos & 0x1)
+ bad >>= 8;
+ else
+ bad &= 0xFF;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
+ page);
+ bad = chip->read_byte(mtd);
+ }
+
+ if (likely(chip->badblockbits == 8))
+ res = bad != 0xFF;
+ else
+ res = hweight8(bad) < chip->badblockbits;
+ ofs += mtd->writesize;
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ i++;
+ } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+
+ return res;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This is the default implementation, which can be overridden by a hardware
+ * specific driver. It provides the details for writing a bad block marker to a
+ * block.
+ */
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ uint8_t buf[2] = { 0, 0 };
+ int ret = 0, res, i = 0;
+
+ memset(&ops, 0, sizeof(ops));
+ ops.oobbuf = buf;
+ ops.ooboffs = chip->badblockpos;
+ if (chip->options & NAND_BUSWIDTH_16) {
+ ops.ooboffs &= ~0x01;
+ ops.len = ops.ooblen = 2;
+ } else {
+ ops.len = ops.ooblen = 1;
+ }
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ /* Write to first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+ ofs += mtd->erasesize - mtd->writesize;
+ do {
+ res = nand_do_write_oob(mtd, ofs, &ops);
+ if (!ret)
+ ret = res;
+
+ i++;
+ ofs += mtd->writesize;
+ } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+ return ret;
+}
+
+/**
+ * nand_block_markbad_lowlevel - mark a block bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * This function performs the generic NAND bad block marking steps (i.e., bad
+ * block table(s) and/or marker(s)). We only allow the hardware driver to
+ * specify how to write bad block markers to OOB (chip->block_markbad).
+ *
+ * We try operations in the following order:
+ * (1) erase the affected block, to allow OOB marker to be written cleanly
+ * (2) write bad block marker to OOB area of affected block (unless flag
+ * NAND_BBT_NO_OOB_BBM is present)
+ * (3) update the BBT
+ * Note that we retain the first error encountered in (2) or (3), finish the
+ * procedures, and dump the error in the end.
+*/
+static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int res, ret = 0;
+
+ if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
+ struct erase_info einfo;
+
+ /* Attempt erase before marking OOB */
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.mtd = mtd;
+ einfo.addr = ofs;
+ einfo.len = 1ULL << chip->phys_erase_shift;
+ nand_erase_nand(mtd, &einfo, 0);
+
+ /* Write bad block marker to OOB */
+ nand_get_device(mtd, FL_WRITING);
+ ret = chip->block_markbad(mtd, ofs);
+ nand_release_device(mtd);
+ }
+
+ /* Mark block bad in BBT */
+ if (chip->bbt) {
+ res = nand_markbad_bbt(mtd, ofs);
+ if (!ret)
+ ret = res;
+ }
+
+ if (!ret)
+ mtd->ecc_stats.badblocks++;
+
+ return ret;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd: MTD device structure
+ *
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
+ */
+static int nand_check_wp(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Broken xD cards report WP despite being writable */
+ if (chip->options & NAND_BROKEN_XD)
+ return 0;
+
+ /* Check the WP bit */
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+}
+
+/**
+ * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ *
+ * Check if the block is marked as reserved.
+ */
+static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!chip->bbt)
+ return 0;
+ /* Return info from the table */
+ return nand_isreserved_bbt(mtd, ofs);
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @allowbbt: 1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (!(chip->options & NAND_SKIP_BBTSCAN) &&
+ !(chip->options & NAND_BBT_SCANNED)) {
+ chip->options |= NAND_BBT_SCANNED;
+ chip->scan_bbt(mtd);
+ }
+
+ if (!chip->bbt)
+ return chip->block_bad(mtd, ofs);
+
+ /* Return info from the table */
+ return nand_isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
+ * nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
+ * @mtd: MTD device structure
+ *
+ * Wait for the ready pin after a command, and warn if a timeout occurs.
+ */
+void nand_wait_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 timeo = (CONFIG_SYS_HZ * 400) / 1000;
+ u32 time_start;
+
+ time_start = get_timer(0);
+ /* Wait until command is processed or timeout occurs */
+ while (get_timer(time_start) < timeo) {
+ if (chip->dev_ready)
+ if (chip->dev_ready(mtd))
+ break;
+ }
+
+ if (!chip->dev_ready(mtd))
+ pr_warn("timeout while waiting for chip to become ready\n");
+}
+EXPORT_SYMBOL_GPL(nand_wait_ready);
+
+/**
+ * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
+ * @mtd: MTD device structure
+ * @timeo: Timeout in ms
+ *
+ * Wait for status ready (i.e. command done) or timeout.
+ */
+static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 time_start;
+
+ timeo = (CONFIG_SYS_HZ * timeo) / 1000;
+ time_start = get_timer(0);
+ while (get_timer(time_start) < timeo) {
+ if ((chip->read_byte(mtd) & NAND_STATUS_READY))
+ break;
+ WATCHDOG_RESET();
+ }
+};
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page devices
+ * (512 Bytes per page).
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+
+ /* Write out the command to the device */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->writesize) {
+ /* OOB area */
+ column -= mtd->writesize;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ chip->cmd_ctrl(mtd, readcmd, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ }
+ chip->cmd_ctrl(mtd, command, ctrl);
+
+ /* Address cycle, when necessary */
+ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+ if (chip->options & NAND_ROW_ADDR_3)
+ chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Program and erase have their own busy handlers status and sequential
+ * in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_READID:
+ case NAND_CMD_SET_FEATURES:
+ return;
+
+ case NAND_CMD_RESET:
+ if (chip->dev_ready)
+ break;
+ udelay(chip->chip_delay);
+ chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd,
+ NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
+ nand_wait_status_ready(mtd, 250);
+ return;
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!chip->dev_ready) {
+ udelay(chip->chip_delay);
+ return;
+ }
+ }
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(100);
+
+ nand_wait_ready(mtd);
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd: MTD device structure
+ * @command: the command to be sent
+ * @column: the column address for this command, -1 if none
+ * @page_addr: the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page
+ * devices. We don't have the separate regions as we have in the small page
+ * devices. We must emulate NAND_CMD_READOOB to keep the code compatible.
+ */
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Command latch cycle */
+ chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+ if (column != -1 || page_addr != -1) {
+ int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ chip->cmd_ctrl(mtd, column >> 8, ctrl);
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ chip->cmd_ctrl(mtd, page_addr >> 8,
+ NAND_NCE | NAND_ALE);
+ if (chip->options & NAND_ROW_ADDR_3)
+ chip->cmd_ctrl(mtd, page_addr >> 16,
+ NAND_NCE | NAND_ALE);
+ }
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Program and erase have their own busy handlers status, sequential
+ * in and status need no delay.
+ */
+ switch (command) {
+
+ case NAND_CMD_CACHEDPROG:
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_RNDIN:
+ case NAND_CMD_STATUS:
+ case NAND_CMD_READID:
+ case NAND_CMD_SET_FEATURES:
+ return;
+
+ case NAND_CMD_RESET:
+ if (chip->dev_ready)
+ break;
+ udelay(chip->chip_delay);
+ chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+ /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
+ nand_wait_status_ready(mtd, 250);
+ return;
+
+ case NAND_CMD_RNDOUT:
+ /* No ready / busy check necessary */
+ chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+ return;
+
+ case NAND_CMD_READ0:
+ chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay.
+ */
+ if (!chip->dev_ready) {
+ udelay(chip->chip_delay);
+ return;
+ }
+ }
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(100);
+
+ nand_wait_ready(mtd);
+}
+
+/**
+ * panic_nand_get_device - [GENERIC] Get chip for selected access
+ * @chip: the nand chip descriptor
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Used when in panic, no locks are taken.
+ */
+static void panic_nand_get_device(struct nand_chip *chip,
+ struct mtd_info *mtd, int new_state)
+{
+ /* Hardware controller shared among independent devices */
+ chip->controller->active = chip;
+ chip->state = new_state;
+}
+
+/**
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @mtd: MTD device structure
+ * @new_state: the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int
+nand_get_device(struct mtd_info *mtd, int new_state)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ chip->state = new_state;
+ return 0;
+}
+
+/**
+ * panic_nand_wait - [GENERIC] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ * @timeo: timeout
+ *
+ * Wait for command done. This is a helper function for nand_wait used when
+ * we are in interrupt context. May happen when in panic and trying to write
+ * an oops through mtdoops.
+ */
+static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
+ unsigned long timeo)
+{
+ int i;
+ for (i = 0; i < timeo; i++) {
+ if (chip->dev_ready) {
+ if (chip->dev_ready(mtd))
+ break;
+ } else {
+ if (chip->read_byte(mtd) & NAND_STATUS_READY)
+ break;
+ }
+ mdelay(1);
+ }
+}
+
+/**
+ * nand_wait - [DEFAULT] wait until the command is done
+ * @mtd: MTD device structure
+ * @chip: NAND chip structure
+ *
+ * Wait for command done. This applies to erase and program only.
+ */
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ int status;
+ unsigned long timeo = 400;
+
+ led_trigger_event(nand_led_trigger, LED_FULL);
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in any
+ * case on any machine.
+ */
+ ndelay(100);
+
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+
+ u32 timer = (CONFIG_SYS_HZ * timeo) / 1000;
+ u32 time_start;
+
+ time_start = get_timer(0);
+ while (get_timer(time_start) < timer) {
+ if (chip->dev_ready) {
+ if (chip->dev_ready(mtd))
+ break;
+ } else {
+ if (chip->read_byte(mtd) & NAND_STATUS_READY)
+ break;
+ }
+ }
+ led_trigger_event(nand_led_trigger, LED_OFF);
+
+ status = (int)chip->read_byte(mtd);
+ /* This can happen if in case of timeout or buggy dev_ready */
+ WARN_ON(!(status & NAND_STATUS_READY));
+ return status;
+}
+
+/**
+ * nand_reset_data_interface - Reset data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Reset the Data interface and timings to ONFI mode 0.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_data_interface *conf;
+ int ret;
+
+ if (!chip->setup_data_interface)
+ return 0;
+
+ /*
+ * The ONFI specification says:
+ * "
+ * To transition from NV-DDR or NV-DDR2 to the SDR data
+ * interface, the host shall use the Reset (FFh) command
+ * using SDR timing mode 0. A device in any timing mode is
+ * required to recognize Reset (FFh) command issued in SDR
+ * timing mode 0.
+ * "
+ *
+ * Configure the data interface in SDR mode and set the
+ * timings to timing mode 0.
+ */
+
+ conf = nand_get_default_data_interface();
+ ret = chip->setup_data_interface(mtd, chipnr, conf);
+ if (ret)
+ pr_err("Failed to configure data interface to SDR timing mode 0\n");
+
+ return ret;
+}
+
+/**
+ * nand_setup_data_interface - Setup the best data interface and timings
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Find and configure the best data interface and NAND timings supported by
+ * the chip and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ if (!chip->setup_data_interface || !chip->data_interface)
+ return 0;
+
+ /*
+ * Ensure the timing mode has been changed on the chip side
+ * before changing timings on the controller side.
+ */
+ if (chip->onfi_version) {
+ u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
+ chip->onfi_timing_mode_default,
+ };
+
+ ret = chip->onfi_set_features(mtd, chip,
+ ONFI_FEATURE_ADDR_TIMING_MODE,
+ tmode_param);
+ if (ret)
+ goto err;
+ }
+
+ ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface);
+err:
+ return ret;
+}
+
+/**
+ * nand_init_data_interface - find the best data interface and timings
+ * @chip: The NAND chip
+ *
+ * Find the best data interface and NAND timings supported by the chip
+ * and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table. After this
+ * function nand_chip->data_interface is initialized with the best timing mode
+ * available.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_init_data_interface(struct nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int modes, mode, ret;
+
+ if (!chip->setup_data_interface)
+ return 0;
+
+ /*
+ * First try to identify the best timings from ONFI parameters and
+ * if the NAND does not support ONFI, fallback to the default ONFI
+ * timing mode.
+ */
+ modes = onfi_get_async_timing_mode(chip);
+ if (modes == ONFI_TIMING_MODE_UNKNOWN) {
+ if (!chip->onfi_timing_mode_default)
+ return 0;
+
+ modes = GENMASK(chip->onfi_timing_mode_default, 0);
+ }
+
+ chip->data_interface = kzalloc(sizeof(*chip->data_interface),
+ GFP_KERNEL);
+ if (!chip->data_interface)
+ return -ENOMEM;
+
+ for (mode = fls(modes) - 1; mode >= 0; mode--) {
+ ret = onfi_init_data_interface(chip, chip->data_interface,
+ NAND_SDR_IFACE, mode);
+ if (ret)
+ continue;
+
+ /* Pass -1 to only */
+ ret = chip->setup_data_interface(mtd,
+ NAND_DATA_IFACE_CHECK_ONLY,
+ chip->data_interface);
+ if (!ret) {
+ chip->onfi_timing_mode_default = mode;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void __maybe_unused nand_release_data_interface(struct nand_chip *chip)
+{
+ kfree(chip->data_interface);
+}
+
+/**
+ * nand_reset - Reset and initialize a NAND device
+ * @chip: The NAND chip
+ * @chipnr: Internal die id
+ *
+ * Returns 0 for success or negative error code otherwise
+ */
+int nand_reset(struct nand_chip *chip, int chipnr)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int ret;
+
+ ret = nand_reset_data_interface(chip, chipnr);
+ if (ret)
+ return ret;
+
+ /*
+ * The CS line has to be released before we can apply the new NAND
+ * interface settings, hence this weird ->select_chip() dance.
+ */
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->select_chip(mtd, -1);
+
+ chip->select_chip(mtd, chipnr);
+ ret = nand_setup_data_interface(chip, chipnr);
+ chip->select_chip(mtd, -1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data
+ * @buf: buffer to test
+ * @len: buffer length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a buffer contains only 0xff, which means the underlying region
+ * has been erased and is ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region is not erased.
+ * Note: The logic of this function has been extracted from the memweight
+ * implementation, except that nand_check_erased_buf function exit before
+ * testing the whole buffer if the number of bitflips exceed the
+ * bitflips_threshold value.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold.
+ */
+static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
+{
+ const unsigned char *bitmap = buf;
+ int bitflips = 0;
+ int weight;
+
+ for (; len && ((uintptr_t)bitmap) % sizeof(long);
+ len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ for (; len >= 4; len -= 4, bitmap += 4) {
+ weight = hweight32(*((u32 *)bitmap));
+ bitflips += 32 - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ for (; len > 0; len--, bitmap++) {
+ weight = hweight8(*bitmap);
+ bitflips += BITS_PER_BYTE - weight;
+ if (unlikely(bitflips > bitflips_threshold))
+ return -EBADMSG;
+ }
+
+ return bitflips;
+}
+
+/**
+ * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only
+ * 0xff data
+ * @data: data buffer to test
+ * @datalen: data length
+ * @ecc: ECC buffer
+ * @ecclen: ECC length
+ * @extraoob: extra OOB buffer
+ * @extraooblen: extra OOB length
+ * @bitflips_threshold: maximum number of bitflips
+ *
+ * Check if a data buffer and its associated ECC and OOB data contains only
+ * 0xff pattern, which means the underlying region has been erased and is
+ * ready to be programmed.
+ * The bitflips_threshold specify the maximum number of bitflips before
+ * considering the region as not erased.
+ *
+ * Note:
+ * 1/ ECC algorithms are working on pre-defined block sizes which are usually
+ * different from the NAND page size. When fixing bitflips, ECC engines will
+ * report the number of errors per chunk, and the NAND core infrastructure
+ * expect you to return the maximum number of bitflips for the whole page.
+ * This is why you should always use this function on a single chunk and
+ * not on the whole page. After checking each chunk you should update your
+ * max_bitflips value accordingly.
+ * 2/ When checking for bitflips in erased pages you should not only check
+ * the payload data but also their associated ECC data, because a user might
+ * have programmed almost all bits to 1 but a few. In this case, we
+ * shouldn't consider the chunk as erased, and checking ECC bytes prevent
+ * this case.
+ * 3/ The extraoob argument is optional, and should be used if some of your OOB
+ * data are protected by the ECC engine.
+ * It could also be used if you support subpages and want to attach some
+ * extra OOB data to an ECC chunk.
+ *
+ * Returns a positive number of bitflips less than or equal to
+ * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the
+ * threshold. In case of success, the passed buffers are filled with 0xff.
+ */
+int nand_check_erased_ecc_chunk(void *data, int datalen,
+ void *ecc, int ecclen,
+ void *extraoob, int extraooblen,
+ int bitflips_threshold)
+{
+ int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0;
+
+ data_bitflips = nand_check_erased_buf(data, datalen,
+ bitflips_threshold);
+ if (data_bitflips < 0)
+ return data_bitflips;
+
+ bitflips_threshold -= data_bitflips;
+
+ ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold);
+ if (ecc_bitflips < 0)
+ return ecc_bitflips;
+
+ bitflips_threshold -= ecc_bitflips;
+
+ extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen,
+ bitflips_threshold);
+ if (extraoob_bitflips < 0)
+ return extraoob_bitflips;
+
+ if (data_bitflips)
+ memset(data, 0xff, datalen);
+
+ if (ecc_bitflips)
+ memset(ecc, 0xff, ecclen);
+
+ if (extraoob_bitflips)
+ memset(extraoob, 0xff, extraooblen);
+
+ return data_bitflips + ecc_bitflips + extraoob_bitflips;
+}
+EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
+
+/**
+ * nand_read_page_raw - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ chip->read_buf(mtd, buf, mtd->writesize);
+ if (oob_required)
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 0;
+}
+
+/**
+ * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size;
+
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ chip->read_buf(mtd, buf, eccsize);
+ buf += eccsize;
+
+ if (chip->ecc.prepad) {
+ chip->read_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->read_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size)
+ chip->read_buf(mtd, oob, size);
+
+ return 0;
+}
+
+/**
+ * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ */
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned int max_bitflips = 0;
+
+ chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ * @page: page number to read
+ */
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+ int page)
+{
+ int start_step, end_step, num_steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p;
+ int data_col_addr, i, gaps = 0;
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+ int index;
+ unsigned int max_bitflips = 0;
+
+ /* Column address within the page aligned to ECC size (256bytes) */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+ index = start_step * chip->ecc.bytes;
+
+ /* Data size aligned to ECC ecc.size */
+ datafrag_len = num_steps * chip->ecc.size;
+ eccfrag_len = num_steps * chip->ecc.bytes;
+
+ data_col_addr = start_step * chip->ecc.size;
+ /* If we read not a page aligned data */
+ if (data_col_addr != 0)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+
+ p = bufpoi + data_col_addr;
+ chip->read_buf(mtd, p, datafrag_len);
+
+ /* Calculate ECC */
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+ chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+ /*
+ * The performance is faster if we position offsets according to
+ * ecc.pos. Let's make sure that there are no gaps in ECC positions.
+ */
+ for (i = 0; i < eccfrag_len - 1; i++) {
+ if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
+ gaps = 1;
+ break;
+ }
+ }
+ if (gaps) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ } else {
+ /*
+ * Send the command to read the particular ECC bytes take care
+ * about buswidth alignment in read_buf.
+ */
+ aligned_pos = eccpos[index] & ~(busw - 1);
+ aligned_len = eccfrag_len;
+ if (eccpos[index] & (busw - 1))
+ aligned_len++;
+ if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
+ aligned_len++;
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + aligned_pos, -1);
+ chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+ }
+
+ for (i = 0; i < eccfrag_len; i++)
+ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
+
+ p = bufpoi + data_col_addr;
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p,
+ &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ &chip->buffers->ecccode[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Not for syndrome calculating ECC controllers which need a special oob layout.
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned int max_bitflips = 0;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first. For this
+ * ECC mode, the write_page method is re-used from ECC_HW. These methods
+ * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
+ * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
+ * the data area, by overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ unsigned int max_bitflips = 0;
+
+ /* Read the OOB area first */
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+ return max_bitflips;
+}
+
+/**
+ * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
+ */
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ unsigned int max_bitflips = 0;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+
+ if (chip->ecc.prepad) {
+ chip->read_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+ chip->read_buf(mtd, oob, eccbytes);
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->read_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ oob - eccpadbytes,
+ eccpadbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->read_buf(mtd, oob, i);
+
+ return max_bitflips;
+}
+
+/**
+ * nand_transfer_oob - [INTERN] Transfer oob to client buffer
+ * @chip: nand chip structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+ struct mtd_oob_ops *ops, size_t len)
+{
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB: {
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ uint32_t boffs = 0, roffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for (; free->length && len; free++, len -= bytes) {
+ /* Read request not from offset 0? */
+ if (unlikely(roffs)) {
+ if (roffs >= free->length) {
+ roffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + roffs;
+ bytes = min_t(size_t, len,
+ (free->length - roffs));
+ roffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(oob, chip->oob_poi + boffs, bytes);
+ oob += bytes;
+ }
+ return oob;
+ }
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/**
+ * nand_setup_read_retry - [INTERN] Set the READ RETRY mode
+ * @mtd: MTD device structure
+ * @retry_mode: the retry mode to use
+ *
+ * Some vendors supply a special command to shift the Vt threshold, to be used
+ * when there are too many bitflips in a page (i.e., ECC error). After setting
+ * a new threshold, the host should retry reading the page.
+ */
+static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("setting READ RETRY mode %d\n", retry_mode);
+
+ if (retry_mode >= chip->read_retries)
+ return -EINVAL;
+
+ if (!chip->setup_read_retry)
+ return -EOPNOTSUPP;
+
+ return chip->setup_read_retry(mtd, retry_mode);
+}
+
+/**
+ * nand_do_read_ops - [INTERN] Read data with ECC
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob ops structure
+ *
+ * Internal function. Called with chip held.
+ */
+static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, realpage, col, bytes, aligned, oob_required;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret = 0;
+ uint32_t readlen = ops->len;
+ uint32_t oobreadlen = ops->ooblen;
+ uint32_t max_oobsize = mtd_oobavail(mtd, ops);
+
+ uint8_t *bufpoi, *oob, *buf;
+ int use_bufpoi;
+ unsigned int max_bitflips = 0;
+ int retry_mode = 0;
+ bool ecc_fail = false;
+
+ chipnr = (int)(from >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ col = (int)(from & (mtd->writesize - 1));
+
+ buf = ops->datbuf;
+ oob = ops->oobbuf;
+ oob_required = oob ? 1 : 0;
+
+ while (1) {
+ unsigned int ecc_failures = mtd->ecc_stats.failed;
+
+ WATCHDOG_RESET();
+ bytes = min(mtd->writesize - col, readlen);
+ aligned = (bytes == mtd->writesize);
+
+ if (!aligned)
+ use_bufpoi = 1;
+ else if (chip->options & NAND_USE_BOUNCE_BUFFER)
+ use_bufpoi = !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bufpoi = 0;
+
+ /* Is the current page in the buffer? */
+ if (realpage != chip->pagebuf || oob) {
+ bufpoi = use_bufpoi ? chip->buffers->databuf : buf;
+
+ if (use_bufpoi && aligned)
+ pr_debug("%s: using read bounce buffer for buf@%p\n",
+ __func__, buf);
+
+read_retry:
+ if (nand_standard_page_accessors(&chip->ecc))
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+
+ /*
+ * Now read the page into the buffer. Absent an error,
+ * the read methods return max bitflips per ecc step.
+ */
+ if (unlikely(ops->mode == MTD_OPS_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
+ oob_required,
+ page);
+ else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
+ !oob)
+ ret = chip->ecc.read_subpage(mtd, chip,
+ col, bytes, bufpoi,
+ page);
+ else
+ ret = chip->ecc.read_page(mtd, chip, bufpoi,
+ oob_required, page);
+ if (ret < 0) {
+ if (use_bufpoi)
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+ break;
+ }
+
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+
+ /* Transfer not aligned data */
+ if (use_bufpoi) {
+ if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
+ !(mtd->ecc_stats.failed - ecc_failures) &&
+ (ops->mode != MTD_OPS_RAW)) {
+ chip->pagebuf = realpage;
+ chip->pagebuf_bitflips = ret;
+ } else {
+ /* Invalidate page cache */
+ chip->pagebuf = -1;
+ }
+ memcpy(buf, chip->buffers->databuf + col, bytes);
+ }
+
+ if (unlikely(oob)) {
+ int toread = min(oobreadlen, max_oobsize);
+
+ if (toread) {
+ oob = nand_transfer_oob(chip,
+ oob, ops, toread);
+ oobreadlen -= toread;
+ }
+ }
+
+ if (chip->options & NAND_NEED_READRDY) {
+ /* Apply delay or wait for ready/busy pin */
+ if (!chip->dev_ready)
+ udelay(chip->chip_delay);
+ else
+ nand_wait_ready(mtd);
+ }
+
+ if (mtd->ecc_stats.failed - ecc_failures) {
+ if (retry_mode + 1 < chip->read_retries) {
+ retry_mode++;
+ ret = nand_setup_read_retry(mtd,
+ retry_mode);
+ if (ret < 0)
+ break;
+
+ /* Reset failures; retry */
+ mtd->ecc_stats.failed = ecc_failures;
+ goto read_retry;
+ } else {
+ /* No more retry modes; real failure */
+ ecc_fail = true;
+ }
+ }
+
+ buf += bytes;
+ } else {
+ memcpy(buf, chip->buffers->databuf + col, bytes);
+ buf += bytes;
+ max_bitflips = max_t(unsigned int, max_bitflips,
+ chip->pagebuf_bitflips);
+ }
+
+ readlen -= bytes;
+
+ /* Reset to retry mode 0 */
+ if (retry_mode) {
+ ret = nand_setup_read_retry(mtd, 0);
+ if (ret < 0)
+ break;
+ retry_mode = 0;
+ }
+
+ if (!readlen)
+ break;
+
+ /* For subsequent reads align to page boundary */
+ col = 0;
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+ chip->select_chip(mtd, -1);
+
+ ops->retlen = ops->len - (size_t) readlen;
+ if (oob)
+ ops->oobretlen = ops->ooblen - oobreadlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (ecc_fail)
+ return -EBADMSG;
+
+ return max_bitflips;
+}
+
+/**
+ * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 0;
+}
+
+/**
+ * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
+ * with syndromes
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int length = mtd->oobsize;
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size;
+ uint8_t *bufpoi = chip->oob_poi;
+ int i, toread, sndrnd = 0, pos;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
+ for (i = 0; i < chip->ecc.steps; i++) {
+ if (sndrnd) {
+ pos = eccsize + i * (eccsize + chunk);
+ if (mtd->writesize > 512)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+ else
+ chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
+ } else
+ sndrnd = 1;
+ toread = min_t(int, length, chunk);
+ chip->read_buf(mtd, bufpoi, toread);
+ bufpoi += toread;
+ length -= toread;
+ }
+ if (length > 0)
+ chip->read_buf(mtd, bufpoi, length);
+
+ return 0;
+}
+
+/**
+ * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ const uint8_t *buf = chip->oob_poi;
+ int length = mtd->oobsize;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+ chip->write_buf(mtd, buf, length);
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/**
+ * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
+ * with syndrome - only for large page flash
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+static int nand_write_oob_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ /*
+ * data-ecc-data-ecc ... ecc-oob
+ * or
+ * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+ */
+ if (!chip->ecc.prepad && !chip->ecc.postpad) {
+ pos = steps * (eccsize + chunk);
+ steps = 0;
+ } else
+ pos = eccsize;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+ for (i = 0; i < steps; i++) {
+ if (sndcmd) {
+ if (mtd->writesize <= 512) {
+ uint32_t fill = 0xFFFFFFFF;
+
+ len = eccsize;
+ while (len > 0) {
+ int num = min_t(int, len, 4);
+ chip->write_buf(mtd, (uint8_t *)&fill,
+ num);
+ len -= num;
+ }
+ } else {
+ pos = eccsize + i * (eccsize + chunk);
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
+ }
+ } else
+ sndcmd = 1;
+ len = min_t(int, length, chunk);
+ chip->write_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ length -= len;
+ }
+ if (length > 0)
+ chip->write_buf(mtd, bufpoi, length);
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/**
+ * nand_do_read_oob - [INTERN] NAND read out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ *
+ * NAND read out-of-band data from the spare area.
+ */
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int page, realpage, chipnr;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_ecc_stats stats;
+ int readlen = ops->ooblen;
+ int len;
+ uint8_t *buf = ops->oobbuf;
+ int ret = 0;
+
+ pr_debug("%s: from = 0x%08Lx, len = %i\n",
+ __func__, (unsigned long long)from, readlen);
+
+ stats = mtd->ecc_stats;
+
+ len = mtd_oobavail(mtd, ops);
+
+ if (unlikely(ops->ooboffs >= len)) {
+ pr_debug("%s: attempt to start read outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
+ ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+ (from >> chip->page_shift)) * len)) {
+ pr_debug("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ chipnr = (int)(from >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ realpage = (int)(from >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ while (1) {
+ WATCHDOG_RESET();
+
+ if (ops->mode == MTD_OPS_RAW)
+ ret = chip->ecc.read_oob_raw(mtd, chip, page);
+ else
+ ret = chip->ecc.read_oob(mtd, chip, page);
+
+ if (ret < 0)
+ break;
+
+ len = min(len, readlen);
+ buf = nand_transfer_oob(chip, buf, ops, len);
+
+ if (chip->options & NAND_NEED_READRDY) {
+ /* Apply delay or wait for ready/busy pin */
+ if (!chip->dev_ready)
+ udelay(chip->chip_delay);
+ else
+ nand_wait_ready(mtd);
+ }
+
+ readlen -= len;
+ if (!readlen)
+ break;
+
+ /* Increment page address */
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+ chip->select_chip(mtd, -1);
+
+ ops->oobretlen = ops->ooblen - readlen;
+
+ if (ret < 0)
+ return ret;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ *
+ * NAND read data and/or out-of-band data.
+ */
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOTSUPP;
+
+ ops->retlen = 0;
+
+ /* Do not allow reads past end of device */
+ if (ops->datbuf && (from + ops->len) > mtd->size) {
+ pr_debug("%s: attempt to read beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ nand_get_device(mtd, FL_READING);
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = nand_do_read_oob(mtd, from, ops);
+ else
+ ret = nand_do_read_ops(mtd, from, ops);
+
+out:
+ nand_release_device(mtd);
+ return ret;
+}
+
+
+/**
+ * nand_write_page_raw - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * Not for syndrome calculating ECC controllers, which use a special oob layout.
+ */
+static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ if (oob_required)
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+/**
+ * nand_write_page_raw_syndrome - [INTERN] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *oob = chip->oob_poi;
+ int steps, size;
+
+ for (steps = chip->ecc.steps; steps > 0; steps--) {
+ chip->write_buf(mtd, buf, eccsize);
+ buf += eccsize;
+
+ if (chip->ecc.prepad) {
+ chip->write_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->write_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ size = mtd->oobsize - (oob - chip->oob_poi);
+ if (size)
+ chip->write_buf(mtd, oob, size);
+
+ return 0;
+}
+/**
+ * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ /* Software ECC calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
+}
+
+/**
+ * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->write_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+
+/**
+ * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @offset: column address of subpage within the page
+ * @data_len: data length
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ */
+static int nand_write_subpage_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint32_t offset,
+ uint32_t data_len, const uint8_t *buf,
+ int oob_required, int page)
+{
+ uint8_t *oob_buf = chip->oob_poi;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ int ecc_size = chip->ecc.size;
+ int ecc_bytes = chip->ecc.bytes;
+ int ecc_steps = chip->ecc.steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t start_step = offset / ecc_size;
+ uint32_t end_step = (offset + data_len - 1) / ecc_size;
+ int oob_bytes = mtd->oobsize / ecc_steps;
+ int step, i;
+
+ for (step = 0; step < ecc_steps; step++) {
+ /* configure controller for WRITE access */
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+
+ /* write data (untouched subpages already masked by 0xFF) */
+ chip->write_buf(mtd, buf, ecc_size);
+
+ /* mask ECC of un-touched subpages by padding 0xFF */
+ if ((step < start_step) || (step > end_step))
+ memset(ecc_calc, 0xff, ecc_bytes);
+ else
+ chip->ecc.calculate(mtd, buf, ecc_calc);
+
+ /* mask OOB of un-touched subpages by padding 0xFF */
+ /* if oob_required, preserve OOB metadata of written subpage */
+ if (!oob_required || (step < start_step) || (step > end_step))
+ memset(oob_buf, 0xff, oob_bytes);
+
+ buf += ecc_size;
+ ecc_calc += ecc_bytes;
+ oob_buf += oob_bytes;
+ }
+
+ /* copy calculated ECC for whole page to chip->buffer->oob */
+ /* this include masked-value(0xFF) for unwritten subpages */
+ ecc_calc = chip->buffers->ecccalc;
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ /* write OOB buffer to NAND device */
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+
+/**
+ * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ *
+ * The hw generator calculates the error syndrome automatically. Therefore we
+ * need a special oob layout and handling.
+ */
+static int nand_write_page_syndrome(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ const uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+ chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+ chip->write_buf(mtd, p, eccsize);
+
+ if (chip->ecc.prepad) {
+ chip->write_buf(mtd, oob, chip->ecc.prepad);
+ oob += chip->ecc.prepad;
+ }
+
+ chip->ecc.calculate(mtd, p, oob);
+ chip->write_buf(mtd, oob, eccbytes);
+ oob += eccbytes;
+
+ if (chip->ecc.postpad) {
+ chip->write_buf(mtd, oob, chip->ecc.postpad);
+ oob += chip->ecc.postpad;
+ }
+ }
+
+ /* Calculate remaining oob bytes */
+ i = mtd->oobsize - (oob - chip->oob_poi);
+ if (i)
+ chip->write_buf(mtd, oob, i);
+
+ return 0;
+}
+
+/**
+ * nand_write_page - [REPLACEABLE] write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @offset: address offset within the page
+ * @data_len: length of actual data to be written
+ * @buf: the data to write
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to write
+ * @raw: use _raw version of write_page
+ */
+static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, int data_len, const uint8_t *buf,
+ int oob_required, int page, int raw)
+{
+ int status, subpage;
+
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+ chip->ecc.write_subpage)
+ subpage = offset || (data_len < mtd->writesize);
+ else
+ subpage = 0;
+
+ if (nand_standard_page_accessors(&chip->ecc))
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (unlikely(raw))
+ status = chip->ecc.write_page_raw(mtd, chip, buf,
+ oob_required, page);
+ else if (subpage)
+ status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
+ buf, oob_required, page);
+ else
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required,
+ page);
+
+ if (status < 0)
+ return status;
+
+ if (nand_standard_page_accessors(&chip->ecc)) {
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @mtd: MTD device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
+ struct mtd_oob_ops *ops)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /*
+ * Initialise to all 0xFF, to avoid the possibility of left over OOB
+ * data from a previous OOB read.
+ */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+ switch (ops->mode) {
+
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_RAW:
+ memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+ return oob + len;
+
+ case MTD_OPS_AUTO_OOB: {
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ uint32_t boffs = 0, woffs = ops->ooboffs;
+ size_t bytes = 0;
+
+ for (; free->length && len; free++, len -= bytes) {
+ /* Write request not from offset 0? */
+ if (unlikely(woffs)) {
+ if (woffs >= free->length) {
+ woffs -= free->length;
+ continue;
+ }
+ boffs = free->offset + woffs;
+ bytes = min_t(size_t, len,
+ (free->length - woffs));
+ woffs = 0;
+ } else {
+ bytes = min_t(size_t, len, free->length);
+ boffs = free->offset;
+ }
+ memcpy(chip->oob_poi + boffs, oob, bytes);
+ oob += bytes;
+ }
+ return oob;
+ }
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+
+/**
+ * nand_do_write_ops - [INTERN] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ *
+ * NAND write with ECC.
+ */
+static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, realpage, page, column;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint32_t writelen = ops->len;
+
+ uint32_t oobwritelen = ops->ooblen;
+ uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
+
+ uint8_t *oob = ops->oobbuf;
+ uint8_t *buf = ops->datbuf;
+ int ret;
+ int oob_required = oob ? 1 : 0;
+
+ ops->retlen = 0;
+ if (!writelen)
+ return 0;
+
+ /* Reject writes, which are not page aligned */
+ if (NOTALIGNED(to)) {
+ pr_notice("%s: attempt to write non page aligned data\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ column = to & (mtd->writesize - 1);
+
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ ret = -EIO;
+ goto err_out;
+ }
+
+ realpage = (int)(to >> chip->page_shift);
+ page = realpage & chip->pagemask;
+
+ /* Invalidate the page cache, when we write to the cached page */
+ if (to <= ((loff_t)chip->pagebuf << chip->page_shift) &&
+ ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len))
+ chip->pagebuf = -1;
+
+ /* Don't allow multipage oob writes with offset */
+ if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ while (1) {
+ int bytes = mtd->writesize;
+ uint8_t *wbuf = buf;
+ int use_bufpoi;
+ int part_pagewr = (column || writelen < mtd->writesize);
+
+ if (part_pagewr)
+ use_bufpoi = 1;
+ else if (chip->options & NAND_USE_BOUNCE_BUFFER)
+ use_bufpoi = !IS_ALIGNED((unsigned long)buf,
+ chip->buf_align);
+ else
+ use_bufpoi = 0;
+
+ WATCHDOG_RESET();
+ /* Partial page write?, or need to use bounce buffer */
+ if (use_bufpoi) {
+ pr_debug("%s: using write bounce buffer for buf@%p\n",
+ __func__, buf);
+ if (part_pagewr)
+ bytes = min_t(int, bytes - column, writelen);
+ chip->pagebuf = -1;
+ memset(chip->buffers->databuf, 0xff, mtd->writesize);
+ memcpy(&chip->buffers->databuf[column], buf, bytes);
+ wbuf = chip->buffers->databuf;
+ }
+
+ if (unlikely(oob)) {
+ size_t len = min(oobwritelen, oobmaxlen);
+ oob = nand_fill_oob(mtd, oob, len, ops);
+ oobwritelen -= len;
+ } else {
+ /* We still need to erase leftover OOB data */
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ }
+ ret = chip->write_page(mtd, chip, column, bytes, wbuf,
+ oob_required, page,
+ (ops->mode == MTD_OPS_RAW));
+ if (ret)
+ break;
+
+ writelen -= bytes;
+ if (!writelen)
+ break;
+
+ column = 0;
+ buf += bytes;
+ realpage++;
+
+ page = realpage & chip->pagemask;
+ /* Check, if we cross a chip boundary */
+ if (!page) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+
+ ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
+
+err_out:
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+
+/**
+ * panic_nand_write - [MTD Interface] NAND write with ECC
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ *
+ * NAND write with ECC. Used when performing writes in interrupt context, this
+ * may for example be called by mtdoops when writing an oops while in panic.
+ */
+static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const uint8_t *buf)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ int ret;
+
+ /* Wait for the device to get ready */
+ panic_nand_wait(mtd, chip, 400);
+
+ /* Grab the device */
+ panic_nand_get_device(chip, mtd, FL_WRITING);
+
+ memset(&ops, 0, sizeof(ops));
+ ops.len = len;
+ ops.datbuf = (uint8_t *)buf;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ ret = nand_do_write_ops(mtd, to, &ops);
+
+ *retlen = ops.retlen;
+ return ret;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * NAND write out-of-band.
+ */
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int chipnr, page, status, len;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ pr_debug("%s: to = 0x%08x, len = %i\n",
+ __func__, (unsigned int)to, (int)ops->ooblen);
+
+ len = mtd_oobavail(mtd, ops);
+
+ /* Do not allow write past end of page */
+ if ((ops->ooboffs + ops->ooblen) > len) {
+ pr_debug("%s: attempt to write past end of page\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (unlikely(ops->ooboffs >= len)) {
+ pr_debug("%s: attempt to start write outside oob\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Do not allow write past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + ops->ooblen >
+ ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * len)) {
+ pr_debug("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ chipnr = (int)(to >> chip->chip_shift);
+
+ /*
+ * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+ * of my DiskOnChip 2000 test units) will clear the whole data page too
+ * if we don't do this. I have no clue why, but I seem to have 'fixed'
+ * it in the doc2000 driver in August 1999. dwmw2.
+ */
+ nand_reset(chip, chipnr);
+
+ chip->select_chip(mtd, chipnr);
+
+ /* Shift to get page */
+ page = (int)(to >> chip->page_shift);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ chip->select_chip(mtd, -1);
+ return -EROFS;
+ }
+
+ /* Invalidate the page cache, if we write to the cached page */
+ if (page == chip->pagebuf)
+ chip->pagebuf = -1;
+
+ nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
+
+ if (ops->mode == MTD_OPS_RAW)
+ status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask);
+ else
+ status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+
+ chip->select_chip(mtd, -1);
+
+ if (status)
+ return status;
+
+ ops->oobretlen = ops->ooblen;
+
+ return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret = -ENOTSUPP;
+
+ ops->retlen = 0;
+
+ /* Do not allow writes past end of device */
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
+ pr_debug("%s: attempt to write beyond end of device\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ nand_get_device(mtd, FL_WRITING);
+
+ switch (ops->mode) {
+ case MTD_OPS_PLACE_OOB:
+ case MTD_OPS_AUTO_OOB:
+ case MTD_OPS_RAW:
+ break;
+
+ default:
+ goto out;
+ }
+
+ if (!ops->datbuf)
+ ret = nand_do_write_oob(mtd, to, ops);
+ else
+ ret = nand_do_write_ops(mtd, to, ops);
+
+out:
+ nand_release_device(mtd);
+ return ret;
+}
+
+/**
+ * single_erase - [GENERIC] NAND standard block erase command function
+ * @mtd: MTD device structure
+ * @page: the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips. Returns NAND status.
+ */
+static int single_erase(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ /* Send commands to erase a block */
+ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+ chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+
+ return chip->waitfunc(mtd, chip);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ *
+ * Erase one ore more blocks.
+ */
+static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ return nand_erase_nand(mtd, instr, 0);
+}
+
+/**
+ * nand_erase_nand - [INTERN] erase block(s)
+ * @mtd: MTD device structure
+ * @instr: erase instruction
+ * @allowbbt: allow erasing the bbt area
+ *
+ * Erase one ore more blocks.
+ */
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+ int allowbbt)
+{
+ int page, status, pages_per_block, ret, chipnr;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ loff_t len;
+
+ pr_debug("%s: start = 0x%012llx, len = %llu\n",
+ __func__, (unsigned long long)instr->addr,
+ (unsigned long long)instr->len);
+
+ if (check_offs_len(mtd, instr->addr, instr->len))
+ return -EINVAL;
+
+ /* Grab the lock and see if the device is available */
+ nand_get_device(mtd, FL_ERASING);
+
+ /* Shift to get first page */
+ page = (int)(instr->addr >> chip->page_shift);
+ chipnr = (int)(instr->addr >> chip->chip_shift);
+
+ /* Calculate pages in each block */
+ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+ /* Select the NAND device */
+ chip->select_chip(mtd, chipnr);
+
+ /* Check, if it is write protected */
+ if (nand_check_wp(mtd)) {
+ pr_debug("%s: device is write protected!\n",
+ __func__);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /* Loop through the pages */
+ len = instr->len;
+
+ instr->state = MTD_ERASING;
+
+ while (len) {
+ WATCHDOG_RESET();
+
+ /* Check if we have a bad block, we do not erase bad blocks! */
+ if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) <<
+ chip->page_shift, allowbbt)) {
+ pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
+ __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }
+
+ /*
+ * Invalidate the page cache, if we erase the block which
+ * contains the current cached page.
+ */
+ if (page <= chip->pagebuf && chip->pagebuf <
+ (page + pages_per_block))
+ chip->pagebuf = -1;
+
+ status = chip->erase(mtd, page & chip->pagemask);
+
+ /* See if block erase succeeded */
+ if (status & NAND_STATUS_FAIL) {
+ pr_debug("%s: failed erase, page 0x%08x\n",
+ __func__, page);
+ instr->state = MTD_ERASE_FAILED;
+ instr->fail_addr =
+ ((loff_t)page << chip->page_shift);
+ goto erase_exit;
+ }
+
+ /* Increment page address and decrement length */
+ len -= (1ULL << chip->phys_erase_shift);
+ page += pages_per_block;
+
+ /* Check, if we cross a chip boundary */
+ if (len && !(page & chip->pagemask)) {
+ chipnr++;
+ chip->select_chip(mtd, -1);
+ chip->select_chip(mtd, chipnr);
+ }
+ }
+ instr->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+ /* Deselect and wake up anyone waiting on the device */
+ chip->select_chip(mtd, -1);
+ nand_release_device(mtd);
+
+ /* Do call back function */
+ if (!ret)
+ mtd_erase_callback(instr);
+
+ /* Return more or less happy */
+ return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd: MTD device structure
+ *
+ * Sync is actually a wait for chip ready function.
+ */
+static void nand_sync(struct mtd_info *mtd)
+{
+ pr_debug("%s: called\n", __func__);
+
+ /* Grab the lock and see if the device is available */
+ nand_get_device(mtd, FL_SYNCING);
+ /* Release it and go back */
+ nand_release_device(mtd);
+}
+
+/**
+ * nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int chipnr = (int)(offs >> chip->chip_shift);
+ int ret;
+
+ /* Select the NAND device */
+ nand_get_device(mtd, FL_READING);
+ chip->select_chip(mtd, chipnr);
+
+ ret = nand_block_checkbad(mtd, offs, 0);
+
+ chip->select_chip(mtd, -1);
+ nand_release_device(mtd);
+
+ return ret;
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd: MTD device structure
+ * @ofs: offset relative to mtd start
+ */
+static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ int ret;
+
+ ret = nand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return nand_block_markbad_lowlevel(mtd, ofs);
+}
+
+/**
+ * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ */
+static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, uint8_t *subfeature_param)
+{
+ int status;
+ int i;
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+ if (!chip->onfi_version ||
+ !(le16_to_cpu(chip->onfi_params.opt_cmd)
+ & ONFI_OPT_CMD_SET_GET_FEATURES))
+ return -ENOTSUPP;
+#endif
+
+ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ chip->write_byte(mtd, subfeature_param[i]);
+
+ status = chip->waitfunc(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+ return 0;
+}
+
+/**
+ * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ */
+static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+ int addr, uint8_t *subfeature_param)
+{
+ int i;
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+ if (!chip->onfi_version ||
+ !(le16_to_cpu(chip->onfi_params.opt_cmd)
+ & ONFI_OPT_CMD_SET_GET_FEATURES))
+ return -ENOTSUPP;
+#endif
+
+ chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ *subfeature_param++ = chip->read_byte(mtd);
+ return 0;
+}
+
+/* Set default functions */
+static void nand_set_defaults(struct nand_chip *chip, int busw)
+{
+ /* check for proper chip_delay setup, set 20us if not */
+ if (!chip->chip_delay)
+ chip->chip_delay = 20;
+
+ /* check, if a user supplied command function given */
+ if (chip->cmdfunc == NULL)
+ chip->cmdfunc = nand_command;
+
+ /* check, if a user supplied wait function given */
+ if (chip->waitfunc == NULL)
+ chip->waitfunc = nand_wait;
+
+ if (!chip->select_chip)
+ chip->select_chip = nand_select_chip;
+
+ /* set for ONFI nand */
+ if (!chip->onfi_set_features)
+ chip->onfi_set_features = nand_onfi_set_features;
+ if (!chip->onfi_get_features)
+ chip->onfi_get_features = nand_onfi_get_features;
+
+ /* If called twice, pointers that depend on busw may need to be reset */
+ if (!chip->read_byte || chip->read_byte == nand_read_byte)
+ chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+ if (!chip->read_word)
+ chip->read_word = nand_read_word;
+ if (!chip->block_bad)
+ chip->block_bad = nand_block_bad;
+ if (!chip->block_markbad)
+ chip->block_markbad = nand_default_block_markbad;
+ if (!chip->write_buf || chip->write_buf == nand_write_buf)
+ chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+ if (!chip->write_byte || chip->write_byte == nand_write_byte)
+ chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+ if (!chip->read_buf || chip->read_buf == nand_read_buf)
+ chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+ if (!chip->scan_bbt)
+ chip->scan_bbt = nand_default_bbt;
+
+ if (!chip->controller) {
+ chip->controller = &chip->hwcontrol;
+ spin_lock_init(&chip->controller->lock);
+ init_waitqueue_head(&chip->controller->wq);
+ }
+
+ if (!chip->buf_align)
+ chip->buf_align = 1;
+}
+
+/* Sanitize ONFI strings so we can safely print them */
+static void sanitize_string(char *s, size_t len)
+{
+ ssize_t i;
+
+ /* Null terminate */
+ s[len - 1] = 0;
+
+ /* Remove non printable chars */
+ for (i = 0; i < len - 1; i++) {
+ if (s[i] < ' ' || s[i] > 127)
+ s[i] = '?';
+ }
+
+ /* Remove trailing spaces */
+ strim(s);
+}
+
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++ << 8;
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+ }
+
+ return crc;
+}
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+/* Parse the Extended Parameter Page. */
+static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
+ struct nand_chip *chip, struct nand_onfi_params *p)
+{
+ struct onfi_ext_param_page *ep;
+ struct onfi_ext_section *s;
+ struct onfi_ext_ecc_info *ecc;
+ uint8_t *cursor;
+ int ret = -EINVAL;
+ int len;
+ int i;
+
+ len = le16_to_cpu(p->ext_param_page_length) * 16;
+ ep = kmalloc(len, GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ /* Send our own NAND_CMD_PARAM. */
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+
+ /* Use the Change Read Column command to skip the ONFI param pages. */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ sizeof(*p) * p->num_of_param_pages , -1);
+
+ /* Read out the Extended Parameter Page. */
+ chip->read_buf(mtd, (uint8_t *)ep, len);
+ if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2)
+ != le16_to_cpu(ep->crc))) {
+ pr_debug("fail in the CRC.\n");
+ goto ext_out;
+ }
+
+ /*
+ * Check the signature.
+ * Do not strictly follow the ONFI spec, maybe changed in future.
+ */
+ if (strncmp((char *)ep->sig, "EPPS", 4)) {
+ pr_debug("The signature is invalid.\n");
+ goto ext_out;
+ }
+
+ /* find the ECC section. */
+ cursor = (uint8_t *)(ep + 1);
+ for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
+ s = ep->sections + i;
+ if (s->type == ONFI_SECTION_TYPE_2)
+ break;
+ cursor += s->length * 16;
+ }
+ if (i == ONFI_EXT_SECTION_MAX) {
+ pr_debug("We can not find the ECC section.\n");
+ goto ext_out;
+ }
+
+ /* get the info we want. */
+ ecc = (struct onfi_ext_ecc_info *)cursor;
+
+ if (!ecc->codeword_size) {
+ pr_debug("Invalid codeword size\n");
+ goto ext_out;
+ }
+
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ ret = 0;
+
+ext_out:
+ kfree(ep);
+ return ret;
+}
+
+static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
+
+ return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
+ feature);
+}
+
+/*
+ * Configure chip properties from Micron vendor-specific ONFI table
+ */
+static void nand_onfi_detect_micron(struct nand_chip *chip,
+ struct nand_onfi_params *p)
+{
+ struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
+
+ if (le16_to_cpu(p->vendor_revision) < 1)
+ return;
+
+ chip->read_retries = micron->read_retry_options;
+ chip->setup_read_retry = nand_setup_read_retry_micron;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
+ int *busw)
+{
+ struct nand_onfi_params *p = &chip->onfi_params;
+ int i, j;
+ int val;
+
+ /* Try ONFI for unknown chip or LP */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+ if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+ chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+ return 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+ le16_to_cpu(p->crc)) {
+ break;
+ }
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid ONFI parameter page; aborting\n");
+ return 0;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 5))
+ chip->onfi_version = 23;
+ else if (val & (1 << 4))
+ chip->onfi_version = 22;
+ else if (val & (1 << 3))
+ chip->onfi_version = 21;
+ else if (val & (1 << 2))
+ chip->onfi_version = 20;
+ else if (val & (1 << 1))
+ chip->onfi_version = 10;
+
+ if (!chip->onfi_version) {
+ pr_info("unsupported ONFI version: %d\n", val);
+ return 0;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ if (!mtd->name)
+ mtd->name = p->model;
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /*
+ * pages_per_block and blocks_per_lun may not be a power-of-2 size
+ * (don't ask me who thought of this...). MTD assumes that these
+ * dimensions will be power-of-2, so just truncate the remaining area.
+ */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* See erasesize comment */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
+ *busw = NAND_BUSWIDTH_16;
+ else
+ *busw = 0;
+
+ if (p->ecc_bits != 0xff) {
+ chip->ecc_strength_ds = p->ecc_bits;
+ chip->ecc_step_ds = 512;
+ } else if (chip->onfi_version >= 21 &&
+ (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
+
+ /*
+ * The nand_flash_detect_ext_param_page() uses the
+ * Change Read Column command which maybe not supported
+ * by the chip->cmdfunc. So try to update the chip->cmdfunc
+ * now. We do not replace user supplied command function.
+ */
+ if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+ chip->cmdfunc = nand_command_lp;
+
+ /* The Extended Parameter Page is supported since ONFI 2.1. */
+ if (nand_flash_detect_ext_param_page(mtd, chip, p))
+ pr_warn("Failed to detect ONFI extended param page\n");
+ } else {
+ pr_warn("Could not retrieve ONFI ECC requirements\n");
+ }
+
+ if (p->jedec_id == NAND_MFR_MICRON)
+ nand_onfi_detect_micron(chip, p);
+
+ return 1;
+}
+#else
+static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
+ int *busw)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
+ int *busw)
+{
+ struct nand_jedec_params *p = &chip->jedec_params;
+ struct jedec_ecc_info *ecc;
+ int val;
+ int i, j;
+
+ /* Try JEDEC for unknown chip or LP */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
+ if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' ||
+ chip->read_byte(mtd) != 'C')
+ return 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1);
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < sizeof(*p); j++)
+ ((uint8_t *)p)[j] = chip->read_byte(mtd);
+
+ if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
+ le16_to_cpu(p->crc))
+ break;
+ }
+
+ if (i == 3) {
+ pr_err("Could not find valid JEDEC parameter page; aborting\n");
+ return 0;
+ }
+
+ /* Check version */
+ val = le16_to_cpu(p->revision);
+ if (val & (1 << 2))
+ chip->jedec_version = 10;
+ else if (val & (1 << 1))
+ chip->jedec_version = 1; /* vendor specific version */
+
+ if (!chip->jedec_version) {
+ pr_info("unsupported JEDEC version: %d\n", val);
+ return 0;
+ }
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+ if (!mtd->name)
+ mtd->name = p->model;
+
+ mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+ mtd->erasesize *= mtd->writesize;
+
+ mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+ /* Please reference to the comment for nand_flash_detect_onfi. */
+ chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+ chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+ chip->bits_per_cell = p->bits_per_cell;
+
+ if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
+ *busw = NAND_BUSWIDTH_16;
+ else
+ *busw = 0;
+
+ /* ECC info */
+ ecc = &p->ecc_info[0];
+
+ if (ecc->codeword_size >= 9) {
+ chip->ecc_strength_ds = ecc->ecc_bits;
+ chip->ecc_step_ds = 1 << ecc->codeword_size;
+ } else {
+ pr_warn("Invalid codeword size\n");
+ }
+
+ return 1;
+}
+
+/*
+ * nand_id_has_period - Check if an ID string has a given wraparound period
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+ * @period: the period of repitition
+ *
+ * Check if an ID string is repeated within a given sequence of bytes at
+ * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a
+ * period of 3). This is a helper function for nand_id_len(). Returns non-zero
+ * if the repetition has a period of @period; otherwise, returns zero.
+ */
+static int nand_id_has_period(u8 *id_data, int arrlen, int period)
+{
+ int i, j;
+ for (i = 0; i < period; i++)
+ for (j = i + period; j < arrlen; j += period)
+ if (id_data[i] != id_data[j])
+ return 0;
+ return 1;
+}
+
+/*
+ * nand_id_len - Get the length of an ID string returned by CMD_READID
+ * @id_data: the ID string
+ * @arrlen: the length of the @id_data array
+
+ * Returns the length of the ID string, according to known wraparound/trailing
+ * zero patterns. If no pattern exists, returns the length of the array.
+ */
+static int nand_id_len(u8 *id_data, int arrlen)
+{
+ int last_nonzero, period;
+
+ /* Find last non-zero byte */
+ for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--)
+ if (id_data[last_nonzero])
+ break;
+
+ /* All zeros */
+ if (last_nonzero < 0)
+ return 0;
+
+ /* Calculate wraparound period */
+ for (period = 1; period < arrlen; period++)
+ if (nand_id_has_period(id_data, arrlen, period))
+ break;
+
+ /* There's a repeated pattern */
+ if (period < arrlen)
+ return period;
+
+ /* There are trailing zeros */
+ if (last_nonzero < arrlen - 1)
+ return last_nonzero + 1;
+
+ /* No pattern detected */
+ return arrlen;
+}
+
+/* Extract the bits of per cell from the 3rd byte of the extended ID */
+static int nand_get_bits_per_cell(u8 cellinfo)
+{
+ int bits;
+
+ bits = cellinfo & NAND_CI_CELLTYPE_MSK;
+ bits >>= NAND_CI_CELLTYPE_SHIFT;
+ return bits + 1;
+}
+
+/*
+ * Many new NAND share similar device ID codes, which represent the size of the
+ * chip. The rest of the parameters must be decoded according to generic or
+ * manufacturer-specific "extended ID" decoding patterns.
+ */
+static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 id_data[8], int *busw)
+{
+ int extid, id_len;
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ /* The 4th id byte is the important one */
+ extid = id_data[3];
+
+ id_len = nand_id_len(id_data, 8);
+
+ /*
+ * Field definitions are in the following datasheets:
+ * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
+ * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
+ * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
+ *
+ * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
+ * ID to decide what to do.
+ */
+ if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
+ !nand_is_slc(chip) && id_data[5] != 0x00) {
+ /* Calc pagesize */
+ mtd->writesize = 2048 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+ case 1:
+ mtd->oobsize = 128;
+ break;
+ case 2:
+ mtd->oobsize = 218;
+ break;
+ case 3:
+ mtd->oobsize = 400;
+ break;
+ case 4:
+ mtd->oobsize = 436;
+ break;
+ case 5:
+ mtd->oobsize = 512;
+ break;
+ case 6:
+ mtd->oobsize = 640;
+ break;
+ case 7:
+ default: /* Other cases are "reserved" (unknown) */
+ mtd->oobsize = 1024;
+ break;
+ }
+ extid >>= 2;
+ /* Calc blocksize */
+ mtd->erasesize = (128 * 1024) <<
+ (((extid >> 1) & 0x04) | (extid & 0x03));
+ *busw = 0;
+ } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
+ !nand_is_slc(chip)) {
+ unsigned int tmp;
+
+ /* Calc pagesize */
+ mtd->writesize = 2048 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
+ case 0:
+ mtd->oobsize = 128;
+ break;
+ case 1:
+ mtd->oobsize = 224;
+ break;
+ case 2:
+ mtd->oobsize = 448;
+ break;
+ case 3:
+ mtd->oobsize = 64;
+ break;
+ case 4:
+ mtd->oobsize = 32;
+ break;
+ case 5:
+ mtd->oobsize = 16;
+ break;
+ default:
+ mtd->oobsize = 640;
+ break;
+ }
+ extid >>= 2;
+ /* Calc blocksize */
+ tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
+ if (tmp < 0x03)
+ mtd->erasesize = (128 * 1024) << tmp;
+ else if (tmp == 0x03)
+ mtd->erasesize = 768 * 1024;
+ else
+ mtd->erasesize = (64 * 1024) << tmp;
+ *busw = 0;
+ } else {
+ /* Calc pagesize */
+ mtd->writesize = 1024 << (extid & 0x03);
+ extid >>= 2;
+ /* Calc oobsize */
+ mtd->oobsize = (8 << (extid & 0x01)) *
+ (mtd->writesize >> 9);
+ extid >>= 2;
+ /* Calc blocksize. Blocksize is multiples of 64KiB */
+ mtd->erasesize = (64 * 1024) << (extid & 0x03);
+ extid >>= 2;
+ /* Get buswidth information */
+ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+ /*
+ * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
+ * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
+ * follows:
+ * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
+ * 110b -> 24nm
+ * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC
+ */
+ if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
+ nand_is_slc(chip) &&
+ (id_data[5] & 0x7) == 0x6 /* 24nm */ &&
+ !(id_data[4] & 0x80) /* !BENAND */) {
+ mtd->oobsize = 32 * mtd->writesize >> 9;
+ }
+
+ }
+}
+
+/*
+ * Old devices have chip data hardcoded in the device ID table. nand_decode_id
+ * decodes a matching ID table entry and assigns the MTD size parameters for
+ * the chip.
+ */
+static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
+ struct nand_flash_dev *type, u8 id_data[8],
+ int *busw)
+{
+ int maf_id = id_data[0];
+
+ mtd->erasesize = type->erasesize;
+ mtd->writesize = type->pagesize;
+ mtd->oobsize = mtd->writesize / 32;
+ *busw = type->options & NAND_BUSWIDTH_16;
+
+ /* All legacy ID NAND are small-page, SLC */
+ chip->bits_per_cell = 1;
+
+ /*
+ * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+ * some Spansion chips have erasesize that conflicts with size
+ * listed in nand_ids table.
+ * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+ */
+ if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
+ && id_data[6] == 0x00 && id_data[7] == 0x00
+ && mtd->writesize == 512) {
+ mtd->erasesize = 128 * 1024;
+ mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
+ }
+}
+
+/*
+ * Set the bad block marker/indicator (BBM/BBI) patterns according to some
+ * heuristic patterns using various detected parameters (e.g., manufacturer,
+ * page size, cell-type information).
+ */
+static void nand_decode_bbm_options(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 id_data[8])
+{
+ int maf_id = id_data[0];
+
+ /* Set the bad block position */
+ if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
+ chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
+ else
+ chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
+
+ /*
+ * Bad block marker is stored in the last page of each block on Samsung
+ * and Hynix MLC devices; stored in first two pages of each block on
+ * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
+ * AMD/Spansion, and Macronix. All others scan only the first page.
+ */
+ if (!nand_is_slc(chip) &&
+ (maf_id == NAND_MFR_SAMSUNG ||
+ maf_id == NAND_MFR_HYNIX))
+ chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
+ else if ((nand_is_slc(chip) &&
+ (maf_id == NAND_MFR_SAMSUNG ||
+ maf_id == NAND_MFR_HYNIX ||
+ maf_id == NAND_MFR_TOSHIBA ||
+ maf_id == NAND_MFR_AMD ||
+ maf_id == NAND_MFR_MACRONIX)) ||
+ (mtd->writesize == 2048 &&
+ maf_id == NAND_MFR_MICRON))
+ chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+}
+
+static inline bool is_full_id_nand(struct nand_flash_dev *type)
+{
+ return type->id_len;
+}
+
+static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
+ struct nand_flash_dev *type, u8 *id_data, int *busw)
+{
+ if (!strncmp((char *)type->id, (char *)id_data, type->id_len)) {
+ mtd->writesize = type->pagesize;
+ mtd->erasesize = type->erasesize;
+ mtd->oobsize = type->oobsize;
+
+ chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
+ chip->chipsize = (uint64_t)type->chipsize << 20;
+ chip->options |= type->options;
+ chip->ecc_strength_ds = NAND_ECC_STRENGTH(type);
+ chip->ecc_step_ds = NAND_ECC_STEP(type);
+ chip->onfi_timing_mode_default =
+ type->onfi_timing_mode_default;
+
+ *busw = type->options & NAND_BUSWIDTH_16;
+
+ if (!mtd->name)
+ mtd->name = type->name;
+
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported.
+ */
+struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ int *maf_id, int *dev_id,
+ struct nand_flash_dev *type)
+{
+ int busw;
+ int i, maf_idx;
+ u8 id_data[8];
+
+ /*
+ * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+ * after power-up.
+ */
+ nand_reset(chip, 0);
+
+ /* Select the device */
+ chip->select_chip(mtd, 0);
+
+ /* Send the command for reading device ID */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+ *maf_id = chip->read_byte(mtd);
+ *dev_id = chip->read_byte(mtd);
+
+ /*
+ * Try again to make sure, as some systems the bus-hold or other
+ * interface concerns can cause random data which looks like a
+ * possibly credible NAND flash to appear. If the two results do
+ * not match, ignore the device completely.
+ */
+
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read entire ID string */
+ for (i = 0; i < 8; i++)
+ id_data[i] = chip->read_byte(mtd);
+
+ if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
+ pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
+ *maf_id, *dev_id, id_data[0], id_data[1]);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (!type)
+ type = nand_flash_ids;
+
+ for (; type->name != NULL; type++) {
+ if (is_full_id_nand(type)) {
+ if (find_full_id_nand(mtd, chip, type, id_data, &busw))
+ goto ident_done;
+ } else if (*dev_id == type->dev_id) {
+ break;
+ }
+ }
+
+ chip->onfi_version = 0;
+ if (!type->name || !type->pagesize) {
+ /* Check if the chip is ONFI compliant */
+ if (nand_flash_detect_onfi(mtd, chip, &busw))
+ goto ident_done;
+
+ /* Check if the chip is JEDEC compliant */
+ if (nand_flash_detect_jedec(mtd, chip, &busw))
+ goto ident_done;
+ }
+
+ if (!type->name)
+ return ERR_PTR(-ENODEV);
+
+ if (!mtd->name)
+ mtd->name = type->name;
+
+ chip->chipsize = (uint64_t)type->chipsize << 20;
+
+ if (!type->pagesize) {
+ /* Decode parameters from extended ID */
+ nand_decode_ext_id(mtd, chip, id_data, &busw);
+ } else {
+ nand_decode_id(mtd, chip, type, id_data, &busw);
+ }
+ /* Get chip options */
+ chip->options |= type->options;
+
+ /*
+ * Check if chip is not a Samsung device. Do not clear the
+ * options for chips which do not have an extended id.
+ */
+ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+ chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+ident_done:
+
+ /* Try to identify manufacturer */
+ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
+ if (nand_manuf_ids[maf_idx].id == *maf_id)
+ break;
+ }
+
+ if (chip->options & NAND_BUSWIDTH_AUTO) {
+ WARN_ON(chip->options & NAND_BUSWIDTH_16);
+ chip->options |= busw;
+ nand_set_defaults(chip, busw);
+ } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+ /*
+ * Check, if buswidth is correct. Hardware drivers should set
+ * chip correct!
+ */
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ *maf_id, *dev_id);
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
+ pr_warn("bus width %d instead %d bit\n",
+ (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
+ busw ? 16 : 8);
+ return ERR_PTR(-EINVAL);
+ }
+
+ nand_decode_bbm_options(mtd, chip, id_data);
+
+ /* Calculate the address shift from the page size */
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ /* Convert chipsize to number of pages per chip -1 */
+ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+
+ chip->bbt_erase_shift = chip->phys_erase_shift =
+ ffs(mtd->erasesize) - 1;
+ if (chip->chipsize & 0xffffffff)
+ chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+ else {
+ chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
+ chip->chip_shift += 32 - 1;
+ }
+
+ if (chip->chip_shift - chip->page_shift > 16)
+ chip->options |= NAND_ROW_ADDR_3;
+
+ chip->badblockbits = 8;
+ chip->erase = single_erase;
+
+ /* Do not replace user supplied command function! */
+ if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+ chip->cmdfunc = nand_command_lp;
+
+ pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+ *maf_id, *dev_id);
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+ if (chip->onfi_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->onfi_params.model);
+ else if (chip->jedec_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->jedec_params.model);
+ else
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ type->name);
+#else
+ if (chip->jedec_version)
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ chip->jedec_params.model);
+ else
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ type->name);
+
+ pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+ type->name);
+#endif
+
+ pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
+ (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
+ mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
+ return type;
+}
+EXPORT_SYMBOL(nand_get_flash_type);
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+DECLARE_GLOBAL_DATA_PTR;
+
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node)
+{
+ int ret, ecc_mode = -1, ecc_strength, ecc_step;
+ const void *blob = gd->fdt_blob;
+ const char *str;
+
+ ret = fdtdec_get_int(blob, node, "nand-bus-width", -1);
+ if (ret == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ if (fdtdec_get_bool(blob, node, "nand-on-flash-bbt"))
+ chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+ str = fdt_getprop(blob, node, "nand-ecc-mode", NULL);
+ if (str) {
+ if (!strcmp(str, "none"))
+ ecc_mode = NAND_ECC_NONE;
+ else if (!strcmp(str, "soft"))
+ ecc_mode = NAND_ECC_SOFT;
+ else if (!strcmp(str, "hw"))
+ ecc_mode = NAND_ECC_HW;
+ else if (!strcmp(str, "hw_syndrome"))
+ ecc_mode = NAND_ECC_HW_SYNDROME;
+ else if (!strcmp(str, "hw_oob_first"))
+ ecc_mode = NAND_ECC_HW_OOB_FIRST;
+ else if (!strcmp(str, "soft_bch"))
+ ecc_mode = NAND_ECC_SOFT_BCH;
+ }
+
+
+ ecc_strength = fdtdec_get_int(blob, node, "nand-ecc-strength", -1);
+ ecc_step = fdtdec_get_int(blob, node, "nand-ecc-step-size", -1);
+
+ if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
+ (!(ecc_step >= 0) && ecc_strength >= 0)) {
+ pr_err("must set both strength and step size in DT\n");
+ return -EINVAL;
+ }
+
+ if (ecc_mode >= 0)
+ chip->ecc.mode = ecc_mode;
+
+ if (ecc_strength >= 0)
+ chip->ecc.strength = ecc_strength;
+
+ if (ecc_step > 0)
+ chip->ecc.size = ecc_step;
+
+ if (fdt_getprop(blob, node, "nand-ecc-maximize", NULL))
+ chip->ecc.options |= NAND_ECC_MAXIMIZE;
+
+ return 0;
+}
+#else
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node)
+{
+ return 0;
+}
+#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
+
+/**
+ * nand_scan_ident - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ * @table: alternative NAND ID table
+ *
+ * This is the first phase of the normal nand_scan() function. It reads the
+ * flash ID and sets up MTD fields accordingly.
+ *
+ */
+int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+ struct nand_flash_dev *table)
+{
+ int i, nand_maf_id, nand_dev_id;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_flash_dev *type;
+ int ret;
+
+ if (chip->flash_node) {
+ ret = nand_dt_init(mtd, chip, chip->flash_node);
+ if (ret)
+ return ret;
+ }
+
+ /* Set the default functions */
+ nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
+
+ /* Read the flash type */
+ type = nand_get_flash_type(mtd, chip, &nand_maf_id,
+ &nand_dev_id, table);
+
+ if (IS_ERR(type)) {
+ if (!(chip->options & NAND_SCAN_SILENT_NODEV))
+ pr_warn("No NAND device found\n");
+ chip->select_chip(mtd, -1);
+ return PTR_ERR(type);
+ }
+
+ /* Initialize the ->data_interface field. */
+ ret = nand_init_data_interface(chip);
+ if (ret)
+ return ret;
+
+ /*
+ * Setup the data interface correctly on the chip and controller side.
+ * This explicit call to nand_setup_data_interface() is only required
+ * for the first die, because nand_reset() has been called before
+ * ->data_interface and ->default_onfi_timing_mode were set.
+ * For the other dies, nand_reset() will automatically switch to the
+ * best mode for us.
+ */
+ ret = nand_setup_data_interface(chip, 0);
+ if (ret)
+ return ret;
+
+ chip->select_chip(mtd, -1);
+
+ /* Check for a chip array */
+ for (i = 1; i < maxchips; i++) {
+ /* See comment in nand_get_flash_type for reset */
+ nand_reset(chip, i);
+
+ chip->select_chip(mtd, i);
+ /* Send the command for reading device ID */
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ /* Read manufacturer and device IDs */
+ if (nand_maf_id != chip->read_byte(mtd) ||
+ nand_dev_id != chip->read_byte(mtd)) {
+ chip->select_chip(mtd, -1);
+ break;
+ }
+ chip->select_chip(mtd, -1);
+ }
+
+#ifdef DEBUG
+ if (i > 1)
+ pr_info("%d chips detected\n", i);
+#endif
+
+ /* Store the number of chips and calc total size for mtd */
+ chip->numchips = i;
+ mtd->size = i * chip->chipsize;
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_scan_ident);
+
+/**
+ * nand_check_ecc_caps - check the sanity of preset ECC settings
+ * @chip: nand chip info structure
+ * @caps: ECC caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * When ECC step size and strength are already set, check if they are supported
+ * by the controller and the calculated ECC bytes fit within the chip's OOB.
+ * On success, the calculated ECC bytes is set.
+ */
+int nand_check_ecc_caps(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int preset_step = chip->ecc.size;
+ int preset_strength = chip->ecc.strength;
+ int nsteps, ecc_bytes;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ if (!preset_step || !preset_strength)
+ return -ENODATA;
+
+ nsteps = mtd->writesize / preset_step;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+
+ if (stepinfo->stepsize != preset_step)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ if (stepinfo->strengths[j] != preset_strength)
+ continue;
+
+ ecc_bytes = caps->calc_ecc_bytes(preset_step,
+ preset_strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ return ecc_bytes;
+
+ if (ecc_bytes * nsteps > oobavail) {
+ pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
+ preset_step, preset_strength);
+ return -ENOSPC;
+ }
+
+ chip->ecc.bytes = ecc_bytes;
+
+ return 0;
+ }
+ }
+
+ pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
+ preset_step, preset_strength);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
+
+/**
+ * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * If a chip's ECC requirement is provided, try to meet it with the least
+ * number of ECC bytes (i.e. with the largest number of OOB-free bytes).
+ * On success, the chosen ECC settings are set.
+ */
+int nand_match_ecc_req(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int req_step = chip->ecc_step_ds;
+ int req_strength = chip->ecc_strength_ds;
+ int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
+ int best_step, best_strength, best_ecc_bytes;
+ int best_ecc_bytes_total = INT_MAX;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ /* No information provided by the NAND chip */
+ if (!req_step || !req_strength)
+ return -ENOTSUPP;
+
+ /* number of correctable bits the chip requires in a page */
+ req_corr = mtd->writesize / req_step * req_strength;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ /*
+ * If both step size and strength are smaller than the
+ * chip's requirement, it is not easy to compare the
+ * resulted reliability.
+ */
+ if (step_size < req_step && strength < req_strength)
+ continue;
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+ ecc_bytes_total = ecc_bytes * nsteps;
+
+ if (ecc_bytes_total > oobavail ||
+ strength * nsteps < req_corr)
+ continue;
+
+ /*
+ * We assume the best is to meet the chip's requrement
+ * with the least number of ECC bytes.
+ */
+ if (ecc_bytes_total < best_ecc_bytes_total) {
+ best_ecc_bytes_total = ecc_bytes_total;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (best_ecc_bytes_total == INT_MAX)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_match_ecc_req);
+
+/**
+ * nand_maximize_ecc - choose the max ECC strength available
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ * @oobavail: OOB size that the ECC engine can use
+ *
+ * Choose the max ECC strength that is supported on the controller, and can fit
+ * within the chip's OOB. On success, the chosen ECC settings are set.
+ */
+int nand_maximize_ecc(struct nand_chip *chip,
+ const struct nand_ecc_caps *caps, int oobavail)
+{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ const struct nand_ecc_step_info *stepinfo;
+ int step_size, strength, nsteps, ecc_bytes, corr;
+ int best_corr = 0;
+ int best_step = 0;
+ int best_strength, best_ecc_bytes;
+ int i, j;
+
+ if (WARN_ON(oobavail < 0))
+ return -EINVAL;
+
+ for (i = 0; i < caps->nstepinfos; i++) {
+ stepinfo = &caps->stepinfos[i];
+ step_size = stepinfo->stepsize;
+
+ /* If chip->ecc.size is already set, respect it */
+ if (chip->ecc.size && step_size != chip->ecc.size)
+ continue;
+
+ for (j = 0; j < stepinfo->nstrengths; j++) {
+ strength = stepinfo->strengths[j];
+
+ if (mtd->writesize % step_size)
+ continue;
+
+ nsteps = mtd->writesize / step_size;
+
+ ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
+ if (WARN_ON_ONCE(ecc_bytes < 0))
+ continue;
+
+ if (ecc_bytes * nsteps > oobavail)
+ continue;
+
+ corr = strength * nsteps;
+
+ /*
+ * If the number of correctable bits is the same,
+ * bigger step_size has more reliability.
+ */
+ if (corr > best_corr ||
+ (corr == best_corr && step_size > best_step)) {
+ best_corr = corr;
+ best_step = step_size;
+ best_strength = strength;
+ best_ecc_bytes = ecc_bytes;
+ }
+ }
+ }
+
+ if (!best_corr)
+ return -ENOTSUPP;
+
+ chip->ecc.size = best_step;
+ chip->ecc.strength = best_strength;
+ chip->ecc.bytes = best_ecc_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nand_maximize_ecc);
+
+/*
+ * Check if the chip configuration meet the datasheet requirements.
+
+ * If our configuration corrects A bits per B bytes and the minimum
+ * required correction level is X bits per Y bytes, then we must ensure
+ * both of the following are true:
+ *
+ * (1) A / B >= X / Y
+ * (2) A >= X
+ *
+ * Requirement (1) ensures we can correct for the required bitflip density.
+ * Requirement (2) ensures we can correct even when all bitflips are clumped
+ * in the same sector.
+ */
+static bool nand_ecc_strength_good(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int corr, ds_corr;
+
+ if (ecc->size == 0 || chip->ecc_step_ds == 0)
+ /* Not enough information */
+ return true;
+
+ /*
+ * We get the number of corrected bits per page to compare
+ * the correction density.
+ */
+ corr = (mtd->writesize * ecc->strength) / ecc->size;
+ ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
+
+ return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
+}
+
+static bool invalid_ecc_page_accessors(struct nand_chip *chip)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ if (nand_standard_page_accessors(ecc))
+ return false;
+
+ /*
+ * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND
+ * controller driver implements all the page accessors because
+ * default helpers are not suitable when the core does not
+ * send the READ0/PAGEPROG commands.
+ */
+ return (!ecc->read_page || !ecc->write_page ||
+ !ecc->read_page_raw || !ecc->write_page_raw ||
+ (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) ||
+ (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage &&
+ ecc->hwctl && ecc->calculate));
+}
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ *
+ * This is the second phase of the normal nand_scan() function. It fills out
+ * all the uninitialized function pointers with the defaults and scans for a
+ * bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ int i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_buffers *nbuf;
+
+ /* New bad blocks should be marked in OOB, flash-based BBT, or both */
+ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+ !(chip->bbt_options & NAND_BBT_USE_FLASH));
+
+ if (invalid_ecc_page_accessors(chip)) {
+ pr_err("Invalid ECC page accessors setup\n");
+ return -EINVAL;
+ }
+
+ if (!(chip->options & NAND_OWN_BUFFERS)) {
+ nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL);
+ chip->buffers = nbuf;
+ } else {
+ if (!chip->buffers)
+ return -ENOMEM;
+ }
+
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->buffers->databuf + mtd->writesize;
+
+ /*
+ * If no default placement scheme is given, select an appropriate one.
+ */
+ if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
+ switch (mtd->oobsize) {
+ case 8:
+ ecc->layout = &nand_oob_8;
+ break;
+ case 16:
+ ecc->layout = &nand_oob_16;
+ break;
+ case 64:
+ ecc->layout = &nand_oob_64;
+ break;
+ case 128:
+ ecc->layout = &nand_oob_128;
+ break;
+ default:
+ pr_warn("No oob scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ BUG();
+ }
+ }
+
+ if (!chip->write_page)
+ chip->write_page = nand_write_page;
+
+ /*
+ * Check ECC mode, default to software if 3byte/512byte hardware ECC is
+ * selected and we have 256 byte pagesize fallback to software ECC
+ */
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW_OOB_FIRST:
+ /* Similar to NAND_ECC_HW, but a separate read_page handle */
+ if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
+ pr_warn("No ECC functions supplied; hardware ECC not possible\n");
+ BUG();
+ }
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc_oob_first;
+
+ case NAND_ECC_HW:
+ /* Use standard hwecc read page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_hwecc;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_hwecc;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_std;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->read_subpage)
+ ecc->read_subpage = nand_read_subpage;
+ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
+ ecc->write_subpage = nand_write_subpage_hwecc;
+
+ case NAND_ECC_HW_SYNDROME:
+ if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
+ (!ecc->read_page ||
+ ecc->read_page == nand_read_page_hwecc ||
+ !ecc->write_page ||
+ ecc->write_page == nand_write_page_hwecc)) {
+ pr_warn("No ECC functions supplied; hardware ECC not possible\n");
+ BUG();
+ }
+ /* Use standard syndrome read/write page function? */
+ if (!ecc->read_page)
+ ecc->read_page = nand_read_page_syndrome;
+ if (!ecc->write_page)
+ ecc->write_page = nand_write_page_syndrome;
+ if (!ecc->read_page_raw)
+ ecc->read_page_raw = nand_read_page_raw_syndrome;
+ if (!ecc->write_page_raw)
+ ecc->write_page_raw = nand_write_page_raw_syndrome;
+ if (!ecc->read_oob)
+ ecc->read_oob = nand_read_oob_syndrome;
+ if (!ecc->write_oob)
+ ecc->write_oob = nand_write_oob_syndrome;
+
+ if (mtd->writesize >= ecc->size) {
+ if (!ecc->strength) {
+ pr_warn("Driver must set ecc.strength when using hardware ECC\n");
+ BUG();
+ }
+ break;
+ }
+ pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
+ ecc->size, mtd->writesize);
+ ecc->mode = NAND_ECC_SOFT;
+
+ case NAND_ECC_SOFT:
+ ecc->calculate = nand_calculate_ecc;
+ ecc->correct = nand_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+ if (!ecc->size)
+ ecc->size = 256;
+ ecc->bytes = 3;
+ ecc->strength = 1;
+ break;
+
+ case NAND_ECC_SOFT_BCH:
+ if (!mtd_nand_has_bch()) {
+ pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ BUG();
+ }
+ ecc->calculate = nand_bch_calculate_ecc;
+ ecc->correct = nand_bch_correct_data;
+ ecc->read_page = nand_read_page_swecc;
+ ecc->read_subpage = nand_read_subpage;
+ ecc->write_page = nand_write_page_swecc;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->write_oob = nand_write_oob_std;
+ /*
+ * Board driver should supply ecc.size and ecc.strength values
+ * to select how many bits are correctable. Otherwise, default
+ * to 4 bits for large page devices.
+ */
+ if (!ecc->size && (mtd->oobsize >= 64)) {
+ ecc->size = 512;
+ ecc->strength = 4;
+ }
+
+ /* See nand_bch_init() for details. */
+ ecc->bytes = 0;
+ ecc->priv = nand_bch_init(mtd);
+ if (!ecc->priv) {
+ pr_warn("BCH ECC initialization failed!\n");
+ BUG();
+ }
+ break;
+
+ case NAND_ECC_NONE:
+ pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
+ ecc->read_page = nand_read_page_raw;
+ ecc->write_page = nand_write_page_raw;
+ ecc->read_oob = nand_read_oob_std;
+ ecc->read_page_raw = nand_read_page_raw;
+ ecc->write_page_raw = nand_write_page_raw;
+ ecc->write_oob = nand_write_oob_std;
+ ecc->size = mtd->writesize;
+ ecc->bytes = 0;
+ ecc->strength = 0;
+ break;
+
+ default:
+ pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
+ BUG();
+ }
+
+ /* For many systems, the standard OOB write also works for raw */
+ if (!ecc->read_oob_raw)
+ ecc->read_oob_raw = ecc->read_oob;
+ if (!ecc->write_oob_raw)
+ ecc->write_oob_raw = ecc->write_oob;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area.
+ */
+ mtd->oobavail = 0;
+ if (ecc->layout) {
+ for (i = 0; ecc->layout->oobfree[i].length; i++)
+ mtd->oobavail += ecc->layout->oobfree[i].length;
+ }
+
+ /* ECC sanity check: warn if it's too weak */
+ if (!nand_ecc_strength_good(mtd))
+ pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+ mtd->name);
+
+ /*
+ * Set the number of read / write steps for one page depending on ECC
+ * mode.
+ */
+ ecc->steps = mtd->writesize / ecc->size;
+ if (ecc->steps * ecc->size != mtd->writesize) {
+ pr_warn("Invalid ECC parameters\n");
+ BUG();
+ }
+ ecc->total = ecc->steps * ecc->bytes;
+
+ /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
+ switch (ecc->steps) {
+ case 2:
+ mtd->subpage_sft = 1;
+ break;
+ case 4:
+ case 8:
+ case 16:
+ mtd->subpage_sft = 2;
+ break;
+ }
+ }
+ chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /* Initialize state */
+ chip->state = FL_READY;
+
+ /* Invalidate the pagebuffer reference */
+ chip->pagebuf = -1;
+
+ /* Large page NAND with SOFT_ECC should support subpage reads */
+ switch (ecc->mode) {
+ case NAND_ECC_SOFT:
+ case NAND_ECC_SOFT_BCH:
+ if (chip->page_shift > 9)
+ chip->options |= NAND_SUBPAGE_READ;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH;
+ mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
+ MTD_CAP_NANDFLASH;
+ mtd->_erase = nand_erase;
+ mtd->_panic_write = panic_nand_write;
+ mtd->_read_oob = nand_read_oob;
+ mtd->_write_oob = nand_write_oob;
+ mtd->_sync = nand_sync;
+ mtd->_lock = NULL;
+ mtd->_unlock = NULL;
+ mtd->_block_isreserved = nand_block_isreserved;
+ mtd->_block_isbad = nand_block_isbad;
+ mtd->_block_markbad = nand_block_markbad;
+ mtd->writebufsize = mtd->writesize;
+
+ /* propagate ecc info to mtd_info */
+ mtd->ecclayout = ecc->layout;
+ mtd->ecc_strength = ecc->strength;
+ mtd->ecc_step_size = ecc->size;
+ /*
+ * Initialize bitflip_threshold to its default prior scan_bbt() call.
+ * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
+ * properly set.
+ */
+ if (!mtd->bitflip_threshold)
+ mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_scan_tail);
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers with the defaults.
+ * The flash ID is read and the mtd/chip structures are filled with the
+ * appropriate values.
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+ int ret;
+
+ ret = nand_scan_ident(mtd, maxchips, NULL);
+ if (!ret)
+ ret = nand_scan_tail(mtd);
+ return ret;
+}
+EXPORT_SYMBOL(nand_scan);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Generic NAND flash driver code");
diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
new file mode 100644
index 0000000000..ba785c5d53
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_bbt.c
@@ -0,0 +1,1373 @@
+/*
+ * Overview:
+ * Bad block table support for the NAND driver
+ *
+ * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table
+ * depending on the options in the BBT descriptor(s). If no flash based BBT
+ * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory
+ * marked good / bad blocks. This information is used to create a memory BBT.
+ * Once a new bad block is discovered then the "factory" information is updated
+ * on the device.
+ * If a flash based BBT is specified then the function first tries to find the
+ * BBT on flash. If a BBT is found then the contents are read and the memory
+ * based BBT is created. If a mirrored BBT is selected then the mirror is
+ * searched too and the versions are compared. If the mirror has a greater
+ * version number, then the mirror BBT is used to build the memory based BBT.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the BBTs is out of date or does not exist it is (re)created.
+ * If no BBT exists at all then the device is scanned for factory marked
+ * good / bad blocks and the bad block tables are created.
+ *
+ * For manufacturer created BBTs like the one found on M-SYS DOC devices
+ * the BBT is searched and read but never created
+ *
+ * The auto generated bad block table is located in the last good blocks
+ * of the device. The table is mirrored, so it can be updated eventually.
+ * The table is marked in the OOB area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date. If the NAND
+ * controller needs the complete OOB area for the ECC information then the
+ * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of
+ * course): it moves the ident pattern and the version byte into the data area
+ * and the OOB area will remain untouched.
+ *
+ * The table uses 2 bits per block
+ * 11b: block is good
+ * 00b: block is factory marked bad
+ * 01b, 10b: block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b: block is good
+ * 01b: block is marked bad due to wear
+ * 10b: block is reserved (to protect the bbt area)
+ * 11b: block is factory marked bad
+ *
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space necessary for a bbt in FLASH does not exceed a block boundary
+ *
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/bbm.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+
+#define BBT_BLOCK_GOOD 0x00
+#define BBT_BLOCK_WORN 0x01
+#define BBT_BLOCK_RESERVED 0x02
+#define BBT_BLOCK_FACTORY_BAD 0x03
+
+#define BBT_ENTRY_MASK 0x03
+#define BBT_ENTRY_SHIFT 2
+
+static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
+
+static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
+{
+ uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
+ entry >>= (block & BBT_ENTRY_MASK) * 2;
+ return entry & BBT_ENTRY_MASK;
+}
+
+static inline void bbt_mark_entry(struct nand_chip *chip, int block,
+ uint8_t mark)
+{
+ uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
+ chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
+}
+
+static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
+{
+ if (memcmp(buf, td->pattern, td->len))
+ return -1;
+ return 0;
+}
+
+/**
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers.
+ */
+static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ if (td->options & NAND_BBT_NO_OOB)
+ return check_pattern_no_oob(buf, td);
+
+ /* Compare the pattern */
+ if (memcmp(buf + paglen + td->offs, td->pattern, td->len))
+ return -1;
+
+ return 0;
+}
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block tables and
+ * good / bad block identifiers. Same as check_pattern, but no optional empty
+ * check.
+ */
+static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
+{
+ /* Compare the pattern */
+ if (memcmp(buf + td->offs, td->pattern, td->len))
+ return -1;
+ return 0;
+}
+
+/**
+ * add_marker_len - compute the length of the marker in data area
+ * @td: BBT descriptor used for computation
+ *
+ * The length will be 0 if the marker is located in OOB area.
+ */
+static u32 add_marker_len(struct nand_bbt_descr *td)
+{
+ u32 len;
+
+ if (!(td->options & NAND_BBT_NO_OOB))
+ return 0;
+
+ len = td->len;
+ if (td->options & NAND_BBT_VERSION)
+ len++;
+ return len;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @td: the bbt describtion table
+ * @offs: block number offset in the table
+ *
+ * Read the bad block table starting from page.
+ */
+static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+ struct nand_bbt_descr *td, int offs)
+{
+ int res, ret = 0, i, j, act = 0;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ size_t retlen, len, totlen;
+ loff_t from;
+ int bits = td->options & NAND_BBT_NRBITS_MSK;
+ uint8_t msk = (uint8_t)((1 << bits) - 1);
+ u32 marker_len;
+ int reserved_block_code = td->reserved_block_code;
+
+ totlen = (num * bits) >> 3;
+ marker_len = add_marker_len(td);
+ from = ((loff_t)page) << this->page_shift;
+
+ while (totlen) {
+ len = min(totlen, (size_t)(1 << this->bbt_erase_shift));
+ if (marker_len) {
+ /*
+ * In case the BBT marker is not in the OOB area it
+ * will be just in the first page.
+ */
+ len -= marker_len;
+ from += marker_len;
+ marker_len = 0;
+ }
+ res = mtd_read(mtd, from, len, &retlen, buf);
+ if (res < 0) {
+ if (mtd_is_eccerr(res)) {
+ pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n",
+ from & ~mtd->writesize);
+ return res;
+ } else if (mtd_is_bitflip(res)) {
+ pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n",
+ from & ~mtd->writesize);
+ ret = res;
+ } else {
+ pr_info("nand_bbt: error reading BBT\n");
+ return res;
+ }
+ }
+
+ /* Analyse data */
+ for (i = 0; i < len; i++) {
+ uint8_t dat = buf[i];
+ for (j = 0; j < 8; j += bits, act++) {
+ uint8_t tmp = (dat >> j) & msk;
+ if (tmp == msk)
+ continue;
+ if (reserved_block_code && (tmp == reserved_block_code)) {
+ pr_info("nand_read_bbt: reserved block at 0x%012llx\n",
+ (loff_t)(offs + act) <<
+ this->bbt_erase_shift);
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_RESERVED);
+ mtd->ecc_stats.bbtblocks++;
+ continue;
+ }
+ /*
+ * Leave it for now, if it's matured we can
+ * move this message to pr_debug.
+ */
+ pr_info("nand_read_bbt: bad block at 0x%012llx\n",
+ (loff_t)(offs + act) <<
+ this->bbt_erase_shift);
+ /* Factory marked bad or worn out? */
+ if (tmp == 0)
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_FACTORY_BAD);
+ else
+ bbt_mark_entry(this, offs + act,
+ BBT_BLOCK_WORN);
+ mtd->ecc_stats.badblocks++;
+ }
+ }
+ totlen -= len;
+ from += len;
+ }
+ return ret;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips; applies only if
+ * NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page. We assume
+ * that the bbt bits are in consecutive order.
+ */
+static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int res = 0, i;
+
+ if (td->options & NAND_BBT_PERCHIP) {
+ int offs = 0;
+ for (i = 0; i < this->numchips; i++) {
+ if (chip == -1 || chip == i)
+ res = read_bbt(mtd, buf, td->pages[i],
+ this->chipsize >> this->bbt_erase_shift,
+ td, offs);
+ if (res)
+ return res;
+ offs += this->chipsize >> this->bbt_erase_shift;
+ }
+ } else {
+ res = read_bbt(mtd, buf, td->pages[0],
+ mtd->size >> this->bbt_erase_shift, td, 0);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+/* BBT marker is in the first page, no OOB */
+static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ struct nand_bbt_descr *td)
+{
+ size_t retlen;
+ size_t len;
+
+ len = td->len;
+ if (td->options & NAND_BBT_VERSION)
+ len++;
+
+ return mtd_read(mtd, offs, len, &retlen, buf);
+}
+
+/**
+ * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @offs: offset at which to scan
+ * @len: length of data region to read
+ *
+ * Scan read data from data+OOB. May traverse multiple pages, interleaving
+ * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
+ * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
+ */
+static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ size_t len)
+{
+ struct mtd_oob_ops ops;
+ int res, ret = 0;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+
+ while (len > 0) {
+ ops.datbuf = buf;
+ ops.len = min(len, (size_t)mtd->writesize);
+ ops.oobbuf = buf + ops.len;
+
+ res = mtd_read_oob(mtd, offs, &ops);
+ if (res) {
+ if (!mtd_is_bitflip_or_eccerr(res))
+ return res;
+ else if (mtd_is_eccerr(res) || !ret)
+ ret = res;
+ }
+
+ buf += mtd->oobsize + mtd->writesize;
+ len -= mtd->writesize;
+ offs += mtd->writesize;
+ }
+ return ret;
+}
+
+static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+ size_t len, struct nand_bbt_descr *td)
+{
+ if (td->options & NAND_BBT_NO_OOB)
+ return scan_read_data(mtd, buf, offs, td);
+ else
+ return scan_read_oob(mtd, buf, offs, len);
+}
+
+/* Scan write data with oob to flash */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+ uint8_t *buf, uint8_t *oob)
+{
+ struct mtd_oob_ops ops;
+
+ ops.mode = MTD_OPS_PLACE_OOB;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+ ops.datbuf = buf;
+ ops.oobbuf = oob;
+ ops.len = len;
+
+ return mtd_write_oob(mtd, offs, &ops);
+}
+
+static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+ u32 ver_offs = td->veroffs;
+
+ if (!(td->options & NAND_BBT_NO_OOB))
+ ver_offs += mtd->writesize;
+ return ver_offs;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page. We
+ * assume that the bbt bits are in consecutive order.
+ */
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ /* Read the primary version, if available */
+ if (td->options & NAND_BBT_VERSION) {
+ scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
+ mtd->writesize, td);
+ td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ td->pages[0], td->version[0]);
+ }
+
+ /* Read the mirror version, if available */
+ if (md && (md->options & NAND_BBT_VERSION)) {
+ scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
+ mtd->writesize, md);
+ md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
+ pr_info("Bad block table at page %d, version 0x%02X\n",
+ md->pages[0], md->version[0]);
+ }
+}
+
+/* Scan a given block partially */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+ loff_t offs, uint8_t *buf, int numpages)
+{
+ struct mtd_oob_ops ops;
+ int j, ret;
+
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ for (j = 0; j < numpages; j++) {
+ /*
+ * Read the full oob until read_oob is fixed to handle single
+ * byte reads for 16 bit buswidth.
+ */
+ ret = mtd_read_oob(mtd, offs, &ops);
+ /* Ignore ECC errors when checking for BBM */
+ if (ret && !mtd_is_bitflip_or_eccerr(ret))
+ return ret;
+
+ if (check_short_pattern(buf, bd))
+ return 1;
+
+ offs += mtd->writesize;
+ }
+ return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips; applies only
+ * if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device for the given good/bad block
+ * identify pattern.
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *bd, int chip)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, numblocks, numpages;
+ int startblock;
+ loff_t from;
+
+ pr_info("Scanning device for bad blocks\n");
+
+ if (bd->options & NAND_BBT_SCAN2NDPAGE)
+ numpages = 2;
+ else
+ numpages = 1;
+
+ if (chip == -1) {
+ numblocks = mtd->size >> this->bbt_erase_shift;
+ startblock = 0;
+ from = 0;
+ } else {
+ if (chip >= this->numchips) {
+ pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n",
+ chip + 1, this->numchips);
+ return -EINVAL;
+ }
+ numblocks = this->chipsize >> this->bbt_erase_shift;
+ startblock = chip * numblocks;
+ numblocks += startblock;
+ from = (loff_t)startblock << this->bbt_erase_shift;
+ }
+
+ if (this->bbt_options & NAND_BBT_SCANLASTPAGE)
+ from += mtd->erasesize - (mtd->writesize * numpages);
+
+ for (i = startblock; i < numblocks; i++) {
+ int ret;
+
+ BUG_ON(bd->options & NAND_BBT_NO_OOB);
+
+ ret = scan_block_fast(mtd, bd, from, buf, numpages);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD);
+ pr_warn("Bad eraseblock %d at 0x%012llx\n",
+ i, (unsigned long long)from);
+ mtd->ecc_stats.badblocks++;
+ }
+
+ from += (1 << this->bbt_erase_shift);
+ }
+ return 0;
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern. Search is
+ * preformed either from the beginning up or from the end of the device
+ * downwards. The search starts always at the start of a block. If the option
+ * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains
+ * the bad block information of this chip. This is necessary to provide support
+ * for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page in a block.
+ */
+static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, chips;
+ int startblock, block, dir;
+ int scanlen = mtd->writesize + mtd->oobsize;
+ int bbtblocks;
+ int blocktopage = this->bbt_erase_shift - this->page_shift;
+
+ /* Search direction top -> down? */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = (mtd->size >> this->bbt_erase_shift) - 1;
+ dir = -1;
+ } else {
+ startblock = 0;
+ dir = 1;
+ }
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ bbtblocks = this->chipsize >> this->bbt_erase_shift;
+ startblock &= bbtblocks - 1;
+ } else {
+ chips = 1;
+ bbtblocks = mtd->size >> this->bbt_erase_shift;
+ }
+
+ for (i = 0; i < chips; i++) {
+ /* Reset version information */
+ td->version[i] = 0;
+ td->pages[i] = -1;
+ /* Scan the maximum number of blocks */
+ for (block = 0; block < td->maxblocks; block++) {
+
+ int actblock = startblock + dir * block;
+ loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
+
+ /* Read first page */
+ scan_read(mtd, buf, offs, mtd->writesize, td);
+ if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
+ td->pages[i] = actblock << blocktopage;
+ if (td->options & NAND_BBT_VERSION) {
+ offs = bbt_get_ver_offs(mtd, td);
+ td->version[i] = buf[offs];
+ }
+ break;
+ }
+ }
+ startblock += this->chipsize >> this->bbt_erase_shift;
+ }
+ /* Check, if we found a bbt for each requested chip */
+ for (i = 0; i < chips; i++) {
+ if (td->pages[i] == -1)
+ pr_warn("Bad block table not found for chip %d\n", i);
+ else
+ pr_info("Bad block table found at page %d, version 0x%02X\n",
+ td->pages[i], td->version[i]);
+ }
+ return 0;
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s).
+ */
+static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td,
+ struct nand_bbt_descr *md)
+{
+ /* Search the primary table */
+ search_bbt(mtd, buf, td);
+
+ /* Search the mirror table */
+ if (md)
+ search_bbt(mtd, buf, md);
+}
+
+/**
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table.
+ */
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+ int chipsel)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct erase_info einfo;
+ int i, res, chip = 0;
+ int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+ int nrchips, pageoffs, ooboffs;
+ uint8_t msk[4];
+ uint8_t rcode = td->reserved_block_code;
+ size_t retlen, len = 0;
+ loff_t to;
+ struct mtd_oob_ops ops;
+
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OPS_PLACE_OOB;
+
+ if (!rcode)
+ rcode = 0xff;
+ /* Write bad block table per chip rather than per device? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ /* Full device write or specific chip? */
+ if (chipsel == -1) {
+ nrchips = this->numchips;
+ } else {
+ nrchips = chipsel + 1;
+ chip = chipsel;
+ }
+ } else {
+ numblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ nrchips = 1;
+ }
+
+ /* Loop through the chips */
+ for (; chip < nrchips; chip++) {
+ /*
+ * There was already a version of the table, reuse the page
+ * This applies for absolute placement too, as we have the
+ * page nr. in td->pages.
+ */
+ if (td->pages[chip] != -1) {
+ page = td->pages[chip];
+ goto write;
+ }
+
+ /*
+ * Automatic placement of the bad block table. Search direction
+ * top -> down?
+ */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = numblocks * (chip + 1) - 1;
+ dir = -1;
+ } else {
+ startblock = chip * numblocks;
+ dir = 1;
+ }
+
+ for (i = 0; i < td->maxblocks; i++) {
+ int block = startblock + dir * i;
+ /* Check, if the block is bad */
+ switch (bbt_get_entry(this, block)) {
+ case BBT_BLOCK_WORN:
+ case BBT_BLOCK_FACTORY_BAD:
+ continue;
+ }
+ page = block <<
+ (this->bbt_erase_shift - this->page_shift);
+ /* Check, if the block is used by the mirror table */
+ if (!md || md->pages[chip] != page)
+ goto write;
+ }
+ pr_err("No space left to write bad block table\n");
+ return -ENOSPC;
+ write:
+
+ /* Set up shift count and masks for the flash table */
+ bits = td->options & NAND_BBT_NRBITS_MSK;
+ msk[2] = ~rcode;
+ switch (bits) {
+ case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
+ msk[3] = 0x01;
+ break;
+ case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
+ msk[3] = 0x03;
+ break;
+ case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
+ msk[3] = 0x0f;
+ break;
+ case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
+ msk[3] = 0xff;
+ break;
+ default: return -EINVAL;
+ }
+
+ to = ((loff_t)page) << this->page_shift;
+
+ /* Must we save the block contents? */
+ if (td->options & NAND_BBT_SAVECONTENT) {
+ /* Make it block aligned */
+ to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
+ len = 1 << this->bbt_erase_shift;
+ res = mtd_read(mtd, to, len, &retlen, buf);
+ if (res < 0) {
+ if (retlen != len) {
+ pr_info("nand_bbt: error reading block for writing the bad block table\n");
+ return res;
+ }
+ pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n");
+ }
+ /* Read oob data */
+ ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
+ ops.oobbuf = &buf[len];
+ res = mtd_read_oob(mtd, to + mtd->writesize, &ops);
+ if (res < 0 || ops.oobretlen != ops.ooblen)
+ goto outerr;
+
+ /* Calc the byte offset in the buffer */
+ pageoffs = page - (int)(to >> this->page_shift);
+ offs = pageoffs << this->page_shift;
+ /* Preset the bbt area with 0xff */
+ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft));
+ ooboffs = len + (pageoffs * mtd->oobsize);
+
+ } else if (td->options & NAND_BBT_NO_OOB) {
+ ooboffs = 0;
+ offs = td->len;
+ /* The version byte */
+ if (td->options & NAND_BBT_VERSION)
+ offs++;
+ /* Calc length */
+ len = (size_t)(numblocks >> sft);
+ len += offs;
+ /* Make it page aligned! */
+ len = ALIGN(len, mtd->writesize);
+ /* Preset the buffer with 0xff */
+ memset(buf, 0xff, len);
+ /* Pattern is located at the begin of first page */
+ memcpy(buf, td->pattern, td->len);
+ } else {
+ /* Calc length */
+ len = (size_t)(numblocks >> sft);
+ /* Make it page aligned! */
+ len = ALIGN(len, mtd->writesize);
+ /* Preset the buffer with 0xff */
+ memset(buf, 0xff, len +
+ (len >> this->page_shift)* mtd->oobsize);
+ offs = 0;
+ ooboffs = len;
+ /* Pattern is located in oob area of first page */
+ memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
+ }
+
+ if (td->options & NAND_BBT_VERSION)
+ buf[ooboffs + td->veroffs] = td->version[chip];
+
+ /* Walk through the memory table */
+ for (i = 0; i < numblocks; i++) {
+ uint8_t dat;
+ int sftcnt = (i << (3 - sft)) & sftmsk;
+ dat = bbt_get_entry(this, chip * numblocks + i);
+ /* Do not store the reserved bbt blocks! */
+ buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt);
+ }
+
+ memset(&einfo, 0, sizeof(einfo));
+ einfo.mtd = mtd;
+ einfo.addr = to;
+ einfo.len = 1 << this->bbt_erase_shift;
+ res = nand_erase_nand(mtd, &einfo, 1);
+ if (res < 0)
+ goto outerr;
+
+ res = scan_write_bbt(mtd, to, len, buf,
+ td->options & NAND_BBT_NO_OOB ? NULL :
+ &buf[len]);
+ if (res < 0)
+ goto outerr;
+
+ pr_info("Bad block table written to 0x%012llx, version 0x%02X\n",
+ (unsigned long long)to, td->version[chip]);
+
+ /* Mark it as used */
+ td->pages[chip] = page;
+ }
+ return 0;
+
+ outerr:
+ pr_warn("nand_bbt: error while writing bad block table %d\n", res);
+ return res;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device for
+ * manufacturer / software marked good / bad blocks.
+ */
+static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ return create_bbt(mtd, this->buffers->databuf, bd, -1);
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if necessary
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt and creates
+ * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found
+ * for the chip/device. Update is necessary if one of the tables is missing or
+ * the version nr. of one table is less than the other.
+ */
+static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+ int i, chips, writeops, create, chipsel, res, res2;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+ struct nand_bbt_descr *rd, *rd2;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP)
+ chips = this->numchips;
+ else
+ chips = 1;
+
+ for (i = 0; i < chips; i++) {
+ writeops = 0;
+ create = 0;
+ rd = NULL;
+ rd2 = NULL;
+ res = res2 = 0;
+ /* Per chip or per device? */
+ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+ /* Mirrored table available? */
+ if (md) {
+ if (td->pages[i] == -1 && md->pages[i] == -1) {
+ create = 1;
+ writeops = 0x03;
+ } else if (td->pages[i] == -1) {
+ rd = md;
+ writeops = 0x01;
+ } else if (md->pages[i] == -1) {
+ rd = td;
+ writeops = 0x02;
+ } else if (td->version[i] == md->version[i]) {
+ rd = td;
+ if (!(td->options & NAND_BBT_VERSION))
+ rd2 = md;
+ } else if (((int8_t)(td->version[i] - md->version[i])) > 0) {
+ rd = td;
+ writeops = 0x02;
+ } else {
+ rd = md;
+ writeops = 0x01;
+ }
+ } else {
+ if (td->pages[i] == -1) {
+ create = 1;
+ writeops = 0x01;
+ } else {
+ rd = td;
+ }
+ }
+
+ if (create) {
+ /* Create the bad block table by scanning the device? */
+ if (!(td->options & NAND_BBT_CREATE))
+ continue;
+
+ /* Create the table in memory by scanning the chip(s) */
+ if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
+ create_bbt(mtd, buf, bd, chipsel);
+
+ td->version[i] = 1;
+ if (md)
+ md->version[i] = 1;
+ }
+
+ /* Read back first? */
+ if (rd) {
+ res = read_abs_bbt(mtd, buf, rd, chipsel);
+ if (mtd_is_eccerr(res)) {
+ /* Mark table as invalid */
+ rd->pages[i] = -1;
+ rd->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+ /* If they weren't versioned, read both */
+ if (rd2) {
+ res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+ if (mtd_is_eccerr(res2)) {
+ /* Mark table as invalid */
+ rd2->pages[i] = -1;
+ rd2->version[i] = 0;
+ i--;
+ continue;
+ }
+ }
+
+ /* Scrub the flash table(s)? */
+ if (mtd_is_bitflip(res) || mtd_is_bitflip(res2))
+ writeops = 0x03;
+
+ /* Update version numbers before writing */
+ if (md) {
+ td->version[i] = max(td->version[i], md->version[i]);
+ md->version[i] = td->version[i];
+ }
+
+ /* Write the bad block table to the device? */
+ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, td, md, chipsel);
+ if (res < 0)
+ return res;
+ }
+
+ /* Write the mirror bad block table to the device? */
+ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, md, td, chipsel);
+ if (res < 0)
+ return res;
+ }
+ }
+ return 0;
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent accidental
+ * erasures / writes. The regions are identified by the mark 0x02.
+ */
+static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int i, j, chips, block, nrblocks, update;
+ uint8_t oldval;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ } else {
+ chips = 1;
+ nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ }
+
+ for (i = 0; i < chips; i++) {
+ if ((td->options & NAND_BBT_ABSPAGE) ||
+ !(td->options & NAND_BBT_WRITE)) {
+ if (td->pages[i] == -1)
+ continue;
+ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+ oldval = bbt_get_entry(this, block);
+ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+ if ((oldval != BBT_BLOCK_RESERVED) &&
+ td->reserved_block_code)
+ nand_update_bbt(mtd, (loff_t)block <<
+ this->bbt_erase_shift);
+ continue;
+ }
+ update = 0;
+ if (td->options & NAND_BBT_LASTBLOCK)
+ block = ((i + 1) * nrblocks) - td->maxblocks;
+ else
+ block = i * nrblocks;
+ for (j = 0; j < td->maxblocks; j++) {
+ oldval = bbt_get_entry(this, block);
+ bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
+ if (oldval != BBT_BLOCK_RESERVED)
+ update = 1;
+ block++;
+ }
+ /*
+ * If we want reserved blocks to be recorded to flash, and some
+ * new ones have been marked, then we need to update the stored
+ * bbts. This should only happen once.
+ */
+ if (update && td->reserved_block_code)
+ nand_update_bbt(mtd, (loff_t)(block - 1) <<
+ this->bbt_erase_shift);
+ }
+}
+
+/**
+ * verify_bbt_descr - verify the bad block description
+ * @mtd: MTD device structure
+ * @bd: the table to verify
+ *
+ * This functions performs a few sanity checks on the bad block description
+ * table.
+ */
+static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u32 pattern_len;
+ u32 bits;
+ u32 table_size;
+
+ if (!bd)
+ return;
+
+ pattern_len = bd->len;
+ bits = bd->options & NAND_BBT_NRBITS_MSK;
+
+ BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) &&
+ !(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!bits);
+
+ if (bd->options & NAND_BBT_VERSION)
+ pattern_len++;
+
+ if (bd->options & NAND_BBT_NO_OOB) {
+ BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH));
+ BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB));
+ BUG_ON(bd->offs);
+ if (bd->options & NAND_BBT_VERSION)
+ BUG_ON(bd->veroffs != bd->len);
+ BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
+ }
+
+ if (bd->options & NAND_BBT_PERCHIP)
+ table_size = this->chipsize >> this->bbt_erase_shift;
+ else
+ table_size = mtd->size >> this->bbt_erase_shift;
+ table_size >>= 3;
+ table_size *= bits;
+ if (bd->options & NAND_BBT_NO_OOB)
+ table_size += pattern_len;
+ BUG_ON(table_size > (1 << this->bbt_erase_shift));
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already available. If
+ * not it scans the device for manufacturer marked good / bad blocks and writes
+ * the bad block table(s) to the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed by calling
+ * the nand_free_bbt function.
+ */
+static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1;
+ /*
+ * Allocate memory (2bit per block) and clear the memory bad block
+ * table.
+ */
+ this->bbt = kzalloc(len, GFP_KERNEL);
+ if (!this->bbt)
+ return -ENOMEM;
+
+ /*
+ * If no primary table decriptor is given, scan the device to build a
+ * memory based bad block table.
+ */
+ if (!td) {
+ if ((res = nand_memory_bbt(mtd, bd))) {
+ pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
+ goto err;
+ }
+ return 0;
+ }
+ verify_bbt_descr(mtd, td);
+ verify_bbt_descr(mtd, md);
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = vmalloc(len);
+ if (!buf) {
+ res = -ENOMEM;
+ goto err;
+ }
+
+ /* Is the bbt at a given page? */
+ if (td->options & NAND_BBT_ABSPAGE) {
+ read_abs_bbts(mtd, buf, td, md);
+ } else {
+ /* Search the bad block table using a pattern in oob */
+ search_read_bbts(mtd, buf, td, md);
+ }
+
+ res = check_create(mtd, buf, bd);
+ if (res)
+ goto err;
+
+ /* Prevent the bbt regions from erasing / writing */
+ mark_bbt_region(mtd, td);
+ if (md)
+ mark_bbt_region(mtd, md);
+
+ vfree(buf);
+ return 0;
+
+err:
+ kfree(this->bbt);
+ this->bbt = NULL;
+ return res;
+}
+
+/**
+ * nand_update_bbt - update bad block table(s)
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s).
+ */
+static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int len, res = 0;
+ int chip, chipsel;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ if (!this->bbt || !td)
+ return -EINVAL;
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Do we have a bbt per chip? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chip = (int)(offs >> this->chip_shift);
+ chipsel = chip;
+ } else {
+ chip = 0;
+ chipsel = -1;
+ }
+
+ td->version[chip]++;
+ if (md)
+ md->version[chip]++;
+
+ /* Write the bad block table to the device? */
+ if (td->options & NAND_BBT_WRITE) {
+ res = write_bbt(mtd, buf, td, md, chipsel);
+ if (res < 0)
+ goto out;
+ }
+ /* Write the mirror bad block table to the device? */
+ if (md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt(mtd, buf, md, td, chipsel);
+ }
+
+ out:
+ kfree(buf);
+ return res;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+/* Generic flash bbt descriptors */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = mirror_pattern
+};
+
+static struct nand_bbt_descr bbt_main_no_oob_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+ | NAND_BBT_NO_OOB,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_no_oob_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
+ | NAND_BBT_NO_OOB,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = NAND_BBT_SCAN_MAXBLOCKS,
+ .pattern = mirror_pattern
+};
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
+/**
+ * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure
+ * @this: NAND chip to create descriptor for
+ *
+ * This function allocates and initializes a nand_bbt_descr for BBM detection
+ * based on the properties of @this. The new descriptor is stored in
+ * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when
+ * passed to this function.
+ */
+static int nand_create_badblock_pattern(struct nand_chip *this)
+{
+ struct nand_bbt_descr *bd;
+ if (this->badblock_pattern) {
+ pr_warn("Bad block pattern already allocated; not replacing\n");
+ return -EINVAL;
+ }
+ bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+ if (!bd)
+ return -ENOMEM;
+ bd->options = this->bbt_options & BADBLOCK_SCAN_MASK;
+ bd->offs = this->badblockpos;
+ bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1;
+ bd->pattern = scan_ff_pattern;
+ bd->options |= NAND_BBT_DYNAMICSTRUCT;
+ this->badblock_pattern = bd;
+ return 0;
+}
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
+ * @mtd: MTD device structure
+ *
+ * This function selects the default bad block table support for the device and
+ * calls the nand_scan_bbt function.
+ */
+int nand_default_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int ret;
+
+ /* Is a flash based bad block table requested? */
+ if (this->bbt_options & NAND_BBT_USE_FLASH) {
+ /* Use the default pattern descriptors */
+ if (!this->bbt_td) {
+ if (this->bbt_options & NAND_BBT_NO_OOB) {
+ this->bbt_td = &bbt_main_no_oob_descr;
+ this->bbt_md = &bbt_mirror_no_oob_descr;
+ } else {
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+ }
+ }
+ } else {
+ this->bbt_td = NULL;
+ this->bbt_md = NULL;
+ }
+
+ if (!this->badblock_pattern) {
+ ret = nand_create_badblock_pattern(this);
+ if (ret)
+ return ret;
+ }
+
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+/**
+ * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ */
+int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED;
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ */
+int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, res;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ res = bbt_get_entry(this, block);
+
+ pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ (unsigned int)offs, block, res);
+
+ switch (res) {
+ case BBT_BLOCK_GOOD:
+ return 0;
+ case BBT_BLOCK_WORN:
+ return 1;
+ case BBT_BLOCK_RESERVED:
+ return allowbbt ? 0 : 1;
+ }
+ return 1;
+}
+
+/**
+ * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT
+ * @mtd: MTD device structure
+ * @offs: offset of the bad block
+ */
+int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int block, ret = 0;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+
+ /* Mark bad block in memory */
+ bbt_mark_entry(this, block, BBT_BLOCK_WORN);
+
+ /* Update flash-based bad block table */
+ if (this->bbt_options & NAND_BBT_USE_FLASH)
+ ret = nand_update_bbt(mtd, offs);
+
+ return ret;
+}
diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
new file mode 100644
index 0000000000..afa0418168
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_bch.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file provides ECC correction for more than 1 bit per block of data,
+ * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
+ *
+ * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
+ *
+ */
+
+#include <common.h>
+/*#include <asm/io.h>*/
+#include <linux/types.h>
+
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/bch.h>
+#include <malloc.h>
+
+/**
+ * struct nand_bch_control - private NAND BCH control structure
+ * @bch: BCH control structure
+ * @ecclayout: private ecc layout for this BCH configuration
+ * @errloc: error location array
+ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
+ */
+struct nand_bch_control {
+ struct bch_control *bch;
+ struct nand_ecclayout ecclayout;
+ unsigned int *errloc;
+ unsigned char *eccmask;
+};
+
+/**
+ * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
+ * @mtd: MTD block structure
+ * @buf: input buffer with raw data
+ * @code: output buffer with ECC
+ */
+int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+{
+ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int i;
+
+ memset(code, 0, chip->ecc.bytes);
+ encode_bch(nbc->bch, buf, chip->ecc.size, code);
+
+ /* apply mask so that an erased page is a valid codeword */
+ for (i = 0; i < chip->ecc.bytes; i++)
+ code[i] ^= nbc->eccmask[i];
+
+ return 0;
+}
+
+/**
+ * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd: MTD block structure
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct bit errors for a data byte block
+ */
+int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ const struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int *errloc = nbc->errloc;
+ int i, count;
+
+ count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
+ NULL, errloc);
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ if (errloc[i] < (chip->ecc.size*8))
+ /* error is located in data, correct it */
+ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
+ /* else error in ecc, no action needed */
+
+ pr_debug("%s: corrected bitflip %u\n",
+ __func__, errloc[i]);
+ }
+ } else if (count < 0) {
+ printk(KERN_ERR "ecc unrecoverable error\n");
+ count = -EBADMSG;
+ }
+ return count;
+}
+
+/**
+ * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
+ * @mtd: MTD block structure
+ *
+ * Returns:
+ * a pointer to a new NAND BCH control structure, or NULL upon failure
+ *
+ * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
+ * are used to compute BCH parameters m (Galois field order) and t (error
+ * correction capability). @eccbytes should be equal to the number of bytes
+ * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
+ *
+ * Example: to configure 4 bit correction per 512 bytes, you should pass
+ * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
+ * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
+ */
+struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ unsigned int m, t, eccsteps, i;
+ struct nand_ecclayout *layout = nand->ecc.layout;
+ struct nand_bch_control *nbc = NULL;
+ unsigned char *erased_page;
+ unsigned int eccsize = nand->ecc.size;
+ unsigned int eccbytes = nand->ecc.bytes;
+ unsigned int eccstrength = nand->ecc.strength;
+
+ if (!eccbytes && eccstrength) {
+ eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
+ nand->ecc.bytes = eccbytes;
+ }
+
+ if (!eccsize || !eccbytes) {
+ printk(KERN_WARNING "ecc parameters not supplied\n");
+ goto fail;
+ }
+
+ m = fls(1+8*eccsize);
+ t = (eccbytes*8)/m;
+
+ nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
+ if (!nbc)
+ goto fail;
+
+ nbc->bch = init_bch(m, t, 0);
+ if (!nbc->bch)
+ goto fail;
+
+ /* verify that eccbytes has the expected value */
+ if (nbc->bch->ecc_bytes != eccbytes) {
+ printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
+ eccbytes, nbc->bch->ecc_bytes);
+ goto fail;
+ }
+
+ eccsteps = mtd->writesize/eccsize;
+
+ /* if no ecc placement scheme was provided, build one */
+ if (!layout) {
+
+ /* handle large page devices only */
+ if (mtd->oobsize < 64) {
+ printk(KERN_WARNING "must provide an oob scheme for "
+ "oobsize %d\n", mtd->oobsize);
+ goto fail;
+ }
+
+ layout = &nbc->ecclayout;
+ layout->eccbytes = eccsteps*eccbytes;
+
+ /* reserve 2 bytes for bad block marker */
+ if (layout->eccbytes+2 > mtd->oobsize) {
+ printk(KERN_WARNING "no suitable oob scheme available "
+ "for oobsize %d eccbytes %u\n", mtd->oobsize,
+ eccbytes);
+ goto fail;
+ }
+ /* put ecc bytes at oob tail */
+ for (i = 0; i < layout->eccbytes; i++)
+ layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
+
+ nand->ecc.layout = layout;
+ }
+
+ /* sanity checks */
+ if (8*(eccsize+eccbytes) >= (1 << m)) {
+ printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
+ goto fail;
+ }
+ if (layout->eccbytes != (eccsteps*eccbytes)) {
+ printk(KERN_WARNING "invalid ecc layout\n");
+ goto fail;
+ }
+
+ nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
+ nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
+ if (!nbc->eccmask || !nbc->errloc)
+ goto fail;
+ /*
+ * compute and store the inverted ecc of an erased ecc block
+ */
+ erased_page = kmalloc(eccsize, GFP_KERNEL);
+ if (!erased_page)
+ goto fail;
+
+ memset(erased_page, 0xff, eccsize);
+ memset(nbc->eccmask, 0, eccbytes);
+ encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
+ kfree(erased_page);
+
+ for (i = 0; i < eccbytes; i++)
+ nbc->eccmask[i] ^= 0xff;
+
+ if (!eccstrength)
+ nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
+
+ return nbc;
+fail:
+ nand_bch_free(nbc);
+ return NULL;
+}
+
+/**
+ * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
+ * @nbc: NAND BCH control structure
+ */
+void nand_bch_free(struct nand_bch_control *nbc)
+{
+ if (nbc) {
+ free_bch(nbc->bch);
+ kfree(nbc->errloc);
+ kfree(nbc->eccmask);
+ kfree(nbc);
+ }
+}
diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c
new file mode 100644
index 0000000000..2bc329be1a
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_ecc.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/raw/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+ * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include <common.h>
+
+#include <linux/errno.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand_ecc.h>
+
+/*
+ * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(),
+ * only nand_correct_data() is needed
+ */
+
+#if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC)
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const u_char nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
+ * @mtd: MTD block structure
+ * @dat: raw data
+ * @ecc_code: buffer for ECC
+ */
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
+ int i;
+
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+
+ /* Build up column parity */
+ for(i = 0; i < 256; i++) {
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[*dat++];
+ reg1 ^= (idx & 0x3f);
+
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (uint8_t) i;
+ reg2 ^= ~((uint8_t) i);
+ }
+ }
+
+ /* Create non-inverted ECC code from line parity */
+ tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
+ tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
+ tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
+ tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
+ tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
+ tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
+ tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
+ tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
+
+ tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
+ tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
+ tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
+ tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
+ tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
+ tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
+ tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
+ tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
+
+ /* Calculate final ECC code */
+ ecc_code[0] = ~tmp1;
+ ecc_code[1] = ~tmp2;
+ ecc_code[2] = ((~reg1) << 2) | 0x03;
+
+ return 0;
+}
+#endif /* CONFIG_NAND_SPL */
+
+static inline int countbits(uint32_t byte)
+{
+ int res = 0;
+
+ for (;byte; byte >>= 1)
+ res += byte & 0x01;
+ return res;
+}
+
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd: MTD block structure
+ * @dat: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+int nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ uint8_t s0, s1, s2;
+
+ s1 = calc_ecc[0] ^ read_ecc[0];
+ s0 = calc_ecc[1] ^ read_ecc[1];
+ s2 = calc_ecc[2] ^ read_ecc[2];
+ if ((s0 | s1 | s2) == 0)
+ return 0;
+
+ /* Check for a single bit error */
+ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+ ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+ ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+ uint32_t byteoffs, bitnum;
+
+ byteoffs = (s1 << 0) & 0x80;
+ byteoffs |= (s1 << 1) & 0x40;
+ byteoffs |= (s1 << 2) & 0x20;
+ byteoffs |= (s1 << 3) & 0x10;
+
+ byteoffs |= (s0 >> 4) & 0x08;
+ byteoffs |= (s0 >> 3) & 0x04;
+ byteoffs |= (s0 >> 2) & 0x02;
+ byteoffs |= (s0 >> 1) & 0x01;
+
+ bitnum = (s2 >> 5) & 0x04;
+ bitnum |= (s2 >> 4) & 0x02;
+ bitnum |= (s2 >> 3) & 0x01;
+
+ dat[byteoffs] ^= (1 << bitnum);
+
+ return 1;
+ }
+
+ if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+ return 1;
+
+ return -EBADMSG;
+}
diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
new file mode 100644
index 0000000000..4009d64123
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_ids.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <common.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
+
+#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
+#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
+
+#define SP_OPTIONS NAND_NEED_READRDY
+#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
+
+/*
+ * The chip ID list:
+ * name, device ID, page size, chip size in MiB, eraseblock size, options
+ *
+ * If page size and eraseblock size are 0, the sizes are taken from the
+ * extended chip ID.
+ */
+struct nand_flash_dev nand_flash_ids[] = {
+#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
+ LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS),
+
+ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS),
+#endif
+ /*
+ * Some incompatible NAND chips share device ID's and so must be
+ * listed by full ID. We list them first so that we can easily identify
+ * the most specific match.
+ */
+ {"TC58NVG0S3E 1G 3.3V 8-bit",
+ { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
+ SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
+ 2 },
+ {"TC58NVG2S0F 4G 3.3V 8-bit",
+ { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
+ SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
+ {"TC58NVG2S0H 4G 3.3V 8-bit",
+ { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
+ SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
+ {"TC58NVG3S0F 8G 3.3V 8-bit",
+ { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
+ SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
+ {"TC58NVG5D2 32G 3.3V 8-bit",
+ { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
+ SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"TC58NVG6D2 64G 3.3V 8-bit",
+ { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
+ SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+ {"SDTNRGAMA 64G 3.3V 8-bit",
+ { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
+ SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
+ {"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
+ { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
+ SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
+ NAND_ECC_INFO(40, SZ_1K), 4 },
+ {"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit",
+ { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} },
+ SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664,
+ NAND_ECC_INFO(56, SZ_1K), 1 },
+
+ LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),
+
+ LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
+ LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),
+
+ LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),
+
+ /*
+ * These are the new chips with large page size. Their page size and
+ * eraseblock size are determined from the extended ID bytes.
+ */
+
+ /* 512 Megabit */
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16),
+
+ /* 1 Gigabit */
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),
+
+ /* 2 Gigabit */
+ EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
+
+ /* 4 Gigabit */
+ EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),
+
+ /* 8 Gigabit */
+ EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),
+
+ /* 16 Gigabit */
+ EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),
+
+ /* 32 Gigabit */
+ EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),
+
+ /* 64 Gigabit */
+ EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),
+
+ /* 128 Gigabit */
+ EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),
+
+ /* 256 Gigabit */
+ EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),
+
+ /* 512 Gigabit */
+ EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS),
+ EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
+ EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),
+
+ {NULL}
+};
+
+/* Manufacturer IDs */
+struct nand_manufacturers nand_manuf_ids[] = {
+ {NAND_MFR_TOSHIBA, "Toshiba"},
+ {NAND_MFR_SAMSUNG, "Samsung"},
+ {NAND_MFR_FUJITSU, "Fujitsu"},
+ {NAND_MFR_NATIONAL, "National"},
+ {NAND_MFR_RENESAS, "Renesas"},
+ {NAND_MFR_STMICRO, "ST Micro"},
+ {NAND_MFR_HYNIX, "Hynix"},
+ {NAND_MFR_MICRON, "Micron"},
+ {NAND_MFR_AMD, "AMD/Spansion"},
+ {NAND_MFR_MACRONIX, "Macronix"},
+ {NAND_MFR_EON, "Eon"},
+ {NAND_MFR_SANDISK, "SanDisk"},
+ {NAND_MFR_INTEL, "Intel"},
+ {NAND_MFR_ATO, "ATO"},
+ {0x0, "Unknown"}
+};
+
+EXPORT_SYMBOL(nand_manuf_ids);
+EXPORT_SYMBOL(nand_flash_ids);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("Nand device & manufacturer IDs");
diff --git a/drivers/mtd/nand/raw/nand_plat.c b/drivers/mtd/nand/raw/nand_plat.c
new file mode 100644
index 0000000000..335c3e3471
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_plat.c
@@ -0,0 +1,64 @@
+/*
+ * Genericish driver for memory mapped NAND devices
+ *
+ * Copyright (c) 2006-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+/* Your board must implement the following macros:
+ * NAND_PLAT_WRITE_CMD(chip, cmd)
+ * NAND_PLAT_WRITE_ADR(chip, cmd)
+ * NAND_PLAT_INIT()
+ *
+ * It may also implement the following:
+ * NAND_PLAT_DEV_READY(chip)
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#ifdef NAND_PLAT_GPIO_DEV_READY
+# include <asm/gpio.h>
+# define NAND_PLAT_DEV_READY(chip) gpio_get_value(NAND_PLAT_GPIO_DEV_READY)
+#endif
+
+#include <nand.h>
+
+static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ NAND_PLAT_WRITE_CMD(this, cmd);
+ else
+ NAND_PLAT_WRITE_ADR(this, cmd);
+}
+
+#ifdef NAND_PLAT_DEV_READY
+static int plat_dev_ready(struct mtd_info *mtd)
+{
+ return NAND_PLAT_DEV_READY((struct nand_chip *)mtd_to_nand(mtd));
+}
+#else
+# define plat_dev_ready NULL
+#endif
+
+int board_nand_init(struct nand_chip *nand)
+{
+#ifdef NAND_PLAT_GPIO_DEV_READY
+ gpio_request(NAND_PLAT_GPIO_DEV_READY, "nand-plat");
+ gpio_direction_input(NAND_PLAT_GPIO_DEV_READY);
+#endif
+
+#ifdef NAND_PLAT_INIT
+ NAND_PLAT_INIT();
+#endif
+
+ nand->cmd_ctrl = plat_cmd_ctrl;
+ nand->dev_ready = plat_dev_ready;
+ nand->ecc.mode = NAND_ECC_SOFT;
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/nand_spl_load.c b/drivers/mtd/nand/raw/nand_spl_load.c
new file mode 100644
index 0000000000..ecd373e054
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_spl_load.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011
+ * Heiko Schocher, DENX Software Engineering, hs@denx.de.
+ */
+
+#include <common.h>
+#include <nand.h>
+
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+
+ /*
+ * Load U-Boot image from NAND into RAM
+ */
+ nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
+ CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (void *)CONFIG_SYS_NAND_U_BOOT_DST);
+
+#ifdef CONFIG_NAND_ENV_DST
+ nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST);
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
+ (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
+#endif
+#endif
+
+ /*
+ * Jump to U-Boot image
+ */
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ (*uboot)();
+}
diff --git a/drivers/mtd/nand/raw/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c
new file mode 100644
index 0000000000..177c12b581
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_spl_loaders.c
@@ -0,0 +1,104 @@
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+ unsigned int block, lastblock;
+ unsigned int page, page_offset;
+
+ /* offs has to be aligned to a page address! */
+ block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
+ lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
+ page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
+ page_offset = offs % CONFIG_SYS_NAND_PAGE_SIZE;
+
+ while (block <= lastblock) {
+ if (!nand_is_bad_block(block)) {
+ /* Skip bad blocks */
+ while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
+ nand_read_page(block, page, dst);
+ /*
+ * When offs is not aligned to page address the
+ * extra offset is copied to dst as well. Copy
+ * the image such that its first byte will be
+ * at the dst.
+ */
+ if (unlikely(page_offset)) {
+ memmove(dst, dst + page_offset,
+ CONFIG_SYS_NAND_PAGE_SIZE);
+ dst = (void *)((int)dst - page_offset);
+ page_offset = 0;
+ }
+ dst += CONFIG_SYS_NAND_PAGE_SIZE;
+ page++;
+ }
+
+ page = 0;
+ } else {
+ lastblock++;
+ }
+
+ block++;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SPL_UBI
+/*
+ * Temporary storage for non NAND page aligned and non NAND page sized
+ * reads. Note: This does not support runtime detected FLASH yet, but
+ * that should be reasonably easy to fix by making the buffer large
+ * enough :)
+ */
+static u8 scratch_buf[CONFIG_SYS_NAND_PAGE_SIZE];
+
+/**
+ * nand_spl_read_block - Read data from physical eraseblock into a buffer
+ * @block: Number of the physical eraseblock
+ * @offset: Data offset from the start of @peb
+ * @len: Data size to read
+ * @dst: Address of the destination buffer
+ *
+ * This could be further optimized if we'd have a subpage read
+ * function in the simple code. On NAND which allows subpage reads
+ * this would spare quite some time to readout e.g. the VID header of
+ * UBI.
+ *
+ * Notes:
+ * @offset + @len are not allowed to be larger than a physical
+ * erase block. No sanity check done for simplicity reasons.
+ *
+ * To support runtime detected flash this needs to be extended by
+ * information about the actual flash geometry, but thats beyond the
+ * scope of this effort and for most applications where fast boot is
+ * required it is not an issue anyway.
+ */
+int nand_spl_read_block(int block, int offset, int len, void *dst)
+{
+ int page, read;
+
+ /* Calculate the page number */
+ page = offset / CONFIG_SYS_NAND_PAGE_SIZE;
+
+ /* Offset to the start of a flash page */
+ offset = offset % CONFIG_SYS_NAND_PAGE_SIZE;
+
+ while (len) {
+ /*
+ * Non page aligned reads go to the scratch buffer.
+ * Page aligned reads go directly to the destination.
+ */
+ if (offset || len < CONFIG_SYS_NAND_PAGE_SIZE) {
+ nand_read_page(block, page, scratch_buf);
+ read = min(len, CONFIG_SYS_NAND_PAGE_SIZE - offset);
+ memcpy(dst, scratch_buf + offset, read);
+ offset = 0;
+ } else {
+ nand_read_page(block, page, dst);
+ read = CONFIG_SYS_NAND_PAGE_SIZE;
+ }
+ page++;
+ len -= read;
+ dst += read;
+ }
+ return 0;
+}
+#endif
diff --git a/drivers/mtd/nand/raw/nand_spl_simple.c b/drivers/mtd/nand/raw/nand_spl_simple.c
new file mode 100644
index 0000000000..09e053541a
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_spl_simple.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+#include <linux/mtd/nand_ecc.h>
+
+static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
+static struct mtd_info *mtd;
+static struct nand_chip nand_chip;
+
+#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
+ CONFIG_SYS_NAND_ECCSIZE)
+#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
+
+
+#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512)
+/*
+ * NAND command for small page NAND devices (512)
+ */
+static int nand_command(int block, int page, uint32_t offs,
+ u8 cmd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ /* Begin command latch cycle */
+ this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ /* Set ALE and clear CLE to start address cycle */
+ /* Column address */
+ this->cmd_ctrl(mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
+ this->cmd_ctrl(mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */
+ this->cmd_ctrl(mtd, (page_addr >> 8) & 0xff,
+ NAND_CTRL_ALE); /* A[24:17] */
+#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE
+ /* One more address cycle for devices > 32MiB */
+ this->cmd_ctrl(mtd, (page_addr >> 16) & 0x0f,
+ NAND_CTRL_ALE); /* A[28:25] */
+#endif
+ /* Latch in address */
+ this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Wait a while for the data to be ready
+ */
+ while (!this->dev_ready(mtd))
+ ;
+
+ return 0;
+}
+#else
+/*
+ * NAND command for large page NAND devices (2k)
+ */
+static int nand_command(int block, int page, uint32_t offs,
+ u8 cmd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
+ void (*hwctrl)(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl) = this->cmd_ctrl;
+
+ while (!this->dev_ready(mtd))
+ ;
+
+ /* Emulate NAND_CMD_READOOB */
+ if (cmd == NAND_CMD_READOOB) {
+ offs += CONFIG_SYS_NAND_PAGE_SIZE;
+ cmd = NAND_CMD_READ0;
+ }
+
+ /* Shift the offset from byte addressing to word addressing. */
+ if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
+ offs >>= 1;
+
+ /* Begin command latch cycle */
+ hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ /* Set ALE and clear CLE to start address cycle */
+ /* Column address */
+ hwctrl(mtd, offs & 0xff,
+ NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
+ hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
+ /* Row address */
+ hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */
+ hwctrl(mtd, ((page_addr >> 8) & 0xff),
+ NAND_CTRL_ALE); /* A[27:20] */
+#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
+ /* One more address cycle for devices > 128MiB */
+ hwctrl(mtd, (page_addr >> 16) & 0x0f,
+ NAND_CTRL_ALE); /* A[31:28] */
+#endif
+ /* Latch in address */
+ hwctrl(mtd, NAND_CMD_READSTART,
+ NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+ hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Wait a while for the data to be ready
+ */
+ while (!this->dev_ready(mtd))
+ ;
+
+ return 0;
+}
+#endif
+
+static int nand_is_bad_block(int block)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char bb_data[2];
+
+ nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS,
+ NAND_CMD_READOOB);
+
+ /*
+ * Read one byte (or two if it's a 16 bit chip).
+ */
+ if (this->options & NAND_BUSWIDTH_16) {
+ this->read_buf(mtd, bb_data, 2);
+ if (bb_data[0] != 0xff || bb_data[1] != 0xff)
+ return 1;
+ } else {
+ this->read_buf(mtd, bb_data, 1);
+ if (bb_data[0] != 0xff)
+ return 1;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST)
+static int nand_read_page(int block, int page, uchar *dst)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ecc_calc[ECCTOTAL];
+ u_char ecc_code[ECCTOTAL];
+ u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
+ int i;
+ int eccsize = CONFIG_SYS_NAND_ECCSIZE;
+ int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
+ int eccsteps = ECCSTEPS;
+ uint8_t *p = dst;
+
+ nand_command(block, page, 0, NAND_CMD_READOOB);
+ this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
+ nand_command(block, page, 0, NAND_CMD_READ0);
+
+ /* Pick the ECC bytes out of the oob data */
+ for (i = 0; i < ECCTOTAL; i++)
+ ecc_code[i] = oob_data[nand_ecc_pos[i]];
+
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ this->ecc.hwctl(mtd, NAND_ECC_READ);
+ this->read_buf(mtd, p, eccsize);
+ this->ecc.calculate(mtd, p, &ecc_calc[i]);
+ this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ }
+
+ return 0;
+}
+#else
+static int nand_read_page(int block, int page, void *dst)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ u_char ecc_calc[ECCTOTAL];
+ u_char ecc_code[ECCTOTAL];
+ u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
+ int i;
+ int eccsize = CONFIG_SYS_NAND_ECCSIZE;
+ int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
+ int eccsteps = ECCSTEPS;
+ uint8_t *p = dst;
+
+ nand_command(block, page, 0, NAND_CMD_READ0);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ if (this->ecc.mode != NAND_ECC_SOFT)
+ this->ecc.hwctl(mtd, NAND_ECC_READ);
+ this->read_buf(mtd, p, eccsize);
+ this->ecc.calculate(mtd, p, &ecc_calc[i]);
+ }
+ this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
+
+ /* Pick the ECC bytes out of the oob data */
+ for (i = 0; i < ECCTOTAL; i++)
+ ecc_code[i] = oob_data[nand_ecc_pos[i]];
+
+ eccsteps = ECCSTEPS;
+ p = dst;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ /* No chance to do something with the possible error message
+ * from correct_data(). We just hope that all possible errors
+ * are corrected by this routine.
+ */
+ this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ }
+
+ return 0;
+}
+#endif
+
+/* nand_init() - initialize data to make nand usable by SPL */
+void nand_init(void)
+{
+ /*
+ * Init board specific nand support
+ */
+ mtd = nand_to_mtd(&nand_chip);
+ nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W =
+ (void __iomem *)CONFIG_SYS_NAND_BASE;
+ board_nand_init(&nand_chip);
+
+#ifdef CONFIG_SPL_NAND_SOFTECC
+ if (nand_chip.ecc.mode == NAND_ECC_SOFT) {
+ nand_chip.ecc.calculate = nand_calculate_ecc;
+ nand_chip.ecc.correct = nand_correct_data;
+ }
+#endif
+
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, 0);
+}
+
+/* Unselect after operation */
+void nand_deselect(void)
+{
+ if (nand_chip.select_chip)
+ nand_chip.select_chip(mtd, -1);
+}
+
+#include "nand_spl_loaders.c"
diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c
new file mode 100644
index 0000000000..c0545a4fb1
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_timings.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <common.h>
+#include <linux/kernel.h>
+#include <linux/mtd/rawnand.h>
+
+static const struct nand_data_interface onfi_sdr_timings[] = {
+ /* Mode 0 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 20000,
+ .tALS_min = 50000,
+ .tAR_min = 25000,
+ .tCEA_max = 100000,
+ .tCEH_min = 20000,
+ .tCH_min = 20000,
+ .tCHZ_max = 100000,
+ .tCLH_min = 20000,
+ .tCLR_min = 20000,
+ .tCLS_min = 50000,
+ .tCOH_min = 0,
+ .tCS_min = 70000,
+ .tDH_min = 20000,
+ .tDS_min = 40000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 10000,
+ .tITC_max = 1000000,
+ .tRC_min = 100000,
+ .tREA_max = 40000,
+ .tREH_min = 30000,
+ .tRHOH_min = 0,
+ .tRHW_min = 200000,
+ .tRHZ_max = 200000,
+ .tRLOH_min = 0,
+ .tRP_min = 50000,
+ .tRR_min = 40000,
+ .tRST_max = 250000000000ULL,
+ .tWB_max = 200000,
+ .tWC_min = 100000,
+ .tWH_min = 30000,
+ .tWHR_min = 120000,
+ .tWP_min = 50000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 1 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 10000,
+ .tALS_min = 25000,
+ .tAR_min = 10000,
+ .tCEA_max = 45000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 25000,
+ .tCOH_min = 15000,
+ .tCS_min = 35000,
+ .tDH_min = 10000,
+ .tDS_min = 20000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 50000,
+ .tREA_max = 30000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 25000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 45000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 25000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 2 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 10000,
+ .tALS_min = 15000,
+ .tAR_min = 10000,
+ .tCEA_max = 30000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 15000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 15000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 35000,
+ .tREA_max = 25000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tRP_min = 17000,
+ .tWC_min = 35000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 17000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 3 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 30000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 15000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 30000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 15000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 4 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 20000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 25000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 12000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 25000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 12000,
+ .tWW_min = 100000,
+ },
+ },
+ /* Mode 5 */
+ {
+ .type = NAND_SDR_IFACE,
+ .timings.sdr = {
+ .tCCS_min = 500000,
+ .tR_max = 200000000,
+ .tADL_min = 400000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 15000,
+ .tDH_min = 5000,
+ .tDS_min = 7000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 20000,
+ .tREA_max = 16000,
+ .tREH_min = 7000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 10000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 20000,
+ .tWH_min = 7000,
+ .tWHR_min = 80000,
+ .tWP_min = 10000,
+ .tWW_min = 100000,
+ },
+ },
+};
+
+/**
+ * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
+ * timings according to the given ONFI timing mode
+ * @mode: ONFI timing mode
+ */
+const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
+{
+ if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
+ return ERR_PTR(-EINVAL);
+
+ return &onfi_sdr_timings[mode].timings.sdr;
+}
+EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
+
+/**
+ * onfi_init_data_interface - [NAND Interface] Initialize a data interface from
+ * given ONFI mode
+ * @iface: The data interface to be initialized
+ * @mode: The ONFI timing mode
+ */
+int onfi_init_data_interface(struct nand_chip *chip,
+ struct nand_data_interface *iface,
+ enum nand_data_interface_type type,
+ int timing_mode)
+{
+ if (type != NAND_SDR_IFACE)
+ return -EINVAL;
+
+ if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
+ return -EINVAL;
+
+ *iface = onfi_sdr_timings[timing_mode];
+
+ /*
+ * Initialize timings that cannot be deduced from timing mode:
+ * tR, tPROG, tCCS, ...
+ * These information are part of the ONFI parameter page.
+ */
+ if (chip->onfi_version) {
+ struct nand_onfi_params *params = &chip->onfi_params;
+ struct nand_sdr_timings *timings = &iface->timings.sdr;
+
+ /* microseconds -> picoseconds */
+ timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog);
+ timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers);
+ timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r);
+
+ /* nanoseconds -> picoseconds */
+ timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(onfi_init_data_interface);
+
+/**
+ * nand_get_default_data_interface - [NAND Interface] Retrieve NAND
+ * data interface for mode 0. This is used as default timing after
+ * reset.
+ */
+const struct nand_data_interface *nand_get_default_data_interface(void)
+{
+ return &onfi_sdr_timings[0];
+}
+EXPORT_SYMBOL(nand_get_default_data_interface);
diff --git a/drivers/mtd/nand/raw/nand_util.c b/drivers/mtd/nand/raw/nand_util.c
new file mode 100644
index 0000000000..fc2235c1a0
--- /dev/null
+++ b/drivers/mtd/nand/raw/nand_util.c
@@ -0,0 +1,904 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/mtd/nand/raw/nand_util.c
+ *
+ * Copyright (C) 2006 by Weiss-Electronic GmbH.
+ * All rights reserved.
+ *
+ * @author: Guido Classen <clagix@gmail.com>
+ * @descr: NAND Flash support
+ * @references: borrowed heavily from Linux mtd-utils code:
+ * flash_eraseall.c by Arcom Control System Ltd
+ * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com)
+ * and Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by
+ * Artem Bityutskiy <dedekind1@gmail.com> from mtd-utils
+ *
+ * Copyright 2010 Freescale Semiconductor
+ */
+
+#include <common.h>
+#include <command.h>
+#include <watchdog.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <div64.h>
+
+#include <linux/errno.h>
+#include <linux/mtd/mtd.h>
+#include <nand.h>
+#include <jffs2/jffs2.h>
+
+typedef struct erase_info erase_info_t;
+typedef struct mtd_info mtd_info_t;
+
+/* support only for native endian JFFS2 */
+#define cpu_to_je16(x) (x)
+#define cpu_to_je32(x) (x)
+
+/**
+ * nand_erase_opts: - erase NAND flash with support for various options
+ * (jffs2 formatting)
+ *
+ * @param mtd nand mtd instance to erase
+ * @param opts options, @see struct nand_erase_options
+ * @return 0 in case of success
+ *
+ * This code is ported from flash_eraseall.c from Linux mtd utils by
+ * Arcom Control System Ltd.
+ */
+int nand_erase_opts(struct mtd_info *mtd,
+ const nand_erase_options_t *opts)
+{
+ struct jffs2_unknown_node cleanmarker;
+ erase_info_t erase;
+ unsigned long erase_length, erased_length; /* in blocks */
+ int result;
+ int percent_complete = -1;
+ const char *mtd_device = mtd->name;
+ struct mtd_oob_ops oob_opts;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if ((opts->offset & (mtd->erasesize - 1)) != 0) {
+ printf("Attempt to erase non block-aligned data\n");
+ return -1;
+ }
+
+ memset(&erase, 0, sizeof(erase));
+ memset(&oob_opts, 0, sizeof(oob_opts));
+
+ erase.mtd = mtd;
+ erase.len = mtd->erasesize;
+ erase.addr = opts->offset;
+ erase_length = lldiv(opts->length + mtd->erasesize - 1,
+ mtd->erasesize);
+
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.totlen = cpu_to_je32(8);
+
+ /* scrub option allows to erase badblock. To prevent internal
+ * check from erase() method, set block check method to dummy
+ * and disable bad block table while erasing.
+ */
+ if (opts->scrub) {
+ erase.scrub = opts->scrub;
+ /*
+ * We don't need the bad block table anymore...
+ * after scrub, there are no bad blocks left!
+ */
+ if (chip->bbt) {
+ kfree(chip->bbt);
+ }
+ chip->bbt = NULL;
+ chip->options &= ~NAND_BBT_SCANNED;
+ }
+
+ for (erased_length = 0;
+ erased_length < erase_length;
+ erase.addr += mtd->erasesize) {
+
+ WATCHDOG_RESET();
+
+ if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) {
+ puts("Size of erase exceeds limit\n");
+ return -EFBIG;
+ }
+ if (!opts->scrub) {
+ int ret = mtd_block_isbad(mtd, erase.addr);
+ if (ret > 0) {
+ if (!opts->quiet)
+ printf("\rSkipping bad block at "
+ "0x%08llx "
+ " \n",
+ erase.addr);
+
+ if (!opts->spread)
+ erased_length++;
+
+ continue;
+
+ } else if (ret < 0) {
+ printf("\n%s: MTD get bad block failed: %d\n",
+ mtd_device,
+ ret);
+ return -1;
+ }
+ }
+
+ erased_length++;
+
+ result = mtd_erase(mtd, &erase);
+ if (result != 0) {
+ printf("\n%s: MTD Erase failure: %d\n",
+ mtd_device, result);
+ continue;
+ }
+
+ /* format for JFFS2 ? */
+ if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) {
+ struct mtd_oob_ops ops;
+ ops.ooblen = 8;
+ ops.datbuf = NULL;
+ ops.oobbuf = (uint8_t *)&cleanmarker;
+ ops.ooboffs = 0;
+ ops.mode = MTD_OPS_AUTO_OOB;
+
+ result = mtd_write_oob(mtd, erase.addr, &ops);
+ if (result != 0) {
+ printf("\n%s: MTD writeoob failure: %d\n",
+ mtd_device, result);
+ continue;
+ }
+ }
+
+ if (!opts->quiet) {
+ unsigned long long n = erased_length * 100ULL;
+ int percent;
+
+ do_div(n, erase_length);
+ percent = (int)n;
+
+ /* output progress message only at whole percent
+ * steps to reduce the number of messages printed
+ * on (slow) serial consoles
+ */
+ if (percent != percent_complete) {
+ percent_complete = percent;
+
+ printf("\rErasing at 0x%llx -- %3d%% complete.",
+ erase.addr, percent);
+
+ if (opts->jffs2 && result == 0)
+ printf(" Cleanmarker written at 0x%llx.",
+ erase.addr);
+ }
+ }
+ }
+ if (!opts->quiet)
+ printf("\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
+
+#define NAND_CMD_LOCK_TIGHT 0x2c
+#define NAND_CMD_LOCK_STATUS 0x7a
+
+/******************************************************************************
+ * Support for locking / unlocking operations of some NAND devices
+ *****************************************************************************/
+
+/**
+ * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
+ * state
+ *
+ * @param mtd nand mtd instance
+ * @param tight bring device in lock tight mode
+ *
+ * @return 0 on success, -1 in case of error
+ *
+ * The lock / lock-tight command only applies to the whole chip. To get some
+ * parts of the chip lock and others unlocked use the following sequence:
+ *
+ * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin)
+ * - Call nand_unlock() once for each consecutive area to be unlocked
+ * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1)
+ *
+ * If the device is in lock-tight state software can't change the
+ * current active lock/unlock state of all pages. nand_lock() / nand_unlock()
+ * calls will fail. It is only posible to leave lock-tight state by
+ * an hardware signal (low pulse on _WP pin) or by power down.
+ */
+int nand_lock(struct mtd_info *mtd, int tight)
+{
+ int ret = 0;
+ int status;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* select the NAND device */
+ chip->select_chip(mtd, 0);
+
+ /* check the Lock Tight Status */
+ chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0);
+ if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) {
+ printf("nand_lock: Device is locked tight!\n");
+ ret = -1;
+ goto out;
+ }
+
+ chip->cmdfunc(mtd,
+ (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
+ -1, -1);
+
+ /* call wait ready function */
+ status = chip->waitfunc(mtd, chip);
+
+ /* see if device thinks it succeeded */
+ if (status & 0x01) {
+ ret = -1;
+ }
+
+ out:
+ /* de-select the NAND device */
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+
+/**
+ * nand_get_lock_status: - query current lock state from one page of NAND
+ * flash
+ *
+ * @param mtd nand mtd instance
+ * @param offset page address to query (must be page-aligned!)
+ *
+ * @return -1 in case of error
+ * >0 lock status:
+ * bitfield with the following combinations:
+ * NAND_LOCK_STATUS_TIGHT: page in tight state
+ * NAND_LOCK_STATUS_UNLOCK: page unlocked
+ *
+ */
+int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
+{
+ int ret = 0;
+ int chipnr;
+ int page;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* select the NAND device */
+ chipnr = (int)(offset >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+
+ if ((offset & (mtd->writesize - 1)) != 0) {
+ printf("nand_get_lock_status: "
+ "Start address must be beginning of "
+ "nand page!\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check the Lock Status */
+ page = (int)(offset >> chip->page_shift);
+ chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
+
+ ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT
+ | NAND_LOCK_STATUS_UNLOCK);
+
+ out:
+ /* de-select the NAND device */
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+
+/**
+ * nand_unlock: - Unlock area of NAND pages
+ * only one consecutive area can be unlocked at one time!
+ *
+ * @param mtd nand mtd instance
+ * @param start start byte address
+ * @param length number of bytes to unlock (must be a multiple of
+ * page size mtd->writesize)
+ * @param allexcept if set, unlock everything not selected
+ *
+ * @return 0 on success, -1 in case of error
+ */
+int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
+ int allexcept)
+{
+ int ret = 0;
+ int chipnr;
+ int status;
+ int page;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ debug("nand_unlock%s: start: %08llx, length: %zd!\n",
+ allexcept ? " (allexcept)" : "", start, length);
+
+ /* select the NAND device */
+ chipnr = (int)(start >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ /* check the WP bit */
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
+ printf("nand_unlock: Device is write protected!\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check the Lock Tight Status */
+ page = (int)(start >> chip->page_shift);
+ chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
+ if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) {
+ printf("nand_unlock: Device is locked tight!\n");
+ ret = -1;
+ goto out;
+ }
+
+ if ((start & (mtd->erasesize - 1)) != 0) {
+ printf("nand_unlock: Start address must be beginning of "
+ "nand block!\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
+ printf("nand_unlock: Length must be a multiple of nand block "
+ "size %08x!\n", mtd->erasesize);
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * Set length so that the last address is set to the
+ * starting address of the last block
+ */
+ length -= mtd->erasesize;
+
+ /* submit address of first page to unlock */
+ chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
+
+ /* submit ADDRESS of LAST page to unlock */
+ page += (int)(length >> chip->page_shift);
+
+ /*
+ * Page addresses for unlocking are supposed to be block-aligned.
+ * At least some NAND chips use the low bit to indicate that the
+ * page range should be inverted.
+ */
+ if (allexcept)
+ page |= 1;
+
+ chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);
+
+ /* call wait ready function */
+ status = chip->waitfunc(mtd, chip);
+ /* see if device thinks it succeeded */
+ if (status & 0x01) {
+ /* there was an error */
+ ret = -1;
+ goto out;
+ }
+
+ out:
+ /* de-select the NAND device */
+ chip->select_chip(mtd, -1);
+ return ret;
+}
+#endif
+
+/**
+ * check_skip_len
+ *
+ * Check if there are any bad blocks, and whether length including bad
+ * blocks fits into device
+ *
+ * @param mtd nand mtd instance
+ * @param offset offset in flash
+ * @param length image length
+ * @param used length of flash needed for the requested length
+ * @return 0 if the image fits and there are no bad blocks
+ * 1 if the image fits, but there are bad blocks
+ * -1 if the image does not fit
+ */
+static int check_skip_len(struct mtd_info *mtd, loff_t offset, size_t length,
+ size_t *used)
+{
+ size_t len_excl_bad = 0;
+ int ret = 0;
+
+ while (len_excl_bad < length) {
+ size_t block_len, block_off;
+ loff_t block_start;
+
+ if (offset >= mtd->size)
+ return -1;
+
+ block_start = offset & ~(loff_t)(mtd->erasesize - 1);
+ block_off = offset & (mtd->erasesize - 1);
+ block_len = mtd->erasesize - block_off;
+
+ if (!nand_block_isbad(mtd, block_start))
+ len_excl_bad += block_len;
+ else
+ ret = 1;
+
+ offset += block_len;
+ *used += block_len;
+ }
+
+ /* If the length is not a multiple of block_len, adjust. */
+ if (len_excl_bad > length)
+ *used -= (len_excl_bad - length);
+
+ return ret;
+}
+
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+static size_t drop_ffs(const struct mtd_info *mtd, const u_char *buf,
+ const size_t *len)
+{
+ size_t l = *len;
+ ssize_t i;
+
+ for (i = l - 1; i >= 0; i--)
+ if (buf[i] != 0xFF)
+ break;
+
+ /* The resulting length must be aligned to the minimum flash I/O size */
+ l = i + 1;
+ l = (l + mtd->writesize - 1) / mtd->writesize;
+ l *= mtd->writesize;
+
+ /*
+ * since the input length may be unaligned, prevent access past the end
+ * of the buffer
+ */
+ return min(l, *len);
+}
+#endif
+
+/**
+ * nand_verify_page_oob:
+ *
+ * Verify a page of NAND flash, including the OOB.
+ * Reads page of NAND and verifies the contents and OOB against the
+ * values in ops.
+ *
+ * @param mtd nand mtd instance
+ * @param ops MTD operations, including data to verify
+ * @param ofs offset in flash
+ * @return 0 in case of success
+ */
+int nand_verify_page_oob(struct mtd_info *mtd, struct mtd_oob_ops *ops,
+ loff_t ofs)
+{
+ int rval;
+ struct mtd_oob_ops vops;
+ size_t verlen = mtd->writesize + mtd->oobsize;
+
+ memcpy(&vops, ops, sizeof(vops));
+
+ vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen);
+
+ if (!vops.datbuf)
+ return -ENOMEM;
+
+ vops.oobbuf = vops.datbuf + mtd->writesize;
+
+ rval = mtd_read_oob(mtd, ofs, &vops);
+ if (!rval)
+ rval = memcmp(ops->datbuf, vops.datbuf, vops.len);
+ if (!rval)
+ rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen);
+
+ free(vops.datbuf);
+
+ return rval ? -EIO : 0;
+}
+
+/**
+ * nand_verify:
+ *
+ * Verify a region of NAND flash.
+ * Reads NAND in page-sized chunks and verifies the contents against
+ * the contents of a buffer. The offset into the NAND must be
+ * page-aligned, and the function doesn't handle skipping bad blocks.
+ *
+ * @param mtd nand mtd instance
+ * @param ofs offset in flash
+ * @param len buffer length
+ * @param buf buffer to read from
+ * @return 0 in case of success
+ */
+int nand_verify(struct mtd_info *mtd, loff_t ofs, size_t len, u_char *buf)
+{
+ int rval = 0;
+ size_t verofs;
+ size_t verlen = mtd->writesize;
+ uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen);
+
+ if (!verbuf)
+ return -ENOMEM;
+
+ /* Read the NAND back in page-size groups to limit malloc size */
+ for (verofs = ofs; verofs < ofs + len;
+ verofs += verlen, buf += verlen) {
+ verlen = min(mtd->writesize, (uint32_t)(ofs + len - verofs));
+ rval = nand_read(mtd, verofs, &verlen, verbuf);
+ if (!rval || (rval == -EUCLEAN))
+ rval = memcmp(buf, verbuf, verlen);
+
+ if (rval)
+ break;
+ }
+
+ free(verbuf);
+
+ return rval ? -EIO : 0;
+}
+
+
+
+/**
+ * nand_write_skip_bad:
+ *
+ * Write image to NAND flash.
+ * Blocks that are marked bad are skipped and the is written to the next
+ * block instead as long as the image is short enough to fit even after
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested write. In the case where the write would
+ * extend beyond the end of the NAND device, both length and actual (if
+ * not NULL) are set to 0. In the case where the write would extend
+ * beyond the limit we are passed, length is set to 0 and actual is set
+ * to the required length.
+ *
+ * @param mtd nand mtd instance
+ * @param offset offset in flash
+ * @param length buffer length
+ * @param actual set to size required to write length worth of
+ * buffer or 0 on error, if not NULL
+ * @param lim maximum size that actual may be in order to not
+ * exceed the buffer
+ * @param buffer buffer to read from
+ * @param flags flags modifying the behaviour of the write to NAND
+ * @return 0 in case of success
+ */
+int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
+ size_t *actual, loff_t lim, u_char *buffer, int flags)
+{
+ int rval = 0, blocksize;
+ size_t left_to_write = *length;
+ size_t used_for_write = 0;
+ u_char *p_buffer = buffer;
+ int need_skip;
+
+ if (actual)
+ *actual = 0;
+
+ blocksize = mtd->erasesize;
+
+ /*
+ * nand_write() handles unaligned, partial page writes.
+ *
+ * We allow length to be unaligned, for convenience in
+ * using the $filesize variable.
+ *
+ * However, starting at an unaligned offset makes the
+ * semantics of bad block skipping ambiguous (really,
+ * you should only start a block skipping access at a
+ * partition boundary). So don't try to handle that.
+ */
+ if ((offset & (mtd->writesize - 1)) != 0) {
+ printf("Attempt to write non page-aligned data\n");
+ *length = 0;
+ return -EINVAL;
+ }
+
+ need_skip = check_skip_len(mtd, offset, *length, &used_for_write);
+
+ if (actual)
+ *actual = used_for_write;
+
+ if (need_skip < 0) {
+ printf("Attempt to write outside the flash area\n");
+ *length = 0;
+ return -EINVAL;
+ }
+
+ if (used_for_write > lim) {
+ puts("Size of write exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
+ if (!need_skip && !(flags & WITH_DROP_FFS)) {
+ rval = nand_write(mtd, offset, length, buffer);
+
+ if ((flags & WITH_WR_VERIFY) && !rval)
+ rval = nand_verify(mtd, offset, *length, buffer);
+
+ if (rval == 0)
+ return 0;
+
+ *length = 0;
+ printf("NAND write to offset %llx failed %d\n",
+ offset, rval);
+ return rval;
+ }
+
+ while (left_to_write > 0) {
+ size_t block_offset = offset & (mtd->erasesize - 1);
+ size_t write_size, truncated_write_size;
+
+ WATCHDOG_RESET();
+
+ if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) {
+ printf("Skip bad block 0x%08llx\n",
+ offset & ~(mtd->erasesize - 1));
+ offset += mtd->erasesize - block_offset;
+ continue;
+ }
+
+ if (left_to_write < (blocksize - block_offset))
+ write_size = left_to_write;
+ else
+ write_size = blocksize - block_offset;
+
+ truncated_write_size = write_size;
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+ if (flags & WITH_DROP_FFS)
+ truncated_write_size = drop_ffs(mtd, p_buffer,
+ &write_size);
+#endif
+
+ rval = nand_write(mtd, offset, &truncated_write_size,
+ p_buffer);
+
+ if ((flags & WITH_WR_VERIFY) && !rval)
+ rval = nand_verify(mtd, offset,
+ truncated_write_size, p_buffer);
+
+ offset += write_size;
+ p_buffer += write_size;
+
+ if (rval != 0) {
+ printf("NAND write to offset %llx failed %d\n",
+ offset, rval);
+ *length -= left_to_write;
+ return rval;
+ }
+
+ left_to_write -= write_size;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_read_skip_bad:
+ *
+ * Read image from NAND flash.
+ * Blocks that are marked bad are skipped and the next block is read
+ * instead as long as the image is short enough to fit even after
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested read. In the case where the read would extend
+ * beyond the end of the NAND device, both length and actual (if not
+ * NULL) are set to 0. In the case where the read would extend beyond
+ * the limit we are passed, length is set to 0 and actual is set to the
+ * required length.
+ *
+ * @param mtd nand mtd instance
+ * @param offset offset in flash
+ * @param length buffer length, on return holds number of read bytes
+ * @param actual set to size required to read length worth of buffer or 0
+ * on error, if not NULL
+ * @param lim maximum size that actual may be in order to not exceed the
+ * buffer
+ * @param buffer buffer to write to
+ * @return 0 in case of success
+ */
+int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
+ size_t *actual, loff_t lim, u_char *buffer)
+{
+ int rval;
+ size_t left_to_read = *length;
+ size_t used_for_read = 0;
+ u_char *p_buffer = buffer;
+ int need_skip;
+
+ if ((offset & (mtd->writesize - 1)) != 0) {
+ printf("Attempt to read non page-aligned data\n");
+ *length = 0;
+ if (actual)
+ *actual = 0;
+ return -EINVAL;
+ }
+
+ need_skip = check_skip_len(mtd, offset, *length, &used_for_read);
+
+ if (actual)
+ *actual = used_for_read;
+
+ if (need_skip < 0) {
+ printf("Attempt to read outside the flash area\n");
+ *length = 0;
+ return -EINVAL;
+ }
+
+ if (used_for_read > lim) {
+ puts("Size of read exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
+ if (!need_skip) {
+ rval = nand_read(mtd, offset, length, buffer);
+ if (!rval || rval == -EUCLEAN)
+ return 0;
+
+ *length = 0;
+ printf("NAND read from offset %llx failed %d\n",
+ offset, rval);
+ return rval;
+ }
+
+ while (left_to_read > 0) {
+ size_t block_offset = offset & (mtd->erasesize - 1);
+ size_t read_length;
+
+ WATCHDOG_RESET();
+
+ if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) {
+ printf("Skipping bad block 0x%08llx\n",
+ offset & ~(mtd->erasesize - 1));
+ offset += mtd->erasesize - block_offset;
+ continue;
+ }
+
+ if (left_to_read < (mtd->erasesize - block_offset))
+ read_length = left_to_read;
+ else
+ read_length = mtd->erasesize - block_offset;
+
+ rval = nand_read(mtd, offset, &read_length, p_buffer);
+ if (rval && rval != -EUCLEAN) {
+ printf("NAND read from offset %llx failed %d\n",
+ offset, rval);
+ *length -= left_to_read;
+ return rval;
+ }
+
+ left_to_read -= read_length;
+ offset += read_length;
+ p_buffer += read_length;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_CMD_NAND_TORTURE
+
+/**
+ * check_pattern:
+ *
+ * Check if buffer contains only a certain byte pattern.
+ *
+ * @param buf buffer to check
+ * @param patt the pattern to check
+ * @param size buffer size in bytes
+ * @return 1 if there are only patt bytes in buf
+ * 0 if something else was found
+ */
+static int check_pattern(const u_char *buf, u_char patt, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (buf[i] != patt)
+ return 0;
+ return 1;
+}
+
+/**
+ * nand_torture:
+ *
+ * Torture a block of NAND flash.
+ * This is useful to determine if a block that caused a write error is still
+ * good or should be marked as bad.
+ *
+ * @param mtd nand mtd instance
+ * @param offset offset in flash
+ * @return 0 if the block is still good
+ */
+int nand_torture(struct mtd_info *mtd, loff_t offset)
+{
+ u_char patterns[] = {0xa5, 0x5a, 0x00};
+ struct erase_info instr = {
+ .mtd = mtd,
+ .addr = offset,
+ .len = mtd->erasesize,
+ };
+ size_t retlen;
+ int err, ret = -1, i, patt_count;
+ u_char *buf;
+
+ if ((offset & (mtd->erasesize - 1)) != 0) {
+ puts("Attempt to torture a block at a non block-aligned offset\n");
+ return -EINVAL;
+ }
+
+ if (offset + mtd->erasesize > mtd->size) {
+ puts("Attempt to torture a block outside the flash area\n");
+ return -EINVAL;
+ }
+
+ patt_count = ARRAY_SIZE(patterns);
+
+ buf = malloc_cache_aligned(mtd->erasesize);
+ if (buf == NULL) {
+ puts("Out of memory for erase block buffer\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < patt_count; i++) {
+ err = mtd_erase(mtd, &instr);
+ if (err) {
+ printf("%s: erase() failed for block at 0x%llx: %d\n",
+ mtd->name, instr.addr, err);
+ goto out;
+ }
+
+ /* Make sure the block contains only 0xff bytes */
+ err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
+ if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
+ printf("%s: read() failed for block at 0x%llx: %d\n",
+ mtd->name, instr.addr, err);
+ goto out;
+ }
+
+ err = check_pattern(buf, 0xff, mtd->erasesize);
+ if (!err) {
+ printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
+ offset);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Write a pattern and check it */
+ memset(buf, patterns[i], mtd->erasesize);
+ err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf);
+ if (err || retlen != mtd->erasesize) {
+ printf("%s: write() failed for block at 0x%llx: %d\n",
+ mtd->name, instr.addr, err);
+ goto out;
+ }
+
+ err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
+ if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
+ printf("%s: read() failed for block at 0x%llx: %d\n",
+ mtd->name, instr.addr, err);
+ goto out;
+ }
+
+ err = check_pattern(buf, patterns[i], mtd->erasesize);
+ if (!err) {
+ printf("Pattern 0x%.2x checking failed for block at "
+ "0x%llx\n", patterns[i], offset);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ ret = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+#endif
diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
new file mode 100644
index 0000000000..35c6dd1f1b
--- /dev/null
+++ b/drivers/mtd/nand/raw/omap_elm.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com>
+ * Mansoor Ahamed <mansoor.ahamed@ti.com>
+ *
+ * BCH Error Location Module (ELM) support.
+ *
+ * NOTE:
+ * 1. Supports only continuous mode. Dont see need for page mode in uboot
+ * 2. Supports only syndrome polynomial 0. i.e. poly local variable is
+ * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial
+ * sets in uboot
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/mtd/omap_elm.h>
+#include <asm/arch/hardware.h>
+
+#define DRIVER_NAME "omap-elm"
+#define ELM_DEFAULT_POLY (0)
+
+struct elm *elm_cfg;
+
+/**
+ * elm_load_syndromes - Load BCH syndromes based on bch_type selection
+ * @syndrome: BCH syndrome
+ * @bch_type: BCH4/BCH8/BCH16
+ * @poly: Syndrome Polynomial set to use
+ */
+static void elm_load_syndromes(u8 *syndrome, enum bch_level bch_type, u8 poly)
+{
+ u32 *ptr;
+ u32 val;
+
+ /* reg 0 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
+ val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
+ (syndrome[3] << 24);
+ writel(val, ptr);
+ /* reg 1 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
+ val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
+ (syndrome[7] << 24);
+ writel(val, ptr);
+
+ if (bch_type == BCH_8_BIT || bch_type == BCH_16_BIT) {
+ /* reg 2 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2];
+ val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) |
+ (syndrome[11] << 24);
+ writel(val, ptr);
+ /* reg 3 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3];
+ val = syndrome[12] | (syndrome[13] << 8) |
+ (syndrome[14] << 16) | (syndrome[15] << 24);
+ writel(val, ptr);
+ }
+
+ if (bch_type == BCH_16_BIT) {
+ /* reg 4 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4];
+ val = syndrome[16] | (syndrome[17] << 8) |
+ (syndrome[18] << 16) | (syndrome[19] << 24);
+ writel(val, ptr);
+
+ /* reg 5 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5];
+ val = syndrome[20] | (syndrome[21] << 8) |
+ (syndrome[22] << 16) | (syndrome[23] << 24);
+ writel(val, ptr);
+
+ /* reg 6 */
+ ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6];
+ val = syndrome[24] | (syndrome[25] << 8) |
+ (syndrome[26] << 16) | (syndrome[27] << 24);
+ writel(val, ptr);
+ }
+}
+
+/**
+ * elm_check_errors - Check for BCH errors and return error locations
+ * @syndrome: BCH syndrome
+ * @bch_type: BCH4/BCH8/BCH16
+ * @error_count: Returns number of errrors in the syndrome
+ * @error_locations: Returns error locations (in decimal) in this array
+ *
+ * Check the provided syndrome for BCH errors and return error count
+ * and locations in the array passed. Returns -1 if error is not correctable,
+ * else returns 0
+ */
+int elm_check_error(u8 *syndrome, enum bch_level bch_type, u32 *error_count,
+ u32 *error_locations)
+{
+ u8 poly = ELM_DEFAULT_POLY;
+ s8 i;
+ u32 location_status;
+
+ elm_load_syndromes(syndrome, bch_type, poly);
+
+ /* start processing */
+ writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6])
+ | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID),
+ &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]);
+
+ /* wait for processing to complete */
+ while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1)
+ ;
+ /* clear status */
+ writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)),
+ &elm_cfg->irqstatus);
+
+ /* check if correctable */
+ location_status = readl(&elm_cfg->error_location[poly].location_status);
+ if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) {
+ printf("%s: uncorrectable ECC errors\n", DRIVER_NAME);
+ return -EBADMSG;
+ }
+
+ /* get error count */
+ *error_count = readl(&elm_cfg->error_location[poly].location_status) &
+ ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK;
+
+ for (i = 0; i < *error_count; i++) {
+ error_locations[i] =
+ readl(&elm_cfg->error_location[poly].error_location_x[i]);
+ }
+
+ return 0;
+}
+
+
+/**
+ * elm_config - Configure ELM module
+ * @level: 4 / 8 / 16 bit BCH
+ *
+ * Configure ELM module based on BCH level.
+ * Set mode as continuous mode.
+ * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used.
+ * Also, the mode is set only for syndrome 0
+ */
+int elm_config(enum bch_level level)
+{
+ u32 val;
+ u8 poly = ELM_DEFAULT_POLY;
+ u32 buffer_size = 0x7FF;
+
+ /* config size and level */
+ val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK;
+ val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) &
+ ELM_LOCATION_CONFIG_ECC_SIZE_MASK);
+ writel(val, &elm_cfg->location_config);
+
+ /* config continous mode */
+ /* enable interrupt generation for syndrome polynomial set */
+ writel((readl(&elm_cfg->irqenable) | (0x1 << poly)),
+ &elm_cfg->irqenable);
+ /* set continuous mode for the syndrome polynomial set */
+ writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)),
+ &elm_cfg->page_ctrl);
+
+ return 0;
+}
+
+/**
+ * elm_reset - Do a soft reset of ELM
+ *
+ * Perform a soft reset of ELM and return after reset is done.
+ */
+void elm_reset(void)
+{
+ /* initiate reset */
+ writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET),
+ &elm_cfg->sysconfig);
+
+ /* wait for reset complete and normal operation */
+ while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) !=
+ ELM_SYSSTATUS_RESETDONE)
+ ;
+}
+
+/**
+ * elm_init - Initialize ELM module
+ *
+ * Initialize ELM support. Currently it does only base address init
+ * and ELM reset.
+ */
+void elm_init(void)
+{
+ elm_cfg = (struct elm *)ELM_BASE;
+ elm_reset();
+}
diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c
new file mode 100644
index 0000000000..6a050501b0
--- /dev/null
+++ b/drivers/mtd/nand/raw/omap_gpmc.c
@@ -0,0 +1,1037 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com>
+ * Rohit Choraria <rohitkc@ti.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <asm/arch/mem.h>
+#include <linux/mtd/omap_gpmc.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/bch.h>
+#include <linux/compiler.h>
+#include <nand.h>
+#include <linux/mtd/omap_elm.h>
+
+#define BADBLOCK_MARKER_LENGTH 2
+#define SECTOR_BYTES 512
+#define ECCCLEAR (0x1 << 8)
+#define ECCRESULTREG1 (0x1 << 0)
+/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
+#define BCH4_BIT_PAD 4
+
+#ifdef CONFIG_BCH
+static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+ 0x97, 0x79, 0xe5, 0x24, 0xb5};
+#endif
+static uint8_t cs_next;
+static __maybe_unused struct nand_ecclayout omap_ecclayout;
+
+#if defined(CONFIG_NAND_OMAP_GPMC_WSCFG)
+static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE] =
+ { CONFIG_NAND_OMAP_GPMC_WSCFG };
+#else
+/* wscfg is preset to zero since its a static variable */
+static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE];
+#endif
+
+/*
+ * Driver configurations
+ */
+struct omap_nand_info {
+ struct bch_control *control;
+ enum omap_ecc ecc_scheme;
+ uint8_t cs;
+ uint8_t ws; /* wait status pin (0,1) */
+};
+
+/* We are wasting a bit of memory but al least we are safe */
+static struct omap_nand_info omap_nand_info[GPMC_MAX_CS];
+
+/*
+ * omap_nand_hwcontrol - Set the address pointers corretly for the
+ * following address/data/command operation
+ */
+static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd,
+ uint32_t ctrl)
+{
+ register struct nand_chip *this = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(this);
+ int cs = info->cs;
+
+ /*
+ * Point the IO_ADDR to DATA and ADDRESS registers instead
+ * of chip address
+ */
+ switch (ctrl) {
+ case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+ this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
+ break;
+ case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+ this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr;
+ break;
+ case NAND_CTRL_CHANGE | NAND_NCE:
+ this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;
+ break;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+/* Check wait pin as dev ready indicator */
+static int omap_dev_ready(struct mtd_info *mtd)
+{
+ register struct nand_chip *this = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(this);
+ return gpmc_cfg->status & (1 << (8 + info->ws));
+}
+
+/*
+ * gen_true_ecc - This function will generate true ECC value, which
+ * can be used when correcting data read from NAND flash memory core
+ *
+ * @ecc_buf: buffer to store ecc code
+ *
+ * @return: re-formatted ECC value
+ */
+static uint32_t gen_true_ecc(uint8_t *ecc_buf)
+{
+ return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) |
+ ((ecc_buf[2] & 0x0F) << 8);
+}
+
+/*
+ * omap_correct_data - Compares the ecc read from nand spare area with ECC
+ * registers values and corrects one bit error if it has occurred
+ * Further details can be had from OMAP TRM and the following selected links:
+ * http://en.wikipedia.org/wiki/Hamming_code
+ * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf
+ *
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from ECC registers
+ *
+ * @return 0 if data is OK or corrected, else returns -1
+ */
+static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ uint32_t orig_ecc, new_ecc, res, hm;
+ uint16_t parity_bits, byte;
+ uint8_t bit;
+
+ /* Regenerate the orginal ECC */
+ orig_ecc = gen_true_ecc(read_ecc);
+ new_ecc = gen_true_ecc(calc_ecc);
+ /* Get the XOR of real ecc */
+ res = orig_ecc ^ new_ecc;
+ if (res) {
+ /* Get the hamming width */
+ hm = hweight32(res);
+ /* Single bit errors can be corrected! */
+ if (hm == 12) {
+ /* Correctable data! */
+ parity_bits = res >> 16;
+ bit = (parity_bits & 0x7);
+ byte = (parity_bits >> 3) & 0x1FF;
+ /* Flip the bit to correct */
+ dat[byte] ^= (0x1 << bit);
+ } else if (hm == 1) {
+ printf("Error: Ecc is wrong\n");
+ /* ECC itself is corrupted */
+ return 2;
+ } else {
+ /*
+ * hm distance != parity pairs OR one, could mean 2 bit
+ * error OR potentially be on a blank page..
+ * orig_ecc: contains spare area data from nand flash.
+ * new_ecc: generated ecc while reading data area.
+ * Note: if the ecc = 0, all data bits from which it was
+ * generated are 0xFF.
+ * The 3 byte(24 bits) ecc is generated per 512byte
+ * chunk of a page. If orig_ecc(from spare area)
+ * is 0xFF && new_ecc(computed now from data area)=0x0,
+ * this means that data area is 0xFF and spare area is
+ * 0xFF. A sure sign of a erased page!
+ */
+ if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000))
+ return 0;
+ printf("Error: Bad compare! failed\n");
+ /* detected 2 bit error */
+ return -EBADMSG;
+ }
+ }
+ return 0;
+}
+
+/*
+ * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+__maybe_unused
+static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(nand);
+ unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
+ unsigned int ecc_algo = 0;
+ unsigned int bch_type = 0;
+ unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00;
+ u32 ecc_size_config_val = 0;
+ u32 ecc_config_val = 0;
+ int cs = info->cs;
+
+ /* configure GPMC for specific ecc-scheme */
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ return;
+ case OMAP_ECC_HAM1_CODE_HW:
+ ecc_algo = 0x0;
+ bch_type = 0x0;
+ bch_wrapmode = 0x00;
+ eccsize0 = 0xFF;
+ eccsize1 = 0xFF;
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ ecc_algo = 0x1;
+ bch_type = 0x1;
+ if (mode == NAND_ECC_WRITE) {
+ bch_wrapmode = 0x01;
+ eccsize0 = 0; /* extra bits in nibbles per sector */
+ eccsize1 = 28; /* OOB bits in nibbles per sector */
+ } else {
+ bch_wrapmode = 0x01;
+ eccsize0 = 26; /* ECC bits in nibbles per sector */
+ eccsize1 = 2; /* non-ECC bits in nibbles per sector */
+ }
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ ecc_algo = 0x1;
+ bch_type = 0x2;
+ if (mode == NAND_ECC_WRITE) {
+ bch_wrapmode = 0x01;
+ eccsize0 = 0; /* extra bits in nibbles per sector */
+ eccsize1 = 52; /* OOB bits in nibbles per sector */
+ } else {
+ bch_wrapmode = 0x01;
+ eccsize0 = 52; /* ECC bits in nibbles per sector */
+ eccsize1 = 0; /* non-ECC bits in nibbles per sector */
+ }
+ break;
+ default:
+ return;
+ }
+ /* Clear ecc and enable bits */
+ writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
+ /* Configure ecc size for BCH */
+ ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12);
+ writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config);
+
+ /* Configure device details for BCH engine */
+ ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */
+ (bch_type << 12) | /* BCH4/BCH8/BCH16 */
+ (bch_wrapmode << 8) | /* wrap mode */
+ (dev_width << 7) | /* bus width */
+ (0x0 << 4) | /* number of sectors */
+ (cs << 1) | /* ECC CS */
+ (0x1)); /* enable ECC */
+ writel(ecc_config_val, &gpmc_cfg->ecc_config);
+}
+
+/*
+ * omap_calculate_ecc - Read ECC result
+ * @mtd: MTD structure
+ * @dat: unused
+ * @ecc_code: ecc_code buffer
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as
+ * long nobody is trying to write data on the seemingly unused page.
+ * Reading an erased page will produce an ECC mismatch between
+ * generated and read ECC bytes that has to be dealt with separately.
+ * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC
+ * is used, the result of read will be 0x0 while the ECC offsets of the
+ * spare area will be 0xFF which will result in an ECC mismatch.
+ */
+static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(chip);
+ const uint32_t *ptr;
+ uint32_t val = 0;
+ int8_t i = 0, j;
+
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ val = readl(&gpmc_cfg->ecc1_result);
+ ecc_code[0] = val & 0xFF;
+ ecc_code[1] = (val >> 16) & 0xFF;
+ ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
+ break;
+#ifdef CONFIG_BCH
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#endif
+ case OMAP_ECC_BCH8_CODE_HW:
+ ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3];
+ val = readl(ptr);
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ ptr--;
+ for (j = 0; j < 3; j++) {
+ val = readl(ptr);
+ ecc_code[i++] = (val >> 24) & 0xFF;
+ ecc_code[i++] = (val >> 16) & 0xFF;
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ ptr--;
+ }
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]);
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]);
+ ecc_code[i++] = (val >> 24) & 0xFF;
+ ecc_code[i++] = (val >> 16) & 0xFF;
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]);
+ ecc_code[i++] = (val >> 24) & 0xFF;
+ ecc_code[i++] = (val >> 16) & 0xFF;
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ for (j = 3; j >= 0; j--) {
+ val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j]
+ );
+ ecc_code[i++] = (val >> 24) & 0xFF;
+ ecc_code[i++] = (val >> 16) & 0xFF;
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* ECC scheme specific syndrome customizations */
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ break;
+#ifdef CONFIG_BCH
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+
+ for (i = 0; i < chip->ecc.bytes; i++)
+ *(ecc_code + i) = *(ecc_code + i) ^
+ bch8_polynomial[i];
+ break;
+#endif
+ case OMAP_ECC_BCH8_CODE_HW:
+ ecc_code[chip->ecc.bytes - 1] = 0x00;
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+
+#define PREFETCH_CONFIG1_CS_SHIFT 24
+#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
+#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
+#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
+#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
+#define ENABLE_PREFETCH (1 << 7)
+
+/**
+ * omap_prefetch_enable - configures and starts prefetch transfer
+ * @fifo_th: fifo threshold to be used for read/ write
+ * @count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ * @cs: chip select to use
+ */
+static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs)
+{
+ uint32_t val;
+
+ if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
+ return -EINVAL;
+
+ if (readl(&gpmc_cfg->prefetch_control))
+ return -EBUSY;
+
+ /* Set the amount of bytes to be prefetched */
+ writel(count, &gpmc_cfg->prefetch_config2);
+
+ val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) |
+ PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH;
+ writel(val, &gpmc_cfg->prefetch_config1);
+
+ /* Start the prefetch engine */
+ writel(1, &gpmc_cfg->prefetch_control);
+
+ return 0;
+}
+
+/**
+ * omap_prefetch_reset - disables and stops the prefetch engine
+ */
+static void omap_prefetch_reset(void)
+{
+ writel(0, &gpmc_cfg->prefetch_control);
+ writel(0, &gpmc_cfg->prefetch_config1);
+}
+
+static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len)
+{
+ int ret;
+ uint32_t cnt;
+ struct omap_nand_info *info = nand_get_controller_data(chip);
+
+ ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs);
+ if (ret < 0)
+ return ret;
+
+ do {
+ int i;
+
+ cnt = readl(&gpmc_cfg->prefetch_status);
+ cnt = PREFETCH_STATUS_FIFO_CNT(cnt);
+
+ for (i = 0; i < cnt / 4; i++) {
+ *buf++ = readl(CONFIG_SYS_NAND_BASE);
+ len -= 4;
+ }
+ } while (len);
+
+ omap_prefetch_reset();
+
+ return 0;
+}
+
+static inline void omap_nand_read(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ nand_read_buf16(mtd, buf, len);
+ else
+ nand_read_buf(mtd, buf, len);
+}
+
+static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int ret;
+ uint32_t head, tail;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /*
+ * If the destination buffer is unaligned, start with reading
+ * the overlap byte-wise.
+ */
+ head = ((uint32_t) buf) % 4;
+ if (head) {
+ omap_nand_read(mtd, buf, head);
+ buf += head;
+ len -= head;
+ }
+
+ /*
+ * Only transfer multiples of 4 bytes in a pre-fetched fashion.
+ * If there's a residue, care for it byte-wise afterwards.
+ */
+ tail = len % 4;
+
+ ret = __read_prefetch_aligned(chip, (uint32_t *)buf, len - tail);
+ if (ret < 0) {
+ /* fallback in case the prefetch engine is busy */
+ omap_nand_read(mtd, buf, len);
+ } else if (tail) {
+ buf += len - tail;
+ omap_nand_read(mtd, buf, tail);
+ }
+}
+#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */
+
+#ifdef CONFIG_NAND_OMAP_ELM
+/*
+ * omap_reverse_list - re-orders list elements in reverse order [internal]
+ * @list: pointer to start of list
+ * @length: length of list
+*/
+static void omap_reverse_list(u8 *list, unsigned int length)
+{
+ unsigned int i, j;
+ unsigned int half_length = length / 2;
+ u8 tmp;
+ for (i = 0, j = length - 1; i < half_length; i++, j--) {
+ tmp = list[i];
+ list[i] = list[j];
+ list[j] = tmp;
+ }
+}
+
+/*
+ * omap_correct_data_bch - Compares the ecc read from nand spare area
+ * with ECC registers values and corrects one bit error if it has occurred
+ *
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash (ignored)
+ * @calc_ecc: ecc read from ECC registers
+ *
+ * @return 0 if data is OK or corrected, else returns -1
+ */
+static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ uint32_t error_count = 0, error_max;
+ uint32_t error_loc[ELM_MAX_ERROR_COUNT];
+ enum bch_level bch_type;
+ uint32_t i, ecc_flag = 0;
+ uint8_t count;
+ uint32_t byte_pos, bit_pos;
+ int err = 0;
+
+ /* check calculated ecc */
+ for (i = 0; i < ecc->bytes && !ecc_flag; i++) {
+ if (calc_ecc[i] != 0x00)
+ ecc_flag = 1;
+ }
+ if (!ecc_flag)
+ return 0;
+
+ /* check for whether its a erased-page */
+ ecc_flag = 0;
+ for (i = 0; i < ecc->bytes && !ecc_flag; i++) {
+ if (read_ecc[i] != 0xff)
+ ecc_flag = 1;
+ }
+ if (!ecc_flag)
+ return 0;
+
+ /*
+ * while reading ECC result we read it in big endian.
+ * Hence while loading to ELM we have rotate to get the right endian.
+ */
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_BCH8_CODE_HW:
+ bch_type = BCH_8_BIT;
+ omap_reverse_list(calc_ecc, ecc->bytes - 1);
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ bch_type = BCH_16_BIT;
+ omap_reverse_list(calc_ecc, ecc->bytes);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* use elm module to check for errors */
+ elm_config(bch_type);
+ err = elm_check_error(calc_ecc, bch_type, &error_count, error_loc);
+ if (err)
+ return err;
+
+ /* correct bch error */
+ for (count = 0; count < error_count; count++) {
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_BCH8_CODE_HW:
+ /* 14th byte in ECC is reserved to match ROM layout */
+ error_max = SECTOR_BYTES + (ecc->bytes - 1);
+ break;
+ case OMAP_ECC_BCH16_CODE_HW:
+ error_max = SECTOR_BYTES + ecc->bytes;
+ break;
+ default:
+ return -EINVAL;
+ }
+ byte_pos = error_max - (error_loc[count] / 8) - 1;
+ bit_pos = error_loc[count] % 8;
+ if (byte_pos < SECTOR_BYTES) {
+ dat[byte_pos] ^= 1 << bit_pos;
+ debug("nand: bit-flip corrected @data=%d\n", byte_pos);
+ } else if (byte_pos < error_max) {
+ read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos;
+ debug("nand: bit-flip corrected @oob=%d\n", byte_pos -
+ SECTOR_BYTES);
+ } else {
+ err = -EBADMSG;
+ printf("nand: error: invalid bit-flip location\n");
+ }
+ }
+ return (err) ? err : error_count;
+}
+
+/**
+ * omap_read_page_bch - hardware ecc based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller expects OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ */
+static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *oob = chip->oob_poi;
+ uint32_t data_pos;
+ uint32_t oob_pos;
+
+ data_pos = 0;
+ /* oob area start */
+ oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
+ oob += chip->ecc.layout->eccpos[0];
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
+ oob += eccbytes) {
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ /* read data */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1);
+ chip->read_buf(mtd, p, eccsize);
+
+ /* read respective ecc from oob area */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
+ chip->read_buf(mtd, oob, eccbytes);
+ /* read syndrome */
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ data_pos += eccsize;
+ oob_pos += eccbytes;
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+#endif /* CONFIG_NAND_OMAP_ELM */
+
+/*
+ * OMAP3 BCH8 support (with BCH library)
+ */
+#ifdef CONFIG_BCH
+/**
+ * omap_correct_data_bch_sw - Decode received data and correct errors
+ * @mtd: MTD device structure
+ * @data: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ */
+static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ int i, count;
+ /* cannot correct more than 8 errors */
+ unsigned int errloc[8];
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(chip);
+
+ count = decode_bch(info->control, NULL, SECTOR_BYTES,
+ read_ecc, calc_ecc, NULL, errloc);
+ if (count > 0) {
+ /* correct errors */
+ for (i = 0; i < count; i++) {
+ /* correct data only, not ecc bytes */
+ if (errloc[i] < SECTOR_BYTES << 3)
+ data[errloc[i] >> 3] ^= 1 << (errloc[i] & 7);
+ debug("corrected bitflip %u\n", errloc[i]);
+#ifdef DEBUG
+ puts("read_ecc: ");
+ /*
+ * BCH8 have 13 bytes of ECC; BCH4 needs adoption
+ * here!
+ */
+ for (i = 0; i < 13; i++)
+ printf("%02x ", read_ecc[i]);
+ puts("\n");
+ puts("calc_ecc: ");
+ for (i = 0; i < 13; i++)
+ printf("%02x ", calc_ecc[i]);
+ puts("\n");
+#endif
+ }
+ } else if (count < 0) {
+ puts("ecc unrecoverable error\n");
+ }
+ return count;
+}
+
+/**
+ * omap_free_bch - Release BCH ecc resources
+ * @mtd: MTD device structure
+ */
+static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = nand_get_controller_data(chip);
+
+ if (info->control) {
+ free_bch(info->control);
+ info->control = NULL;
+ }
+}
+#endif /* CONFIG_BCH */
+
+/**
+ * omap_select_ecc_scheme - configures driver for particular ecc-scheme
+ * @nand: NAND chip device structure
+ * @ecc_scheme: ecc scheme to configure
+ * @pagesize: number of main-area bytes per page of NAND device
+ * @oobsize: number of OOB/spare bytes per page of NAND device
+ */
+static int omap_select_ecc_scheme(struct nand_chip *nand,
+ enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) {
+ struct omap_nand_info *info = nand_get_controller_data(nand);
+ struct nand_ecclayout *ecclayout = &omap_ecclayout;
+ int eccsteps = pagesize / SECTOR_BYTES;
+ int i;
+
+ switch (ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n");
+ /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are
+ * initialized in nand_scan_tail(), so just set ecc.mode */
+ info->control = NULL;
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.layout = NULL;
+ nand->ecc.size = 0;
+ break;
+
+ case OMAP_ECC_HAM1_CODE_HW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (3 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ info->control = NULL;
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 1;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 3;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data;
+ nand->ecc.calculate = omap_calculate_ecc;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ for (i = 0; i < ecclayout->eccbytes; i++) {
+ if (nand->options & NAND_BUSWIDTH_16)
+ ecclayout->eccpos[i] = i + 2;
+ else
+ ecclayout->eccpos[i] = i + 1;
+ }
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ break;
+
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#ifdef CONFIG_BCH
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (13 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ /* check if BCH S/W library can be used for error detection */
+ info->control = init_bch(13, 8, 0x201b);
+ if (!info->control) {
+ printf("nand: error: could not init_bch()\n");
+ return -ENODEV;
+ }
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 13;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data_bch_sw;
+ nand->ecc.calculate = omap_calculate_ecc;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH;
+ for (i = 1; i < ecclayout->eccbytes; i++) {
+ if (i % nand->ecc.bytes)
+ ecclayout->eccpos[i] =
+ ecclayout->eccpos[i - 1] + 1;
+ else
+ ecclayout->eccpos[i] =
+ ecclayout->eccpos[i - 1] + 2;
+ }
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ break;
+#else
+ printf("nand: error: CONFIG_BCH required for ECC\n");
+ return -EINVAL;
+#endif
+
+ case OMAP_ECC_BCH8_CODE_HW:
+#ifdef CONFIG_NAND_OMAP_ELM
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (14 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ /* intialize ELM for ECC error detection */
+ elm_init();
+ info->control = NULL;
+ /* populate ecc specific fields */
+ memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 14;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data_bch;
+ nand->ecc.calculate = omap_calculate_ecc;
+ nand->ecc.read_page = omap_read_page_bch;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
+ BADBLOCK_MARKER_LENGTH;
+ break;
+#else
+ printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
+ return -EINVAL;
+#endif
+
+ case OMAP_ECC_BCH16_CODE_HW:
+#ifdef CONFIG_NAND_OMAP_ELM
+ debug("nand: using OMAP_ECC_BCH16_CODE_HW\n");
+ /* check ecc-scheme requirements before updating ecc info */
+ if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) {
+ printf("nand: error: insufficient OOB: require=%d\n", (
+ (26 * eccsteps) + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ /* intialize ELM for ECC error detection */
+ elm_init();
+ /* populate ecc specific fields */
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = SECTOR_BYTES;
+ nand->ecc.bytes = 26;
+ nand->ecc.strength = 16;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data_bch;
+ nand->ecc.calculate = omap_calculate_ecc;
+ nand->ecc.read_page = omap_read_page_bch;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+ BADBLOCK_MARKER_LENGTH;
+ break;
+#else
+ printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
+ return -EINVAL;
+#endif
+ default:
+ debug("nand: error: ecc scheme not enabled or supported\n");
+ return -EINVAL;
+ }
+
+ /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */
+ if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW)
+ nand->ecc.layout = ecclayout;
+
+ info->ecc_scheme = ecc_scheme;
+ return 0;
+}
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ * omap_nand_switch_ecc - switch the ECC operation between different engines
+ * (h/w and s/w) and different algorithms (hamming and BCHx)
+ *
+ * @hardware - true if one of the HW engines should be used
+ * @eccstrength - the number of bits that could be corrected
+ * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)
+ */
+int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
+{
+ struct nand_chip *nand;
+ struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device);
+ int err = 0;
+
+ if (!mtd) {
+ printf("nand: error: no NAND devices found\n");
+ return -ENODEV;
+ }
+
+ nand = mtd_to_nand(mtd);
+ nand->options |= NAND_OWN_BUFFERS;
+ nand->options &= ~NAND_SUBPAGE_READ;
+ /* Setup the ecc configurations again */
+ if (hardware) {
+ if (eccstrength == 1) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_HAM1_CODE_HW,
+ mtd->writesize, mtd->oobsize);
+ } else if (eccstrength == 8) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_BCH8_CODE_HW,
+ mtd->writesize, mtd->oobsize);
+ } else if (eccstrength == 16) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_BCH16_CODE_HW,
+ mtd->writesize, mtd->oobsize);
+ } else {
+ printf("nand: error: unsupported ECC scheme\n");
+ return -EINVAL;
+ }
+ } else {
+ if (eccstrength == 1) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_HAM1_CODE_SW,
+ mtd->writesize, mtd->oobsize);
+ } else if (eccstrength == 8) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
+ mtd->writesize, mtd->oobsize);
+ } else {
+ printf("nand: error: unsupported ECC scheme\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Update NAND handling after ECC mode switch */
+ if (!err)
+ err = nand_scan_tail(mtd);
+ return err;
+}
+#endif /* CONFIG_SPL_BUILD */
+
+/*
+ * Board-specific NAND initialization. The following members of the
+ * argument are board-specific:
+ * - IO_ADDR_R: address to read the 8 I/O lines of the flash device
+ * - IO_ADDR_W: address to write the 8 I/O lines of the flash device
+ * - cmd_ctrl: hardwarespecific function for accesing control-lines
+ * - waitfunc: hardwarespecific function for accesing device ready/busy line
+ * - ecc.hwctl: function to enable (reset) hardware ecc generator
+ * - ecc.mode: mode of ecc, see defines
+ * - chip_delay: chip dependent delay for transfering data from array to
+ * read regs (tR)
+ * - options: various chip options. They can partly be set to inform
+ * nand_scan about special functionality. See the defines for further
+ * explanation
+ */
+int board_nand_init(struct nand_chip *nand)
+{
+ int32_t gpmc_config = 0;
+ int cs = cs_next++;
+ int err = 0;
+ /*
+ * xloader/Uboot's gpmc configuration would have configured GPMC for
+ * nand type of memory. The following logic scans and latches on to the
+ * first CS with NAND type memory.
+ * TBD: need to make this logic generic to handle multiple CS NAND
+ * devices.
+ */
+ while (cs < GPMC_MAX_CS) {
+ /* Check if NAND type is set */
+ if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) {
+ /* Found it!! */
+ break;
+ }
+ cs++;
+ }
+ if (cs >= GPMC_MAX_CS) {
+ printf("nand: error: Unable to find NAND settings in "
+ "GPMC Configuration - quitting\n");
+ return -ENODEV;
+ }
+
+ gpmc_config = readl(&gpmc_cfg->config);
+ /* Disable Write protect */
+ gpmc_config |= 0x10;
+ writel(gpmc_config, &gpmc_cfg->config);
+
+ nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;
+ nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
+ omap_nand_info[cs].control = NULL;
+ omap_nand_info[cs].cs = cs;
+ omap_nand_info[cs].ws = wscfg[cs];
+ nand_set_controller_data(nand, &omap_nand_info[cs]);
+ nand->cmd_ctrl = omap_nand_hwcontrol;
+ nand->options |= NAND_NO_PADDING | NAND_CACHEPRG;
+ nand->chip_delay = 100;
+ nand->ecc.layout = &omap_ecclayout;
+
+ /* configure driver and controller based on NAND device bus-width */
+ gpmc_config = readl(&gpmc_cfg->cs[cs].config1);
+#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT)
+ nand->options |= NAND_BUSWIDTH_16;
+ writel(gpmc_config | (0x1 << 12), &gpmc_cfg->cs[cs].config1);
+#else
+ nand->options &= ~NAND_BUSWIDTH_16;
+ writel(gpmc_config & ~(0x1 << 12), &gpmc_cfg->cs[cs].config1);
+#endif
+ /* select ECC scheme */
+#if defined(CONFIG_NAND_OMAP_ECCSCHEME)
+ err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME,
+ CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);
+#else
+ /* pagesize and oobsize are not required to configure sw ecc-scheme */
+ err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW,
+ 0, 0);
+#endif
+ if (err)
+ return err;
+
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+ nand->read_buf = omap_nand_read_prefetch;
+#else
+ if (nand->options & NAND_BUSWIDTH_16)
+ nand->read_buf = nand_read_buf16;
+ else
+ nand->read_buf = nand_read_buf;
+#endif
+
+ nand->dev_ready = omap_dev_ready;
+
+ return 0;
+}
diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c
new file mode 100644
index 0000000000..4c783f1e1e
--- /dev/null
+++ b/drivers/mtd/nand/raw/pxa3xx_nand.c
@@ -0,0 +1,1828 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/mtd/nand/raw/pxa3xx_nand.c
+ *
+ * Copyright © 2005 Intel Corporation
+ * Copyright © 2006 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <fdtdec.h>
+#include <nand.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/types.h>
+
+#include "pxa3xx_nand.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define TIMEOUT_DRAIN_FIFO 5 /* in ms */
+#define CHIP_DELAY_TIMEOUT 200
+#define NAND_STOP_DELAY 40
+
+/*
+ * Define a buffer size for the initial command that detects the flash device:
+ * STATUS, READID and PARAM.
+ * ONFI param page is 256 bytes, and there are three redundant copies
+ * to be read. JEDEC param page is 512 bytes, and there are also three
+ * redundant copies to be read.
+ * Hence this buffer should be at least 512 x 3. Let's pick 2048.
+ */
+#define INIT_BUFFER_SIZE 2048
+
+/* registers and bit definitions */
+#define NDCR (0x00) /* Control register */
+#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */
+#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */
+#define NDSR (0x14) /* Status Register */
+#define NDPCR (0x18) /* Page Count Register */
+#define NDBDR0 (0x1C) /* Bad Block Register 0 */
+#define NDBDR1 (0x20) /* Bad Block Register 1 */
+#define NDECCCTRL (0x28) /* ECC control */
+#define NDDB (0x40) /* Data Buffer */
+#define NDCB0 (0x48) /* Command Buffer0 */
+#define NDCB1 (0x4C) /* Command Buffer1 */
+#define NDCB2 (0x50) /* Command Buffer2 */
+
+#define NDCR_SPARE_EN (0x1 << 31)
+#define NDCR_ECC_EN (0x1 << 30)
+#define NDCR_DMA_EN (0x1 << 29)
+#define NDCR_ND_RUN (0x1 << 28)
+#define NDCR_DWIDTH_C (0x1 << 27)
+#define NDCR_DWIDTH_M (0x1 << 26)
+#define NDCR_PAGE_SZ (0x1 << 24)
+#define NDCR_NCSX (0x1 << 23)
+#define NDCR_ND_MODE (0x3 << 21)
+#define NDCR_NAND_MODE (0x0)
+#define NDCR_CLR_PG_CNT (0x1 << 20)
+#define NFCV1_NDCR_ARB_CNTL (0x1 << 19)
+#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
+#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
+
+#define NDCR_RA_START (0x1 << 15)
+#define NDCR_PG_PER_BLK (0x1 << 14)
+#define NDCR_ND_ARB_EN (0x1 << 12)
+#define NDCR_INT_MASK (0xFFF)
+
+#define NDSR_MASK (0xfff)
+#define NDSR_ERR_CNT_OFF (16)
+#define NDSR_ERR_CNT_MASK (0x1f)
+#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK)
+#define NDSR_RDY (0x1 << 12)
+#define NDSR_FLASH_RDY (0x1 << 11)
+#define NDSR_CS0_PAGED (0x1 << 10)
+#define NDSR_CS1_PAGED (0x1 << 9)
+#define NDSR_CS0_CMDD (0x1 << 8)
+#define NDSR_CS1_CMDD (0x1 << 7)
+#define NDSR_CS0_BBD (0x1 << 6)
+#define NDSR_CS1_BBD (0x1 << 5)
+#define NDSR_UNCORERR (0x1 << 4)
+#define NDSR_CORERR (0x1 << 3)
+#define NDSR_WRDREQ (0x1 << 2)
+#define NDSR_RDDREQ (0x1 << 1)
+#define NDSR_WRCMDREQ (0x1)
+
+#define NDCB0_LEN_OVRD (0x1 << 28)
+#define NDCB0_ST_ROW_EN (0x1 << 26)
+#define NDCB0_AUTO_RS (0x1 << 25)
+#define NDCB0_CSEL (0x1 << 24)
+#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29)
+#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK)
+#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
+#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
+#define NDCB0_NC (0x1 << 20)
+#define NDCB0_DBC (0x1 << 19)
+#define NDCB0_ADDR_CYC_MASK (0x7 << 16)
+#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
+#define NDCB0_CMD2_MASK (0xff << 8)
+#define NDCB0_CMD1_MASK (0xff)
+#define NDCB0_ADDR_CYC_SHIFT (16)
+
+#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */
+#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */
+#define EXT_CMD_TYPE_READ 4 /* Read */
+#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */
+#define EXT_CMD_TYPE_FINAL 3 /* Final command */
+#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */
+#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
+
+/*
+ * This should be large enough to read 'ONFI' and 'JEDEC'.
+ * Let's use 7 bytes, which is the maximum ID count supported
+ * by the controller (see NDCR_RD_ID_CNT_MASK).
+ */
+#define READ_ID_BYTES 7
+
+/* macros for registers read/write */
+#define nand_writel(info, off, val) \
+ writel((val), (info)->mmio_base + (off))
+
+#define nand_readl(info, off) \
+ readl((info)->mmio_base + (off))
+
+/* error code and state */
+enum {
+ ERR_NONE = 0,
+ ERR_DMABUSERR = -1,
+ ERR_SENDCMD = -2,
+ ERR_UNCORERR = -3,
+ ERR_BBERR = -4,
+ ERR_CORERR = -5,
+};
+
+enum {
+ STATE_IDLE = 0,
+ STATE_PREPARED,
+ STATE_CMD_HANDLE,
+ STATE_DMA_READING,
+ STATE_DMA_WRITING,
+ STATE_DMA_DONE,
+ STATE_PIO_READING,
+ STATE_PIO_WRITING,
+ STATE_CMD_DONE,
+ STATE_READY,
+};
+
+enum pxa3xx_nand_variant {
+ PXA3XX_NAND_VARIANT_PXA,
+ PXA3XX_NAND_VARIANT_ARMADA370,
+};
+
+struct pxa3xx_nand_host {
+ struct nand_chip chip;
+ void *info_data;
+
+ /* page size of attached chip */
+ int use_ecc;
+ int cs;
+
+ /* calculated from pxa3xx_nand_flash data */
+ unsigned int col_addr_cycles;
+ unsigned int row_addr_cycles;
+};
+
+struct pxa3xx_nand_info {
+ struct nand_hw_control controller;
+ struct pxa3xx_nand_platform_data *pdata;
+
+ struct clk *clk;
+ void __iomem *mmio_base;
+ unsigned long mmio_phys;
+ int cmd_complete, dev_ready;
+
+ unsigned int buf_start;
+ unsigned int buf_count;
+ unsigned int buf_size;
+ unsigned int data_buff_pos;
+ unsigned int oob_buff_pos;
+
+ unsigned char *data_buff;
+ unsigned char *oob_buff;
+
+ struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];
+ unsigned int state;
+
+ /*
+ * This driver supports NFCv1 (as found in PXA SoC)
+ * and NFCv2 (as found in Armada 370/XP SoC).
+ */
+ enum pxa3xx_nand_variant variant;
+
+ int cs;
+ int use_ecc; /* use HW ECC ? */
+ int ecc_bch; /* using BCH ECC? */
+ int use_spare; /* use spare ? */
+ int need_wait;
+
+ /* Amount of real data per full chunk */
+ unsigned int chunk_size;
+
+ /* Amount of spare data per full chunk */
+ unsigned int spare_size;
+
+ /* Number of full chunks (i.e chunk_size + spare_size) */
+ unsigned int nfullchunks;
+
+ /*
+ * Total number of chunks. If equal to nfullchunks, then there
+ * are only full chunks. Otherwise, there is one last chunk of
+ * size (last_chunk_size + last_spare_size)
+ */
+ unsigned int ntotalchunks;
+
+ /* Amount of real data in the last chunk */
+ unsigned int last_chunk_size;
+
+ /* Amount of spare data in the last chunk */
+ unsigned int last_spare_size;
+
+ unsigned int ecc_size;
+ unsigned int ecc_err_cnt;
+ unsigned int max_bitflips;
+ int retcode;
+
+ /*
+ * Variables only valid during command
+ * execution. step_chunk_size and step_spare_size is the
+ * amount of real data and spare data in the current
+ * chunk. cur_chunk is the current chunk being
+ * read/programmed.
+ */
+ unsigned int step_chunk_size;
+ unsigned int step_spare_size;
+ unsigned int cur_chunk;
+
+ /* cached register value */
+ uint32_t reg_ndcr;
+ uint32_t ndtr0cs0;
+ uint32_t ndtr1cs0;
+
+ /* generated NDCBx register values */
+ uint32_t ndcb0;
+ uint32_t ndcb1;
+ uint32_t ndcb2;
+ uint32_t ndcb3;
+};
+
+static struct pxa3xx_nand_timing timing[] = {
+ /*
+ * tCH Enable signal hold time
+ * tCS Enable signal setup time
+ * tWH ND_nWE high duration
+ * tWP ND_nWE pulse time
+ * tRH ND_nRE high duration
+ * tRP ND_nRE pulse width
+ * tR ND_nWE high to ND_nRE low for read
+ * tWHR ND_nWE high to ND_nRE low for status read
+ * tAR ND_ALE low to ND_nRE low delay
+ */
+ /*ch cs wh wp rh rp r whr ar */
+ { 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
+ { 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
+ { 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
+ { 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
+ { 5, 20, 10, 12, 10, 12, 25000, 60, 10, },
+};
+
+static struct pxa3xx_nand_flash builtin_flash_types[] = {
+ /*
+ * chip_id
+ * flash_width Width of Flash memory (DWIDTH_M)
+ * dfc_width Width of flash controller(DWIDTH_C)
+ * *timing
+ * http://www.linux-mtd.infradead.org/nand-data/nanddata.html
+ */
+ { 0x46ec, 16, 16, &timing[1] },
+ { 0xdaec, 8, 8, &timing[1] },
+ { 0xd7ec, 8, 8, &timing[1] },
+ { 0xa12c, 8, 8, &timing[2] },
+ { 0xb12c, 16, 16, &timing[2] },
+ { 0xdc2c, 8, 8, &timing[2] },
+ { 0xcc2c, 16, 16, &timing[2] },
+ { 0xba20, 16, 16, &timing[3] },
+ { 0xda98, 8, 8, &timing[4] },
+};
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
+static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 8,
+ .len = 6,
+ .veroffs = 14,
+ .maxblocks = 8, /* Last 8 blocks in each chip */
+ .pattern = bbt_mirror_pattern
+};
+#endif
+
+static struct nand_ecclayout ecc_layout_2KB_bch4bit = {
+ .eccbytes = 32,
+ .eccpos = {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = { {2, 30} }
+};
+
+static struct nand_ecclayout ecc_layout_2KB_bch8bit = {
+ .eccbytes = 64,
+ .eccpos = {
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127},
+ .oobfree = { {1, 4}, {6, 26} }
+};
+
+static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
+ .eccbytes = 64,
+ .eccpos = {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127},
+ /* Bootrom looks in bytes 0 & 5 for bad blocks */
+ .oobfree = { {6, 26}, { 64, 32} }
+};
+
+static struct nand_ecclayout ecc_layout_8KB_bch4bit = {
+ .eccbytes = 128,
+ .eccpos = {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255},
+
+ /* Bootrom looks in bytes 0 & 5 for bad blocks */
+ .oobfree = { {1, 4}, {6, 26}, { 64, 32}, {128, 32}, {192, 32} }
+};
+
+static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
+ .eccbytes = 128,
+ .eccpos = {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = { }
+};
+
+static struct nand_ecclayout ecc_layout_8KB_bch8bit = {
+ .eccbytes = 256,
+ .eccpos = {},
+ /* HW ECC handles all ECC data and all spare area is free for OOB */
+ .oobfree = {{0, 160} }
+};
+
+#define NDTR0_tCH(c) (min((c), 7) << 19)
+#define NDTR0_tCS(c) (min((c), 7) << 16)
+#define NDTR0_tWH(c) (min((c), 7) << 11)
+#define NDTR0_tWP(c) (min((c), 7) << 8)
+#define NDTR0_tRH(c) (min((c), 7) << 3)
+#define NDTR0_tRP(c) (min((c), 7) << 0)
+
+#define NDTR1_tR(c) (min((c), 65535) << 16)
+#define NDTR1_tWHR(c) (min((c), 15) << 4)
+#define NDTR1_tAR(c) (min((c), 15) << 0)
+
+/* convert nano-seconds to nand flash controller clock cycles */
+#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
+
+static enum pxa3xx_nand_variant pxa3xx_nand_get_variant(void)
+{
+ /* We only support the Armada 370/XP/38x for now */
+ return PXA3XX_NAND_VARIANT_ARMADA370;
+}
+
+static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
+ const struct pxa3xx_nand_timing *t)
+{
+ struct pxa3xx_nand_info *info = host->info_data;
+ unsigned long nand_clk = mvebu_get_nand_clock();
+ uint32_t ndtr0, ndtr1;
+
+ ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) |
+ NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) |
+ NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) |
+ NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) |
+ NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) |
+ NDTR0_tRP(ns2cycle(t->tRP, nand_clk));
+
+ ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) |
+ NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
+ NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
+
+ info->ndtr0cs0 = ndtr0;
+ info->ndtr1cs0 = ndtr1;
+ nand_writel(info, NDTR0CS0, ndtr0);
+ nand_writel(info, NDTR1CS0, ndtr1);
+}
+
+static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host,
+ const struct nand_sdr_timings *t)
+{
+ struct pxa3xx_nand_info *info = host->info_data;
+ struct nand_chip *chip = &host->chip;
+ unsigned long nand_clk = mvebu_get_nand_clock();
+ uint32_t ndtr0, ndtr1;
+
+ u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000);
+ u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000);
+ u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000);
+ u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000);
+ u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000);
+ u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000);
+ u32 tR = chip->chip_delay * 1000;
+ u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000);
+ u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);
+
+ /* fallback to a default value if tR = 0 */
+ if (!tR)
+ tR = 20000;
+
+ ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) |
+ NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) |
+ NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) |
+ NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) |
+ NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) |
+ NDTR0_tRP(ns2cycle(tRP_min, nand_clk));
+
+ ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) |
+ NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) |
+ NDTR1_tAR(ns2cycle(tAR_min, nand_clk));
+
+ info->ndtr0cs0 = ndtr0;
+ info->ndtr1cs0 = ndtr1;
+ nand_writel(info, NDTR0CS0, ndtr0);
+ nand_writel(info, NDTR1CS0, ndtr1);
+}
+
+static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host)
+{
+ const struct nand_sdr_timings *timings;
+ struct nand_chip *chip = &host->chip;
+ struct pxa3xx_nand_info *info = host->info_data;
+ const struct pxa3xx_nand_flash *f = NULL;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+ int mode, id, ntypes, i;
+
+ mode = onfi_get_async_timing_mode(chip);
+ if (mode == ONFI_TIMING_MODE_UNKNOWN) {
+ ntypes = ARRAY_SIZE(builtin_flash_types);
+
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ id = chip->read_byte(mtd);
+ id |= chip->read_byte(mtd) << 0x8;
+
+ for (i = 0; i < ntypes; i++) {
+ f = &builtin_flash_types[i];
+
+ if (f->chip_id == id)
+ break;
+ }
+
+ if (i == ntypes) {
+ dev_err(&info->pdev->dev, "Error: timings not found\n");
+ return -EINVAL;
+ }
+
+ pxa3xx_nand_set_timing(host, f->timing);
+
+ if (f->flash_width == 16) {
+ info->reg_ndcr |= NDCR_DWIDTH_M;
+ chip->options |= NAND_BUSWIDTH_16;
+ }
+
+ info->reg_ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0;
+ } else {
+ mode = fls(mode) - 1;
+ if (mode < 0)
+ mode = 0;
+
+ timings = onfi_async_timing_mode_to_sdr_timings(mode);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ pxa3xx_nand_set_sdr_timing(host, timings);
+ }
+
+ return 0;
+}
+
+/**
+ * NOTE: it is a must to set ND_RUN first, then write
+ * command buffer, otherwise, it does not work.
+ * We enable all the interrupt at the same time, and
+ * let pxa3xx_nand_irq to handle all logic.
+ */
+static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
+{
+ uint32_t ndcr;
+
+ ndcr = info->reg_ndcr;
+
+ if (info->use_ecc) {
+ ndcr |= NDCR_ECC_EN;
+ if (info->ecc_bch)
+ nand_writel(info, NDECCCTRL, 0x1);
+ } else {
+ ndcr &= ~NDCR_ECC_EN;
+ if (info->ecc_bch)
+ nand_writel(info, NDECCCTRL, 0x0);
+ }
+
+ ndcr &= ~NDCR_DMA_EN;
+
+ if (info->use_spare)
+ ndcr |= NDCR_SPARE_EN;
+ else
+ ndcr &= ~NDCR_SPARE_EN;
+
+ ndcr |= NDCR_ND_RUN;
+
+ /* clear status bits and run */
+ nand_writel(info, NDSR, NDSR_MASK);
+ nand_writel(info, NDCR, 0);
+ nand_writel(info, NDCR, ndcr);
+}
+
+static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
+{
+ uint32_t ndcr;
+
+ ndcr = nand_readl(info, NDCR);
+ nand_writel(info, NDCR, ndcr | int_mask);
+}
+
+static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
+{
+ if (info->ecc_bch) {
+ u32 ts;
+
+ /*
+ * According to the datasheet, when reading from NDDB
+ * with BCH enabled, after each 32 bytes reads, we
+ * have to make sure that the NDSR.RDDREQ bit is set.
+ *
+ * Drain the FIFO 8 32 bits reads at a time, and skip
+ * the polling on the last read.
+ */
+ while (len > 8) {
+ readsl(info->mmio_base + NDDB, data, 8);
+
+ ts = get_timer(0);
+ while (!(nand_readl(info, NDSR) & NDSR_RDDREQ)) {
+ if (get_timer(ts) > TIMEOUT_DRAIN_FIFO) {
+ dev_err(&info->pdev->dev,
+ "Timeout on RDDREQ while draining the FIFO\n");
+ return;
+ }
+ }
+
+ data += 32;
+ len -= 8;
+ }
+ }
+
+ readsl(info->mmio_base + NDDB, data, len);
+}
+
+static void handle_data_pio(struct pxa3xx_nand_info *info)
+{
+ switch (info->state) {
+ case STATE_PIO_WRITING:
+ if (info->step_chunk_size)
+ writesl(info->mmio_base + NDDB,
+ info->data_buff + info->data_buff_pos,
+ DIV_ROUND_UP(info->step_chunk_size, 4));
+
+ if (info->step_spare_size)
+ writesl(info->mmio_base + NDDB,
+ info->oob_buff + info->oob_buff_pos,
+ DIV_ROUND_UP(info->step_spare_size, 4));
+ break;
+ case STATE_PIO_READING:
+ if (info->step_chunk_size)
+ drain_fifo(info,
+ info->data_buff + info->data_buff_pos,
+ DIV_ROUND_UP(info->step_chunk_size, 4));
+
+ if (info->step_spare_size)
+ drain_fifo(info,
+ info->oob_buff + info->oob_buff_pos,
+ DIV_ROUND_UP(info->step_spare_size, 4));
+ break;
+ default:
+ dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
+ info->state);
+ BUG();
+ }
+
+ /* Update buffer pointers for multi-page read/write */
+ info->data_buff_pos += info->step_chunk_size;
+ info->oob_buff_pos += info->step_spare_size;
+}
+
+static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info)
+{
+ handle_data_pio(info);
+
+ info->state = STATE_CMD_DONE;
+ nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
+}
+
+static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info)
+{
+ unsigned int status, is_completed = 0, is_ready = 0;
+ unsigned int ready, cmd_done;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (info->cs == 0) {
+ ready = NDSR_FLASH_RDY;
+ cmd_done = NDSR_CS0_CMDD;
+ } else {
+ ready = NDSR_RDY;
+ cmd_done = NDSR_CS1_CMDD;
+ }
+
+ /* TODO - find out why we need the delay during write operation. */
+ ndelay(1);
+
+ status = nand_readl(info, NDSR);
+
+ if (status & NDSR_UNCORERR)
+ info->retcode = ERR_UNCORERR;
+ if (status & NDSR_CORERR) {
+ info->retcode = ERR_CORERR;
+ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 &&
+ info->ecc_bch)
+ info->ecc_err_cnt = NDSR_ERR_CNT(status);
+ else
+ info->ecc_err_cnt = 1;
+
+ /*
+ * Each chunk composing a page is corrected independently,
+ * and we need to store maximum number of corrected bitflips
+ * to return it to the MTD layer in ecc.read_page().
+ */
+ info->max_bitflips = max_t(unsigned int,
+ info->max_bitflips,
+ info->ecc_err_cnt);
+ }
+ if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
+ info->state = (status & NDSR_RDDREQ) ?
+ STATE_PIO_READING : STATE_PIO_WRITING;
+ /* Call the IRQ thread in U-Boot directly */
+ pxa3xx_nand_irq_thread(info);
+ return 0;
+ }
+ if (status & cmd_done) {
+ info->state = STATE_CMD_DONE;
+ is_completed = 1;
+ }
+ if (status & ready) {
+ info->state = STATE_READY;
+ is_ready = 1;
+ }
+
+ /*
+ * Clear all status bit before issuing the next command, which
+ * can and will alter the status bits and will deserve a new
+ * interrupt on its own. This lets the controller exit the IRQ
+ */
+ nand_writel(info, NDSR, status);
+
+ if (status & NDSR_WRCMDREQ) {
+ status &= ~NDSR_WRCMDREQ;
+ info->state = STATE_CMD_HANDLE;
+
+ /*
+ * Command buffer registers NDCB{0-2} (and optionally NDCB3)
+ * must be loaded by writing directly either 12 or 16
+ * bytes directly to NDCB0, four bytes at a time.
+ *
+ * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored
+ * but each NDCBx register can be read.
+ */
+ nand_writel(info, NDCB0, info->ndcb0);
+ nand_writel(info, NDCB0, info->ndcb1);
+ nand_writel(info, NDCB0, info->ndcb2);
+
+ /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */
+ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
+ nand_writel(info, NDCB0, info->ndcb3);
+ }
+
+ if (is_completed)
+ info->cmd_complete = 1;
+ if (is_ready)
+ info->dev_ready = 1;
+
+ return ret;
+}
+
+static inline int is_buf_blank(uint8_t *buf, size_t len)
+{
+ for (; len > 0; len--)
+ if (*buf++ != 0xff)
+ return 0;
+ return 1;
+}
+
+static void set_command_address(struct pxa3xx_nand_info *info,
+ unsigned int page_size, uint16_t column, int page_addr)
+{
+ /* small page addr setting */
+ if (page_size < info->chunk_size) {
+ info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+ | (column & 0xFF);
+
+ info->ndcb2 = 0;
+ } else {
+ info->ndcb1 = ((page_addr & 0xFFFF) << 16)
+ | (column & 0xFFFF);
+
+ if (page_addr & 0xFF0000)
+ info->ndcb2 = (page_addr & 0xFF0000) >> 16;
+ else
+ info->ndcb2 = 0;
+ }
+}
+
+static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
+{
+ struct pxa3xx_nand_host *host = info->host[info->cs];
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
+
+ /* reset data and oob column point to handle data */
+ info->buf_start = 0;
+ info->buf_count = 0;
+ info->data_buff_pos = 0;
+ info->oob_buff_pos = 0;
+ info->step_chunk_size = 0;
+ info->step_spare_size = 0;
+ info->cur_chunk = 0;
+ info->use_ecc = 0;
+ info->use_spare = 1;
+ info->retcode = ERR_NONE;
+ info->ecc_err_cnt = 0;
+ info->ndcb3 = 0;
+ info->need_wait = 0;
+
+ switch (command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ case NAND_CMD_PAGEPROG:
+ info->use_ecc = 1;
+ break;
+ case NAND_CMD_PARAM:
+ info->use_spare = 0;
+ break;
+ default:
+ info->ndcb1 = 0;
+ info->ndcb2 = 0;
+ break;
+ }
+
+ /*
+ * If we are about to issue a read command, or about to set
+ * the write address, then clean the data buffer.
+ */
+ if (command == NAND_CMD_READ0 ||
+ command == NAND_CMD_READOOB ||
+ command == NAND_CMD_SEQIN) {
+ info->buf_count = mtd->writesize + mtd->oobsize;
+ memset(info->data_buff, 0xFF, info->buf_count);
+ }
+}
+
+static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
+ int ext_cmd_type, uint16_t column, int page_addr)
+{
+ int addr_cycle, exec_cmd;
+ struct pxa3xx_nand_host *host;
+ struct mtd_info *mtd;
+
+ host = info->host[info->cs];
+ mtd = nand_to_mtd(&host->chip);
+ addr_cycle = 0;
+ exec_cmd = 1;
+
+ if (info->cs != 0)
+ info->ndcb0 = NDCB0_CSEL;
+ else
+ info->ndcb0 = 0;
+
+ if (command == NAND_CMD_SEQIN)
+ exec_cmd = 0;
+
+ addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
+ + host->col_addr_cycles);
+
+ switch (command) {
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ info->buf_start = column;
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | addr_cycle
+ | NAND_CMD_READ0;
+
+ if (command == NAND_CMD_READOOB)
+ info->buf_start += mtd->writesize;
+
+ if (info->cur_chunk < info->nfullchunks) {
+ info->step_chunk_size = info->chunk_size;
+ info->step_spare_size = info->spare_size;
+ } else {
+ info->step_chunk_size = info->last_chunk_size;
+ info->step_spare_size = info->last_spare_size;
+ }
+
+ /*
+ * Multiple page read needs an 'extended command type' field,
+ * which is either naked-read or last-read according to the
+ * state.
+ */
+ if (mtd->writesize == info->chunk_size) {
+ info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8);
+ } else if (mtd->writesize > info->chunk_size) {
+ info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8)
+ | NDCB0_LEN_OVRD
+ | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
+ info->ndcb3 = info->step_chunk_size +
+ info->step_spare_size;
+ }
+
+ set_command_address(info, mtd->writesize, column, page_addr);
+ break;
+
+ case NAND_CMD_SEQIN:
+
+ info->buf_start = column;
+ set_command_address(info, mtd->writesize, 0, page_addr);
+
+ /*
+ * Multiple page programming needs to execute the initial
+ * SEQIN command that sets the page address.
+ */
+ if (mtd->writesize > info->chunk_size) {
+ info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+ | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
+ | addr_cycle
+ | command;
+ exec_cmd = 1;
+ }
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ if (is_buf_blank(info->data_buff,
+ (mtd->writesize + mtd->oobsize))) {
+ exec_cmd = 0;
+ break;
+ }
+
+ if (info->cur_chunk < info->nfullchunks) {
+ info->step_chunk_size = info->chunk_size;
+ info->step_spare_size = info->spare_size;
+ } else {
+ info->step_chunk_size = info->last_chunk_size;
+ info->step_spare_size = info->last_spare_size;
+ }
+
+ /* Second command setting for large pages */
+ if (mtd->writesize > info->chunk_size) {
+ /*
+ * Multiple page write uses the 'extended command'
+ * field. This can be used to issue a command dispatch
+ * or a naked-write depending on the current stage.
+ */
+ info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+ | NDCB0_LEN_OVRD
+ | NDCB0_EXT_CMD_TYPE(ext_cmd_type);
+ info->ndcb3 = info->step_chunk_size +
+ info->step_spare_size;
+
+ /*
+ * This is the command dispatch that completes a chunked
+ * page program operation.
+ */
+ if (info->cur_chunk == info->ntotalchunks) {
+ info->ndcb0 = NDCB0_CMD_TYPE(0x1)
+ | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
+ | command;
+ info->ndcb1 = 0;
+ info->ndcb2 = 0;
+ info->ndcb3 = 0;
+ }
+ } else {
+ info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+ | NDCB0_AUTO_RS
+ | NDCB0_ST_ROW_EN
+ | NDCB0_DBC
+ | (NAND_CMD_PAGEPROG << 8)
+ | NAND_CMD_SEQIN
+ | addr_cycle;
+ }
+ break;
+
+ case NAND_CMD_PARAM:
+ info->buf_count = INIT_BUFFER_SIZE;
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | NDCB0_ADDR_CYC(1)
+ | NDCB0_LEN_OVRD
+ | command;
+ info->ndcb1 = (column & 0xFF);
+ info->ndcb3 = INIT_BUFFER_SIZE;
+ info->step_chunk_size = INIT_BUFFER_SIZE;
+ break;
+
+ case NAND_CMD_READID:
+ info->buf_count = READ_ID_BYTES;
+ info->ndcb0 |= NDCB0_CMD_TYPE(3)
+ | NDCB0_ADDR_CYC(1)
+ | command;
+ info->ndcb1 = (column & 0xFF);
+
+ info->step_chunk_size = 8;
+ break;
+ case NAND_CMD_STATUS:
+ info->buf_count = 1;
+ info->ndcb0 |= NDCB0_CMD_TYPE(4)
+ | NDCB0_ADDR_CYC(1)
+ | command;
+
+ info->step_chunk_size = 8;
+ break;
+
+ case NAND_CMD_ERASE1:
+ info->ndcb0 |= NDCB0_CMD_TYPE(2)
+ | NDCB0_AUTO_RS
+ | NDCB0_ADDR_CYC(3)
+ | NDCB0_DBC
+ | (NAND_CMD_ERASE2 << 8)
+ | NAND_CMD_ERASE1;
+ info->ndcb1 = page_addr;
+ info->ndcb2 = 0;
+
+ break;
+ case NAND_CMD_RESET:
+ info->ndcb0 |= NDCB0_CMD_TYPE(5)
+ | command;
+
+ break;
+
+ case NAND_CMD_ERASE2:
+ exec_cmd = 0;
+ break;
+
+ default:
+ exec_cmd = 0;
+ dev_err(&info->pdev->dev, "non-supported command %x\n",
+ command);
+ break;
+ }
+
+ return exec_cmd;
+}
+
+static void nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ int exec_cmd;
+
+ /*
+ * if this is a x16 device ,then convert the input
+ * "byte" address into a "word" address appropriate
+ * for indexing a word-oriented device
+ */
+ if (info->reg_ndcr & NDCR_DWIDTH_M)
+ column /= 2;
+
+ /*
+ * There may be different NAND chip hooked to
+ * different chip select, so check whether
+ * chip select has been changed, if yes, reset the timing
+ */
+ if (info->cs != host->cs) {
+ info->cs = host->cs;
+ nand_writel(info, NDTR0CS0, info->ndtr0cs0);
+ nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+ }
+
+ prepare_start_command(info, command);
+
+ info->state = STATE_PREPARED;
+ exec_cmd = prepare_set_command(info, command, 0, column, page_addr);
+
+ if (exec_cmd) {
+ u32 ts;
+
+ info->cmd_complete = 0;
+ info->dev_ready = 0;
+ info->need_wait = 1;
+ pxa3xx_nand_start(info);
+
+ ts = get_timer(0);
+ while (1) {
+ u32 status;
+
+ status = nand_readl(info, NDSR);
+ if (status)
+ pxa3xx_nand_irq(info);
+
+ if (info->cmd_complete)
+ break;
+
+ if (get_timer(ts) > CHIP_DELAY_TIMEOUT) {
+ dev_err(&info->pdev->dev, "Wait timeout!!!\n");
+ return;
+ }
+ }
+ }
+ info->state = STATE_IDLE;
+}
+
+static void nand_cmdfunc_extended(struct mtd_info *mtd,
+ const unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ int exec_cmd, ext_cmd_type;
+
+ /*
+ * if this is a x16 device then convert the input
+ * "byte" address into a "word" address appropriate
+ * for indexing a word-oriented device
+ */
+ if (info->reg_ndcr & NDCR_DWIDTH_M)
+ column /= 2;
+
+ /*
+ * There may be different NAND chip hooked to
+ * different chip select, so check whether
+ * chip select has been changed, if yes, reset the timing
+ */
+ if (info->cs != host->cs) {
+ info->cs = host->cs;
+ nand_writel(info, NDTR0CS0, info->ndtr0cs0);
+ nand_writel(info, NDTR1CS0, info->ndtr1cs0);
+ }
+
+ /* Select the extended command for the first command */
+ switch (command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ ext_cmd_type = EXT_CMD_TYPE_MONO;
+ break;
+ case NAND_CMD_SEQIN:
+ ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
+ break;
+ case NAND_CMD_PAGEPROG:
+ ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
+ break;
+ default:
+ ext_cmd_type = 0;
+ break;
+ }
+
+ prepare_start_command(info, command);
+
+ /*
+ * Prepare the "is ready" completion before starting a command
+ * transaction sequence. If the command is not executed the
+ * completion will be completed, see below.
+ *
+ * We can do that inside the loop because the command variable
+ * is invariant and thus so is the exec_cmd.
+ */
+ info->need_wait = 1;
+ info->dev_ready = 0;
+
+ do {
+ u32 ts;
+
+ info->state = STATE_PREPARED;
+ exec_cmd = prepare_set_command(info, command, ext_cmd_type,
+ column, page_addr);
+ if (!exec_cmd) {
+ info->need_wait = 0;
+ info->dev_ready = 1;
+ break;
+ }
+
+ info->cmd_complete = 0;
+ pxa3xx_nand_start(info);
+
+ ts = get_timer(0);
+ while (1) {
+ u32 status;
+
+ status = nand_readl(info, NDSR);
+ if (status)
+ pxa3xx_nand_irq(info);
+
+ if (info->cmd_complete)
+ break;
+
+ if (get_timer(ts) > CHIP_DELAY_TIMEOUT) {
+ dev_err(&info->pdev->dev, "Wait timeout!!!\n");
+ return;
+ }
+ }
+
+ /* Only a few commands need several steps */
+ if (command != NAND_CMD_PAGEPROG &&
+ command != NAND_CMD_READ0 &&
+ command != NAND_CMD_READOOB)
+ break;
+
+ info->cur_chunk++;
+
+ /* Check if the sequence is complete */
+ if (info->cur_chunk == info->ntotalchunks &&
+ command != NAND_CMD_PAGEPROG)
+ break;
+
+ /*
+ * After a splitted program command sequence has issued
+ * the command dispatch, the command sequence is complete.
+ */
+ if (info->cur_chunk == (info->ntotalchunks + 1) &&
+ command == NAND_CMD_PAGEPROG &&
+ ext_cmd_type == EXT_CMD_TYPE_DISPATCH)
+ break;
+
+ if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) {
+ /* Last read: issue a 'last naked read' */
+ if (info->cur_chunk == info->ntotalchunks - 1)
+ ext_cmd_type = EXT_CMD_TYPE_LAST_RW;
+ else
+ ext_cmd_type = EXT_CMD_TYPE_NAKED_RW;
+
+ /*
+ * If a splitted program command has no more data to transfer,
+ * the command dispatch must be issued to complete.
+ */
+ } else if (command == NAND_CMD_PAGEPROG &&
+ info->cur_chunk == info->ntotalchunks) {
+ ext_cmd_type = EXT_CMD_TYPE_DISPATCH;
+ }
+ } while (1);
+
+ info->state = STATE_IDLE;
+}
+
+static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
+ int page)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required,
+ int page)
+{
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (info->retcode == ERR_CORERR && info->use_ecc) {
+ mtd->ecc_stats.corrected += info->ecc_err_cnt;
+
+ } else if (info->retcode == ERR_UNCORERR) {
+ /*
+ * for blank page (all 0xff), HW will calculate its ECC as
+ * 0, which is different from the ECC information within
+ * OOB, ignore such uncorrectable errors
+ */
+ if (is_buf_blank(buf, mtd->writesize))
+ info->retcode = ERR_NONE;
+ else
+ mtd->ecc_stats.failed++;
+ }
+
+ return info->max_bitflips;
+}
+
+static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ char retval = 0xFF;
+
+ if (info->buf_start < info->buf_count)
+ /* Has just send a new command? */
+ retval = info->data_buff[info->buf_start++];
+
+ return retval;
+}
+
+static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ u16 retval = 0xFFFF;
+
+ if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) {
+ retval = *((u16 *)(info->data_buff+info->buf_start));
+ info->buf_start += 2;
+ }
+ return retval;
+}
+
+static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+
+ memcpy(buf, info->data_buff + info->buf_start, real_len);
+ info->buf_start += real_len;
+}
+
+static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
+
+ memcpy(info->data_buff + info->buf_start, buf, real_len);
+ info->buf_start += real_len;
+}
+
+static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ return;
+}
+
+static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+
+ if (info->need_wait) {
+ u32 ts;
+
+ info->need_wait = 0;
+
+ ts = get_timer(0);
+ while (1) {
+ u32 status;
+
+ status = nand_readl(info, NDSR);
+ if (status)
+ pxa3xx_nand_irq(info);
+
+ if (info->dev_ready)
+ break;
+
+ if (get_timer(ts) > CHIP_DELAY_TIMEOUT) {
+ dev_err(&info->pdev->dev, "Ready timeout!!!\n");
+ return NAND_STATUS_FAIL;
+ }
+ }
+ }
+
+ /* pxa3xx_nand_send_command has waited for command complete */
+ if (this->state == FL_WRITING || this->state == FL_ERASING) {
+ if (info->retcode == ERR_NONE)
+ return 0;
+ else
+ return NAND_STATUS_FAIL;
+ }
+
+ return NAND_STATUS_READY;
+}
+
+static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
+
+ /* Configure default flash values */
+ info->reg_ndcr = 0x0; /* enable all interrupts */
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
+ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
+ info->reg_ndcr |= NDCR_SPARE_EN;
+
+ return 0;
+}
+
+static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_host *host = info->host[info->cs];
+ struct mtd_info *mtd = nand_to_mtd(&info->host[info->cs]->chip);
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
+ info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
+ info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
+}
+
+static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
+ uint32_t ndcr = nand_readl(info, NDCR);
+
+ /* Set an initial chunk size */
+ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
+ info->reg_ndcr = ndcr &
+ ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
+ info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
+ info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
+}
+
+static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
+{
+ info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
+ if (info->data_buff == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host)
+{
+ struct pxa3xx_nand_info *info = host->info_data;
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ const struct nand_sdr_timings *timings;
+ int ret;
+
+ mtd = nand_to_mtd(&info->host[info->cs]->chip);
+ chip = mtd_to_nand(mtd);
+
+ /* configure default flash values */
+ info->reg_ndcr = 0x0; /* enable all interrupts */
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
+ info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
+ info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
+
+ /* use the common timing to make a try */
+ timings = onfi_async_timing_mode_to_sdr_timings(0);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ pxa3xx_nand_set_sdr_timing(host, timings);
+
+ chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+ ret = chip->waitfunc(mtd, chip);
+ if (ret & NAND_STATUS_FAIL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int pxa_ecc_init(struct pxa3xx_nand_info *info,
+ struct nand_ecc_ctrl *ecc,
+ int strength, int ecc_stepsize, int page_size)
+{
+ if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
+ info->nfullchunks = 1;
+ info->ntotalchunks = 1;
+ info->chunk_size = 2048;
+ info->spare_size = 40;
+ info->ecc_size = 24;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = 512;
+ ecc->strength = 1;
+
+ } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
+ info->nfullchunks = 1;
+ info->ntotalchunks = 1;
+ info->chunk_size = 512;
+ info->spare_size = 8;
+ info->ecc_size = 8;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = 512;
+ ecc->strength = 1;
+
+ /*
+ * Required ECC: 4-bit correction per 512 bytes
+ * Select: 16-bit correction per 2048 bytes
+ */
+ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 1;
+ info->ntotalchunks = 1;
+ info->chunk_size = 2048;
+ info->spare_size = 32;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_2KB_bch4bit;
+ ecc->strength = 16;
+
+ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 2;
+ info->ntotalchunks = 2;
+ info->chunk_size = 2048;
+ info->spare_size = 32;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_4KB_bch4bit;
+ ecc->strength = 16;
+
+ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 8192) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 4;
+ info->ntotalchunks = 4;
+ info->chunk_size = 2048;
+ info->spare_size = 32;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_8KB_bch4bit;
+ ecc->strength = 16;
+
+ /*
+ * Required ECC: 8-bit correction per 512 bytes
+ * Select: 16-bit correction per 1024 bytes
+ */
+ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 2048) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 1;
+ info->ntotalchunks = 2;
+ info->chunk_size = 1024;
+ info->spare_size = 0;
+ info->last_chunk_size = 1024;
+ info->last_spare_size = 64;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_2KB_bch8bit;
+ ecc->strength = 16;
+
+ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 4;
+ info->ntotalchunks = 5;
+ info->chunk_size = 1024;
+ info->spare_size = 0;
+ info->last_chunk_size = 0;
+ info->last_spare_size = 64;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_4KB_bch8bit;
+ ecc->strength = 16;
+
+ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 8192) {
+ info->ecc_bch = 1;
+ info->nfullchunks = 8;
+ info->ntotalchunks = 9;
+ info->chunk_size = 1024;
+ info->spare_size = 0;
+ info->last_chunk_size = 0;
+ info->last_spare_size = 160;
+ info->ecc_size = 32;
+ ecc->mode = NAND_ECC_HW;
+ ecc->size = info->chunk_size;
+ ecc->layout = &ecc_layout_8KB_bch8bit;
+ ecc->strength = 16;
+
+ } else {
+ dev_err(&info->pdev->dev,
+ "ECC strength %d at page size %d is not supported\n",
+ strength, page_size);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int pxa3xx_nand_scan(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
+ struct pxa3xx_nand_info *info = host->info_data;
+ struct pxa3xx_nand_platform_data *pdata = info->pdata;
+ int ret;
+ uint16_t ecc_strength, ecc_step;
+
+ if (pdata->keep_config) {
+ pxa3xx_nand_detect_config(info);
+ } else {
+ ret = pxa3xx_nand_config_ident(info);
+ if (ret)
+ return ret;
+ ret = pxa3xx_nand_sensing(host);
+ if (ret) {
+ dev_info(&info->pdev->dev,
+ "There is no chip on cs %d!\n",
+ info->cs);
+ return ret;
+ }
+ }
+
+ /* Device detection must be done with ECC disabled */
+ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
+ nand_writel(info, NDECCCTRL, 0x0);
+
+ if (nand_scan_ident(mtd, 1, NULL))
+ return -ENODEV;
+
+ if (!pdata->keep_config) {
+ ret = pxa3xx_nand_init_timings(host);
+ if (ret) {
+ dev_err(&info->pdev->dev,
+ "Failed to set timings: %d\n", ret);
+ return ret;
+ }
+ }
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ /*
+ * We'll use a bad block table stored in-flash and don't
+ * allow writing the bad block marker to the flash.
+ */
+ chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB_BBM;
+ chip->bbt_td = &bbt_main_descr;
+ chip->bbt_md = &bbt_mirror_descr;
+#endif
+
+ if (pdata->ecc_strength && pdata->ecc_step_size) {
+ ecc_strength = pdata->ecc_strength;
+ ecc_step = pdata->ecc_step_size;
+ } else {
+ ecc_strength = chip->ecc_strength_ds;
+ ecc_step = chip->ecc_step_ds;
+ }
+
+ /* Set default ECC strength requirements on non-ONFI devices */
+ if (ecc_strength < 1 && ecc_step < 1) {
+ ecc_strength = 1;
+ ecc_step = 512;
+ }
+
+ ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
+ ecc_step, mtd->writesize);
+ if (ret)
+ return ret;
+
+ /*
+ * If the page size is bigger than the FIFO size, let's check
+ * we are given the right variant and then switch to the extended
+ * (aka split) command handling,
+ */
+ if (mtd->writesize > info->chunk_size) {
+ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) {
+ chip->cmdfunc = nand_cmdfunc_extended;
+ } else {
+ dev_err(&info->pdev->dev,
+ "unsupported page size on this variant\n");
+ return -ENODEV;
+ }
+ }
+
+ /* calculate addressing information */
+ if (mtd->writesize >= 2048)
+ host->col_addr_cycles = 2;
+ else
+ host->col_addr_cycles = 1;
+
+ /* release the initial buffer */
+ kfree(info->data_buff);
+
+ /* allocate the real data + oob buffer */
+ info->buf_size = mtd->writesize + mtd->oobsize;
+ ret = pxa3xx_nand_init_buff(info);
+ if (ret)
+ return ret;
+ info->oob_buff = info->data_buff + mtd->writesize;
+
+ if ((mtd->size >> chip->page_shift) > 65536)
+ host->row_addr_cycles = 3;
+ else
+ host->row_addr_cycles = 2;
+
+ if (!pdata->keep_config)
+ pxa3xx_nand_config_tail(info);
+
+ return nand_scan_tail(mtd);
+}
+
+static int alloc_nand_resource(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ struct pxa3xx_nand_host *host;
+ struct nand_chip *chip = NULL;
+ struct mtd_info *mtd;
+ int ret, cs;
+
+ pdata = info->pdata;
+ if (pdata->num_cs <= 0)
+ return -ENODEV;
+
+ info->variant = pxa3xx_nand_get_variant();
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ chip = (struct nand_chip *)
+ ((u8 *)&info[1] + sizeof(*host) * cs);
+ mtd = nand_to_mtd(chip);
+ host = (struct pxa3xx_nand_host *)chip;
+ info->host[cs] = host;
+ host->cs = cs;
+ host->info_data = info;
+ mtd->owner = THIS_MODULE;
+
+ nand_set_controller_data(chip, host);
+ chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
+ chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
+ chip->controller = &info->controller;
+ chip->waitfunc = pxa3xx_nand_waitfunc;
+ chip->select_chip = pxa3xx_nand_select_chip;
+ chip->read_word = pxa3xx_nand_read_word;
+ chip->read_byte = pxa3xx_nand_read_byte;
+ chip->read_buf = pxa3xx_nand_read_buf;
+ chip->write_buf = pxa3xx_nand_write_buf;
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+ chip->cmdfunc = nand_cmdfunc;
+ }
+
+ /* Allocate a buffer to allow flash detection */
+ info->buf_size = INIT_BUFFER_SIZE;
+ info->data_buff = kmalloc(info->buf_size, GFP_KERNEL);
+ if (info->data_buff == NULL) {
+ ret = -ENOMEM;
+ goto fail_disable_clk;
+ }
+
+ /* initialize all interrupts to be disabled */
+ disable_int(info, NDSR_MASK);
+
+ return 0;
+
+ kfree(info->data_buff);
+fail_disable_clk:
+ return ret;
+}
+
+static int pxa3xx_nand_probe_dt(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ const void *blob = gd->fdt_blob;
+ int node = -1;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ /* Get address decoding nodes from the FDT blob */
+ do {
+ node = fdt_node_offset_by_compatible(blob, node,
+ "marvell,mvebu-pxa3xx-nand");
+ if (node < 0)
+ break;
+
+ /* Bypass disabeld nodes */
+ if (!fdtdec_get_is_enabled(blob, node))
+ continue;
+
+ /* Get the first enabled NAND controler base address */
+ info->mmio_base =
+ (void __iomem *)fdtdec_get_addr_size_auto_noparent(
+ blob, node, "reg", 0, NULL, true);
+
+ pdata->num_cs = fdtdec_get_int(blob, node, "num-cs", 1);
+ if (pdata->num_cs != 1) {
+ pr_err("pxa3xx driver supports single CS only\n");
+ break;
+ }
+
+ if (fdtdec_get_bool(blob, node, "nand-enable-arbiter"))
+ pdata->enable_arbiter = 1;
+
+ if (fdtdec_get_bool(blob, node, "nand-keep-config"))
+ pdata->keep_config = 1;
+
+ /*
+ * ECC parameters.
+ * If these are not set, they will be selected according
+ * to the detected flash type.
+ */
+ /* ECC strength */
+ pdata->ecc_strength = fdtdec_get_int(blob, node,
+ "nand-ecc-strength", 0);
+
+ /* ECC step size */
+ pdata->ecc_step_size = fdtdec_get_int(blob, node,
+ "nand-ecc-step-size", 0);
+
+ info->pdata = pdata;
+
+ /* Currently support only a single NAND controller */
+ return 0;
+
+ } while (node >= 0);
+
+ return -EINVAL;
+}
+
+static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ int ret, cs, probe_success;
+
+ ret = pxa3xx_nand_probe_dt(info);
+ if (ret)
+ return ret;
+
+ pdata = info->pdata;
+
+ ret = alloc_nand_resource(info);
+ if (ret) {
+ dev_err(&pdev->dev, "alloc nand resource failed\n");
+ return ret;
+ }
+
+ probe_success = 0;
+ for (cs = 0; cs < pdata->num_cs; cs++) {
+ struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip);
+
+ /*
+ * The mtd name matches the one used in 'mtdparts' kernel
+ * parameter. This name cannot be changed or otherwise
+ * user's mtd partitions configuration would get broken.
+ */
+ mtd->name = "pxa3xx_nand-0";
+ info->cs = cs;
+ ret = pxa3xx_nand_scan(mtd);
+ if (ret) {
+ dev_info(&pdev->dev, "failed to scan nand at cs %d\n",
+ cs);
+ continue;
+ }
+
+ if (nand_register(cs, mtd))
+ continue;
+
+ probe_success = 1;
+ }
+
+ if (!probe_success)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * Main initialization routine
+ */
+void board_nand_init(void)
+{
+ struct pxa3xx_nand_info *info;
+ struct pxa3xx_nand_host *host;
+ int ret;
+
+ info = kzalloc(sizeof(*info) +
+ sizeof(*host) * CONFIG_SYS_MAX_NAND_DEVICE,
+ GFP_KERNEL);
+ if (!info)
+ return;
+
+ ret = pxa3xx_nand_probe(info);
+ if (ret)
+ return;
+}
diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.h b/drivers/mtd/nand/raw/pxa3xx_nand.h
new file mode 100644
index 0000000000..8f24ae6d18
--- /dev/null
+++ b/drivers/mtd/nand/raw/pxa3xx_nand.h
@@ -0,0 +1,64 @@
+#ifndef __ASM_ARCH_PXA3XX_NAND_H
+#define __ASM_ARCH_PXA3XX_NAND_H
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+struct pxa3xx_nand_timing {
+ unsigned int tCH; /* Enable signal hold time */
+ unsigned int tCS; /* Enable signal setup time */
+ unsigned int tWH; /* ND_nWE high duration */
+ unsigned int tWP; /* ND_nWE pulse time */
+ unsigned int tRH; /* ND_nRE high duration */
+ unsigned int tRP; /* ND_nRE pulse width */
+ unsigned int tR; /* ND_nWE high to ND_nRE low for read */
+ unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
+ unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
+};
+
+struct pxa3xx_nand_flash {
+ uint32_t chip_id;
+ unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
+ unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
+ struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
+};
+
+/*
+ * Current pxa3xx_nand controller has two chip select which
+ * both be workable.
+ *
+ * Notice should be taken that:
+ * When you want to use this feature, you should not enable the
+ * keep configuration feature, for two chip select could be
+ * attached with different nand chip. The different page size
+ * and timing requirement make the keep configuration impossible.
+ */
+
+/* The max num of chip select current support */
+#define NUM_CHIP_SELECT (2)
+struct pxa3xx_nand_platform_data {
+ /* the data flash bus is shared between the Static Memory
+ * Controller and the Data Flash Controller, the arbiter
+ * controls the ownership of the bus
+ */
+ int enable_arbiter;
+
+ /* allow platform code to keep OBM/bootloader defined NFC config */
+ int keep_config;
+
+ /* indicate how many chip selects will be used */
+ int num_cs;
+
+ /* use an flash-based bad block table */
+ bool flash_bbt;
+
+ /* requested ECC strength and ECC step size */
+ int ecc_strength, ecc_step_size;
+
+ const struct mtd_partition *parts[NUM_CHIP_SELECT];
+ unsigned int nr_parts[NUM_CHIP_SELECT];
+
+ const struct pxa3xx_nand_flash *flash;
+ size_t num_flash;
+};
+#endif /* __ASM_ARCH_PXA3XX_NAND_H */
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
new file mode 100644
index 0000000000..3ccb168d13
--- /dev/null
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -0,0 +1,1850 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
+ * Copyright (C) 2015 Roy Spliet <r.spliet@ultimaker.com>
+ *
+ * Derived from:
+ * https://github.com/yuq/sunxi-nfc-mtd
+ * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
+ *
+ * https://github.com/hno/Allwinner-Info
+ * Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
+ * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <memalign.h>
+#include <nand.h>
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#include <asm/gpio.h>
+#include <asm/arch/clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NFC_REG_CTL 0x0000
+#define NFC_REG_ST 0x0004
+#define NFC_REG_INT 0x0008
+#define NFC_REG_TIMING_CTL 0x000C
+#define NFC_REG_TIMING_CFG 0x0010
+#define NFC_REG_ADDR_LOW 0x0014
+#define NFC_REG_ADDR_HIGH 0x0018
+#define NFC_REG_SECTOR_NUM 0x001C
+#define NFC_REG_CNT 0x0020
+#define NFC_REG_CMD 0x0024
+#define NFC_REG_RCMD_SET 0x0028
+#define NFC_REG_WCMD_SET 0x002C
+#define NFC_REG_IO_DATA 0x0030
+#define NFC_REG_ECC_CTL 0x0034
+#define NFC_REG_ECC_ST 0x0038
+#define NFC_REG_DEBUG 0x003C
+#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
+#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
+#define NFC_REG_SPARE_AREA 0x00A0
+#define NFC_REG_PAT_ID 0x00A4
+#define NFC_RAM0_BASE 0x0400
+#define NFC_RAM1_BASE 0x0800
+
+/* define bit use in NFC_CTL */
+#define NFC_EN BIT(0)
+#define NFC_RESET BIT(1)
+#define NFC_BUS_WIDTH_MSK BIT(2)
+#define NFC_BUS_WIDTH_8 (0 << 2)
+#define NFC_BUS_WIDTH_16 (1 << 2)
+#define NFC_RB_SEL_MSK BIT(3)
+#define NFC_RB_SEL(x) ((x) << 3)
+#define NFC_CE_SEL_MSK (0x7 << 24)
+#define NFC_CE_SEL(x) ((x) << 24)
+#define NFC_CE_CTL BIT(6)
+#define NFC_PAGE_SHIFT_MSK (0xf << 8)
+#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8)
+#define NFC_SAM BIT(12)
+#define NFC_RAM_METHOD BIT(14)
+#define NFC_DEBUG_CTL BIT(31)
+
+/* define bit use in NFC_ST */
+#define NFC_RB_B2R BIT(0)
+#define NFC_CMD_INT_FLAG BIT(1)
+#define NFC_DMA_INT_FLAG BIT(2)
+#define NFC_CMD_FIFO_STATUS BIT(3)
+#define NFC_STA BIT(4)
+#define NFC_NATCH_INT_FLAG BIT(5)
+#define NFC_RB_STATE(x) BIT(x + 8)
+
+/* define bit use in NFC_INT */
+#define NFC_B2R_INT_ENABLE BIT(0)
+#define NFC_CMD_INT_ENABLE BIT(1)
+#define NFC_DMA_INT_ENABLE BIT(2)
+#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
+ NFC_CMD_INT_ENABLE | \
+ NFC_DMA_INT_ENABLE)
+
+/* define bit use in NFC_TIMING_CTL */
+#define NFC_TIMING_CTL_EDO BIT(8)
+
+/* define NFC_TIMING_CFG register layout */
+#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
+ (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \
+ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
+ (((tCAD) & 0x7) << 8))
+
+/* define bit use in NFC_CMD */
+#define NFC_CMD_LOW_BYTE_MSK 0xff
+#define NFC_CMD_HIGH_BYTE_MSK (0xff << 8)
+#define NFC_CMD(x) (x)
+#define NFC_ADR_NUM_MSK (0x7 << 16)
+#define NFC_ADR_NUM(x) (((x) - 1) << 16)
+#define NFC_SEND_ADR BIT(19)
+#define NFC_ACCESS_DIR BIT(20)
+#define NFC_DATA_TRANS BIT(21)
+#define NFC_SEND_CMD1 BIT(22)
+#define NFC_WAIT_FLAG BIT(23)
+#define NFC_SEND_CMD2 BIT(24)
+#define NFC_SEQ BIT(25)
+#define NFC_DATA_SWAP_METHOD BIT(26)
+#define NFC_ROW_AUTO_INC BIT(27)
+#define NFC_SEND_CMD3 BIT(28)
+#define NFC_SEND_CMD4 BIT(29)
+#define NFC_CMD_TYPE_MSK (0x3 << 30)
+#define NFC_NORMAL_OP (0 << 30)
+#define NFC_ECC_OP (1 << 30)
+#define NFC_PAGE_OP (2 << 30)
+
+/* define bit use in NFC_RCMD_SET */
+#define NFC_READ_CMD_MSK 0xff
+#define NFC_RND_READ_CMD0_MSK (0xff << 8)
+#define NFC_RND_READ_CMD1_MSK (0xff << 16)
+
+/* define bit use in NFC_WCMD_SET */
+#define NFC_PROGRAM_CMD_MSK 0xff
+#define NFC_RND_WRITE_CMD_MSK (0xff << 8)
+#define NFC_READ_CMD0_MSK (0xff << 16)
+#define NFC_READ_CMD1_MSK (0xff << 24)
+
+/* define bit use in NFC_ECC_CTL */
+#define NFC_ECC_EN BIT(0)
+#define NFC_ECC_PIPELINE BIT(3)
+#define NFC_ECC_EXCEPTION BIT(4)
+#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
+#define NFC_ECC_BLOCK_512 (1 << 5)
+#define NFC_RANDOM_EN BIT(9)
+#define NFC_RANDOM_DIRECTION BIT(10)
+#define NFC_ECC_MODE_MSK (0xf << 12)
+#define NFC_ECC_MODE(x) ((x) << 12)
+#define NFC_RANDOM_SEED_MSK (0x7fff << 16)
+#define NFC_RANDOM_SEED(x) ((x) << 16)
+
+/* define bit use in NFC_ECC_ST */
+#define NFC_ECC_ERR(x) BIT(x)
+#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
+#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff)
+
+#define NFC_DEFAULT_TIMEOUT_MS 1000
+
+#define NFC_SRAM_SIZE 1024
+
+#define NFC_MAX_CS 7
+
+/*
+ * Ready/Busy detection type: describes the Ready/Busy detection modes
+ *
+ * @RB_NONE: no external detection available, rely on STATUS command
+ * and software timeouts
+ * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy
+ * pin of the NAND flash chip must be connected to one of the
+ * native NAND R/B pins (those which can be muxed to the NAND
+ * Controller)
+ * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy
+ * pin of the NAND flash chip must be connected to a GPIO capable
+ * pin.
+ */
+enum sunxi_nand_rb_type {
+ RB_NONE,
+ RB_NATIVE,
+ RB_GPIO,
+};
+
+/*
+ * Ready/Busy structure: stores information related to Ready/Busy detection
+ *
+ * @type: the Ready/Busy detection mode
+ * @info: information related to the R/B detection mode. Either a gpio
+ * id or a native R/B id (those supported by the NAND controller).
+ */
+struct sunxi_nand_rb {
+ enum sunxi_nand_rb_type type;
+ union {
+ struct gpio_desc gpio;
+ int nativeid;
+ } info;
+};
+
+/*
+ * Chip Select structure: stores information related to NAND Chip Select
+ *
+ * @cs: the NAND CS id used to communicate with a NAND Chip
+ * @rb: the Ready/Busy description
+ */
+struct sunxi_nand_chip_sel {
+ u8 cs;
+ struct sunxi_nand_rb rb;
+};
+
+/*
+ * sunxi HW ECC infos: stores information related to HW ECC support
+ *
+ * @mode: the sunxi ECC mode field deduced from ECC requirements
+ * @layout: the OOB layout depending on the ECC requirements and the
+ * selected ECC mode
+ */
+struct sunxi_nand_hw_ecc {
+ int mode;
+ struct nand_ecclayout layout;
+};
+
+/*
+ * NAND chip structure: stores NAND chip device related information
+ *
+ * @node: used to store NAND chips into a list
+ * @nand: base NAND chip structure
+ * @mtd: base MTD structure
+ * @clk_rate: clk_rate required for this NAND chip
+ * @timing_cfg TIMING_CFG register value for this NAND chip
+ * @selected: current active CS
+ * @nsels: number of CS lines required by the NAND chip
+ * @sels: array of CS lines descriptions
+ */
+struct sunxi_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+ unsigned long clk_rate;
+ u32 timing_cfg;
+ u32 timing_ctl;
+ int selected;
+ int addr_cycles;
+ u32 addr[2];
+ int cmd_cycles;
+ u8 cmd[2];
+ int nsels;
+ struct sunxi_nand_chip_sel sels[0];
+};
+
+static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
+{
+ return container_of(nand, struct sunxi_nand_chip, nand);
+}
+
+/*
+ * NAND Controller structure: stores sunxi NAND controller information
+ *
+ * @controller: base controller structure
+ * @dev: parent device (used to print error messages)
+ * @regs: NAND controller registers
+ * @ahb_clk: NAND Controller AHB clock
+ * @mod_clk: NAND Controller mod clock
+ * @assigned_cs: bitmask describing already assigned CS lines
+ * @clk_rate: NAND controller current clock rate
+ * @chips: a list containing all the NAND chips attached to
+ * this NAND controller
+ * @complete: a completion object used to wait for NAND
+ * controller events
+ */
+struct sunxi_nfc {
+ struct nand_hw_control controller;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *ahb_clk;
+ struct clk *mod_clk;
+ unsigned long assigned_cs;
+ unsigned long clk_rate;
+ struct list_head chips;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static void sunxi_nfc_set_clk_rate(unsigned long hz)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ int div_m, div_n;
+
+ div_m = (clock_get_pll6() + hz - 1) / hz;
+ for (div_n = 0; div_n < 3 && div_m > 16; div_n++) {
+ if (div_m % 2)
+ div_m++;
+ div_m >>= 1;
+ }
+ if (div_m > 16)
+ div_m = 16;
+
+ /* config mod clock */
+ writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 |
+ CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m),
+ &ccm->nand0_clk_cfg);
+
+ /* gate on nand clock */
+ setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0));
+#ifdef CONFIG_MACH_SUN9I
+ setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
+#else
+ setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
+#endif
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+ unsigned int timeout_ms)
+{
+ unsigned int timeout_ticks;
+ u32 time_start, status;
+ int ret = -ETIMEDOUT;
+
+ if (!timeout_ms)
+ timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
+
+ timeout_ticks = (timeout_ms * CONFIG_SYS_HZ) / 1000;
+
+ time_start = get_timer(0);
+
+ do {
+ status = readl(nfc->regs + NFC_REG_ST);
+ if ((status & flags) == flags) {
+ ret = 0;
+ break;
+ }
+
+ udelay(1);
+ } while (get_timer(time_start) < timeout_ticks);
+
+ writel(status & flags, nfc->regs + NFC_REG_ST);
+
+ return ret;
+}
+
+static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
+{
+ unsigned long timeout = (CONFIG_SYS_HZ *
+ NFC_DEFAULT_TIMEOUT_MS) / 1000;
+ u32 time_start;
+
+ time_start = get_timer(0);
+ do {
+ if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ return 0;
+ } while (get_timer(time_start) < timeout);
+
+ dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
+ return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
+{
+ unsigned long timeout = (CONFIG_SYS_HZ *
+ NFC_DEFAULT_TIMEOUT_MS) / 1000;
+ u32 time_start;
+
+ writel(0, nfc->regs + NFC_REG_ECC_CTL);
+ writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
+
+ time_start = get_timer(0);
+ do {
+ if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
+ return 0;
+ } while (get_timer(time_start) < timeout);
+
+ dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
+ return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct sunxi_nand_rb *rb;
+ unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
+ int ret;
+
+ if (sunxi_nand->selected < 0)
+ return 0;
+
+ rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
+
+ switch (rb->type) {
+ case RB_NATIVE:
+ ret = !!(readl(nfc->regs + NFC_REG_ST) &
+ NFC_RB_STATE(rb->info.nativeid));
+ if (ret)
+ break;
+
+ sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+ ret = !!(readl(nfc->regs + NFC_REG_ST) &
+ NFC_RB_STATE(rb->info.nativeid));
+ break;
+ case RB_GPIO:
+ ret = dm_gpio_get_value(&rb->info.gpio);
+ break;
+ case RB_NONE:
+ default:
+ ret = 0;
+ dev_err(nfc->dev, "cannot check R/B NAND status!\n");
+ break;
+ }
+
+ return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct sunxi_nand_chip_sel *sel;
+ u32 ctl;
+
+ if (chip > 0 && chip >= sunxi_nand->nsels)
+ return;
+
+ if (chip == sunxi_nand->selected)
+ return;
+
+ ctl = readl(nfc->regs + NFC_REG_CTL) &
+ ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
+
+ if (chip >= 0) {
+ sel = &sunxi_nand->sels[chip];
+
+ ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
+ NFC_PAGE_SHIFT(nand->page_shift - 10);
+ if (sel->rb.type == RB_NONE) {
+ nand->dev_ready = NULL;
+ } else {
+ nand->dev_ready = sunxi_nfc_dev_ready;
+ if (sel->rb.type == RB_NATIVE)
+ ctl |= NFC_RB_SEL(sel->rb.info.nativeid);
+ }
+
+ writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+ if (nfc->clk_rate != sunxi_nand->clk_rate) {
+ sunxi_nfc_set_clk_rate(sunxi_nand->clk_rate);
+ nfc->clk_rate = sunxi_nand->clk_rate;
+ }
+ }
+
+ writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
+ writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
+ writel(ctl, nfc->regs + NFC_REG_CTL);
+
+ sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+ int cnt;
+ int offs = 0;
+ u32 tmp;
+
+ while (len > offs) {
+ cnt = min(len - offs, NFC_SRAM_SIZE);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ break;
+
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ if (ret)
+ break;
+
+ if (buf)
+ memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+ cnt);
+ offs += cnt;
+ }
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+ int cnt;
+ int offs = 0;
+ u32 tmp;
+
+ while (len > offs) {
+ cnt = min(len - offs, NFC_SRAM_SIZE);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ break;
+
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ if (ret)
+ break;
+
+ offs += cnt;
+ }
+}
+
+static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ sunxi_nfc_read_buf(mtd, &ret, 1);
+
+ return ret;
+}
+
+static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int ret;
+ u32 tmp;
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ tmp = readl(nfc->regs + NFC_REG_CTL);
+ if (ctrl & NAND_NCE)
+ tmp |= NFC_CE_CTL;
+ else
+ tmp &= ~NFC_CE_CTL;
+ writel(tmp, nfc->regs + NFC_REG_CTL);
+ }
+
+ if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
+ !(ctrl & (NAND_CLE | NAND_ALE))) {
+ u32 cmd = 0;
+
+ if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
+ return;
+
+ if (sunxi_nand->cmd_cycles--)
+ cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
+
+ if (sunxi_nand->cmd_cycles--) {
+ cmd |= NFC_SEND_CMD2;
+ writel(sunxi_nand->cmd[1],
+ nfc->regs + NFC_REG_RCMD_SET);
+ }
+
+ sunxi_nand->cmd_cycles = 0;
+
+ if (sunxi_nand->addr_cycles) {
+ cmd |= NFC_SEND_ADR |
+ NFC_ADR_NUM(sunxi_nand->addr_cycles);
+ writel(sunxi_nand->addr[0],
+ nfc->regs + NFC_REG_ADDR_LOW);
+ }
+
+ if (sunxi_nand->addr_cycles > 4)
+ writel(sunxi_nand->addr[1],
+ nfc->regs + NFC_REG_ADDR_HIGH);
+
+ writel(cmd, nfc->regs + NFC_REG_CMD);
+ sunxi_nand->addr[0] = 0;
+ sunxi_nand->addr[1] = 0;
+ sunxi_nand->addr_cycles = 0;
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ }
+
+ if (ctrl & NAND_CLE) {
+ sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
+ } else if (ctrl & NAND_ALE) {
+ sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
+ dat << ((sunxi_nand->addr_cycles % 4) * 8);
+ sunxi_nand->addr_cycles++;
+ }
+}
+
+/* These seed values have been extracted from Allwinner's BSP */
+static const u16 sunxi_nfc_randomizer_page_seeds[] = {
+ 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+ 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+ 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+ 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+ 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+ 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+ 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+ 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+ 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+ 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+ 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+ 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+ 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+ 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+ 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+ 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+/*
+ * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
+ * have been generated using
+ * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
+ * the randomizer engine does internally before de/scrambling OOB data.
+ *
+ * Those tables are statically defined to avoid calculating randomizer state
+ * at runtime.
+ */
+static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
+ 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
+ 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
+ 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
+ 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
+ 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
+ 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
+ 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
+ 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
+ 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
+ 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
+ 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
+ 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
+ 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
+ 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
+ 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
+ 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
+};
+
+static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
+ 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
+ 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
+ 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
+ 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
+ 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
+ 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
+ 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
+ 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
+ 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
+ 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
+ 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
+ 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
+ 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
+ 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
+ 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
+ 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
+};
+
+static u16 sunxi_nfc_randomizer_step(u16 state, int count)
+{
+ state &= 0x7fff;
+
+ /*
+ * This loop is just a simple implementation of a Fibonacci LFSR using
+ * the x16 + x15 + 1 polynomial.
+ */
+ while (count--)
+ state = ((state >> 1) |
+ (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+ return state;
+}
+
+static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc)
+{
+ const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
+ int mod = mtd->erasesize / mtd->writesize;
+
+ if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
+ mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
+
+ if (ecc) {
+ if (mtd->ecc_step_size == 512)
+ seeds = sunxi_nfc_randomizer_ecc512_seeds;
+ else
+ seeds = sunxi_nfc_randomizer_ecc1024_seeds;
+ }
+
+ return seeds[page % mod];
+}
+
+static void sunxi_nfc_randomizer_config(struct mtd_info *mtd,
+ int page, bool ecc)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ u16 state;
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ state = sunxi_nfc_randomizer_state(mtd, page, ecc);
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
+ writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ if (!(nand->options & NAND_NEED_SCRAMBLING))
+ return;
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm)
+{
+ u16 state = sunxi_nfc_randomizer_state(mtd, page, true);
+
+ bbm[0] ^= state;
+ bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
+}
+
+static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf, int len,
+ bool ecc, int page)
+{
+ sunxi_nfc_randomizer_config(mtd, page, ecc);
+ sunxi_nfc_randomizer_enable(mtd);
+ sunxi_nfc_write_buf(mtd, buf, len);
+ sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf,
+ int len, bool ecc, int page)
+{
+ sunxi_nfc_randomizer_config(mtd, page, ecc);
+ sunxi_nfc_randomizer_enable(mtd);
+ sunxi_nfc_read_buf(mtd, buf, len);
+ sunxi_nfc_randomizer_disable(mtd);
+}
+
+static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
+ u32 ecc_ctl;
+
+ ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
+ NFC_ECC_BLOCK_SIZE_MSK);
+ ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION;
+
+ if (nand->ecc.size == 512)
+ ecc_ctl |= NFC_ECC_BLOCK_512;
+
+ writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
+ nfc->regs + NFC_REG_ECC_CTL);
+}
+
+static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
+{
+ buf[0] = user_data;
+ buf[1] = user_data >> 8;
+ buf[2] = user_data >> 16;
+ buf[3] = user_data >> 24;
+}
+
+static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
+ u8 *data, int data_off,
+ u8 *oob, int oob_off,
+ int *cur_off,
+ unsigned int *max_bitflips,
+ bool bbm, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int raw_mode = 0;
+ u32 status;
+ int ret;
+
+ if (*cur_off != data_off)
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
+
+ sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page);
+
+ if (data_off + ecc->size != oob_off)
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ sunxi_nfc_randomizer_enable(mtd);
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ sunxi_nfc_randomizer_disable(mtd);
+ if (ret)
+ return ret;
+
+ *cur_off = oob_off + ecc->bytes + 4;
+
+ status = readl(nfc->regs + NFC_REG_ECC_ST);
+ if (status & NFC_ECC_PAT_FOUND(0)) {
+ u8 pattern = 0xff;
+
+ if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
+ pattern = 0x0;
+
+ memset(data, pattern, ecc->size);
+ memset(oob, pattern, ecc->bytes + 4);
+
+ return 1;
+ }
+
+ ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
+
+ memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
+
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+ sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
+
+ if (status & NFC_ECC_ERR(0)) {
+ /*
+ * Re-read the data with the randomizer disabled to identify
+ * bitflips in erased pages.
+ */
+ if (nand->options & NAND_NEED_SCRAMBLING) {
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
+ nand->read_buf(mtd, data, ecc->size);
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
+ nand->read_buf(mtd, oob, ecc->bytes + 4);
+ }
+
+ ret = nand_check_erased_ecc_chunk(data, ecc->size,
+ oob, ecc->bytes + 4,
+ NULL, 0, ecc->strength);
+ if (ret >= 0)
+ raw_mode = 1;
+ } else {
+ /*
+ * The engine protects 4 bytes of OOB data per chunk.
+ * Retrieve the corrected OOB bytes.
+ */
+ sunxi_nfc_user_data_to_buf(readl(nfc->regs +
+ NFC_REG_USER_DATA(0)),
+ oob);
+
+ /* De-randomize the Bad Block Marker. */
+ if (bbm && nand->options & NAND_NEED_SCRAMBLING)
+ sunxi_nfc_randomize_bbm(mtd, page, oob);
+ }
+
+ if (ret < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += ret;
+ *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+ }
+
+ return raw_mode;
+}
+
+static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
+ u8 *oob, int *cur_off,
+ bool randomize, int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int offset = ((ecc->bytes + 4) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ if (*cur_off != offset)
+ nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ offset + mtd->writesize, -1);
+
+ if (!randomize)
+ sunxi_nfc_read_buf(mtd, oob + offset, len);
+ else
+ sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
+ false, page);
+
+ *cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
+ const u8 *data, int data_off,
+ const u8 *oob, int oob_off,
+ int *cur_off, bool bbm,
+ int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int ret;
+
+ if (data_off != *cur_off)
+ nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1);
+
+ sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
+
+ /* Fill OOB data in */
+ if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
+ u8 user_data[4];
+
+ memcpy(user_data, oob, 4);
+ sunxi_nfc_randomize_bbm(mtd, page, user_data);
+ writel(sunxi_nfc_buf_to_user_data(user_data),
+ nfc->regs + NFC_REG_USER_DATA(0));
+ } else {
+ writel(sunxi_nfc_buf_to_user_data(oob),
+ nfc->regs + NFC_REG_USER_DATA(0));
+ }
+
+ if (data_off + ecc->size != oob_off)
+ nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
+
+ ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+ if (ret)
+ return ret;
+
+ sunxi_nfc_randomizer_enable(mtd);
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR | NFC_ECC_OP,
+ nfc->regs + NFC_REG_CMD);
+
+ ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ sunxi_nfc_randomizer_disable(mtd);
+ if (ret)
+ return ret;
+
+ *cur_off = oob_off + ecc->bytes + 4;
+
+ return 0;
+}
+
+static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
+ u8 *oob, int *cur_off,
+ int page)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ int offset = ((ecc->bytes + 4) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ if (*cur_off != offset)
+ nand->cmdfunc(mtd, NAND_CMD_RNDIN,
+ offset + mtd->writesize, -1);
+
+ sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
+
+ *cur_off = mtd->oobsize + mtd->writesize;
+}
+
+static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ unsigned int max_bitflips = 0;
+ int ret, i, cur_off = 0;
+ bool raw_mode = false;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = buf + data_off;
+ u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, &max_bitflips,
+ !i, page);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ raw_mode = true;
+ }
+
+ if (oob_required)
+ sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
+ !raw_mode, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen,
+ uint8_t *bufpoi, int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+ unsigned int max_bitflips = 0;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ for (i = data_offs / ecc->size;
+ i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ u8 *data = bufpoi + data_off;
+ u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
+ oob, oob_off + mtd->writesize,
+ &cur_off, &max_bitflips, !i, page);
+ if (ret < 0)
+ return ret;
+ }
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf, int oob_required,
+ int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ const u8 *data = buf + data_off;
+ const u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, !i, page);
+ if (ret)
+ return ret;
+ }
+
+ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+ &cur_off, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return 0;
+}
+
+static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ u32 data_offs, u32 data_len,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = data_offs / ecc->size;
+ i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
+ int data_off = i * ecc->size;
+ int oob_off = i * (ecc->bytes + 4);
+ const u8 *data = buf + data_off;
+ const u8 *oob = chip->oob_poi + oob_off;
+
+ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob,
+ oob_off + mtd->writesize,
+ &cur_off, !i, page);
+ if (ret)
+ return ret;
+ }
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return 0;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf, int oob_required,
+ int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ unsigned int max_bitflips = 0;
+ int ret, i, cur_off = 0;
+ bool raw_mode = false;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * (ecc->size + ecc->bytes + 4);
+ int oob_off = data_off + ecc->size;
+ u8 *data = buf + (i * ecc->size);
+ u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+
+ ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
+ oob_off, &cur_off,
+ &max_bitflips, !i, page);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ raw_mode = true;
+ }
+
+ if (oob_required)
+ sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off,
+ !raw_mode, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required, int page)
+{
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int ret, i, cur_off = 0;
+
+ sunxi_nfc_hw_ecc_enable(mtd);
+
+ for (i = 0; i < ecc->steps; i++) {
+ int data_off = i * (ecc->size + ecc->bytes + 4);
+ int oob_off = data_off + ecc->size;
+ const u8 *data = buf + (i * ecc->size);
+ const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4));
+
+ ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off,
+ oob, oob_off, &cur_off,
+ false, page);
+ if (ret)
+ return ret;
+ }
+
+ if (oob_required || (chip->options & NAND_NEED_SCRAMBLING))
+ sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi,
+ &cur_off, page);
+
+ sunxi_nfc_hw_ecc_disable(mtd);
+
+ return 0;
+}
+
+static const s32 tWB_lut[] = {6, 12, 16, 20};
+static const s32 tRHW_lut[] = {4, 8, 12, 20};
+
+static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
+ u32 clk_period)
+{
+ u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
+ int i;
+
+ for (i = 0; i < lut_size; i++) {
+ if (clk_cycles <= lut[i])
+ return i;
+ }
+
+ /* Doesn't fit */
+ return -EINVAL;
+}
+
+#define sunxi_nand_lookup_timing(l, p, c) \
+ _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
+
+static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
+ const struct nand_sdr_timings *timings)
+{
+ u32 min_clk_period = 0;
+ s32 tWB, tADL, tWHR, tRHW, tCAD;
+
+ /* T1 <=> tCLS */
+ if (timings->tCLS_min > min_clk_period)
+ min_clk_period = timings->tCLS_min;
+
+ /* T2 <=> tCLH */
+ if (timings->tCLH_min > min_clk_period)
+ min_clk_period = timings->tCLH_min;
+
+ /* T3 <=> tCS */
+ if (timings->tCS_min > min_clk_period)
+ min_clk_period = timings->tCS_min;
+
+ /* T4 <=> tCH */
+ if (timings->tCH_min > min_clk_period)
+ min_clk_period = timings->tCH_min;
+
+ /* T5 <=> tWP */
+ if (timings->tWP_min > min_clk_period)
+ min_clk_period = timings->tWP_min;
+
+ /* T6 <=> tWH */
+ if (timings->tWH_min > min_clk_period)
+ min_clk_period = timings->tWH_min;
+
+ /* T7 <=> tALS */
+ if (timings->tALS_min > min_clk_period)
+ min_clk_period = timings->tALS_min;
+
+ /* T8 <=> tDS */
+ if (timings->tDS_min > min_clk_period)
+ min_clk_period = timings->tDS_min;
+
+ /* T9 <=> tDH */
+ if (timings->tDH_min > min_clk_period)
+ min_clk_period = timings->tDH_min;
+
+ /* T10 <=> tRR */
+ if (timings->tRR_min > (min_clk_period * 3))
+ min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+
+ /* T11 <=> tALH */
+ if (timings->tALH_min > min_clk_period)
+ min_clk_period = timings->tALH_min;
+
+ /* T12 <=> tRP */
+ if (timings->tRP_min > min_clk_period)
+ min_clk_period = timings->tRP_min;
+
+ /* T13 <=> tREH */
+ if (timings->tREH_min > min_clk_period)
+ min_clk_period = timings->tREH_min;
+
+ /* T14 <=> tRC */
+ if (timings->tRC_min > (min_clk_period * 2))
+ min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+
+ /* T15 <=> tWC */
+ if (timings->tWC_min > (min_clk_period * 2))
+ min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+
+ /* T16 - T19 + tCAD */
+ tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
+ min_clk_period);
+ if (tWB < 0) {
+ dev_err(nfc->dev, "unsupported tWB\n");
+ return tWB;
+ }
+
+ tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
+ if (tADL > 3) {
+ dev_err(nfc->dev, "unsupported tADL\n");
+ return -EINVAL;
+ }
+
+ tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
+ if (tWHR > 3) {
+ dev_err(nfc->dev, "unsupported tWHR\n");
+ return -EINVAL;
+ }
+
+ tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
+ min_clk_period);
+ if (tRHW < 0) {
+ dev_err(nfc->dev, "unsupported tRHW\n");
+ return tRHW;
+ }
+
+ /*
+ * TODO: according to ONFI specs this value only applies for DDR NAND,
+ * but Allwinner seems to set this to 0x7. Mimic them for now.
+ */
+ tCAD = 0x7;
+
+ /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
+ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
+
+ /*
+ * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
+ * output cycle timings shall be used if the host drives tRC less than
+ * 30 ns.
+ */
+ chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0;
+
+ /* Convert min_clk_period from picoseconds to nanoseconds */
+ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
+
+ /*
+ * Convert min_clk_period into a clk frequency, then get the
+ * appropriate rate for the NAND controller IP given this formula
+ * (specified in the datasheet):
+ * nand clk_rate = min_clk_rate
+ */
+ chip->clk_rate = 1000000000L / min_clk_period;
+
+ return 0;
+}
+
+static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip)
+{
+ struct mtd_info *mtd = nand_to_mtd(&chip->nand);
+ const struct nand_sdr_timings *timings;
+ int ret;
+ int mode;
+
+ mode = onfi_get_async_timing_mode(&chip->nand);
+ if (mode == ONFI_TIMING_MODE_UNKNOWN) {
+ mode = chip->nand.onfi_timing_mode_default;
+ } else {
+ uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
+ int i;
+
+ mode = fls(mode) - 1;
+ if (mode < 0)
+ mode = 0;
+
+ feature[0] = mode;
+ for (i = 0; i < chip->nsels; i++) {
+ chip->nand.select_chip(mtd, i);
+ ret = chip->nand.onfi_set_features(mtd,
+ &chip->nand,
+ ONFI_FEATURE_ADDR_TIMING_MODE,
+ feature);
+ chip->nand.select_chip(mtd, -1);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+ }
+ }
+
+ timings = onfi_async_timing_mode_to_sdr_timings(mode);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ return sunxi_nand_chip_set_timings(chip, timings);
+}
+
+static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
+ struct sunxi_nand_hw_ecc *data;
+ struct nand_ecclayout *layout;
+ int nsectors;
+ int ret;
+ int i;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (ecc->size != 512 && ecc->size != 1024)
+ return -EINVAL;
+
+ /* Prefer 1k ECC chunk over 512 ones */
+ if (ecc->size == 512 && mtd->writesize > 512) {
+ ecc->size = 1024;
+ ecc->strength *= 2;
+ }
+
+ /* Add ECC info retrieval from DT */
+ for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ if (ecc->strength <= strengths[i]) {
+ /*
+ * Update ecc->strength value with the actual strength
+ * that will be used by the ECC engine.
+ */
+ ecc->strength = strengths[i];
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(strengths)) {
+ dev_err(nfc->dev, "unsupported strength\n");
+ ret = -ENOTSUPP;
+ goto err;
+ }
+
+ data->mode = i;
+
+ /* HW ECC always request ECC bytes for 1024 bytes blocks */
+ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
+
+ /* HW ECC always work with even numbers of ECC bytes */
+ ecc->bytes = ALIGN(ecc->bytes, 2);
+
+ layout = &data->layout;
+ nsectors = mtd->writesize / ecc->size;
+
+ if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ layout->eccbytes = (ecc->bytes * nsectors);
+
+ ecc->layout = layout;
+ ecc->priv = data;
+
+ return 0;
+
+err:
+ kfree(data);
+
+ return ret;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
+{
+ kfree(ecc->priv);
+}
+#endif /* __UBOOT__ */
+
+static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ struct nand_ecclayout *layout;
+ int nsectors;
+ int i, j;
+ int ret;
+
+ ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+
+ ecc->read_page = sunxi_nfc_hw_ecc_read_page;
+ ecc->write_page = sunxi_nfc_hw_ecc_write_page;
+ ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
+ ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
+ layout = ecc->layout;
+ nsectors = mtd->writesize / ecc->size;
+
+ for (i = 0; i < nsectors; i++) {
+ if (i) {
+ layout->oobfree[i].offset =
+ layout->oobfree[i - 1].offset +
+ layout->oobfree[i - 1].length +
+ ecc->bytes;
+ layout->oobfree[i].length = 4;
+ } else {
+ /*
+ * The first 2 bytes are used for BB markers, hence we
+ * only have 2 bytes available in the first user data
+ * section.
+ */
+ layout->oobfree[i].length = 2;
+ layout->oobfree[i].offset = 2;
+ }
+
+ for (j = 0; j < ecc->bytes; j++)
+ layout->eccpos[(ecc->bytes * i) + j] =
+ layout->oobfree[i].offset +
+ layout->oobfree[i].length + j;
+ }
+
+ if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
+ layout->oobfree[nsectors].offset =
+ layout->oobfree[nsectors - 1].offset +
+ layout->oobfree[nsectors - 1].length +
+ ecc->bytes;
+ layout->oobfree[nsectors].length = mtd->oobsize -
+ ((ecc->bytes + 4) * nsectors);
+ }
+
+ return 0;
+}
+
+static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ struct nand_ecclayout *layout;
+ int nsectors;
+ int i;
+ int ret;
+
+ ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+
+ ecc->prepad = 4;
+ ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
+ ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
+
+ layout = ecc->layout;
+ nsectors = mtd->writesize / ecc->size;
+
+ for (i = 0; i < (ecc->bytes * nsectors); i++)
+ layout->eccpos[i] = i;
+
+ layout->oobfree[0].length = mtd->oobsize - i;
+ layout->oobfree[0].offset = i;
+
+ return 0;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
+{
+ switch (ecc->mode) {
+ case NAND_ECC_HW:
+ case NAND_ECC_HW_SYNDROME:
+ sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
+ break;
+ case NAND_ECC_NONE:
+ kfree(ecc->layout);
+ default:
+ break;
+ }
+}
+#endif /* __UBOOT__ */
+
+static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ int ret;
+
+ if (!ecc->size) {
+ ecc->size = nand->ecc_step_ds;
+ ecc->strength = nand->ecc_strength_ds;
+ }
+
+ if (!ecc->size || !ecc->strength)
+ return -EINVAL;
+
+ switch (ecc->mode) {
+ case NAND_ECC_SOFT_BCH:
+ break;
+ case NAND_ECC_HW:
+ ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+ break;
+ case NAND_ECC_HW_SYNDROME:
+ ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+ break;
+ case NAND_ECC_NONE:
+ ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
+ if (!ecc->layout)
+ return -ENOMEM;
+ ecc->layout->oobfree[0].length = mtd->oobsize;
+ case NAND_ECC_SOFT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum)
+{
+ const struct nand_sdr_timings *timings;
+ const void *blob = gd->fdt_blob;
+ struct sunxi_nand_chip *chip;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ int nsels;
+ int ret;
+ int i;
+ u32 cs[8], rb[8];
+
+ if (!fdt_getprop(blob, node, "reg", &nsels))
+ return -EINVAL;
+
+ nsels /= sizeof(u32);
+ if (!nsels || nsels > 8) {
+ dev_err(dev, "invalid reg property size\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(*chip) +
+ (nsels * sizeof(struct sunxi_nand_chip_sel)),
+ GFP_KERNEL);
+ if (!chip) {
+ dev_err(dev, "could not allocate chip\n");
+ return -ENOMEM;
+ }
+
+ chip->nsels = nsels;
+ chip->selected = -1;
+
+ for (i = 0; i < nsels; i++) {
+ cs[i] = -1;
+ rb[i] = -1;
+ }
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", cs, nsels);
+ if (ret) {
+ dev_err(dev, "could not retrieve reg property: %d\n", ret);
+ return ret;
+ }
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", rb,
+ nsels);
+ if (ret) {
+ dev_err(dev, "could not retrieve reg property: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < nsels; i++) {
+ int tmp = cs[i];
+
+ if (tmp > NFC_MAX_CS) {
+ dev_err(dev,
+ "invalid reg value: %u (max CS = 7)\n",
+ tmp);
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+ dev_err(dev, "CS %d already assigned\n", tmp);
+ return -EINVAL;
+ }
+
+ chip->sels[i].cs = tmp;
+
+ tmp = rb[i];
+ if (tmp >= 0 && tmp < 2) {
+ chip->sels[i].rb.type = RB_NATIVE;
+ chip->sels[i].rb.info.nativeid = tmp;
+ } else {
+ ret = gpio_request_by_name_nodev(offset_to_ofnode(node),
+ "rb-gpios", i,
+ &chip->sels[i].rb.info.gpio,
+ GPIOD_IS_IN);
+ if (ret)
+ chip->sels[i].rb.type = RB_GPIO;
+ else
+ chip->sels[i].rb.type = RB_NONE;
+ }
+ }
+
+ timings = onfi_async_timing_mode_to_sdr_timings(0);
+ if (IS_ERR(timings)) {
+ ret = PTR_ERR(timings);
+ dev_err(dev,
+ "could not retrieve timings for ONFI mode 0: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = sunxi_nand_chip_set_timings(chip, timings);
+ if (ret) {
+ dev_err(dev, "could not configure chip timings: %d\n", ret);
+ return ret;
+ }
+
+ nand = &chip->nand;
+ /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
+ nand->chip_delay = 200;
+ nand->controller = &nfc->controller;
+ /*
+ * Set the ECC mode to the default value in case nothing is specified
+ * in the DT.
+ */
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->flash_node = node;
+ nand->select_chip = sunxi_nfc_select_chip;
+ nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
+ nand->read_buf = sunxi_nfc_read_buf;
+ nand->write_buf = sunxi_nfc_write_buf;
+ nand->read_byte = sunxi_nfc_read_byte;
+
+ mtd = nand_to_mtd(nand);
+ ret = nand_scan_ident(mtd, nsels, NULL);
+ if (ret)
+ return ret;
+
+ if (nand->bbt_options & NAND_BBT_USE_FLASH)
+ nand->bbt_options |= NAND_BBT_NO_OOB;
+
+ if (nand->options & NAND_NEED_SCRAMBLING)
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->options |= NAND_SUBPAGE_READ;
+
+ ret = sunxi_nand_chip_init_timings(chip);
+ if (ret) {
+ dev_err(dev, "could not configure chip timings: %d\n", ret);
+ return ret;
+ }
+
+ ret = sunxi_nand_ecc_init(mtd, &nand->ecc);
+ if (ret) {
+ dev_err(dev, "ECC init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = nand_register(devnum, mtd);
+ if (ret) {
+ dev_err(dev, "failed to register mtd device: %d\n", ret);
+ return ret;
+ }
+
+ list_add_tail(&chip->node, &nfc->chips);
+
+ return 0;
+}
+
+static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc)
+{
+ const void *blob = gd->fdt_blob;
+ int nand_node;
+ int ret, i = 0;
+
+ for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
+ nand_node = fdt_next_subnode(blob, nand_node))
+ i++;
+
+ if (i > 8) {
+ dev_err(dev, "too many NAND chips: %d (max = 8)\n", i);
+ return -EINVAL;
+ }
+
+ i = 0;
+ for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
+ nand_node = fdt_next_subnode(blob, nand_node)) {
+ ret = sunxi_nand_chip_init(nand_node, nfc, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifndef __UBOOT__
+static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
+{
+ struct sunxi_nand_chip *chip;
+
+ while (!list_empty(&nfc->chips)) {
+ chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
+ node);
+ nand_release(&chip->mtd);
+ sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+ list_del(&chip->node);
+ kfree(chip);
+ }
+}
+#endif /* __UBOOT__ */
+
+void sunxi_nand_init(void)
+{
+ const void *blob = gd->fdt_blob;
+ struct sunxi_nfc *nfc;
+ fdt_addr_t regs;
+ int node;
+ int ret;
+
+ nfc = kzalloc(sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return;
+
+ spin_lock_init(&nfc->controller.lock);
+ init_waitqueue_head(&nfc->controller.wq);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_SUNXI_NAND);
+ if (node < 0) {
+ pr_err("unable to find nfc node in device tree\n");
+ goto err;
+ }
+
+ if (!fdtdec_get_is_enabled(blob, node)) {
+ pr_err("nfc disabled in device tree\n");
+ goto err;
+ }
+
+ regs = fdtdec_get_addr(blob, node, "reg");
+ if (regs == FDT_ADDR_T_NONE) {
+ pr_err("unable to find nfc address in device tree\n");
+ goto err;
+ }
+
+ nfc->regs = (void *)regs;
+
+ ret = sunxi_nfc_rst(nfc);
+ if (ret)
+ goto err;
+
+ ret = sunxi_nand_chips_init(node, nfc);
+ if (ret) {
+ dev_err(dev, "failed to init nand chips\n");
+ goto err;
+ }
+
+ return;
+
+err:
+ kfree(nfc);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c
new file mode 100644
index 0000000000..6cde9814c4
--- /dev/null
+++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
+ * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
+ */
+
+#include <asm/arch/clock.h>
+#include <asm/io.h>
+#include <common.h>
+#include <config.h>
+#include <nand.h>
+#include <linux/ctype.h>
+
+/* registers */
+#define NFC_CTL 0x00000000
+#define NFC_ST 0x00000004
+#define NFC_INT 0x00000008
+#define NFC_TIMING_CTL 0x0000000C
+#define NFC_TIMING_CFG 0x00000010
+#define NFC_ADDR_LOW 0x00000014
+#define NFC_ADDR_HIGH 0x00000018
+#define NFC_SECTOR_NUM 0x0000001C
+#define NFC_CNT 0x00000020
+#define NFC_CMD 0x00000024
+#define NFC_RCMD_SET 0x00000028
+#define NFC_WCMD_SET 0x0000002C
+#define NFC_IO_DATA 0x00000030
+#define NFC_ECC_CTL 0x00000034
+#define NFC_ECC_ST 0x00000038
+#define NFC_DEBUG 0x0000003C
+#define NFC_ECC_CNT0 0x00000040
+#define NFC_ECC_CNT1 0x00000044
+#define NFC_ECC_CNT2 0x00000048
+#define NFC_ECC_CNT3 0x0000004C
+#define NFC_USER_DATA_BASE 0x00000050
+#define NFC_EFNAND_STATUS 0x00000090
+#define NFC_SPARE_AREA 0x000000A0
+#define NFC_PATTERN_ID 0x000000A4
+#define NFC_RAM0_BASE 0x00000400
+#define NFC_RAM1_BASE 0x00000800
+
+#define NFC_CTL_EN (1 << 0)
+#define NFC_CTL_RESET (1 << 1)
+#define NFC_CTL_RAM_METHOD (1 << 14)
+#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8)
+#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8)
+
+
+#define NFC_ECC_EN (1 << 0)
+#define NFC_ECC_PIPELINE (1 << 3)
+#define NFC_ECC_EXCEPTION (1 << 4)
+#define NFC_ECC_BLOCK_SIZE (1 << 5)
+#define NFC_ECC_RANDOM_EN (1 << 9)
+#define NFC_ECC_RANDOM_DIRECTION (1 << 10)
+
+
+#define NFC_ADDR_NUM_OFFSET 16
+#define NFC_SEND_ADDR (1 << 19)
+#define NFC_ACCESS_DIR (1 << 20)
+#define NFC_DATA_TRANS (1 << 21)
+#define NFC_SEND_CMD1 (1 << 22)
+#define NFC_WAIT_FLAG (1 << 23)
+#define NFC_SEND_CMD2 (1 << 24)
+#define NFC_SEQ (1 << 25)
+#define NFC_DATA_SWAP_METHOD (1 << 26)
+#define NFC_ROW_AUTO_INC (1 << 27)
+#define NFC_SEND_CMD3 (1 << 28)
+#define NFC_SEND_CMD4 (1 << 29)
+#define NFC_RAW_CMD (0 << 30)
+#define NFC_ECC_CMD (1 << 30)
+#define NFC_PAGE_CMD (2 << 30)
+
+#define NFC_ST_CMD_INT_FLAG (1 << 1)
+#define NFC_ST_DMA_INT_FLAG (1 << 2)
+#define NFC_ST_CMD_FIFO_STAT (1 << 3)
+
+#define NFC_READ_CMD_OFFSET 0
+#define NFC_RANDOM_READ_CMD0_OFFSET 8
+#define NFC_RANDOM_READ_CMD1_OFFSET 16
+
+#define NFC_CMD_RNDOUTSTART 0xE0
+#define NFC_CMD_RNDOUT 0x05
+#define NFC_CMD_READSTART 0x30
+
+struct nfc_config {
+ int page_size;
+ int ecc_strength;
+ int ecc_size;
+ int addr_cycles;
+ int nseeds;
+ bool randomize;
+ bool valid;
+};
+
+/* minimal "boot0" style NAND support for Allwinner A20 */
+
+/* random seed used by linux */
+const uint16_t random_seed[128] = {
+ 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+ 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+ 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+ 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+ 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+ 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+ 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+ 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+ 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+ 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+ 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+ 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+ 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+ 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+ 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+ 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+#define DEFAULT_TIMEOUT_US 100000
+
+static int check_value_inner(int offset, int expected_bits,
+ int timeout_us, int negation)
+{
+ do {
+ int val = readl(offset) & expected_bits;
+ if (negation ? !val : val)
+ return 1;
+ udelay(1);
+ } while (--timeout_us);
+
+ return 0;
+}
+
+static inline int check_value(int offset, int expected_bits,
+ int timeout_us)
+{
+ return check_value_inner(offset, expected_bits, timeout_us, 0);
+}
+
+static inline int check_value_negated(int offset, int unexpected_bits,
+ int timeout_us)
+{
+ return check_value_inner(offset, unexpected_bits, timeout_us, 1);
+}
+
+static int nand_wait_cmd_fifo_empty(void)
+{
+ if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT,
+ DEFAULT_TIMEOUT_US)) {
+ printf("nand: timeout waiting for empty cmd FIFO\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int nand_wait_int(void)
+{
+ if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
+ DEFAULT_TIMEOUT_US)) {
+ printf("nand: timeout waiting for interruption\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int nand_exec_cmd(u32 cmd)
+{
+ int ret;
+
+ ret = nand_wait_cmd_fifo_empty();
+ if (ret)
+ return ret;
+
+ writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
+ writel(cmd, SUNXI_NFC_BASE + NFC_CMD);
+
+ return nand_wait_int();
+}
+
+void nand_init(void)
+{
+ uint32_t val;
+
+ board_nand_init();
+
+ val = readl(SUNXI_NFC_BASE + NFC_CTL);
+ /* enable and reset CTL */
+ writel(val | NFC_CTL_EN | NFC_CTL_RESET,
+ SUNXI_NFC_BASE + NFC_CTL);
+
+ if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL,
+ NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) {
+ printf("Couldn't initialize nand\n");
+ }
+
+ /* reset NAND */
+ nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET);
+}
+
+static void nand_apply_config(const struct nfc_config *conf)
+{
+ u32 val;
+
+ nand_wait_cmd_fifo_empty();
+
+ val = readl(SUNXI_NFC_BASE + NFC_CTL);
+ val &= ~NFC_CTL_PAGE_SIZE_MASK;
+ writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size),
+ SUNXI_NFC_BASE + NFC_CTL);
+ writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
+ writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA);
+}
+
+static int nand_load_page(const struct nfc_config *conf, u32 offs)
+{
+ int page = offs / conf->page_size;
+
+ writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
+ (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
+ (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
+ SUNXI_NFC_BASE + NFC_RCMD_SET);
+ writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW);
+ writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH);
+
+ return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
+ NFC_SEND_ADDR | NFC_WAIT_FLAG |
+ ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET));
+}
+
+static int nand_change_column(u16 column)
+{
+ int ret;
+
+ writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
+ (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
+ (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
+ SUNXI_NFC_BASE + NFC_RCMD_SET);
+ writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW);
+
+ ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
+ (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR |
+ NFC_CMD_RNDOUT);
+ if (ret)
+ return ret;
+
+ /* Ensure tCCS has passed before reading data */
+ udelay(1);
+
+ return 0;
+}
+
+static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
+
+static int nand_read_page(const struct nfc_config *conf, u32 offs,
+ void *dest, int len)
+{
+ int nsectors = len / conf->ecc_size;
+ u16 rand_seed = 0;
+ int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
+ int page = offs / conf->page_size;
+ u32 ecc_st;
+ int i;
+
+ if (offs % conf->page_size || len % conf->ecc_size ||
+ len > conf->page_size || len < 0)
+ return -EINVAL;
+
+ /* Choose correct seed if randomized */
+ if (conf->randomize)
+ rand_seed = random_seed[page % conf->nseeds];
+
+ /* Retrieve data from SRAM (PIO) */
+ for (i = 0; i < nsectors; i++) {
+ int data_off = i * conf->ecc_size;
+ int oob_off = conf->page_size + (i * oob_chunk_sz);
+ u8 *data = dest + data_off;
+
+ /* Clear ECC status and restart ECC engine */
+ writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
+ writel((rand_seed << 16) | (conf->ecc_strength << 12) |
+ (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
+ (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
+ NFC_ECC_EN | NFC_ECC_EXCEPTION,
+ SUNXI_NFC_BASE + NFC_ECC_CTL);
+
+ /* Move the data in SRAM */
+ nand_change_column(data_off);
+ writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
+ nand_exec_cmd(NFC_DATA_TRANS);
+
+ /*
+ * Let the ECC engine consume the ECC bytes and possibly correct
+ * the data.
+ */
+ nand_change_column(oob_off);
+ nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);
+
+ /* Get the ECC status */
+ ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
+
+ /* ECC error detected. */
+ if (ecc_st & 0xffff)
+ return -EIO;
+
+ /*
+ * Return 1 if the first chunk is empty (needed for
+ * configuration detection).
+ */
+ if (!i && (ecc_st & 0x10000))
+ return 1;
+
+ /* Retrieve the data from SRAM */
+ memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE,
+ conf->ecc_size);
+
+ /* Stop the ECC engine */
+ writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
+ SUNXI_NFC_BASE + NFC_ECC_CTL);
+
+ if (data_off + conf->ecc_size >= len)
+ break;
+ }
+
+ return 0;
+}
+
+static int nand_max_ecc_strength(struct nfc_config *conf)
+{
+ int max_oobsize, max_ecc_bytes;
+ int nsectors = conf->page_size / conf->ecc_size;
+ int i;
+
+ /*
+ * ECC strength is limited by the size of the OOB area which is
+ * correlated with the page size.
+ */
+ switch (conf->page_size) {
+ case 2048:
+ max_oobsize = 64;
+ break;
+ case 4096:
+ max_oobsize = 256;
+ break;
+ case 8192:
+ max_oobsize = 640;
+ break;
+ case 16384:
+ max_oobsize = 1664;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ max_ecc_bytes = max_oobsize / nsectors;
+
+ for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {
+ if (ecc_bytes[i] > max_ecc_bytes)
+ break;
+ }
+
+ if (!i)
+ return -EINVAL;
+
+ return i - 1;
+}
+
+static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,
+ void *dest)
+{
+ /* NAND with pages > 4k will likely require 1k sector size. */
+ int min_ecc_size = conf->page_size > 4096 ? 1024 : 512;
+ int page = offs / conf->page_size;
+ int ret;
+
+ /*
+ * In most cases, 1k sectors are preferred over 512b ones, start
+ * testing this config first.
+ */
+ for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size;
+ conf->ecc_size >>= 1) {
+ int max_ecc_strength = nand_max_ecc_strength(conf);
+
+ nand_apply_config(conf);
+
+ /*
+ * We are starting from the maximum ECC strength because
+ * most of the time NAND vendors provide an OOB area that
+ * barely meets the ECC requirements.
+ */
+ for (conf->ecc_strength = max_ecc_strength;
+ conf->ecc_strength >= 0;
+ conf->ecc_strength--) {
+ conf->randomize = false;
+ if (nand_change_column(0))
+ return -EIO;
+
+ /*
+ * Only read the first sector to speedup detection.
+ */
+ ret = nand_read_page(conf, offs, dest, conf->ecc_size);
+ if (!ret) {
+ return 0;
+ } else if (ret > 0) {
+ /*
+ * If page is empty we can't deduce anything
+ * about the ECC config => stop the detection.
+ */
+ return -EINVAL;
+ }
+
+ conf->randomize = true;
+ conf->nseeds = ARRAY_SIZE(random_seed);
+ do {
+ if (nand_change_column(0))
+ return -EIO;
+
+ if (!nand_read_page(conf, offs, dest,
+ conf->ecc_size))
+ return 0;
+
+ /*
+ * Find the next ->nseeds value that would
+ * change the randomizer seed for the page
+ * we're trying to read.
+ */
+ while (conf->nseeds >= 16) {
+ int seed = page % conf->nseeds;
+
+ conf->nseeds >>= 1;
+ if (seed != page % conf->nseeds)
+ break;
+ }
+ } while (conf->nseeds >= 16);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest)
+{
+ if (conf->valid)
+ return 0;
+
+ /*
+ * Modern NANDs are more likely than legacy ones, so we start testing
+ * with 5 address cycles.
+ */
+ for (conf->addr_cycles = 5;
+ conf->addr_cycles >= 4;
+ conf->addr_cycles--) {
+ int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384;
+
+ /*
+ * Ignoring 1k pages cause I'm not even sure this case exist
+ * in the real world.
+ */
+ for (conf->page_size = 2048; conf->page_size <= max_page_size;
+ conf->page_size <<= 1) {
+ if (nand_load_page(conf, offs))
+ return -1;
+
+ if (!nand_detect_ecc_config(conf, offs, dest)) {
+ conf->valid = true;
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int nand_read_buffer(struct nfc_config *conf, uint32_t offs,
+ unsigned int size, void *dest)
+{
+ int first_seed = 0, page, ret;
+
+ size = ALIGN(size, conf->page_size);
+ page = offs / conf->page_size;
+ if (conf->randomize)
+ first_seed = page % conf->nseeds;
+
+ for (; size; size -= conf->page_size) {
+ if (nand_load_page(conf, offs))
+ return -1;
+
+ ret = nand_read_page(conf, offs, dest, conf->page_size);
+ /*
+ * The ->nseeds value should be equal to the number of pages
+ * in an eraseblock. Since we don't know this information in
+ * advance we might have picked a wrong value.
+ */
+ if (ret < 0 && conf->randomize) {
+ int cur_seed = page % conf->nseeds;
+
+ /*
+ * We already tried all the seed values => we are
+ * facing a real corruption.
+ */
+ if (cur_seed < first_seed)
+ return -EIO;
+
+ /* Try to adjust ->nseeds and read the page again... */
+ conf->nseeds = cur_seed;
+
+ if (nand_change_column(0))
+ return -EIO;
+
+ /* ... it still fails => it's a real corruption. */
+ if (nand_read_page(conf, offs, dest, conf->page_size))
+ return -EIO;
+ } else if (ret && conf->randomize) {
+ memset(dest, 0xff, conf->page_size);
+ }
+
+ page++;
+ offs += conf->page_size;
+ dest += conf->page_size;
+ }
+
+ return 0;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
+{
+ static struct nfc_config conf = { };
+ int ret;
+
+ ret = nand_detect_config(&conf, offs, dest);
+ if (ret)
+ return ret;
+
+ return nand_read_buffer(&conf, offs, size, dest);
+}
+
+void nand_deselect(void)
+{
+ struct sunxi_ccm_reg *const ccm =
+ (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+ clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
+#ifdef CONFIG_MACH_SUN9I
+ clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
+#else
+ clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
+#endif
+ clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
+}
diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c
new file mode 100644
index 0000000000..74acdfb308
--- /dev/null
+++ b/drivers/mtd/nand/raw/tegra_nand.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ * (C) Copyright 2006 Detlev Zundel, dzu@denx.de
+ * (C) Copyright 2006 DENX Software Engineering
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <memalign.h>
+#include <nand.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/funcmux.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <linux/errno.h>
+#include <asm/gpio.h>
+#include <fdtdec.h>
+#include <bouncebuf.h>
+#include <dm.h>
+#include "tegra_nand.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NAND_CMD_TIMEOUT_MS 10
+
+#define SKIPPED_SPARE_BYTES 4
+
+/* ECC bytes to be generated for tag data */
+#define TAG_ECC_BYTES 4
+
+static const struct udevice_id tegra_nand_dt_ids[] = {
+ {
+ .compatible = "nvidia,tegra20-nand",
+ },
+ { /* sentinel */ }
+};
+
+/* 64 byte oob block info for large page (== 2KB) device
+ *
+ * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC:
+ * Skipped bytes(4)
+ * Main area Ecc(36)
+ * Tag data(20)
+ * Tag data Ecc(4)
+ *
+ * Yaffs2 will use 16 tag bytes.
+ */
+static struct nand_ecclayout eccoob = {
+ .eccbytes = 36,
+ .eccpos = {
+ 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ },
+ .oobavail = 20,
+ .oobfree = {
+ {
+ .offset = 40,
+ .length = 20,
+ },
+ }
+};
+
+enum {
+ ECC_OK,
+ ECC_TAG_ERROR = 1 << 0,
+ ECC_DATA_ERROR = 1 << 1
+};
+
+/* Timing parameters */
+enum {
+ FDT_NAND_MAX_TRP_TREA,
+ FDT_NAND_TWB,
+ FDT_NAND_MAX_TCR_TAR_TRR,
+ FDT_NAND_TWHR,
+ FDT_NAND_MAX_TCS_TCH_TALS_TALH,
+ FDT_NAND_TWH,
+ FDT_NAND_TWP,
+ FDT_NAND_TRH,
+ FDT_NAND_TADL,
+
+ FDT_NAND_TIMING_COUNT
+};
+
+/* Information about an attached NAND chip */
+struct fdt_nand {
+ struct nand_ctlr *reg;
+ int enabled; /* 1 to enable, 0 to disable */
+ struct gpio_desc wp_gpio; /* write-protect GPIO */
+ s32 width; /* bit width, normally 8 */
+ u32 timing[FDT_NAND_TIMING_COUNT];
+};
+
+struct nand_drv {
+ struct nand_ctlr *reg;
+ struct fdt_nand config;
+};
+
+struct tegra_nand_info {
+ struct udevice *dev;
+ struct nand_drv nand_ctrl;
+ struct nand_chip nand_chip;
+};
+
+/**
+ * Wait for command completion
+ *
+ * @param reg nand_ctlr structure
+ * @return
+ * 1 - Command completed
+ * 0 - Timeout
+ */
+static int nand_waitfor_cmd_completion(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+ int running;
+ int i;
+
+ for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) {
+ if ((readl(&reg->command) & CMD_GO) ||
+ !(readl(&reg->status) & STATUS_RBSY0) ||
+ !(readl(&reg->isr) & ISR_IS_CMD_DONE)) {
+ udelay(1);
+ continue;
+ }
+ reg_val = readl(&reg->dma_mst_ctrl);
+ /*
+ * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE
+ * is set, that means DMA engine is running.
+ *
+ * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE
+ * is cleared, indicating DMA transfer completion.
+ */
+ running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE |
+ DMA_MST_CTRL_EN_B_ENABLE);
+ if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE))
+ return 1;
+ udelay(1);
+ }
+ return 0;
+}
+
+/**
+ * Read one byte from the chip
+ *
+ * @param mtd MTD device structure
+ * @return data byte
+ *
+ * Read function for 8bit bus-width
+ */
+static uint8_t read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ writel(CMD_GO | CMD_PIO | CMD_RX | CMD_CE0 | CMD_A_VALID,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command timeout\n");
+
+ return (uint8_t)readl(&info->reg->resp);
+}
+
+/**
+ * Read len bytes from the chip into a buffer
+ *
+ * @param mtd MTD device structure
+ * @param buf buffer to store data to
+ * @param len number of bytes to read
+ *
+ * Read function for 8bit bus-width
+ */
+static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i, s;
+ unsigned int reg;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ for (i = 0; i < len; i += 4) {
+ s = (len - i) > 4 ? 4 : len - i;
+ writel(CMD_PIO | CMD_RX | CMD_A_VALID | CMD_CE0 |
+ ((s - 1) << CMD_TRANS_SIZE_SHIFT) | CMD_GO,
+ &info->reg->command);
+ if (!nand_waitfor_cmd_completion(info->reg))
+ puts("Command timeout during read_buf\n");
+ reg = readl(&info->reg->resp);
+ memcpy(buf + i, &reg, s);
+ }
+}
+
+/**
+ * Check NAND status to see if it is ready or not
+ *
+ * @param mtd MTD device structure
+ * @return
+ * 1 - ready
+ * 0 - not ready
+ */
+static int nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int reg_val;
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ reg_val = readl(&info->reg->status);
+ if (reg_val & STATUS_RBSY0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Dummy implementation: we don't support multiple chips */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ switch (chipnr) {
+ case -1:
+ case 0:
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+/**
+ * Clear all interrupt status bits
+ *
+ * @param reg nand_ctlr structure
+ */
+static void nand_clear_interrupt_status(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ /* Clear interrupt status */
+ reg_val = readl(&reg->isr);
+ writel(reg_val, &reg->isr);
+}
+
+/**
+ * Send command to NAND device
+ *
+ * @param mtd MTD device structure
+ * @param command the command to be sent
+ * @param column the column address for this command, -1 if none
+ * @param page_addr the page address for this command, -1 if none
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_drv *info;
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+
+ /*
+ * Write out the command to the device.
+ *
+ * Only command NAND_CMD_RESET or NAND_CMD_READID will come
+ * here before mtd->writesize is initialized.
+ */
+
+ /* Emulate NAND_CMD_READOOB */
+ if (command == NAND_CMD_READOOB) {
+ assert(mtd->writesize != 0);
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Adjust columns for 16 bit bus-width */
+ if (column != -1 && (chip->options & NAND_BUSWIDTH_16))
+ column >>= 1;
+
+ nand_clear_interrupt_status(info->reg);
+
+ /* Stop DMA engine, clear DMA completion status */
+ writel(DMA_MST_CTRL_EN_A_DISABLE
+ | DMA_MST_CTRL_EN_B_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &info->reg->dma_mst_ctrl);
+
+ /*
+ * Program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+ case NAND_CMD_READID:
+ writel(NAND_CMD_READID, &info->reg->cmd_reg1);
+ writel(column & 0xFF, &info->reg->addr_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0,
+ &info->reg->command);
+ break;
+ case NAND_CMD_PARAM:
+ writel(NAND_CMD_PARAM, &info->reg->cmd_reg1);
+ writel(column & 0xFF, &info->reg->addr_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0,
+ &info->reg->command);
+ break;
+ case NAND_CMD_READ0:
+ writel(NAND_CMD_READ0, &info->reg->cmd_reg1);
+ writel(NAND_CMD_READSTART, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16, &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_SEQIN:
+ writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1);
+ writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2);
+ writel((page_addr << 16) | (column & 0xFFFF),
+ &info->reg->addr_reg1);
+ writel(page_addr >> 16,
+ &info->reg->addr_reg2);
+ return;
+ case NAND_CMD_PAGEPROG:
+ return;
+ case NAND_CMD_ERASE1:
+ writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1);
+ writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2);
+ writel(page_addr, &info->reg->addr_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_ALE |
+ CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3,
+ &info->reg->command);
+ break;
+ case NAND_CMD_ERASE2:
+ return;
+ case NAND_CMD_STATUS:
+ writel(NAND_CMD_STATUS, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX
+ | ((1 - 0) << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0,
+ &info->reg->command);
+ break;
+ case NAND_CMD_RESET:
+ writel(NAND_CMD_RESET, &info->reg->cmd_reg1);
+ writel(CMD_GO | CMD_CLE | CMD_CE0,
+ &info->reg->command);
+ break;
+ case NAND_CMD_RNDOUT:
+ default:
+ printf("%s: Unsupported command %d\n", __func__, command);
+ return;
+ }
+ if (!nand_waitfor_cmd_completion(info->reg))
+ printf("Command 0x%02X timeout\n", command);
+}
+
+/**
+ * Check whether the pointed buffer are all 0xff (blank).
+ *
+ * @param buf data buffer for blank check
+ * @param len length of the buffer in byte
+ * @return
+ * 1 - blank
+ * 0 - non-blank
+ */
+static int blank_check(u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xFF)
+ return 0;
+ return 1;
+}
+
+/**
+ * After a DMA transfer for read, we call this function to see whether there
+ * is any uncorrectable error on the pointed data buffer or oob buffer.
+ *
+ * @param reg nand_ctlr structure
+ * @param databuf data buffer
+ * @param a_len data buffer length
+ * @param oobbuf oob buffer
+ * @param b_len oob buffer length
+ * @return
+ * ECC_OK - no ECC error or correctable ECC error
+ * ECC_TAG_ERROR - uncorrectable tag ECC error
+ * ECC_DATA_ERROR - uncorrectable data ECC error
+ * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error
+ */
+static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf,
+ int a_len, u8 *oobbuf, int b_len)
+{
+ int return_val = ECC_OK;
+ u32 reg_val;
+
+ if (!(readl(&reg->isr) & ISR_IS_ECC_ERR))
+ return ECC_OK;
+
+ /*
+ * Area A is used for the data block (databuf). Area B is used for
+ * the spare block (oobbuf)
+ */
+ reg_val = readl(&reg->dec_status);
+ if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on data area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) &&
+ !blank_check(databuf, a_len))
+ return_val |= ECC_DATA_ERROR;
+ }
+
+ if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) {
+ reg_val = readl(&reg->bch_dec_status_buf);
+ /*
+ * If uncorrectable error occurs on tag area, then see whether
+ * they are all FF. If all are FF, it's a blank page.
+ * Not error.
+ */
+ if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) &&
+ !blank_check(oobbuf, b_len))
+ return_val |= ECC_TAG_ERROR;
+ }
+
+ return return_val;
+}
+
+/**
+ * Set GO bit to send command to device
+ *
+ * @param reg nand_ctlr structure
+ */
+static void start_command(struct nand_ctlr *reg)
+{
+ u32 reg_val;
+
+ reg_val = readl(&reg->command);
+ reg_val |= CMD_GO;
+ writel(reg_val, &reg->command);
+}
+
+/**
+ * Clear command GO bit, DMA GO bit, and DMA completion status
+ *
+ * @param reg nand_ctlr structure
+ */
+static void stop_command(struct nand_ctlr *reg)
+{
+ /* Stop command */
+ writel(0, &reg->command);
+
+ /* Stop DMA engine and clear DMA completion status */
+ writel(DMA_MST_CTRL_GO_DISABLE
+ | DMA_MST_CTRL_IS_DMA_DONE,
+ &reg->dma_mst_ctrl);
+}
+
+/**
+ * Set up NAND bus width and page size
+ *
+ * @param info nand_info structure
+ * @param *reg_val address of reg_val
+ * @return 0 if ok, -1 on error
+ */
+static int set_bus_width_page_size(struct mtd_info *our_mtd,
+ struct fdt_nand *config, u32 *reg_val)
+{
+ if (config->width == 8)
+ *reg_val = CFG_BUS_WIDTH_8BIT;
+ else if (config->width == 16)
+ *reg_val = CFG_BUS_WIDTH_16BIT;
+ else {
+ debug("%s: Unsupported bus width %d\n", __func__,
+ config->width);
+ return -1;
+ }
+
+ if (our_mtd->writesize == 512)
+ *reg_val |= CFG_PAGE_SIZE_512;
+ else if (our_mtd->writesize == 2048)
+ *reg_val |= CFG_PAGE_SIZE_2048;
+ else if (our_mtd->writesize == 4096)
+ *reg_val |= CFG_PAGE_SIZE_4096;
+ else {
+ debug("%s: Unsupported page size %d\n", __func__,
+ our_mtd->writesize);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Page read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ * @param page page number
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ /* 4*128=512 (byte) is the value that our HW can support. */
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128);
+ char *tag_ptr;
+ struct nand_drv *info;
+ struct fdt_nand *config;
+ unsigned int bbflags;
+ struct bounce_buffer bbstate, bbstate_oob;
+
+ if ((uintptr_t)buf & 0x03) {
+ printf("buf %p has to be 4-byte aligned\n", buf);
+ return -EINVAL;
+ }
+
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+ config = &info->config;
+ if (set_bus_width_page_size(mtd, config, &reg_val))
+ return -EINVAL;
+
+ /* Need to be 4-byte aligned */
+ tag_ptr = (char *)tag_buf;
+
+ stop_command(info->reg);
+
+ if (is_writing)
+ bbflags = GEN_BB_READ;
+ else
+ bbflags = GEN_BB_WRITE;
+
+ bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift,
+ bbflags);
+ writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
+ writel(virt_to_phys(bbstate.bounce_buffer), &info->reg->data_block_ptr);
+
+ /* Set ECC selection, configure ECC settings */
+ if (with_ecc) {
+ if (is_writing)
+ memcpy(tag_ptr, chip->oob_poi + free->offset,
+ chip->ecc.layout->oobavail + TAG_ECC_BYTES);
+ tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES;
+ reg_val |= (CFG_SKIP_SPARE_SEL_4
+ | CFG_SKIP_SPARE_ENABLE
+ | CFG_HW_ECC_CORRECTION_ENABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_SEL_RS
+ | CFG_HW_ECC_ENABLE
+ | CFG_TVAL4
+ | (tag_size - 1));
+
+ if (!is_writing)
+ tag_size += SKIPPED_SPARE_BYTES;
+ bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size,
+ bbflags);
+ } else {
+ tag_size = mtd->oobsize;
+ reg_val |= (CFG_SKIP_SPARE_DISABLE
+ | CFG_HW_ECC_CORRECTION_DISABLE
+ | CFG_ECC_EN_TAG_DISABLE
+ | CFG_HW_ECC_DISABLE
+ | (tag_size - 1));
+ bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi,
+ tag_size, bbflags);
+ }
+ writel(reg_val, &info->reg->config);
+ writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_A_VALID
+ | CMD_B_VALID
+ | (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT)
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_A_ENABLE
+ | DMA_MST_CTRL_EN_B_ENABLE;
+
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read Page 0x%X timeout ", page);
+ else
+ printf("Write Page 0x%X timeout ", page);
+ if (with_ecc)
+ printf("with ECC");
+ else
+ printf("without ECC");
+ printf("\n");
+ return -EIO;
+ }
+
+ bounce_buffer_stop(&bbstate_oob);
+ bounce_buffer_stop(&bbstate);
+
+ if (with_ecc && !is_writing) {
+ memcpy(chip->oob_poi, tag_ptr,
+ SKIPPED_SPARE_BYTES);
+ memcpy(chip->oob_poi + free->offset,
+ tag_ptr + SKIPPED_SPARE_BYTES,
+ chip->ecc.layout->oobavail);
+ reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf,
+ 1 << chip->page_shift,
+ (u8 *)(tag_ptr + SKIPPED_SPARE_BYTES),
+ chip->ecc.layout->oobavail);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read Page 0x%X tag ECC error\n", page);
+ if (reg_val & ECC_DATA_ERROR)
+ printf("Read Page 0x%X data ECC error\n",
+ page);
+ if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR))
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * Hardware ecc based page read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -EIO when command timeout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 1, 0);
+}
+
+/**
+ * Hardware ecc based page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ */
+static int nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required,
+ int page)
+{
+ nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
+ return 0;
+}
+
+
+/**
+ * Read raw page data without ecc
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf buffer to store read data
+ * @param page page number to read
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ return nand_rw_page(mtd, chip, buf, page, 0, 0);
+}
+
+/**
+ * Raw page write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param buf data buffer
+ */
+static int nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf,
+ int oob_required, int page)
+{
+ nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1);
+ return 0;
+}
+
+/**
+ * OOB data read/write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ * @param with_ecc 1 to enable ECC, 0 to disable ECC
+ * @param is_writing 0 for read, 1 for write
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int with_ecc, int is_writing)
+{
+ u32 reg_val;
+ int tag_size;
+ struct nand_oobfree *free = chip->ecc.layout->oobfree;
+ struct nand_drv *info;
+ unsigned int bbflags;
+ struct bounce_buffer bbstate_oob;
+
+ if (((int)chip->oob_poi) & 0x03)
+ return -EINVAL;
+ info = (struct nand_drv *)nand_get_controller_data(chip);
+ if (set_bus_width_page_size(mtd, &info->config, &reg_val))
+ return -EINVAL;
+
+ stop_command(info->reg);
+
+ /* Set ECC selection */
+ tag_size = mtd->oobsize;
+ if (with_ecc)
+ reg_val |= CFG_ECC_EN_TAG_ENABLE;
+ else
+ reg_val |= (CFG_ECC_EN_TAG_DISABLE);
+
+ reg_val |= ((tag_size - 1) |
+ CFG_SKIP_SPARE_DISABLE |
+ CFG_HW_ECC_CORRECTION_DISABLE |
+ CFG_HW_ECC_DISABLE);
+ writel(reg_val, &info->reg->config);
+
+ if (is_writing && with_ecc)
+ tag_size -= TAG_ECC_BYTES;
+
+ if (is_writing)
+ bbflags = GEN_BB_READ;
+ else
+ bbflags = GEN_BB_WRITE;
+
+ bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size,
+ bbflags);
+ writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
+
+ writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
+ writel(tag_size - 1, &info->reg->dma_cfg_b);
+
+ nand_clear_interrupt_status(info->reg);
+
+ reg_val = CMD_CLE | CMD_ALE
+ | CMD_SEC_CMD
+ | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
+ | CMD_B_VALID
+ | CMD_CE0;
+ if (!is_writing)
+ reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
+ else
+ reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
+ writel(reg_val, &info->reg->command);
+
+ /* Setup DMA engine */
+ reg_val = DMA_MST_CTRL_GO_ENABLE
+ | DMA_MST_CTRL_BURST_8WORDS
+ | DMA_MST_CTRL_EN_B_ENABLE;
+ if (!is_writing)
+ reg_val |= DMA_MST_CTRL_DIR_READ;
+ else
+ reg_val |= DMA_MST_CTRL_DIR_WRITE;
+
+ writel(reg_val, &info->reg->dma_mst_ctrl);
+
+ start_command(info->reg);
+
+ if (!nand_waitfor_cmd_completion(info->reg)) {
+ if (!is_writing)
+ printf("Read OOB of Page 0x%X timeout\n", page);
+ else
+ printf("Write OOB of Page 0x%X timeout\n", page);
+ return -EIO;
+ }
+
+ bounce_buffer_stop(&bbstate_oob);
+
+ if (with_ecc && !is_writing) {
+ reg_val = (u32)check_ecc_error(info->reg, 0, 0,
+ (u8 *)(chip->oob_poi + free->offset),
+ chip->ecc.layout->oobavail);
+ if (reg_val & ECC_TAG_ERROR)
+ printf("Read OOB of Page 0x%X tag ECC error\n", page);
+ }
+ return 0;
+}
+
+/**
+ * OOB data read function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to read
+ */
+static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ nand_rw_oob(mtd, chip, page, 0, 0);
+ return 0;
+}
+
+/**
+ * OOB data write function
+ *
+ * @param mtd mtd info structure
+ * @param chip nand chip info structure
+ * @param page page number to write
+ * @return 0 when successfully completed
+ * -EINVAL when chip->oob_poi is not double-word aligned
+ * -EIO when command timeout
+ */
+static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+ return nand_rw_oob(mtd, chip, page, 0, 1);
+}
+
+/**
+ * Set up NAND memory timings according to the provided parameters
+ *
+ * @param timing Timing parameters
+ * @param reg NAND controller register address
+ */
+static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT],
+ struct nand_ctlr *reg)
+{
+ u32 reg_val, clk_rate, clk_period, time_val;
+
+ clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH,
+ CLOCK_ID_PERIPH) / 1000000;
+ clk_period = 1000 / clk_rate;
+ reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWB] / clk_period) <<
+ TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period;
+ if (time_val > 2)
+ reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) &
+ TIMING_TCR_TAR_TRR_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) <<
+ TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK;
+ time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period;
+ if (time_val > 1)
+ reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) &
+ TIMING_TCS_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWH] / clk_period) <<
+ TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TWP] / clk_period) <<
+ TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_TRH] / clk_period) <<
+ TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK;
+ reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
+ TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK;
+ writel(reg_val, &reg->timing);
+
+ reg_val = 0;
+ time_val = timing[FDT_NAND_TADL] / clk_period;
+ if (time_val > 2)
+ reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK;
+ writel(reg_val, &reg->timing2);
+}
+
+/**
+ * Decode NAND parameters from the device tree
+ *
+ * @param dev Driver model device
+ * @param config Device tree NAND configuration
+ * @return 0 if ok, -ve on error (FDT_ERR_...)
+ */
+static int fdt_decode_nand(struct udevice *dev, struct fdt_nand *config)
+{
+ int err;
+
+ config->reg = (struct nand_ctlr *)dev_read_addr(dev);
+ config->enabled = dev_read_enabled(dev);
+ config->width = dev_read_u32_default(dev, "nvidia,nand-width", 8);
+ err = gpio_request_by_name(dev, "nvidia,wp-gpios", 0, &config->wp_gpio,
+ GPIOD_IS_OUT);
+ if (err)
+ return err;
+ err = dev_read_u32_array(dev, "nvidia,timing", config->timing,
+ FDT_NAND_TIMING_COUNT);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int tegra_probe(struct udevice *dev)
+{
+ struct tegra_nand_info *tegra = dev_get_priv(dev);
+ struct nand_chip *nand = &tegra->nand_chip;
+ struct nand_drv *info = &tegra->nand_ctrl;
+ struct fdt_nand *config = &info->config;
+ struct mtd_info *our_mtd;
+ int ret;
+
+ if (fdt_decode_nand(dev, config)) {
+ printf("Could not decode nand-flash in device tree\n");
+ return -1;
+ }
+ if (!config->enabled)
+ return -1;
+ info->reg = config->reg;
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &eccoob;
+
+ nand->options = LP_OPTIONS;
+ nand->cmdfunc = nand_command;
+ nand->read_byte = read_byte;
+ nand->read_buf = read_buf;
+ nand->ecc.read_page = nand_read_page_hwecc;
+ nand->ecc.write_page = nand_write_page_hwecc;
+ nand->ecc.read_page_raw = nand_read_page_raw;
+ nand->ecc.write_page_raw = nand_write_page_raw;
+ nand->ecc.read_oob = nand_read_oob;
+ nand->ecc.write_oob = nand_write_oob;
+ nand->ecc.strength = 1;
+ nand->select_chip = nand_select_chip;
+ nand->dev_ready = nand_dev_ready;
+ nand_set_controller_data(nand, &tegra->nand_ctrl);
+
+ /* Disable subpage writes as we do not provide ecc->hwctl */
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ /* Adjust controller clock rate */
+ clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000);
+
+ /* Adjust timing for NAND device */
+ setup_timing(config->timing, info->reg);
+
+ dm_gpio_set_value(&config->wp_gpio, 1);
+
+ our_mtd = nand_to_mtd(nand);
+ ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
+ if (ret)
+ return ret;
+
+ nand->ecc.size = our_mtd->writesize;
+ nand->ecc.bytes = our_mtd->oobsize;
+
+ ret = nand_scan_tail(our_mtd);
+ if (ret)
+ return ret;
+
+ ret = nand_register(0, our_mtd);
+ if (ret) {
+ dev_err(dev, "Failed to register MTD: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(tegra_nand) = {
+ .name = "tegra-nand",
+ .id = UCLASS_MTD,
+ .of_match = tegra_nand_dt_ids,
+ .probe = tegra_probe,
+ .priv_auto_alloc_size = sizeof(struct tegra_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_GET_DRIVER(tegra_nand), &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize %s. (error %d)\n", dev->name,
+ ret);
+}
diff --git a/drivers/mtd/nand/raw/tegra_nand.h b/drivers/mtd/nand/raw/tegra_nand.h
new file mode 100644
index 0000000000..7740160661
--- /dev/null
+++ b/drivers/mtd/nand/raw/tegra_nand.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
+ */
+
+/* register offset */
+#define COMMAND_0 0x00
+#define CMD_GO (1 << 31)
+#define CMD_CLE (1 << 30)
+#define CMD_ALE (1 << 29)
+#define CMD_PIO (1 << 28)
+#define CMD_TX (1 << 27)
+#define CMD_RX (1 << 26)
+#define CMD_SEC_CMD (1 << 25)
+#define CMD_AFT_DAT_MASK (1 << 24)
+#define CMD_AFT_DAT_DISABLE 0
+#define CMD_AFT_DAT_ENABLE (1 << 24)
+#define CMD_TRANS_SIZE_SHIFT 20
+#define CMD_TRANS_SIZE_PAGE 8
+#define CMD_A_VALID (1 << 19)
+#define CMD_B_VALID (1 << 18)
+#define CMD_RD_STATUS_CHK (1 << 17)
+#define CMD_R_BSY_CHK (1 << 16)
+#define CMD_CE7 (1 << 15)
+#define CMD_CE6 (1 << 14)
+#define CMD_CE5 (1 << 13)
+#define CMD_CE4 (1 << 12)
+#define CMD_CE3 (1 << 11)
+#define CMD_CE2 (1 << 10)
+#define CMD_CE1 (1 << 9)
+#define CMD_CE0 (1 << 8)
+#define CMD_CLE_BYTE_SIZE_SHIFT 4
+enum {
+ CMD_CLE_BYTES1 = 0,
+ CMD_CLE_BYTES2,
+ CMD_CLE_BYTES3,
+ CMD_CLE_BYTES4,
+};
+#define CMD_ALE_BYTE_SIZE_SHIFT 0
+enum {
+ CMD_ALE_BYTES1 = 0,
+ CMD_ALE_BYTES2,
+ CMD_ALE_BYTES3,
+ CMD_ALE_BYTES4,
+ CMD_ALE_BYTES5,
+ CMD_ALE_BYTES6,
+ CMD_ALE_BYTES7,
+ CMD_ALE_BYTES8
+};
+
+#define STATUS_0 0x04
+#define STATUS_RBSY0 (1 << 8)
+
+#define ISR_0 0x08
+#define ISR_IS_CMD_DONE (1 << 5)
+#define ISR_IS_ECC_ERR (1 << 4)
+
+#define IER_0 0x0C
+
+#define CFG_0 0x10
+#define CFG_HW_ECC_MASK (1 << 31)
+#define CFG_HW_ECC_DISABLE 0
+#define CFG_HW_ECC_ENABLE (1 << 31)
+#define CFG_HW_ECC_SEL_MASK (1 << 30)
+#define CFG_HW_ECC_SEL_HAMMING 0
+#define CFG_HW_ECC_SEL_RS (1 << 30)
+#define CFG_HW_ECC_CORRECTION_MASK (1 << 29)
+#define CFG_HW_ECC_CORRECTION_DISABLE 0
+#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29)
+#define CFG_PIPELINE_EN_MASK (1 << 28)
+#define CFG_PIPELINE_EN_DISABLE 0
+#define CFG_PIPELINE_EN_ENABLE (1 << 28)
+#define CFG_ECC_EN_TAG_MASK (1 << 27)
+#define CFG_ECC_EN_TAG_DISABLE 0
+#define CFG_ECC_EN_TAG_ENABLE (1 << 27)
+#define CFG_TVALUE_MASK (3 << 24)
+enum {
+ CFG_TVAL4 = 0 << 24,
+ CFG_TVAL6 = 1 << 24,
+ CFG_TVAL8 = 2 << 24
+};
+#define CFG_SKIP_SPARE_MASK (1 << 23)
+#define CFG_SKIP_SPARE_DISABLE 0
+#define CFG_SKIP_SPARE_ENABLE (1 << 23)
+#define CFG_COM_BSY_MASK (1 << 22)
+#define CFG_COM_BSY_DISABLE 0
+#define CFG_COM_BSY_ENABLE (1 << 22)
+#define CFG_BUS_WIDTH_MASK (1 << 21)
+#define CFG_BUS_WIDTH_8BIT 0
+#define CFG_BUS_WIDTH_16BIT (1 << 21)
+#define CFG_LPDDR1_MODE_MASK (1 << 20)
+#define CFG_LPDDR1_MODE_DISABLE 0
+#define CFG_LPDDR1_MODE_ENABLE (1 << 20)
+#define CFG_EDO_MODE_MASK (1 << 19)
+#define CFG_EDO_MODE_DISABLE 0
+#define CFG_EDO_MODE_ENABLE (1 << 19)
+#define CFG_PAGE_SIZE_SEL_MASK (7 << 16)
+enum {
+ CFG_PAGE_SIZE_256 = 0 << 16,
+ CFG_PAGE_SIZE_512 = 1 << 16,
+ CFG_PAGE_SIZE_1024 = 2 << 16,
+ CFG_PAGE_SIZE_2048 = 3 << 16,
+ CFG_PAGE_SIZE_4096 = 4 << 16
+};
+#define CFG_SKIP_SPARE_SEL_MASK (3 << 14)
+enum {
+ CFG_SKIP_SPARE_SEL_4 = 0 << 14,
+ CFG_SKIP_SPARE_SEL_8 = 1 << 14,
+ CFG_SKIP_SPARE_SEL_12 = 2 << 14,
+ CFG_SKIP_SPARE_SEL_16 = 3 << 14
+};
+#define CFG_TAG_BYTE_SIZE_MASK 0x1FF
+
+#define TIMING_0 0x14
+#define TIMING_TRP_RESP_CNT_SHIFT 28
+#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT)
+#define TIMING_TWB_CNT_SHIFT 24
+#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT)
+#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20
+#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT)
+#define TIMING_TWHR_CNT_SHIFT 16
+#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT)
+#define TIMING_TCS_CNT_SHIFT 14
+#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT)
+#define TIMING_TWH_CNT_SHIFT 12
+#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT)
+#define TIMING_TWP_CNT_SHIFT 8
+#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT)
+#define TIMING_TRH_CNT_SHIFT 4
+#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT)
+#define TIMING_TRP_CNT_SHIFT 0
+#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT)
+
+#define RESP_0 0x18
+
+#define TIMING2_0 0x1C
+#define TIMING2_TADL_CNT_SHIFT 0
+#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT)
+
+#define CMD_REG1_0 0x20
+#define CMD_REG2_0 0x24
+#define ADDR_REG1_0 0x28
+#define ADDR_REG2_0 0x2C
+
+#define DMA_MST_CTRL_0 0x30
+#define DMA_MST_CTRL_GO_MASK (1 << 31)
+#define DMA_MST_CTRL_GO_DISABLE 0
+#define DMA_MST_CTRL_GO_ENABLE (1 << 31)
+#define DMA_MST_CTRL_DIR_MASK (1 << 30)
+#define DMA_MST_CTRL_DIR_READ 0
+#define DMA_MST_CTRL_DIR_WRITE (1 << 30)
+#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29)
+#define DMA_MST_CTRL_PERF_EN_DISABLE 0
+#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29)
+#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27)
+#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0
+#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27)
+#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24
+#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT)
+enum {
+ DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
+ DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT
+};
+#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20)
+#define DMA_MST_CTRL_EN_A_MASK (1 << 2)
+#define DMA_MST_CTRL_EN_A_DISABLE 0
+#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2)
+#define DMA_MST_CTRL_EN_B_MASK (1 << 1)
+#define DMA_MST_CTRL_EN_B_DISABLE 0
+#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1)
+
+#define DMA_CFG_A_0 0x34
+#define DMA_CFG_B_0 0x38
+#define FIFO_CTRL_0 0x3C
+#define DATA_BLOCK_PTR_0 0x40
+#define TAG_PTR_0 0x44
+#define ECC_PTR_0 0x48
+
+#define DEC_STATUS_0 0x4C
+#define DEC_STATUS_A_ECC_FAIL (1 << 1)
+#define DEC_STATUS_B_ECC_FAIL (1 << 0)
+
+#define BCH_CONFIG_0 0xCC
+#define BCH_CONFIG_BCH_TVALUE_SHIFT 4
+#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT)
+enum {
+ BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT,
+ BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT
+};
+#define BCH_CONFIG_BCH_ECC_MASK (1 << 0)
+#define BCH_CONFIG_BCH_ECC_DISABLE 0
+#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0)
+
+#define BCH_DEC_RESULT_0 0xD0
+#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8)
+#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF
+
+#define BCH_DEC_STATUS_BUF_0 0xD4
+#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000
+#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000
+#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14)
+#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13)
+#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8)
+#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF
+
+#define LP_OPTIONS 0
+
+struct nand_ctlr {
+ u32 command; /* offset 00h */
+ u32 status; /* offset 04h */
+ u32 isr; /* offset 08h */
+ u32 ier; /* offset 0Ch */
+ u32 config; /* offset 10h */
+ u32 timing; /* offset 14h */
+ u32 resp; /* offset 18h */
+ u32 timing2; /* offset 1Ch */
+ u32 cmd_reg1; /* offset 20h */
+ u32 cmd_reg2; /* offset 24h */
+ u32 addr_reg1; /* offset 28h */
+ u32 addr_reg2; /* offset 2Ch */
+ u32 dma_mst_ctrl; /* offset 30h */
+ u32 dma_cfg_a; /* offset 34h */
+ u32 dma_cfg_b; /* offset 38h */
+ u32 fifo_ctrl; /* offset 3Ch */
+ u32 data_block_ptr; /* offset 40h */
+ u32 tag_ptr; /* offset 44h */
+ u32 resv1; /* offset 48h */
+ u32 dec_status; /* offset 4Ch */
+ u32 hwstatus_cmd; /* offset 50h */
+ u32 hwstatus_mask; /* offset 54h */
+ u32 resv2[29];
+ u32 bch_config; /* offset CCh */
+ u32 bch_dec_result; /* offset D0h */
+ u32 bch_dec_status_buf;
+ /* offset D4h */
+};
diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c
new file mode 100644
index 0000000000..619d0403e9
--- /dev/null
+++ b/drivers/mtd/nand/raw/vf610_nfc.c
@@ -0,0 +1,768 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2009-2015 Freescale Semiconductor, Inc. and others
+ *
+ * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver.
+ * Ported to U-Boot by Stefan Agner
+ * Based on RFC driver posted on Kernel Mailing list by Bill Pringlemeir
+ * Jason ported to M54418TWR and MVFA5.
+ * Authors: Stefan Agner <stefan.agner@toradex.com>
+ * Bill Pringlemeir <bpringlemeir@nbsps.com>
+ * Shaohui Xie <b21989@freescale.com>
+ * Jason Jin <Jason.jin@freescale.com>
+ *
+ * Based on original driver mpc5121_nfc.c.
+ *
+ * Limitations:
+ * - Untested on MPC5125 and M54418.
+ * - DMA and pipelining not used.
+ * - 2K pages or less.
+ * - HW ECC: Only 2K page with 64+ OOB.
+ * - HW ECC: Only 24 and 32-bit error correction implemented.
+ */
+
+#include <common.h>
+#include <malloc.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+
+#include <nand.h>
+#include <errno.h>
+#include <asm/io.h>
+
+/* Register Offsets */
+#define NFC_FLASH_CMD1 0x3F00
+#define NFC_FLASH_CMD2 0x3F04
+#define NFC_COL_ADDR 0x3F08
+#define NFC_ROW_ADDR 0x3F0c
+#define NFC_ROW_ADDR_INC 0x3F14
+#define NFC_FLASH_STATUS1 0x3F18
+#define NFC_FLASH_STATUS2 0x3F1c
+#define NFC_CACHE_SWAP 0x3F28
+#define NFC_SECTOR_SIZE 0x3F2c
+#define NFC_FLASH_CONFIG 0x3F30
+#define NFC_IRQ_STATUS 0x3F38
+
+/* Addresses for NFC MAIN RAM BUFFER areas */
+#define NFC_MAIN_AREA(n) ((n) * 0x1000)
+
+#define PAGE_2K 0x0800
+#define OOB_64 0x0040
+#define OOB_MAX 0x0100
+
+/*
+ * NFC_CMD2[CODE] values. See section:
+ * - 31.4.7 Flash Command Code Description, Vybrid manual
+ * - 23.8.6 Flash Command Sequencer, MPC5125 manual
+ *
+ * Briefly these are bitmasks of controller cycles.
+ */
+#define READ_PAGE_CMD_CODE 0x7EE0
+#define READ_ONFI_PARAM_CMD_CODE 0x4860
+#define PROGRAM_PAGE_CMD_CODE 0x7FC0
+#define ERASE_CMD_CODE 0x4EC0
+#define READ_ID_CMD_CODE 0x4804
+#define RESET_CMD_CODE 0x4040
+#define STATUS_READ_CMD_CODE 0x4068
+
+/* NFC ECC mode define */
+#define ECC_BYPASS 0
+#define ECC_45_BYTE 6
+#define ECC_60_BYTE 7
+
+/*** Register Mask and bit definitions */
+
+/* NFC_FLASH_CMD1 Field */
+#define CMD_BYTE2_MASK 0xFF000000
+#define CMD_BYTE2_SHIFT 24
+
+/* NFC_FLASH_CM2 Field */
+#define CMD_BYTE1_MASK 0xFF000000
+#define CMD_BYTE1_SHIFT 24
+#define CMD_CODE_MASK 0x00FFFF00
+#define CMD_CODE_SHIFT 8
+#define BUFNO_MASK 0x00000006
+#define BUFNO_SHIFT 1
+#define START_BIT (1<<0)
+
+/* NFC_COL_ADDR Field */
+#define COL_ADDR_MASK 0x0000FFFF
+#define COL_ADDR_SHIFT 0
+
+/* NFC_ROW_ADDR Field */
+#define ROW_ADDR_MASK 0x00FFFFFF
+#define ROW_ADDR_SHIFT 0
+#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
+#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
+#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
+#define ROW_ADDR_CHIP_SEL_SHIFT 24
+
+/* NFC_FLASH_STATUS2 Field */
+#define STATUS_BYTE1_MASK 0x000000FF
+
+/* NFC_FLASH_CONFIG Field */
+#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000
+#define CONFIG_ECC_SRAM_ADDR_SHIFT 22
+#define CONFIG_ECC_SRAM_REQ_BIT (1<<21)
+#define CONFIG_DMA_REQ_BIT (1<<20)
+#define CONFIG_ECC_MODE_MASK 0x000E0000
+#define CONFIG_ECC_MODE_SHIFT 17
+#define CONFIG_FAST_FLASH_BIT (1<<16)
+#define CONFIG_16BIT (1<<7)
+#define CONFIG_BOOT_MODE_BIT (1<<6)
+#define CONFIG_ADDR_AUTO_INCR_BIT (1<<5)
+#define CONFIG_BUFNO_AUTO_INCR_BIT (1<<4)
+#define CONFIG_PAGE_CNT_MASK 0xF
+#define CONFIG_PAGE_CNT_SHIFT 0
+
+/* NFC_IRQ_STATUS Field */
+#define IDLE_IRQ_BIT (1<<29)
+#define IDLE_EN_BIT (1<<20)
+#define CMD_DONE_CLEAR_BIT (1<<18)
+#define IDLE_CLEAR_BIT (1<<17)
+
+#define NFC_TIMEOUT (1000)
+
+/*
+ * ECC status - seems to consume 8 bytes (double word). The documented
+ * status byte is located in the lowest byte of the second word (which is
+ * the 4th or 7th byte depending on endianness).
+ * Calculate an offset to store the ECC status at the end of the buffer.
+ */
+#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
+
+#define ECC_STATUS 0x4
+#define ECC_STATUS_MASK 0x80
+#define ECC_STATUS_ERR_COUNT 0x3F
+
+enum vf610_nfc_alt_buf {
+ ALT_BUF_DATA = 0,
+ ALT_BUF_ID = 1,
+ ALT_BUF_STAT = 2,
+ ALT_BUF_ONFI = 3,
+};
+
+struct vf610_nfc {
+ struct nand_chip chip;
+ void __iomem *regs;
+ uint buf_offset;
+ int write_sz;
+ /* Status and ID are in alternate locations. */
+ enum vf610_nfc_alt_buf alt_buf;
+};
+
+#define mtd_to_nfc(_mtd) nand_get_controller_data(mtd_to_nand(_mtd))
+
+#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
+#define ECC_HW_MODE ECC_45_BYTE
+
+static struct nand_ecclayout vf610_nfc_ecc = {
+ .eccbytes = 45,
+ .eccpos = {19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+ {.offset = 2,
+ .length = 17} }
+};
+#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
+#define ECC_HW_MODE ECC_60_BYTE
+
+static struct nand_ecclayout vf610_nfc_ecc = {
+ .eccbytes = 60,
+ .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63 },
+ .oobfree = {
+ {.offset = 2,
+ .length = 2} }
+};
+#endif
+
+static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ return readl(nfc->regs + reg);
+}
+
+static inline void vf610_nfc_write(struct mtd_info *mtd, uint reg, u32 val)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ writel(val, nfc->regs + reg);
+}
+
+static inline void vf610_nfc_set(struct mtd_info *mtd, uint reg, u32 bits)
+{
+ vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) | bits);
+}
+
+static inline void vf610_nfc_clear(struct mtd_info *mtd, uint reg, u32 bits)
+{
+ vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) & ~bits);
+}
+
+static inline void vf610_nfc_set_field(struct mtd_info *mtd, u32 reg,
+ u32 mask, u32 shift, u32 val)
+{
+ vf610_nfc_write(mtd, reg,
+ (vf610_nfc_read(mtd, reg) & (~mask)) | val << shift);
+}
+
+static inline void vf610_nfc_memcpy(void *dst, const void *src, size_t n)
+{
+ /*
+ * Use this accessor for the internal SRAM buffers. On the ARM
+ * Freescale Vybrid SoC it's known that the driver can treat
+ * the SRAM buffer as if it's memory. Other platform might need
+ * to treat the buffers differently.
+ *
+ * For the time being, use memcpy
+ */
+ memcpy(dst, src, n);
+}
+
+/* Clear flags for upcoming command */
+static inline void vf610_nfc_clear_status(void __iomem *regbase)
+{
+ void __iomem *reg = regbase + NFC_IRQ_STATUS;
+ u32 tmp = __raw_readl(reg);
+ tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT;
+ __raw_writel(tmp, reg);
+}
+
+/* Wait for complete operation */
+static void vf610_nfc_done(struct mtd_info *mtd)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ uint start;
+
+ /*
+ * Barrier is needed after this write. This write need
+ * to be done before reading the next register the first
+ * time.
+ * vf610_nfc_set implicates such a barrier by using writel
+ * to write to the register.
+ */
+ vf610_nfc_set(mtd, NFC_FLASH_CMD2, START_BIT);
+
+ start = get_timer(0);
+
+ while (!(vf610_nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) {
+ if (get_timer(start) > NFC_TIMEOUT) {
+ printf("Timeout while waiting for IDLE.\n");
+ return;
+ }
+ }
+ vf610_nfc_clear_status(nfc->regs);
+}
+
+static u8 vf610_nfc_get_id(struct mtd_info *mtd, int col)
+{
+ u32 flash_id;
+
+ if (col < 4) {
+ flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS1);
+ flash_id >>= (3 - col) * 8;
+ } else {
+ flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS2);
+ flash_id >>= 24;
+ }
+
+ return flash_id & 0xff;
+}
+
+static u8 vf610_nfc_get_status(struct mtd_info *mtd)
+{
+ return vf610_nfc_read(mtd, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK;
+}
+
+/* Single command */
+static void vf610_nfc_send_command(void __iomem *regbase, u32 cmd_byte1,
+ u32 cmd_code)
+{
+ void __iomem *reg = regbase + NFC_FLASH_CMD2;
+ u32 tmp;
+ vf610_nfc_clear_status(regbase);
+
+ tmp = __raw_readl(reg);
+ tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK);
+ tmp |= cmd_byte1 << CMD_BYTE1_SHIFT;
+ tmp |= cmd_code << CMD_CODE_SHIFT;
+ __raw_writel(tmp, reg);
+}
+
+/* Two commands */
+static void vf610_nfc_send_commands(void __iomem *regbase, u32 cmd_byte1,
+ u32 cmd_byte2, u32 cmd_code)
+{
+ void __iomem *reg = regbase + NFC_FLASH_CMD1;
+ u32 tmp;
+ vf610_nfc_send_command(regbase, cmd_byte1, cmd_code);
+
+ tmp = __raw_readl(reg);
+ tmp &= ~CMD_BYTE2_MASK;
+ tmp |= cmd_byte2 << CMD_BYTE2_SHIFT;
+ __raw_writel(tmp, reg);
+}
+
+static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
+{
+ if (column != -1) {
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ if (nfc->chip.options & NAND_BUSWIDTH_16)
+ column = column / 2;
+ vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK,
+ COL_ADDR_SHIFT, column);
+ }
+ if (page != -1)
+ vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
+ ROW_ADDR_SHIFT, page);
+}
+
+static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode)
+{
+ vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
+ CONFIG_ECC_MODE_MASK,
+ CONFIG_ECC_MODE_SHIFT, ecc_mode);
+}
+
+static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size)
+{
+ __raw_writel(size, regbase + NFC_SECTOR_SIZE);
+}
+
+/* Send command to NAND chip */
+static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
+ int column, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
+
+ nfc->buf_offset = max(column, 0);
+ nfc->alt_buf = ALT_BUF_DATA;
+
+ switch (command) {
+ case NAND_CMD_SEQIN:
+ /* Use valid column/page from preread... */
+ vf610_nfc_addr_cycle(mtd, column, page);
+ nfc->buf_offset = 0;
+
+ /*
+ * SEQIN => data => PAGEPROG sequence is done by the controller
+ * hence we do not need to issue the command here...
+ */
+ return;
+ case NAND_CMD_PAGEPROG:
+ trfr_sz += nfc->write_sz;
+ vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
+ vf610_nfc_transfer_size(nfc->regs, trfr_sz);
+ vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN,
+ command, PROGRAM_PAGE_CMD_CODE);
+ break;
+
+ case NAND_CMD_RESET:
+ vf610_nfc_transfer_size(nfc->regs, 0);
+ vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE);
+ break;
+
+ case NAND_CMD_READOOB:
+ trfr_sz += mtd->oobsize;
+ column = mtd->writesize;
+ vf610_nfc_transfer_size(nfc->regs, trfr_sz);
+ vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
+ NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
+ vf610_nfc_addr_cycle(mtd, column, page);
+ vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
+ break;
+
+ case NAND_CMD_READ0:
+ trfr_sz += mtd->writesize + mtd->oobsize;
+ vf610_nfc_transfer_size(nfc->regs, trfr_sz);
+ vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
+ vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
+ NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
+ vf610_nfc_addr_cycle(mtd, column, page);
+ break;
+
+ case NAND_CMD_PARAM:
+ nfc->alt_buf = ALT_BUF_ONFI;
+ trfr_sz = 3 * sizeof(struct nand_onfi_params);
+ vf610_nfc_transfer_size(nfc->regs, trfr_sz);
+ vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM,
+ READ_ONFI_PARAM_CMD_CODE);
+ vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
+ ROW_ADDR_SHIFT, column);
+ vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
+ break;
+
+ case NAND_CMD_ERASE1:
+ vf610_nfc_transfer_size(nfc->regs, 0);
+ vf610_nfc_send_commands(nfc->regs, command,
+ NAND_CMD_ERASE2, ERASE_CMD_CODE);
+ vf610_nfc_addr_cycle(mtd, column, page);
+ break;
+
+ case NAND_CMD_READID:
+ nfc->alt_buf = ALT_BUF_ID;
+ nfc->buf_offset = 0;
+ vf610_nfc_transfer_size(nfc->regs, 0);
+ vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE);
+ vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
+ ROW_ADDR_SHIFT, column);
+ break;
+
+ case NAND_CMD_STATUS:
+ nfc->alt_buf = ALT_BUF_STAT;
+ vf610_nfc_transfer_size(nfc->regs, 0);
+ vf610_nfc_send_command(nfc->regs, command, STATUS_READ_CMD_CODE);
+ break;
+ default:
+ return;
+ }
+
+ vf610_nfc_done(mtd);
+
+ nfc->write_sz = 0;
+}
+
+/* Read data from NFC buffers */
+static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ uint c = nfc->buf_offset;
+
+ /* Alternate buffers are only supported through read_byte */
+ if (nfc->alt_buf)
+ return;
+
+ vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
+
+ nfc->buf_offset += len;
+}
+
+/* Write data to NFC buffers */
+static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ uint c = nfc->buf_offset;
+ uint l;
+
+ l = min_t(uint, len, mtd->writesize + mtd->oobsize - c);
+ vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l);
+
+ nfc->write_sz += l;
+ nfc->buf_offset += l;
+}
+
+/* Read byte from NFC buffers */
+static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ u8 tmp;
+ uint c = nfc->buf_offset;
+
+ switch (nfc->alt_buf) {
+ case ALT_BUF_ID:
+ tmp = vf610_nfc_get_id(mtd, c);
+ break;
+ case ALT_BUF_STAT:
+ tmp = vf610_nfc_get_status(mtd);
+ break;
+#ifdef __LITTLE_ENDIAN
+ case ALT_BUF_ONFI:
+ /* Reverse byte since the controller uses big endianness */
+ c = nfc->buf_offset ^ 0x3;
+ /* fall-through */
+#endif
+ default:
+ tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
+ break;
+ }
+ nfc->buf_offset++;
+ return tmp;
+}
+
+/* Read word from NFC buffers */
+static u16 vf610_nfc_read_word(struct mtd_info *mtd)
+{
+ u16 tmp;
+
+ vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
+ return tmp;
+}
+
+/* If not provided, upper layers apply a fixed delay. */
+static int vf610_nfc_dev_ready(struct mtd_info *mtd)
+{
+ /* NFC handles R/B internally; always ready. */
+ return 1;
+}
+
+/*
+ * This function supports Vybrid only (MPC5125 would have full RB and four CS)
+ */
+static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+#ifdef CONFIG_VF610
+ u32 tmp = vf610_nfc_read(mtd, NFC_ROW_ADDR);
+ tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
+
+ if (chip >= 0) {
+ tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
+ tmp |= (1 << chip) << ROW_ADDR_CHIP_SEL_SHIFT;
+ }
+
+ vf610_nfc_write(mtd, NFC_ROW_ADDR, tmp);
+#endif
+}
+
+/* Count the number of 0's in buff upto max_bits */
+static inline int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+ uint32_t *buff32 = (uint32_t *)buff;
+ int k, written_bits = 0;
+
+ for (k = 0; k < (size / 4); k++) {
+ written_bits += hweight32(~buff32[k]);
+ if (written_bits > max_bits)
+ break;
+ }
+
+ return written_bits;
+}
+
+static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *oob, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+ u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
+ u8 ecc_status;
+ u8 ecc_count;
+ int flips;
+ int flips_threshold = nfc->chip.ecc.strength / 2;
+
+ ecc_status = vf610_nfc_read(mtd, ecc_status_off) & 0xff;
+ ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
+
+ if (!(ecc_status & ECC_STATUS_MASK))
+ return ecc_count;
+
+ /* Read OOB without ECC unit enabled */
+ vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page);
+ vf610_nfc_read_buf(mtd, oob, mtd->oobsize);
+
+ /*
+ * On an erased page, bit count (including OOB) should be zero or
+ * at least less then half of the ECC strength.
+ */
+ flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold);
+ flips += count_written_bits(oob, mtd->oobsize, flips_threshold);
+
+ if (unlikely(flips > flips_threshold))
+ return -EINVAL;
+
+ /* Erased page. */
+ memset(dat, 0xff, nfc->chip.ecc.size);
+ memset(oob, 0xff, mtd->oobsize);
+ return flips;
+}
+
+static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int eccsize = chip->ecc.size;
+ int stat;
+
+ vf610_nfc_read_buf(mtd, buf, eccsize);
+ if (oob_required)
+ vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ return 0;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ return stat;
+ }
+}
+
+/*
+ * ECC will be calculated automatically
+ */
+static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required, int page)
+{
+ struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+
+ vf610_nfc_write_buf(mtd, buf, mtd->writesize);
+ if (oob_required)
+ vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /* Always write whole page including OOB due to HW ECC */
+ nfc->write_sz = mtd->writesize + mtd->oobsize;
+
+ return 0;
+}
+
+struct vf610_nfc_config {
+ int hardware_ecc;
+ int width;
+ int flash_bbt;
+};
+
+static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct vf610_nfc *nfc;
+ int err = 0;
+ struct vf610_nfc_config cfg = {
+ .hardware_ecc = 1,
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
+ .width = 16,
+#else
+ .width = 8,
+#endif
+ .flash_bbt = 1,
+ };
+
+ nfc = malloc(sizeof(*nfc));
+ if (!nfc) {
+ printf(KERN_ERR "%s: Memory exhausted!\n", __func__);
+ return -ENOMEM;
+ }
+
+ chip = &nfc->chip;
+ nfc->regs = addr;
+
+ mtd = nand_to_mtd(chip);
+ nand_set_controller_data(chip, nfc);
+
+ if (cfg.width == 16)
+ chip->options |= NAND_BUSWIDTH_16;
+
+ chip->dev_ready = vf610_nfc_dev_ready;
+ chip->cmdfunc = vf610_nfc_command;
+ chip->read_byte = vf610_nfc_read_byte;
+ chip->read_word = vf610_nfc_read_word;
+ chip->read_buf = vf610_nfc_read_buf;
+ chip->write_buf = vf610_nfc_write_buf;
+ chip->select_chip = vf610_nfc_select_chip;
+
+ chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+ chip->ecc.size = PAGE_2K;
+
+ /* Set configuration register. */
+ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
+ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
+ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT);
+ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
+ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
+ vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
+
+ /* Disable virtual pages, only one elementary transfer unit */
+ vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
+ CONFIG_PAGE_CNT_SHIFT, 1);
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) {
+ err = -ENXIO;
+ goto error;
+ }
+
+ if (cfg.width == 16)
+ vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
+
+ /* Bad block options. */
+ if (cfg.flash_bbt)
+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB |
+ NAND_BBT_CREATE;
+
+ /* Single buffer only, max 256 OOB minus ECC status */
+ if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
+ dev_err(nfc->dev, "Unsupported flash page size\n");
+ err = -ENXIO;
+ goto error;
+ }
+
+ if (cfg.hardware_ecc) {
+ if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
+ dev_err(nfc->dev, "Unsupported flash with hwecc\n");
+ err = -ENXIO;
+ goto error;
+ }
+
+ if (chip->ecc.size != mtd->writesize) {
+ dev_err(nfc->dev, "ecc size: %d\n", chip->ecc.size);
+ dev_err(nfc->dev, "Step size needs to be page size\n");
+ err = -ENXIO;
+ goto error;
+ }
+
+ /* Current HW ECC layouts only use 64 bytes of OOB */
+ if (mtd->oobsize > 64)
+ mtd->oobsize = 64;
+
+ /* propagate ecc.layout to mtd_info */
+ mtd->ecclayout = chip->ecc.layout;
+ chip->ecc.read_page = vf610_nfc_read_page;
+ chip->ecc.write_page = vf610_nfc_write_page;
+ chip->ecc.mode = NAND_ECC_HW;
+
+ chip->ecc.size = PAGE_2K;
+ chip->ecc.layout = &vf610_nfc_ecc;
+#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
+ chip->ecc.strength = 24;
+ chip->ecc.bytes = 45;
+#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
+ chip->ecc.strength = 32;
+ chip->ecc.bytes = 60;
+#endif
+
+ /* Set ECC_STATUS offset */
+ vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
+ CONFIG_ECC_SRAM_ADDR_MASK,
+ CONFIG_ECC_SRAM_ADDR_SHIFT,
+ ECC_SRAM_ADDR >> 3);
+
+ /* Enable ECC status in SRAM */
+ vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
+ }
+
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ return err;
+
+ err = nand_register(devnum, mtd);
+ if (err)
+ return err;
+
+ return 0;
+
+error:
+ return err;
+}
+
+void board_nand_init(void)
+{
+ int err = vf610_nfc_nand_init(0, (void __iomem *)CONFIG_SYS_NAND_BASE);
+ if (err)
+ printf("VF610 NAND init failed (err %d)\n", err);
+}
diff --git a/drivers/mtd/nand/raw/zynq_nand.c b/drivers/mtd/nand/raw/zynq_nand.c
new file mode 100644
index 0000000000..e932a58bf6
--- /dev/null
+++ b/drivers/mtd/nand/raw/zynq_nand.c
@@ -0,0 +1,1254 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016 Xilinx, Inc.
+ *
+ * Xilinx Zynq NAND Flash Controller Driver
+ * This driver is based on plat_nand.c and mxc_nand.c drivers
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand_ecc.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/sys_proto.h>
+
+/* The NAND flash driver defines */
+#define ZYNQ_NAND_CMD_PHASE 1
+#define ZYNQ_NAND_DATA_PHASE 2
+#define ZYNQ_NAND_ECC_SIZE 512
+#define ZYNQ_NAND_SET_OPMODE_8BIT (0 << 0)
+#define ZYNQ_NAND_SET_OPMODE_16BIT (1 << 0)
+#define ZYNQ_NAND_ECC_STATUS (1 << 6)
+#define ZYNQ_MEMC_CLRCR_INT_CLR1 (1 << 4)
+#define ZYNQ_MEMC_SR_RAW_INT_ST1 (1 << 6)
+#define ZYNQ_MEMC_SR_INT_ST1 (1 << 4)
+#define ZYNQ_MEMC_NAND_ECC_MODE_MASK 0xC
+
+/* Flash memory controller operating parameters */
+#define ZYNQ_NAND_CLR_CONFIG ((0x1 << 1) | /* Disable interrupt */ \
+ (0x1 << 4) | /* Clear interrupt */ \
+ (0x1 << 6)) /* Disable ECC interrupt */
+
+#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS
+
+/* Assuming 50MHz clock (20ns cycle time) and 3V operation */
+#define ZYNQ_NAND_SET_CYCLES ((0x2 << 20) | /* t_rr from nand_cycles */ \
+ (0x2 << 17) | /* t_ar from nand_cycles */ \
+ (0x1 << 14) | /* t_clr from nand_cycles */ \
+ (0x3 << 11) | /* t_wp from nand_cycles */ \
+ (0x2 << 8) | /* t_rea from nand_cycles */ \
+ (0x5 << 4) | /* t_wc from nand_cycles */ \
+ (0x5 << 0)) /* t_rc from nand_cycles */
+#endif
+
+
+#define ZYNQ_NAND_DIRECT_CMD ((0x4 << 23) | /* Chip 0 from interface 1 */ \
+ (0x2 << 21)) /* UpdateRegs operation */
+
+#define ZYNQ_NAND_ECC_CONFIG ((0x1 << 2) | /* ECC available on APB */ \
+ (0x1 << 4) | /* ECC read at end of page */ \
+ (0x0 << 5)) /* No Jumping */
+
+#define ZYNQ_NAND_ECC_CMD1 ((0x80) | /* Write command */ \
+ (0x00 << 8) | /* Read command */ \
+ (0x30 << 16) | /* Read End command */ \
+ (0x1 << 24)) /* Read End command calid */
+
+#define ZYNQ_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \
+ (0x05 << 8) | /* Read col change cmd */ \
+ (0xE0 << 16) | /* Read col change end cmd */ \
+ (0x1 << 24)) /* Read col change
+ end cmd valid */
+/* AXI Address definitions */
+#define START_CMD_SHIFT 3
+#define END_CMD_SHIFT 11
+#define END_CMD_VALID_SHIFT 20
+#define ADDR_CYCLES_SHIFT 21
+#define CLEAR_CS_SHIFT 21
+#define ECC_LAST_SHIFT 10
+#define COMMAND_PHASE (0 << 19)
+#define DATA_PHASE (1 << 19)
+#define ONDIE_ECC_FEATURE_ADDR 0x90
+#define ONDIE_ECC_FEATURE_ENABLE 0x08
+
+#define ZYNQ_NAND_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */
+#define ZYNQ_NAND_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */
+
+/* ECC block registers bit position and bit mask */
+#define ZYNQ_NAND_ECC_BUSY (1 << 6) /* ECC block is busy */
+#define ZYNQ_NAND_ECC_MASK 0x00FFFFFF /* ECC value mask */
+
+#define ZYNQ_NAND_ROW_ADDR_CYCL_MASK 0x0F
+#define ZYNQ_NAND_COL_ADDR_CYCL_MASK 0xF0
+
+#define ZYNQ_NAND_MIO_NUM_NAND_8BIT 13
+#define ZYNQ_NAND_MIO_NUM_NAND_16BIT 8
+
+enum zynq_nand_bus_width {
+ NAND_BW_UNKNOWN = -1,
+ NAND_BW_8BIT,
+ NAND_BW_16BIT,
+};
+
+#ifndef NAND_CMD_LOCK_TIGHT
+#define NAND_CMD_LOCK_TIGHT 0x2c
+#endif
+
+#ifndef NAND_CMD_LOCK_STATUS
+#define NAND_CMD_LOCK_STATUS 0x7a
+#endif
+
+/* SMC register set */
+struct zynq_nand_smc_regs {
+ u32 csr; /* 0x00 */
+ u32 reserved0[2];
+ u32 cfr; /* 0x0C */
+ u32 dcr; /* 0x10 */
+ u32 scr; /* 0x14 */
+ u32 sor; /* 0x18 */
+ u32 reserved1[249];
+ u32 esr; /* 0x400 */
+ u32 emcr; /* 0x404 */
+ u32 emcmd1r; /* 0x408 */
+ u32 emcmd2r; /* 0x40C */
+ u32 reserved2[2];
+ u32 eval0r; /* 0x418 */
+};
+#define zynq_nand_smc_base ((struct zynq_nand_smc_regs __iomem *)\
+ ZYNQ_SMC_BASEADDR)
+
+/*
+ * struct zynq_nand_info - Defines the NAND flash driver instance
+ * @parts: Pointer to the mtd_partition structure
+ * @nand_base: Virtual address of the NAND flash device
+ * @end_cmd_pending: End command is pending
+ * @end_cmd: End command
+ */
+struct zynq_nand_info {
+ void __iomem *nand_base;
+ u8 end_cmd_pending;
+ u8 end_cmd;
+};
+
+/*
+ * struct zynq_nand_command_format - Defines NAND flash command format
+ * @start_cmd: First cycle command (Start command)
+ * @end_cmd: Second cycle command (Last command)
+ * @addr_cycles: Number of address cycles required to send the address
+ * @end_cmd_valid: The second cycle command is valid for cmd or data phase
+ */
+struct zynq_nand_command_format {
+ u8 start_cmd;
+ u8 end_cmd;
+ u8 addr_cycles;
+ u8 end_cmd_valid;
+};
+
+/* The NAND flash operations command format */
+static const struct zynq_nand_command_format zynq_nand_commands[] = {
+ {NAND_CMD_READ0, NAND_CMD_READSTART, 5, ZYNQ_NAND_CMD_PHASE},
+ {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, ZYNQ_NAND_CMD_PHASE},
+ {NAND_CMD_READID, NAND_CMD_NONE, 1, 0},
+ {NAND_CMD_STATUS, NAND_CMD_NONE, 0, 0},
+ {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, ZYNQ_NAND_DATA_PHASE},
+ {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, 0},
+ {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, ZYNQ_NAND_CMD_PHASE},
+ {NAND_CMD_RESET, NAND_CMD_NONE, 0, 0},
+ {NAND_CMD_PARAM, NAND_CMD_NONE, 1, 0},
+ {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, 0},
+ {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, 0},
+ {NAND_CMD_LOCK, NAND_CMD_NONE, 0, 0},
+ {NAND_CMD_LOCK_TIGHT, NAND_CMD_NONE, 0, 0},
+ {NAND_CMD_UNLOCK1, NAND_CMD_NONE, 3, 0},
+ {NAND_CMD_UNLOCK2, NAND_CMD_NONE, 3, 0},
+ {NAND_CMD_LOCK_STATUS, NAND_CMD_NONE, 3, 0},
+ {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
+ /* Add all the flash commands supported by the flash device */
+};
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_ecclayout nand_oob_16 = {
+ .eccbytes = 3,
+ .eccpos = {0, 1, 2},
+ .oobfree = {
+ { .offset = 8, .length = 8 }
+ }
+};
+
+static struct nand_ecclayout nand_oob_64 = {
+ .eccbytes = 12,
+ .eccpos = {
+ 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+ { .offset = 2, .length = 50 }
+ }
+};
+
+static struct nand_ecclayout ondie_nand_oob_64 = {
+ .eccbytes = 32,
+
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 56, 57, 58, 59, 60, 61, 62, 63
+ },
+
+ .oobfree = {
+ { .offset = 4, .length = 4 },
+ { .offset = 20, .length = 4 },
+ { .offset = 36, .length = 4 },
+ { .offset = 52, .length = 4 }
+ }
+};
+
+/* bbt decriptors for chips with on-die ECC and
+ chips with 64-byte OOB */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+/*
+ * zynq_nand_waitfor_ecc_completion - Wait for ECC completion
+ *
+ * returns: status for command completion, -1 for Timeout
+ */
+static int zynq_nand_waitfor_ecc_completion(void)
+{
+ unsigned long timeout;
+ u32 status;
+
+ /* Wait max 10us */
+ timeout = 10;
+ status = readl(&zynq_nand_smc_base->esr);
+ while (status & ZYNQ_NAND_ECC_BUSY) {
+ status = readl(&zynq_nand_smc_base->esr);
+ if (timeout == 0)
+ return -1;
+ timeout--;
+ udelay(1);
+ }
+
+ return status;
+}
+
+/*
+ * zynq_nand_init_nand_flash - Initialize NAND controller
+ * @option: Device property flags
+ *
+ * This function initializes the NAND flash interface on the NAND controller.
+ *
+ * returns: 0 on success or error value on failure
+ */
+static int zynq_nand_init_nand_flash(int option)
+{
+ u32 status;
+
+ /* disable interrupts */
+ writel(ZYNQ_NAND_CLR_CONFIG, &zynq_nand_smc_base->cfr);
+#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS
+ /* Initialize the NAND interface by setting cycles and operation mode */
+ writel(ZYNQ_NAND_SET_CYCLES, &zynq_nand_smc_base->scr);
+#endif
+ if (option & NAND_BUSWIDTH_16)
+ writel(ZYNQ_NAND_SET_OPMODE_16BIT, &zynq_nand_smc_base->sor);
+ else
+ writel(ZYNQ_NAND_SET_OPMODE_8BIT, &zynq_nand_smc_base->sor);
+
+ writel(ZYNQ_NAND_DIRECT_CMD, &zynq_nand_smc_base->dcr);
+
+ /* Wait till the ECC operation is complete */
+ status = zynq_nand_waitfor_ecc_completion();
+ if (status < 0) {
+ printf("%s: Timeout\n", __func__);
+ return status;
+ }
+
+ /* Set the command1 and command2 register */
+ writel(ZYNQ_NAND_ECC_CMD1, &zynq_nand_smc_base->emcmd1r);
+ writel(ZYNQ_NAND_ECC_CMD2, &zynq_nand_smc_base->emcmd2r);
+
+ return 0;
+}
+
+/*
+ * zynq_nand_calculate_hwecc - Calculate Hardware ECC
+ * @mtd: Pointer to the mtd_info structure
+ * @data: Pointer to the page data
+ * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
+ *
+ * This function retrieves the Hardware ECC data from the controller and returns
+ * ECC data back to the MTD subsystem.
+ *
+ * returns: 0 on success or error value on failure
+ */
+static int zynq_nand_calculate_hwecc(struct mtd_info *mtd, const u8 *data,
+ u8 *ecc_code)
+{
+ u32 ecc_value = 0;
+ u8 ecc_reg, ecc_byte;
+ u32 ecc_status;
+
+ /* Wait till the ECC operation is complete */
+ ecc_status = zynq_nand_waitfor_ecc_completion();
+ if (ecc_status < 0) {
+ printf("%s: Timeout\n", __func__);
+ return ecc_status;
+ }
+
+ for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
+ /* Read ECC value for each block */
+ ecc_value = readl(&zynq_nand_smc_base->eval0r + ecc_reg);
+
+ /* Get the ecc status from ecc read value */
+ ecc_status = (ecc_value >> 24) & 0xFF;
+
+ /* ECC value valid */
+ if (ecc_status & ZYNQ_NAND_ECC_STATUS) {
+ for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
+ /* Copy ECC bytes to MTD buffer */
+ *ecc_code = ecc_value & 0xFF;
+ ecc_value = ecc_value >> 8;
+ ecc_code++;
+ }
+ } else {
+ debug("%s: ecc status failed\n", __func__);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * onehot - onehot function
+ * @value: value to check for onehot
+ *
+ * This function checks whether a value is onehot or not.
+ * onehot is if and only if one bit is set.
+ *
+ * FIXME: Try to move this in common.h
+ */
+static bool onehot(unsigned short value)
+{
+ bool onehot;
+
+ onehot = value && !(value & (value - 1));
+ return onehot;
+}
+
+/*
+ * zynq_nand_correct_data - ECC correction function
+ * @mtd: Pointer to the mtd_info structure
+ * @buf: Pointer to the page data
+ * @read_ecc: Pointer to the ECC value read from spare data area
+ * @calc_ecc: Pointer to the calculated ECC value
+ *
+ * This function corrects the ECC single bit errors & detects 2-bit errors.
+ *
+ * returns: 0 if no ECC errors found
+ * 1 if single bit error found and corrected.
+ * -1 if multiple ECC errors found.
+ */
+static int zynq_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ unsigned char bit_addr;
+ unsigned int byte_addr;
+ unsigned short ecc_odd, ecc_even;
+ unsigned short read_ecc_lower, read_ecc_upper;
+ unsigned short calc_ecc_lower, calc_ecc_upper;
+
+ read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
+ read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff;
+
+ calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
+ calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff;
+
+ ecc_odd = read_ecc_lower ^ calc_ecc_lower;
+ ecc_even = read_ecc_upper ^ calc_ecc_upper;
+
+ if ((ecc_odd == 0) && (ecc_even == 0))
+ return 0; /* no error */
+
+ if (ecc_odd == (~ecc_even & 0xfff)) {
+ /* bits [11:3] of error code is byte offset */
+ byte_addr = (ecc_odd >> 3) & 0x1ff;
+ /* bits [2:0] of error code is bit offset */
+ bit_addr = ecc_odd & 0x7;
+ /* Toggling error bit */
+ buf[byte_addr] ^= (1 << bit_addr);
+ return 1;
+ }
+
+ if (onehot(ecc_odd | ecc_even))
+ return 1; /* one error in parity */
+
+ return -1; /* Uncorrectable error */
+}
+
+/*
+ * zynq_nand_read_oob - [REPLACABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ * @sndcmd: flag whether to issue read command or not
+ */
+static int zynq_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ unsigned long data_phase_addr = 0;
+ int data_width = 4;
+ u8 *p;
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+
+ p = chip->oob_poi;
+ chip->read_buf(mtd, p, (mtd->oobsize - data_width));
+ p += mtd->oobsize - data_width;
+
+ data_phase_addr = (unsigned long)chip->IO_ADDR_R;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+ chip->read_buf(mtd, p, data_width);
+
+ return 0;
+}
+
+/*
+ * zynq_nand_write_oob - [REPLACABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+static int zynq_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0, data_width = 4;
+ const u8 *buf = chip->oob_poi;
+ unsigned long data_phase_addr = 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+ chip->write_buf(mtd, buf, (mtd->oobsize - data_width));
+ buf += mtd->oobsize - data_width;
+
+ data_phase_addr = (unsigned long)chip->IO_ADDR_W;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
+ chip->write_buf(mtd, buf, data_width);
+
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/*
+ * zynq_nand_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to read
+ */
+static int zynq_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+ u8 *p;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+
+ p = chip->oob_poi;
+ chip->read_buf(mtd, p, (mtd->oobsize - data_width));
+ p += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long)chip->IO_ADDR_R;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+
+ chip->read_buf(mtd, p, data_width);
+ return 0;
+}
+
+static int zynq_nand_read_page_raw_nooob(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ chip->read_buf(mtd, buf, mtd->writesize);
+ return 0;
+}
+
+static int zynq_nand_read_subpage_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u32 data_offs,
+ u32 readlen, u8 *buf, int page)
+{
+ if (data_offs != 0) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1);
+ buf += data_offs;
+ }
+ chip->read_buf(mtd, buf, readlen);
+
+ return 0;
+}
+
+/*
+ * zynq_nand_write_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ */
+static int zynq_nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int oob_required, int page)
+{
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+ u8 *p;
+
+ chip->write_buf(mtd, buf, mtd->writesize);
+
+ p = chip->oob_poi;
+ chip->write_buf(mtd, p, (mtd->oobsize - data_width));
+ p += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long)chip->IO_ADDR_W;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
+
+ chip->write_buf(mtd, p, data_width);
+
+ return 0;
+}
+
+/*
+ * nand_write_page_hwecc - Hardware ECC based page write function
+ * @mtd: Pointer to the mtd info structure
+ * @chip: Pointer to the NAND chip info structure
+ * @buf: Pointer to the data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ *
+ * This functions writes data and hardware generated ECC values in to the page.
+ */
+static int zynq_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int oob_required, int page)
+{
+ int i, eccsteps, eccsize = chip->ecc.size;
+ u8 *ecc_calc = chip->buffers->ecccalc;
+ const u8 *p = buf;
+ u32 *eccpos = chip->ecc.layout->eccpos;
+ unsigned long data_phase_addr = 0;
+ unsigned long data_width = 4;
+ u8 *oob_ptr;
+
+ for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) {
+ chip->write_buf(mtd, p, eccsize);
+ p += eccsize;
+ }
+ chip->write_buf(mtd, p, (eccsize - data_width));
+ p += eccsize - data_width;
+
+ /* Set ECC Last bit to 1 */
+ data_phase_addr = (unsigned long) chip->IO_ADDR_W;
+ data_phase_addr |= ZYNQ_NAND_ECC_LAST;
+ chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
+ chip->write_buf(mtd, p, data_width);
+
+ /* Wait for ECC to be calculated and read the error values */
+ p = buf;
+ chip->ecc.calculate(mtd, p, &ecc_calc[0]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]);
+
+ /* Clear ECC last bit */
+ data_phase_addr = (unsigned long)chip->IO_ADDR_W;
+ data_phase_addr &= ~ZYNQ_NAND_ECC_LAST;
+ chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
+
+ /* Write the spare area with ECC bytes */
+ oob_ptr = chip->oob_poi;
+ chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width));
+
+ data_phase_addr = (unsigned long)chip->IO_ADDR_W;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
+ oob_ptr += (mtd->oobsize - data_width);
+ chip->write_buf(mtd, oob_ptr, data_width);
+
+ return 0;
+}
+
+/*
+ * zynq_nand_write_page_swecc - [REPLACABLE] software ecc based page
+ * write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ * @oob_required: must write chip->oob_poi to OOB
+ */
+static int zynq_nand_write_page_swecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ u8 *ecc_calc = chip->buffers->ecccalc;
+ const u8 *p = buf;
+ u32 *eccpos = chip->ecc.layout->eccpos;
+
+ /* Software ecc calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
+}
+
+/*
+ * nand_read_page_hwecc - Hardware ECC based page read function
+ * @mtd: Pointer to the mtd info structure
+ * @chip: Pointer to the NAND chip info structure
+ * @buf: Pointer to the buffer to store read data
+ * @oob_required: must write chip->oob_poi to OOB
+ * @page: page number to read
+ *
+ * This functions reads data and checks the data integrity by comparing hardware
+ * generated ECC values and read ECC values from spare area.
+ *
+ * returns: 0 always and updates ECC operation status in to MTD structure
+ */
+static int zynq_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ int i, stat, eccsteps, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ u8 *p = buf;
+ u8 *ecc_calc = chip->buffers->ecccalc;
+ u8 *ecc_code = chip->buffers->ecccode;
+ u32 *eccpos = chip->ecc.layout->eccpos;
+ unsigned long data_phase_addr = 0;
+ unsigned long data_width = 4;
+ u8 *oob_ptr;
+
+ for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) {
+ chip->read_buf(mtd, p, eccsize);
+ p += eccsize;
+ }
+ chip->read_buf(mtd, p, (eccsize - data_width));
+ p += eccsize - data_width;
+
+ /* Set ECC Last bit to 1 */
+ data_phase_addr = (unsigned long)chip->IO_ADDR_R;
+ data_phase_addr |= ZYNQ_NAND_ECC_LAST;
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+ chip->read_buf(mtd, p, data_width);
+
+ /* Read the calculated ECC value */
+ p = buf;
+ chip->ecc.calculate(mtd, p, &ecc_calc[0]);
+
+ /* Clear ECC last bit */
+ data_phase_addr = (unsigned long)chip->IO_ADDR_R;
+ data_phase_addr &= ~ZYNQ_NAND_ECC_LAST;
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+
+ /* Read the stored ECC value */
+ oob_ptr = chip->oob_poi;
+ chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width));
+
+ /* de-assert chip select */
+ data_phase_addr = (unsigned long)chip->IO_ADDR_R;
+ data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+
+ oob_ptr += (mtd->oobsize - data_width);
+ chip->read_buf(mtd, oob_ptr, data_width);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = ~(chip->oob_poi[eccpos[i]]);
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ /* Check ECC error for all blocks and correct if it is correctable */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/*
+ * zynq_nand_read_page_swecc - [REPLACABLE] software ecc based page
+ * read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
+ */
+static int zynq_nand_read_page_swecc(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ u8 *p = buf;
+ u8 *ecc_calc = chip->buffers->ecccalc;
+ u8 *ecc_code = chip->buffers->ecccode;
+ u32 *eccpos = chip->ecc.layout->eccpos;
+
+ chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/*
+ * zynq_nand_select_chip - Select the flash device
+ * @mtd: Pointer to the mtd_info structure
+ * @chip: Chip number to be selected
+ *
+ * This function is empty as the NAND controller handles chip select line
+ * internally based on the chip address passed in command and data phase.
+ */
+static void zynq_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ /* Not support multiple chips yet */
+}
+
+/*
+ * zynq_nand_cmd_function - Send command to NAND device
+ * @mtd: Pointer to the mtd_info structure
+ * @command: The command to be sent to the flash device
+ * @column: The column address for this command, -1 if none
+ * @page_addr: The page address for this command, -1 if none
+ */
+static void zynq_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ const struct zynq_nand_command_format *curr_cmd = NULL;
+ u8 addr_cycles = 0;
+ struct zynq_nand_info *xnand = (struct zynq_nand_info *)chip->priv;
+ void *cmd_addr;
+ unsigned long cmd_data = 0;
+ unsigned long cmd_phase_addr = 0;
+ unsigned long data_phase_addr = 0;
+ u8 end_cmd = 0;
+ u8 end_cmd_valid = 0;
+ u32 index;
+
+ if (xnand->end_cmd_pending) {
+ /* Check for end command if this command request is same as the
+ * pending command then return
+ */
+ if (xnand->end_cmd == command) {
+ xnand->end_cmd = 0;
+ xnand->end_cmd_pending = 0;
+ return;
+ }
+ }
+
+ /* Emulate NAND_CMD_READOOB for large page device */
+ if ((mtd->writesize > ZYNQ_NAND_ECC_SIZE) &&
+ (command == NAND_CMD_READOOB)) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Get the command format */
+ for (index = 0; index < ARRAY_SIZE(zynq_nand_commands); index++)
+ if (command == zynq_nand_commands[index].start_cmd)
+ break;
+
+ if (index == ARRAY_SIZE(zynq_nand_commands)) {
+ printf("%s: Unsupported start cmd %02x\n", __func__, command);
+ return;
+ }
+ curr_cmd = &zynq_nand_commands[index];
+
+ /* Clear interrupt */
+ writel(ZYNQ_MEMC_CLRCR_INT_CLR1, &zynq_nand_smc_base->cfr);
+
+ /* Get the command phase address */
+ if (curr_cmd->end_cmd_valid == ZYNQ_NAND_CMD_PHASE)
+ end_cmd_valid = 1;
+
+ if (curr_cmd->end_cmd == NAND_CMD_NONE)
+ end_cmd = 0x0;
+ else
+ end_cmd = curr_cmd->end_cmd;
+
+ if (command == NAND_CMD_READ0 ||
+ command == NAND_CMD_SEQIN) {
+ addr_cycles = chip->onfi_params.addr_cycles &
+ ZYNQ_NAND_ROW_ADDR_CYCL_MASK;
+ addr_cycles += ((chip->onfi_params.addr_cycles &
+ ZYNQ_NAND_COL_ADDR_CYCL_MASK) >> 4);
+ } else {
+ addr_cycles = curr_cmd->addr_cycles;
+ }
+
+ cmd_phase_addr = (unsigned long)xnand->nand_base |
+ (addr_cycles << ADDR_CYCLES_SHIFT) |
+ (end_cmd_valid << END_CMD_VALID_SHIFT) |
+ (COMMAND_PHASE) |
+ (end_cmd << END_CMD_SHIFT) |
+ (curr_cmd->start_cmd << START_CMD_SHIFT);
+
+ cmd_addr = (void __iomem *)cmd_phase_addr;
+
+ /* Get the data phase address */
+ end_cmd_valid = 0;
+
+ data_phase_addr = (unsigned long)xnand->nand_base |
+ (0x0 << CLEAR_CS_SHIFT) |
+ (end_cmd_valid << END_CMD_VALID_SHIFT) |
+ (DATA_PHASE) |
+ (end_cmd << END_CMD_SHIFT) |
+ (0x0 << ECC_LAST_SHIFT);
+
+ chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
+ chip->IO_ADDR_W = chip->IO_ADDR_R;
+
+ /* Command phase AXI Read & Write */
+ if (column != -1 && page_addr != -1) {
+ /* Adjust columns for 16 bit bus width */
+ if (chip->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ cmd_data = column;
+ if (mtd->writesize > ZYNQ_NAND_ECC_SIZE) {
+ cmd_data |= page_addr << 16;
+ /* Another address cycle for devices > 128MiB */
+ if (chip->chipsize > (128 << 20)) {
+ writel(cmd_data, cmd_addr);
+ cmd_data = (page_addr >> 16);
+ }
+ } else {
+ cmd_data |= page_addr << 8;
+ }
+ } else if (page_addr != -1) { /* Erase */
+ cmd_data = page_addr;
+ } else if (column != -1) { /* Change read/write column, read id etc */
+ /* Adjust columns for 16 bit bus width */
+ if ((chip->options & NAND_BUSWIDTH_16) &&
+ ((command == NAND_CMD_READ0) ||
+ (command == NAND_CMD_SEQIN) ||
+ (command == NAND_CMD_RNDOUT) ||
+ (command == NAND_CMD_RNDIN)))
+ column >>= 1;
+ cmd_data = column;
+ }
+
+ writel(cmd_data, cmd_addr);
+
+ if (curr_cmd->end_cmd_valid) {
+ xnand->end_cmd = curr_cmd->end_cmd;
+ xnand->end_cmd_pending = 1;
+ }
+
+ ndelay(100);
+
+ if ((command == NAND_CMD_READ0) ||
+ (command == NAND_CMD_RESET) ||
+ (command == NAND_CMD_PARAM) ||
+ (command == NAND_CMD_GET_FEATURES))
+ /* wait until command is processed */
+ nand_wait_ready(mtd);
+}
+
+/*
+ * zynq_nand_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void zynq_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ /* Make sure that buf is 32 bit aligned */
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (((unsigned long)buf & 0x1) != 0) {
+ if (len) {
+ *buf = readb(chip->IO_ADDR_R);
+ buf += 1;
+ len--;
+ }
+ }
+
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (len >= 2) {
+ *(u16 *)buf = readw(chip->IO_ADDR_R);
+ buf += 2;
+ len -= 2;
+ }
+ }
+ }
+
+ /* copy aligned data */
+ while (len >= 4) {
+ *(u32 *)buf = readl(chip->IO_ADDR_R);
+ buf += 4;
+ len -= 4;
+ }
+
+ /* mop up any remaining bytes */
+ if (len) {
+ if (len >= 2) {
+ *(u16 *)buf = readw(chip->IO_ADDR_R);
+ buf += 2;
+ len -= 2;
+ }
+ if (len)
+ *buf = readb(chip->IO_ADDR_R);
+ }
+}
+
+/*
+ * zynq_nand_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void zynq_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ const u32 *nand = chip->IO_ADDR_W;
+
+ /* Make sure that buf is 32 bit aligned */
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (((unsigned long)buf & 0x1) != 0) {
+ if (len) {
+ writeb(*buf, nand);
+ buf += 1;
+ len--;
+ }
+ }
+
+ if (((unsigned long)buf & 0x3) != 0) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+ }
+ }
+
+ /* copy aligned data */
+ while (len >= 4) {
+ writel(*(u32 *)buf, nand);
+ buf += 4;
+ len -= 4;
+ }
+
+ /* mop up any remaining bytes */
+ if (len) {
+ if (len >= 2) {
+ writew(*(u16 *)buf, nand);
+ buf += 2;
+ len -= 2;
+ }
+
+ if (len)
+ writeb(*buf, nand);
+ }
+}
+
+/*
+ * zynq_nand_device_ready - Check device ready/busy line
+ * @mtd: Pointer to the mtd_info structure
+ *
+ * returns: 0 on busy or 1 on ready state
+ */
+static int zynq_nand_device_ready(struct mtd_info *mtd)
+{
+ u32 csr_val;
+
+ csr_val = readl(&zynq_nand_smc_base->csr);
+ /* Check the raw_int_status1 bit */
+ if (csr_val & ZYNQ_MEMC_SR_RAW_INT_ST1) {
+ /* Clear the interrupt condition */
+ writel(ZYNQ_MEMC_SR_INT_ST1, &zynq_nand_smc_base->cfr);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int zynq_nand_check_is_16bit_bw_flash(void)
+{
+ int is_16bit_bw = NAND_BW_UNKNOWN;
+ int mio_num_8bit = 0, mio_num_16bit = 0;
+
+ mio_num_8bit = zynq_slcr_get_mio_pin_status("nand8");
+ if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT)
+ is_16bit_bw = NAND_BW_8BIT;
+
+ mio_num_16bit = zynq_slcr_get_mio_pin_status("nand16");
+ if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT &&
+ mio_num_16bit == ZYNQ_NAND_MIO_NUM_NAND_16BIT)
+ is_16bit_bw = NAND_BW_16BIT;
+
+ return is_16bit_bw;
+}
+
+static int zynq_nand_init(struct nand_chip *nand_chip, int devnum)
+{
+ struct zynq_nand_info *xnand;
+ struct mtd_info *mtd;
+ unsigned long ecc_page_size;
+ u8 maf_id, dev_id, i;
+ u8 get_feature[4];
+ u8 set_feature[4] = {ONDIE_ECC_FEATURE_ENABLE, 0x00, 0x00, 0x00};
+ unsigned long ecc_cfg;
+ int ondie_ecc_enabled = 0;
+ int err = -1;
+ int is_16bit_bw;
+
+ xnand = calloc(1, sizeof(struct zynq_nand_info));
+ if (!xnand) {
+ printf("%s: failed to allocate\n", __func__);
+ goto fail;
+ }
+
+ xnand->nand_base = (void __iomem *)ZYNQ_NAND_BASEADDR;
+ mtd = nand_to_mtd(nand_chip);
+
+ nand_chip->priv = xnand;
+ mtd->priv = nand_chip;
+
+ /* Set address of NAND IO lines */
+ nand_chip->IO_ADDR_R = xnand->nand_base;
+ nand_chip->IO_ADDR_W = xnand->nand_base;
+
+ /* Set the driver entry points for MTD */
+ nand_chip->cmdfunc = zynq_nand_cmd_function;
+ nand_chip->dev_ready = zynq_nand_device_ready;
+ nand_chip->select_chip = zynq_nand_select_chip;
+
+ /* If we don't set this delay driver sets 20us by default */
+ nand_chip->chip_delay = 30;
+
+ /* Buffer read/write routines */
+ nand_chip->read_buf = zynq_nand_read_buf;
+ nand_chip->write_buf = zynq_nand_write_buf;
+
+ is_16bit_bw = zynq_nand_check_is_16bit_bw_flash();
+ if (is_16bit_bw == NAND_BW_UNKNOWN) {
+ printf("%s: Unable detect NAND based on MIO settings\n",
+ __func__);
+ goto fail;
+ }
+
+ if (is_16bit_bw == NAND_BW_16BIT)
+ nand_chip->options = NAND_BUSWIDTH_16;
+
+ nand_chip->bbt_options = NAND_BBT_USE_FLASH;
+
+ /* Initialize the NAND flash interface on NAND controller */
+ if (zynq_nand_init_nand_flash(nand_chip->options) < 0) {
+ printf("%s: nand flash init failed\n", __func__);
+ goto fail;
+ }
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ printf("%s: nand_scan_ident failed\n", __func__);
+ goto fail;
+ }
+ /* Send the command for reading device ID */
+ nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+ maf_id = nand_chip->read_byte(mtd);
+ dev_id = nand_chip->read_byte(mtd);
+
+ if ((maf_id == 0x2c) && ((dev_id == 0xf1) ||
+ (dev_id == 0xa1) || (dev_id == 0xb1) ||
+ (dev_id == 0xaa) || (dev_id == 0xba) ||
+ (dev_id == 0xda) || (dev_id == 0xca) ||
+ (dev_id == 0xac) || (dev_id == 0xbc) ||
+ (dev_id == 0xdc) || (dev_id == 0xcc) ||
+ (dev_id == 0xa3) || (dev_id == 0xb3) ||
+ (dev_id == 0xd3) || (dev_id == 0xc3))) {
+ nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+ for (i = 0; i < 4; i++)
+ writeb(set_feature[i], nand_chip->IO_ADDR_W);
+
+ /* Wait for 1us after writing data with SET_FEATURES command */
+ ndelay(1000);
+
+ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+ nand_chip->read_buf(mtd, get_feature, 4);
+
+ if (get_feature[0] & ONDIE_ECC_FEATURE_ENABLE) {
+ debug("%s: OnDie ECC flash\n", __func__);
+ ondie_ecc_enabled = 1;
+ } else {
+ printf("%s: Unable to detect OnDie ECC\n", __func__);
+ }
+ }
+
+ if (ondie_ecc_enabled) {
+ /* Bypass the controller ECC block */
+ ecc_cfg = readl(&zynq_nand_smc_base->emcr);
+ ecc_cfg &= ~ZYNQ_MEMC_NAND_ECC_MODE_MASK;
+ writel(ecc_cfg, &zynq_nand_smc_base->emcr);
+
+ /* The software ECC routines won't work
+ * with the SMC controller
+ */
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.read_page = zynq_nand_read_page_raw_nooob;
+ nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw;
+ nand_chip->ecc.write_page = zynq_nand_write_page_raw;
+ nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw;
+ nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw;
+ nand_chip->ecc.read_oob = zynq_nand_read_oob;
+ nand_chip->ecc.write_oob = zynq_nand_write_oob;
+ nand_chip->ecc.size = mtd->writesize;
+ nand_chip->ecc.bytes = 0;
+
+ /* NAND with on-die ECC supports subpage reads */
+ nand_chip->options |= NAND_SUBPAGE_READ;
+
+ /* On-Die ECC spare bytes offset 8 is used for ECC codes */
+ if (ondie_ecc_enabled) {
+ nand_chip->ecc.layout = &ondie_nand_oob_64;
+ /* Use the BBT pattern descriptors */
+ nand_chip->bbt_td = &bbt_main_descr;
+ nand_chip->bbt_md = &bbt_mirror_descr;
+ }
+ } else {
+ /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.size = ZYNQ_NAND_ECC_SIZE;
+ nand_chip->ecc.bytes = 3;
+ nand_chip->ecc.calculate = zynq_nand_calculate_hwecc;
+ nand_chip->ecc.correct = zynq_nand_correct_data;
+ nand_chip->ecc.hwctl = NULL;
+ nand_chip->ecc.read_page = zynq_nand_read_page_hwecc;
+ nand_chip->ecc.write_page = zynq_nand_write_page_hwecc;
+ nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw;
+ nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw;
+ nand_chip->ecc.read_oob = zynq_nand_read_oob;
+ nand_chip->ecc.write_oob = zynq_nand_write_oob;
+
+ switch (mtd->writesize) {
+ case 512:
+ ecc_page_size = 0x1;
+ /* Set the ECC memory config register */
+ writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
+ &zynq_nand_smc_base->emcr);
+ break;
+ case 1024:
+ ecc_page_size = 0x2;
+ /* Set the ECC memory config register */
+ writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
+ &zynq_nand_smc_base->emcr);
+ break;
+ case 2048:
+ ecc_page_size = 0x3;
+ /* Set the ECC memory config register */
+ writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
+ &zynq_nand_smc_base->emcr);
+ break;
+ default:
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.calculate = nand_calculate_ecc;
+ nand_chip->ecc.correct = nand_correct_data;
+ nand_chip->ecc.read_page = zynq_nand_read_page_swecc;
+ nand_chip->ecc.write_page = zynq_nand_write_page_swecc;
+ nand_chip->ecc.size = 256;
+ break;
+ }
+
+ if (mtd->oobsize == 16)
+ nand_chip->ecc.layout = &nand_oob_16;
+ else if (mtd->oobsize == 64)
+ nand_chip->ecc.layout = &nand_oob_64;
+ else
+ printf("%s: No oob layout found\n", __func__);
+ }
+
+ /* Second phase scan */
+ if (nand_scan_tail(mtd)) {
+ printf("%s: nand_scan_tail failed\n", __func__);
+ goto fail;
+ }
+ if (nand_register(devnum, mtd))
+ goto fail;
+ return 0;
+fail:
+ free(xnand);
+ return err;
+}
+
+static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
+
+void board_nand_init(void)
+{
+ struct nand_chip *nand = &nand_chip[0];
+
+ if (zynq_nand_init(nand, 0))
+ puts("ZYNQ NAND init failed\n");
+}