mtd: move NAND files into a raw/ subdirectory
authorMiquel Raynal <miquel.raynal@bootlin.com>
Thu, 16 Aug 2018 15:30:07 +0000 (17:30 +0200)
committerJagan Teki <jagan@amarulasolutions.com>
Thu, 20 Sep 2018 14:40:49 +0000 (20:10 +0530)
NAND flavors, like serial and parallel, have a lot in common and would
benefit to share code. Let's move raw (parallel) NAND specific code in a
raw/ subdirectory, to ease the addition of a core file in nand/ and the
introduction of a spi/ subdirectory specific to SPI NANDs.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
115 files changed:
MAINTAINERS
Makefile
README
arch/arm/mach-uniphier/board_late_init.c
common/spl/Kconfig
common/spl/spl_spi.c
doc/README.SPL
doc/README.arm-relocation
doc/README.nand
doc/README.zynq
drivers/Makefile
drivers/mtd/Makefile
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/am335x_spl_bch.c [deleted file]
drivers/mtd/nand/arasan_nfc.c [deleted file]
drivers/mtd/nand/atmel_nand.c [deleted file]
drivers/mtd/nand/atmel_nand_ecc.h [deleted file]
drivers/mtd/nand/davinci_nand.c [deleted file]
drivers/mtd/nand/denali.c [deleted file]
drivers/mtd/nand/denali.h [deleted file]
drivers/mtd/nand/denali_dt.c [deleted file]
drivers/mtd/nand/denali_spl.c [deleted file]
drivers/mtd/nand/fsl_elbc_nand.c [deleted file]
drivers/mtd/nand/fsl_elbc_spl.c [deleted file]
drivers/mtd/nand/fsl_ifc_nand.c [deleted file]
drivers/mtd/nand/fsl_ifc_spl.c [deleted file]
drivers/mtd/nand/fsl_upm.c [deleted file]
drivers/mtd/nand/fsmc_nand.c [deleted file]
drivers/mtd/nand/kb9202_nand.c [deleted file]
drivers/mtd/nand/kirkwood_nand.c [deleted file]
drivers/mtd/nand/kmeter1_nand.c [deleted file]
drivers/mtd/nand/lpc32xx_nand_mlc.c [deleted file]
drivers/mtd/nand/lpc32xx_nand_slc.c [deleted file]
drivers/mtd/nand/mxc_nand.c [deleted file]
drivers/mtd/nand/mxc_nand.h [deleted file]
drivers/mtd/nand/mxc_nand_spl.c [deleted file]
drivers/mtd/nand/mxs_nand.c [deleted file]
drivers/mtd/nand/mxs_nand.h [deleted file]
drivers/mtd/nand/mxs_nand_dt.c [deleted file]
drivers/mtd/nand/mxs_nand_spl.c [deleted file]
drivers/mtd/nand/nand.c [deleted file]
drivers/mtd/nand/nand_base.c [deleted file]
drivers/mtd/nand/nand_bbt.c [deleted file]
drivers/mtd/nand/nand_bch.c [deleted file]
drivers/mtd/nand/nand_ecc.c [deleted file]
drivers/mtd/nand/nand_ids.c [deleted file]
drivers/mtd/nand/nand_plat.c [deleted file]
drivers/mtd/nand/nand_spl_load.c [deleted file]
drivers/mtd/nand/nand_spl_loaders.c [deleted file]
drivers/mtd/nand/nand_spl_simple.c [deleted file]
drivers/mtd/nand/nand_timings.c [deleted file]
drivers/mtd/nand/nand_util.c [deleted file]
drivers/mtd/nand/omap_elm.c [deleted file]
drivers/mtd/nand/omap_gpmc.c [deleted file]
drivers/mtd/nand/pxa3xx_nand.c [deleted file]
drivers/mtd/nand/pxa3xx_nand.h [deleted file]
drivers/mtd/nand/raw/Kconfig [new file with mode: 0644]
drivers/mtd/nand/raw/Makefile [new file with mode: 0644]
drivers/mtd/nand/raw/am335x_spl_bch.c [new file with mode: 0644]
drivers/mtd/nand/raw/arasan_nfc.c [new file with mode: 0644]
drivers/mtd/nand/raw/atmel_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/atmel_nand_ecc.h [new file with mode: 0644]
drivers/mtd/nand/raw/davinci_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/denali.c [new file with mode: 0644]
drivers/mtd/nand/raw/denali.h [new file with mode: 0644]
drivers/mtd/nand/raw/denali_dt.c [new file with mode: 0644]
drivers/mtd/nand/raw/denali_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsl_elbc_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsl_elbc_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsl_ifc_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsl_ifc_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsl_upm.c [new file with mode: 0644]
drivers/mtd/nand/raw/fsmc_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/kb9202_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/kirkwood_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/kmeter1_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/lpc32xx_nand_mlc.c [new file with mode: 0644]
drivers/mtd/nand/raw/lpc32xx_nand_slc.c [new file with mode: 0644]
drivers/mtd/nand/raw/mxc_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/mxc_nand.h [new file with mode: 0644]
drivers/mtd/nand/raw/mxc_nand_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/mxs_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/mxs_nand.h [new file with mode: 0644]
drivers/mtd/nand/raw/mxs_nand_dt.c [new file with mode: 0644]
drivers/mtd/nand/raw/mxs_nand_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_base.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_bbt.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_bch.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_ecc.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_ids.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_plat.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_spl_load.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_spl_loaders.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_spl_simple.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_timings.c [new file with mode: 0644]
drivers/mtd/nand/raw/nand_util.c [new file with mode: 0644]
drivers/mtd/nand/raw/omap_elm.c [new file with mode: 0644]
drivers/mtd/nand/raw/omap_gpmc.c [new file with mode: 0644]
drivers/mtd/nand/raw/pxa3xx_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/pxa3xx_nand.h [new file with mode: 0644]
drivers/mtd/nand/raw/sunxi_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/sunxi_nand_spl.c [new file with mode: 0644]
drivers/mtd/nand/raw/tegra_nand.c [new file with mode: 0644]
drivers/mtd/nand/raw/tegra_nand.h [new file with mode: 0644]
drivers/mtd/nand/raw/vf610_nfc.c [new file with mode: 0644]
drivers/mtd/nand/raw/zynq_nand.c [new file with mode: 0644]
drivers/mtd/nand/sunxi_nand.c [deleted file]
drivers/mtd/nand/sunxi_nand_spl.c [deleted file]
drivers/mtd/nand/tegra_nand.c [deleted file]
drivers/mtd/nand/tegra_nand.h [deleted file]
drivers/mtd/nand/vf610_nfc.c [deleted file]
drivers/mtd/nand/zynq_nand.c [deleted file]
include/configs/MPC8313ERDB.h

index 64fb41ece7c0a24df9dbb9f45c95fa181e8b063c..20a0a5e12bc9395153456e3f24f75f0e8e93bb63 100644 (file)
@@ -298,7 +298,7 @@ F:  drivers/i2c/i2c-cdns.c
 F:     drivers/i2c/muxes/pca954x.c
 F:     drivers/i2c/zynq_i2c.c
 F:     drivers/mmc/zynq_sdhci.c
-F:     drivers/mtd/nand/zynq_nand.c
+F:     drivers/mtd/nand/raw/zynq_nand.c
 F:     drivers/net/phy/xilinx_phy.c
 F:     drivers/net/zynq_gem.c
 F:     drivers/serial/serial_zynq.c
@@ -322,7 +322,7 @@ F:  drivers/i2c/i2c-cdns.c
 F:     drivers/i2c/muxes/pca954x.c
 F:     drivers/i2c/zynq_i2c.c
 F:     drivers/mmc/zynq_sdhci.c
-F:     drivers/mtd/nand/zynq_nand.c
+F:     drivers/mtd/nand/raw/zynq_nand.c
 F:     drivers/net/phy/xilinx_phy.c
 F:     drivers/net/zynq_gem.c
 F:     drivers/serial/serial_zynq.c
@@ -467,7 +467,7 @@ NAND FLASH
 #M:    Scott Wood <oss@buserror.net>
 S:     Orphaned (Since 2018-07)
 T:     git git://git.denx.de/u-boot-nand-flash.git
-F:     drivers/mtd/nand/
+F:     drivers/mtd/nand/raw/
 
 NDS32
 M:     Macpaul Lin <macpaul@andestech.com>
index e38966edbaa75701f84e25040e47a0a00a61178d..807e803f996dcc53d87e60735833e5c95d2211b4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -689,7 +689,7 @@ libs-y += drivers/dma/
 libs-y += drivers/gpio/
 libs-y += drivers/i2c/
 libs-y += drivers/mtd/
-libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/
+libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/raw/
 libs-y += drivers/mtd/onenand/
 libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/
 libs-y += drivers/mtd/spi/
diff --git a/README b/README
index 09822a317da8da4e71f8af460b1d305af4bcd9cd..35305a61a3aa314d2871075c32c65927afb9dfa5 100644 (file)
--- a/README
+++ b/README
@@ -3256,8 +3256,8 @@ Low Level (hardware related) configuration options:
                a 16 bit bus.
                Not all NAND drivers use this symbol.
                Example of drivers that use it:
-               - drivers/mtd/nand/ndfc.c
-               - drivers/mtd/nand/mxc_nand.c
+               - drivers/mtd/nand/raw/ndfc.c
+               - drivers/mtd/nand/raw/mxc_nand.c
 
 - CONFIG_SYS_NDFC_EBC0_CFG
                Sets the EBC0_CFG register for the NDFC. If not defined
@@ -3374,7 +3374,7 @@ Low Level (hardware related) configuration options:
 - CONFIG_SYS_NAND_NO_SUBPAGE_WRITE
                Option to disable subpage write in NAND driver
                driver that uses this:
-               drivers/mtd/nand/davinci_nand.c
+               drivers/mtd/nand/raw/davinci_nand.c
 
 Freescale QE/FMAN Firmware Support:
 -----------------------------------
index 8ffb9a8d3c2c05683d2537f402025e522009c7c4..1b871c62ced280acf0fe0f4730a38595e3c9f296 100644 (file)
@@ -12,7 +12,7 @@
 #include <stdio.h>
 #include <linux/io.h>
 #include <linux/printk.h>
-#include <../drivers/mtd/nand/denali.h>
+#include <../drivers/mtd/nand/raw/denali.h>
 
 #include "init.h"
 
index 280496fbe0b6a975ecd084358a2c9c915475f0e8..18dbc238bf6f3803c2a7c553c8523943514bc74e 100644 (file)
@@ -487,7 +487,7 @@ config SPL_NAND_SUPPORT
        help
          Enable support for NAND (Negative AND) flash in SPL. NAND flash
          can be used to allow SPL to load U-Boot from supported devices.
-         This enables the drivers in drivers/mtd/nand as part of an SPL
+         This enables the drivers in drivers/mtd/nand/raw as part of an SPL
          build.
 
 config SPL_NET_SUPPORT
index ba60a3a3c5053d07b026d3aeb96914faa6dbf111..0016d497390595b5237a3b8af96f671f3da32689 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2011 OMICRON electronics GmbH
  *
- * based on drivers/mtd/nand/nand_spl_load.c
+ * based on drivers/mtd/nand/raw/nand_spl_load.c
  *
  * Copyright (C) 2011
  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
index 3ba313caa8f73feec3fd0664447f99d3a55664af..fc1ca1ad4f5124d3c06bd05e8bc54c484c577fd5 100644 (file)
@@ -57,11 +57,11 @@ CONFIG_SPL_FAT_SUPPORT (fs/fat/libfat.o)
 CONFIG_SPL_EXT_SUPPORT
 CONFIG_SPL_LIBGENERIC_SUPPORT (lib/libgeneric.o)
 CONFIG_SPL_POWER_SUPPORT (drivers/power/libpower.o)
-CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/libnand.o)
+CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/raw/libnand.o)
 CONFIG_SPL_DRIVERS_MISC_SUPPORT (drivers/misc)
 CONFIG_SPL_DMA_SUPPORT (drivers/dma/libdma.o)
 CONFIG_SPL_POST_MEM_SUPPORT (post/drivers/memory.o)
-CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/nand_spl_load.o)
+CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/raw/nand_spl_load.o)
 CONFIG_SPL_SPI_LOAD (drivers/mtd/spi/spi_spl_load.o)
 CONFIG_SPL_RAM_DEVICE (common/spl/spl.c)
 CONFIG_SPL_WATCHDOG_SUPPORT (drivers/watchdog/libwatchdog.o)
index 645b3746c8a88fe25f7c9a33cd9b8b17aa7b5a57..d2a7e8122e90edce005eb5abf0e6afc1928eda56 100644 (file)
@@ -84,7 +84,7 @@ Relocation with SPL (example for the tx25 booting from NAND Flash):
 - cpu copies the first page from NAND to 0xbb000000 (IMX_NFC_BASE)
   and start with code execution on this address.
 
-- The First page contains u-boot code from drivers/mtd/nand/mxc_nand_spl.c
+- The First page contains u-boot code from drivers/mtd/nand/raw/mxc_nand_spl.c
   which inits the dram, cpu registers, reloacte itself to CONFIG_SPL_TEXT_BASE and loads
   the "real" u-boot to CONFIG_SYS_NAND_U_BOOT_DST and starts execution
   @CONFIG_SYS_NAND_U_BOOT_START
index cda11b43ffc59fa3f04614165f67d733121541ae..ec461b2dc933b315a971607ee24a14e03a35e024 100644 (file)
@@ -116,7 +116,7 @@ Configuration Options:
       The maximum number of NAND chips per device to be supported.
 
    CONFIG_SYS_NAND_SELF_INIT
-      Traditionally, glue code in drivers/mtd/nand/nand.c has driven
+      Traditionally, glue code in drivers/mtd/nand/raw/nand.c has driven
       the initialization process -- it provides the mtd and nand
       structs, calls a board init function for a specific device,
       calls nand_scan(), and registers with mtd.
@@ -125,7 +125,7 @@ Configuration Options:
       run code between nand_scan_ident() and nand_scan_tail(), or other
       deviations from the "normal" flow.
 
-      If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/nand.c
+      If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/raw/nand.c
       will make one call to board_nand_init(), with no arguments.  That
       function is responsible for calling a driver init function for
       each NAND device on the board, that performs all initialization
@@ -280,7 +280,7 @@ NOTE:
 =====
 
 The Disk On Chip driver is currently broken and has been for some time.
-There is a driver in drivers/mtd/nand, taken from Linux, that works with
+There is a driver in drivers/mtd/nand/raw, taken from Linux, that works with
 the current NAND system but has not yet been adapted to the u-boot
 environment.
 
index 5b0672c50359d95e0cd18a7630bdb8456d097cb6..da977b2016a6c14b4ca4510382cc2cafb2e59dd2 100644 (file)
@@ -63,7 +63,7 @@ bootmode strings at runtime.
   spi - drivers/spi/zynq_spi.c
   qspi - drivers/spi/zynq_qspi.c
   i2c - drivers/i2c/zynq_i2c.c
-  nand - drivers/mtd/nand/zynq_nand.c
+  nand - drivers/mtd/nand/raw/zynq_nand.c
 - Done proper cleanups on board configurations
 - Added basic FDT support for zynq boards
 - d-cache support for zynq_gem.c
index 23ea609b094f519f88fddbfb178b69b26417cff1..fe8d40dddb0106a29f595cc55875f3805816fe06 100644 (file)
@@ -6,7 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/
 obj-$(CONFIG_$(SPL_TPL_)I2C_SUPPORT) += i2c/
 obj-$(CONFIG_$(SPL_TPL_)LED) += led/
 obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/
-obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/
+obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/raw/
 obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/
 obj-$(CONFIG_$(SPL_TPL_)PINCTRL) += pinctrl/
 obj-$(CONFIG_$(SPL_TPL_)RAM) += ram/
index 9cec06510c3eb47330fc75b14866b2e9415f5074..cdf515256a0fcd12fbcfc48bb7ecd00e1642d7bb 100644 (file)
@@ -18,3 +18,5 @@ obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o
 obj-$(CONFIG_ST_SMI) += st_smi.o
 obj-$(CONFIG_STM32_FLASH) += stm32_flash.o
 obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o
+
+obj-y += nand/
index 1e4ea7bdd4d00772848ffcffffa9c71535c08f6b..6d5373471809d93885c6ac622a4ba556a0f1c563 100644 (file)
@@ -1,297 +1 @@
-
-menuconfig NAND
-       bool "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
+source "drivers/mtd/nand/raw/Kconfig"
index c61e3f38391aeb9027fd1599805d91e88c7eb2a9..69f40d15635d3a236d88140a21821e7a902e1968 100644 (file)
@@ -1,77 +1,2 @@
 # 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/am335x_spl_bch.c b/drivers/mtd/nand/am335x_spl_bch.c
deleted file mode 100644 (file)
index ba2f33a..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-// 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/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c
deleted file mode 100644 (file)
index 41db9f8..0000000
+++ /dev/null
@@ -1,1270 +0,0 @@
-// 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/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
deleted file mode 100644 (file)
index a5b76e1..0000000
+++ /dev/null
@@ -1,1511 +0,0 @@
-// 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/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
deleted file mode 100644 (file)
index 05eeedb..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/* 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/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
deleted file mode 100644 (file)
index 305e68a..0000000
+++ /dev/null
@@ -1,833 +0,0 @@
-// 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/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/denali.c b/drivers/mtd/nand/denali.c
deleted file mode 100644 (file)
index d1cac06..0000000
+++ /dev/null
@@ -1,1371 +0,0 @@
-// 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/denali.h b/drivers/mtd/nand/denali.h
deleted file mode 100644 (file)
index 9b797be..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/* 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/denali_dt.c b/drivers/mtd/nand/denali_dt.c
deleted file mode 100644 (file)
index 65a7797..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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/denali_spl.c b/drivers/mtd/nand/denali_spl.c
deleted file mode 100644 (file)
index dbaba3c..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-// 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/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
deleted file mode 100644 (file)
index 263d46e..0000000
+++ /dev/null
@@ -1,810 +0,0 @@
-// 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/fsl_elbc_spl.c b/drivers/mtd/nand/fsl_elbc_spl.c
deleted file mode 100644 (file)
index 30c3308..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
deleted file mode 100644 (file)
index 29f30d8..0000000
+++ /dev/null
@@ -1,1064 +0,0 @@
-// 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/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c
deleted file mode 100644 (file)
index 7137eb4..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-// 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/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
deleted file mode 100644 (file)
index dfbdbca..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-// 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/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
deleted file mode 100644 (file)
index 1f4c74f..0000000
+++ /dev/null
@@ -1,518 +0,0 @@
-// 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/kb9202_nand.c b/drivers/mtd/nand/kb9202_nand.c
deleted file mode 100644 (file)
index 0f68f1c..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-// 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/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c
deleted file mode 100644 (file)
index 0757fa8..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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/kmeter1_nand.c b/drivers/mtd/nand/kmeter1_nand.c
deleted file mode 100644 (file)
index 7103300..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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/lpc32xx_nand_mlc.c b/drivers/mtd/nand/lpc32xx_nand_mlc.c
deleted file mode 100644 (file)
index 5d4ffea..0000000
+++ /dev/null
@@ -1,761 +0,0 @@
-// 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/lpc32xx_nand_slc.c b/drivers/mtd/nand/lpc32xx_nand_slc.c
deleted file mode 100644 (file)
index 99f6e15..0000000
+++ /dev/null
@@ -1,597 +0,0 @@
-// 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/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
deleted file mode 100644 (file)
index cf97e0f..0000000
+++ /dev/null
@@ -1,1307 +0,0 @@
-// 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/mxc_nand.h b/drivers/mtd/nand/mxc_nand.h
deleted file mode 100644 (file)
index 1c7f3a2..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/* 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/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c
deleted file mode 100644 (file)
index 6c03db8..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-// 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/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
deleted file mode 100644 (file)
index e334181..0000000
+++ /dev/null
@@ -1,1302 +0,0 @@
-// 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/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h
deleted file mode 100644 (file)
index 4bd65cd..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/* 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/mxs_nand_dt.c b/drivers/mtd/nand/mxs_nand_dt.c
deleted file mode 100644 (file)
index 44dec5d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c
deleted file mode 100644 (file)
index 2d7bbe8..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-// 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/nand.c b/drivers/mtd/nand/nand.c
deleted file mode 100644 (file)
index bca51ff..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-// 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/nand_base.c b/drivers/mtd/nand/nand_base.c
deleted file mode 100644 (file)
index 92daebe..0000000
+++ /dev/null
@@ -1,4619 +0,0 @@
-/*
- *  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/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
deleted file mode 100644 (file)
index ba785c5..0000000
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- *  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/nand_bch.c b/drivers/mtd/nand/nand_bch.c
deleted file mode 100644 (file)
index afa0418..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-// 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/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
deleted file mode 100644 (file)
index 05e55fc..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-// 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/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/nand_ids.c b/drivers/mtd/nand/nand_ids.c
deleted file mode 100644 (file)
index 4009d64..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- *  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/nand_plat.c b/drivers/mtd/nand/nand_plat.c
deleted file mode 100644 (file)
index 335c3e3..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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/nand_spl_load.c b/drivers/mtd/nand/nand_spl_load.c
deleted file mode 100644 (file)
index ecd373e..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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/nand_spl_loaders.c b/drivers/mtd/nand/nand_spl_loaders.c
deleted file mode 100644 (file)
index 177c12b..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-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/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c
deleted file mode 100644 (file)
index 09e0535..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-// 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/nand_timings.c b/drivers/mtd/nand/nand_timings.c
deleted file mode 100644 (file)
index c0545a4..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- *  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/nand_util.c b/drivers/mtd/nand/nand_util.c
deleted file mode 100644 (file)
index 1ded7aa..0000000
+++ /dev/null
@@ -1,904 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/mtd/nand/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/omap_elm.c b/drivers/mtd/nand/omap_elm.c
deleted file mode 100644 (file)
index 35c6dd1..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-// 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/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
deleted file mode 100644 (file)
index 6a05050..0000000
+++ /dev/null
@@ -1,1037 +0,0 @@
-// 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/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
deleted file mode 100644 (file)
index 2a02a9d..0000000
+++ /dev/null
@@ -1,1828 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * drivers/mtd/nand/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/pxa3xx_nand.h b/drivers/mtd/nand/pxa3xx_nand.h
deleted file mode 100644 (file)
index 8f24ae6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#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/Kconfig b/drivers/mtd/nand/raw/Kconfig
new file mode 100644 (file)
index 0000000..1e4ea7b
--- /dev/null
@@ -0,0 +1,297 @@
+
+menuconfig NAND
+       bool "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 (file)
index 0000000..c61e3f3
--- /dev/null
@@ -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 (file)
index 0000000..ba2f33a
--- /dev/null
@@ -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 (file)
index 0000000..41db9f8
--- /dev/null
@@ -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 (file)
index 0000000..a5b76e1
--- /dev/null
@@ -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 (file)
index 0000000..05eeedb
--- /dev/null
@@ -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 (file)
index 0000000..e6a84a5
--- /dev/null
@@ -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 (file)
index 0000000..d1cac06
--- /dev/null
@@ -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 (file)
index 0000000..9b797be
--- /dev/null
@@ -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 (file)
index 0000000..65a7797
--- /dev/null
@@ -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 (file)
index 0000000..dbaba3c
--- /dev/null
@@ -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 (file)
index 0000000..263d46e
--- /dev/null
@@ -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 (file)
index 0000000..30c3308
--- /dev/null
@@ -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 (file)
index 0000000..29f30d8
--- /dev/null
@@ -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 (file)
index 0000000..7137eb4
--- /dev/null
@@ -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 (file)
index 0000000..dfbdbca
--- /dev/null
@@ -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 (file)
index 0000000..1f4c74f
--- /dev/null
@@ -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 (file)
index 0000000..0f68f1c
--- /dev/null
@@ -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 (file)
index 0000000..0757fa8
--- /dev/null
@@ -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 (file)
index 0000000..7103300
--- /dev/null
@@ -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 (file)
index 0000000..5d4ffea
--- /dev/null
@@ -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 (file)
index 0000000..99f6e15
--- /dev/null
@@ -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 (file)
index 0000000..cf97e0f
--- /dev/null
@@ -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 (file)
index 0000000..1c7f3a2
--- /dev/null
@@ -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 (file)
index 0000000..6c03db8
--- /dev/null
@@ -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 (file)
index 0000000..e334181
--- /dev/null
@@ -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 (file)
index 0000000..4bd65cd
--- /dev/null
@@ -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 (file)
index 0000000..44dec5d
--- /dev/null
@@ -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 (file)
index 0000000..2d7bbe8
--- /dev/null
@@ -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 (file)
index 0000000..bca51ff
--- /dev/null
@@ -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 (file)
index 0000000..92daebe
--- /dev/null
@@ -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 (file)
index 0000000..ba785c5
--- /dev/null
@@ -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 (file)
index 0000000..afa0418
--- /dev/null
@@ -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 (file)
index 0000000..2bc329b
--- /dev/null
@@ -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 (file)
index 0000000..4009d64
--- /dev/null
@@ -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 (file)
index 0000000..335c3e3
--- /dev/null
@@ -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 (file)
index 0000000..ecd373e
--- /dev/null
@@ -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 (file)
index 0000000..177c12b
--- /dev/null
@@ -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 (file)
index 0000000..09e0535
--- /dev/null
@@ -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 (file)
index 0000000..c0545a4
--- /dev/null
@@ -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 (file)
index 0000000..fc2235c
--- /dev/null
@@ -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 (file)
index 0000000..35c6dd1
--- /dev/null
@@ -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 (file)
index 0000000..6a05050
--- /dev/null
@@ -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 (file)
index 0000000..4c783f1
--- /dev/null
@@ -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 (file)
index 0000000..8f24ae6
--- /dev/null
@@ -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 (file)
index 0000000..3ccb168
--- /dev/null
@@ -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 (file)
index 0000000..6cde981
--- /dev/null
@@ -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 (file)
index 0000000..74acdfb
--- /dev/null
@@ -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 (file)
index 0000000..7740160
--- /dev/null
@@ -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 (file)
index 0000000..619d040
--- /dev/null
@@ -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 (file)
index 0000000..e932a58
--- /dev/null
@@ -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");
+}
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
deleted file mode 100644 (file)
index 3ccb168..0000000
+++ /dev/null
@@ -1,1850 +0,0 @@
-// 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/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c
deleted file mode 100644 (file)
index 6cde981..0000000
+++ /dev/null
@@ -1,548 +0,0 @@
-// 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/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c
deleted file mode 100644 (file)
index 74acdfb..0000000
+++ /dev/null
@@ -1,1002 +0,0 @@
-// 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/tegra_nand.h b/drivers/mtd/nand/tegra_nand.h
deleted file mode 100644 (file)
index 7740160..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/* 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/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c
deleted file mode 100644 (file)
index 619d040..0000000
+++ /dev/null
@@ -1,768 +0,0 @@
-// 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/zynq_nand.c b/drivers/mtd/nand/zynq_nand.c
deleted file mode 100644 (file)
index e932a58..0000000
+++ /dev/null
@@ -1,1254 +0,0 @@
-// 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");
-}
index 884dd4125e55a481fe66132eb6c156bd268ac32d..5b5c38f2c9faaf4efe2b4380965691a7c18a080c 100644 (file)
                                /* LB refresh timer prescal, 266MHz/32 */
 #define CONFIG_SYS_LBC_MRTPR   0x20000000  /*TODO */
 
-/* drivers/mtd/nand/nand.c */
+/* drivers/mtd/nand/raw/nand.c */
 #if defined(CONFIG_NAND) && defined(CONFIG_SPL_BUILD)
 #define CONFIG_SYS_NAND_BASE           0xFFF00000
 #else