From: Ricard Wanderlof <rica...@axis.com>

This is a request for comments for a driver for the Evatronix NANDFLASH-CTRL
IP (version 1.15). It is designed to be applied to Linux 3.16 or the current
linux-mtd tree.

Currently, it supports one instance of the NANDFLASH-CTRL IP, with up
to two connected NAND flash chips, one per bank. (The IP itself is very
configurable, but the intent has been to get something working for a
specific configuration.)

Signed-off-by: Ricard Wanderlof <rica...@axis.com>
---
I haven't had actual hardware to test this on, only a software
model, which is why the code is sprinkled with TODOs. I still felt it was worth
posting it here for comments.

The driver has been tested by running a number of UBI operations on a
simulated flash connected to the driver in a software model: ubiattach,
ubimkvol, mount volume, write files, read files, umount volume, as well as
some different nanddump and nandwrite invokations.

 .../devicetree/bindings/mtd/evatronix-nand.txt     |   29 +
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 drivers/mtd/nand/Kconfig                           |   17 +
 drivers/mtd/nand/Makefile                          |    1 +
 drivers/mtd/nand/evatronix_nand.c                  | 1358 ++++++++++++++++++++
 drivers/mtd/nand/evatronix_nand.h                  |  401 ++++++
 6 files changed, 1807 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/evatronix-nand.txt
 create mode 100644 drivers/mtd/nand/evatronix_nand.c
 create mode 100644 drivers/mtd/nand/evatronix_nand.h

diff --git a/Documentation/devicetree/bindings/mtd/evatronix-nand.txt 
b/Documentation/devicetree/bindings/mtd/evatronix-nand.txt
new file mode 100644
index 0000000..d7d017b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/evatronix-nand.txt
@@ -0,0 +1,29 @@
+Evatronix NANDFLASH-CTRL NAND flash controller
+
+Required properties:
+- compatible : "evatronix,nandflash-ctrl"
+- reg : specify bus address and register area size.
+- interrupts : controller interrupt number and irq type.
+- nand-ecc-mode : See nand.txt. Supported values "hw", "soft_bch".
+- nand-ecc-strength : See nand.txt. Supported values: 4, 8, 16, 24, 32.
+- nand-ecc-step-size : See nand.txt. Supported values: 256, 512, 1024.
+
+Optional properties:
+- nand-on-flash-bbt: See nand.txt.
+
+Example:
+
+nand: nand@f801e000 {
+       compatible = "evatronix,nandflash-ctrl";
+       reg = <0xf801e000 0x0200>;
+       interrupts = <0 139 IRQ_TYPE_LEVEL_HIGH>;
+       /* ONFi mode 0 timing. */
+       /* Order is TIME_SEQ_0, TIME_SEQ_1, TIMINGS_ASYN,
+        * TIME_GEN_SEQ_0, _1, _2, _3 */
+       timings = <0x0d151533 0x000b0515 0x00000046
+                  0x00150000 0x00000000 0x00000005 0x00000015>;
+       nand-ecc-mode = "hw";
+       nand-on-flash-bbt;
+       nand-ecc-strength = <8>;
+       nand-ecc-step-size = <512>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt 
b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 46a311e..49d66b3 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -46,6 +46,7 @@ epfl  Ecole Polytechnique Fédérale de Lausanne
 epson  Seiko Epson Corp.
 est    ESTeem Wireless Modems
 eukrea  Eukréa Electromatique
+evatronix Evatronix SA
 excito Excito
 fsl    Freescale Semiconductor
 GEFanuc        GE Fanuc Intelligent Platforms Embedded Systems, Inc.
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1cf503..6510aa2 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -74,6 +74,23 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
           scratch register here to enable this feature. On Intel Moorestown
           boards, the scratch register is at 0xFF108018.
 
+config MTD_NAND_EVATRONIX
+       bool "Enable Evatronix NANDFLASH-CTRL driver"
+       help
+         NAND hardware driver for Evatronix NANDFLASH-CTRL
+         NAND flash controller.
+
+config MTD_NAND_EVATRONIX_CHIPS
+       int "Number of NAND chips probed by Evatronix NAND flash driver"
+       default 1
+       range 1 2
+       depends on MTD_NAND_EVATRONIX
+       help
+         Number of NAND chips that should be supported by Evatronix NAND flash
+         driver. If multiple chips are detected, they will be concatenated
+         and presented as one large NAND device.
+         Currently a maximum of two chips are supported.
+
 config MTD_NAND_GPIO
        tristate "GPIO NAND Flash driver"
        depends on GPIOLIB
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 542b568..c5bb52c 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_S3C2410)                += s3c2410.o
 obj-$(CONFIG_MTD_NAND_DAVINCI)         += davinci_nand.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)      += diskonchip.o
 obj-$(CONFIG_MTD_NAND_DOCG4)           += docg4.o
+obj-$(CONFIG_MTD_NAND_EVATRONIX)       += evatronix_nand.o
 obj-$(CONFIG_MTD_NAND_FSMC)            += fsmc_nand.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)         += sharpsl.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)         += nandsim.o
diff --git a/drivers/mtd/nand/evatronix_nand.c 
b/drivers/mtd/nand/evatronix_nand.c
new file mode 100644
index 0000000..d87d13f
--- /dev/null
+++ b/drivers/mtd/nand/evatronix_nand.c
@@ -0,0 +1,1358 @@
+/*
+ * evatronix_nand.c - NAND Flash Driver for Evatronix NANDFLASH-CTRL
+ * NAND Flash Controller IP.
+ *
+ * Intended to handle one NFC, with up to two connected NAND flash chips,
+ * one per bank.
+ *
+ * This implementation has been designed against Rev 1.15 of the
+ * NANDFLASH-CTRL Design Specification.
+ *
+ * Copyright (c) 2014 Axis Communication AB, Lund, Sweden.
+ * Portions Copyright (c) 2010 ST Microelectronics
+ *
+ * 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 <asm/dma.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_mtd.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/concat.h>
+#include <linux/mtd/partitions.h>
+#include <linux/version.h>
+
+#include "evatronix_nand.h"
+
+/* Driver configuration */
+
+#define ETX_CE_BANK_SEL /* Separate chips connected as separate banks */
+#define ETX_RB_WIRED_AND /* Use wired-AND for ready/busy from flash chip */
+
+#undef POLLED_XFERS
+
+/* Workarounds for development environment */
+
+#define WORKAROUND_NO_ECC_CNT
+
+/* Debugging */
+
+#if 0
+#define MTD_TRACE(FORMAT, ...) \
+       pr_info("mtd trace: %s: " FORMAT, __func__, ## __VA_ARGS__)
+#else
+#define MTD_TRACE(FORMAT, ...) do { } while (0)
+#endif
+
+#ifndef CONFIG_MTD_NAND_EVATRONIX_CHIPS
+#define CONFIG_MTD_NAND_EVATRONIX_CHIPS 1
+#endif
+
+/* Read modes */
+enum etx_read_mode {
+       ETX_READ_STD, /* Standard page read with ECC */
+       ETX_READ_RAW, /* Raw mode read of main area without ECC */
+       ETX_READ_OOB, /* Read oob only (no ECC) */
+       ETX_READ_ALL  /* Read main+oob in raw mode (no ECC) */
+};
+
+#define DMA_BUF_SIZE (8192 + 640) /* main + spare for 8k page flash */
+
+/* # bytes into the OOB we put our ECC */
+#define ECC_OFFSET 2
+
+/* Timing parameters, from dt */
+struct etx_timings {
+       uint32_t time_seq_0;
+       uint32_t time_seq_1;
+       uint32_t timings_asyn;
+       uint32_t time_gen_seq_0;
+       uint32_t time_gen_seq_1;
+       uint32_t time_gen_seq_2;
+       uint32_t time_gen_seq_3;
+};
+
+/* Configuration, from dt */
+struct etx_setup {
+       nand_ecc_modes_t ecc_mode;
+       int ecc_blksize;
+       int ecc_strength;
+       bool on_flash_bbt;
+       struct etx_timings timings;
+};
+
+/* DMA buffer, from both software (buf) and hardware (phys) perspective. */
+struct etx_dma {
+       void *buf; /* mapped address */
+       dma_addr_t phys; /* physical address */
+       int bytes_left; /* how much data left to read from buffer? */
+       uint8_t *ptr; /* work pointer */
+};
+
+#ifndef POLLED_XFERS
+/* Interrupt management */
+struct etx_irq {
+       int done; /* interrupt triggered, consequently we're done. */
+       uint32_t int_status; /* INT_STATUS at time of interrupt */
+       wait_queue_head_t wq; /* For waiting on controller interrupt */
+};
+#endif
+
+/* Information common to all chips, including the NANDFLASH-CTRL IP */
+struct etx_info {
+       unsigned char __iomem *regbase;
+       struct device *dev;
+       struct nand_hw_control *controller;
+       spinlock_t lock;
+       struct etx_setup *setup;
+       struct etx_dma dma;
+#ifndef POLLED_XFERS
+       struct etx_irq irq;
+#endif
+};
+
+/* Per-chip controller configuration */
+struct etx_config {
+       uint32_t mem_ctrl;
+       uint32_t control;
+       uint32_t ecc_ctrl;
+       uint32_t ecc_offset;
+       uint32_t mem_status_mask;
+};
+
+/* Cache for info that we need to save across calls to etx_nand_command */
+struct etx_cmd_cache {
+       unsigned int command;
+       int page;
+       int column;
+       int oob_required;
+       int write_raw;
+};
+
+/* Information for each physical NAND chip. */
+struct chip_info {
+       struct mtd_info mtd;
+       struct nand_chip chip;
+       struct etx_cmd_cache cmd_cache;
+       struct etx_config etx_config;
+};
+
+/* What we tell mtd is an mtd_info actually is a complete chip_info */
+#define TO_CHIP_INFO(mtd) ((struct chip_info *)(mtd))
+
+/* This is a global pointer, as we only support one single instance of the NFC.
+ * For multiple instances, we would need to add etx_info as a parameter to
+ * several functions, as well as adding it as a member of the chip_info struct.
+ * Since most likely a system would only have one NFC instance, we don't
+ * go all the way implementing that feature now. */
+static struct etx_info *etx_info;
+
+/* The timing setup is expected to come via DT. We keep some default timings
+ * here for reference, based on a 100 MHz reference clock. */
+
+static const struct etx_timings default_mode0_pll_enabled = {
+       0x0d151533, 0x000b0515, 0x00000046,
+       0x00150000, 0x00000000, 0x00000005, 0x00000015 };
+
+/**** Utility routines. */
+
+/* Count the number of 0's in buff upto a max of max_bits */
+/* Used to determine how many bit flips there are in an allegely erased block 
*/
+static int count_zero_bits(uint8_t *buff, int size, int max_bits)
+{
+       int k, zero_bits = 0;
+
+       for (k = 0; k < size; k++) {
+               zero_bits += hweight8(~buff[k]);
+               if (zero_bits > max_bits)
+                       break;
+       }
+
+       return zero_bits;
+}
+
+/**** Low level stuff. Read and write registers, interrupt routine, etc. */
+
+/* Read and write NFC SFR registers */
+
+static uint32_t etx_read(uint reg_offset)
+{
+       return ioread32(etx_info->regbase + reg_offset);
+}
+
+static void etx_write(uint32_t data, uint reg_offset)
+{
+       /* Note: According to NANDFLASH-CTRL Design Specification, rev 1.14,
+        * p19, the NFC SFR's can only be written when STATUS.CTRL_STAT is 0.
+        * So, should really check for that here. */
+       iowrite32(data, etx_info->regbase  + reg_offset);
+}
+
+#ifndef POLLED_XFERS
+static irqreturn_t etx_irq(int irq, void *device_info)
+{
+       /* Note that device_info = etx_info, so if we don't want a global
+        * etx_info we can get it via device_info. */
+
+       /* Save interrupt status in case caller wants to check what actually
+        * happened. */
+       etx_info->irq.int_status = etx_read(INT_STATUS_REG);
+
+       MTD_TRACE("Got interrupt %d, INT_STATUS 0x%08x\n",
+                 irq, etx_info->irq.int_status);
+
+       /* Note: We can't (at least in the software model) clear the interrupts
+        * by clearing CONTROL.INT_EN, as that does not disable the interrupt
+        * output port from the nfc towards the gic. */
+       etx_write(0, INT_STATUS_REG);
+
+       etx_info->irq.done = 1;
+       wake_up(&etx_info->irq.wq);
+
+       return IRQ_HANDLED;
+}
+#endif
+
+/* Get resources from platform: register bank mapping, irqs, etc */
+static int etx_init_resources(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *resource;
+#ifndef POLLED_XFERS
+       int irq;
+#endif
+       int res;
+
+       /* Register base for controller, ultimately from device tree */
+       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!resource) {
+               dev_err(dev, "No register addresses configured!\n");
+               return -ENOMEM;
+       }
+       etx_info->regbase = devm_ioremap_resource(dev, resource);
+       if (IS_ERR(etx_info->regbase))
+               return PTR_ERR(etx_info->regbase);
+
+       dev_info(dev, "Got SFRs at phys %p..%p, mapped to %p\n",
+                (void *)resource->start, (void *)resource->end,
+                etx_info->regbase);
+
+       /* A DMA buffer */
+       etx_info->dma.buf =
+               dma_alloc_coherent(dev, DMA_BUF_SIZE,
+                                  &etx_info->dma.phys, GFP_KERNEL);
+       if (etx_info->dma.buf == NULL) {
+               dev_err(dev, "dma_alloc_coherent failed!\n");
+               return -ENOMEM;
+       }
+
+       dev_info(dev, "DMA buffer %p at physical %p\n",
+                etx_info->dma.buf, (void *)etx_info->dma.phys);
+
+#ifndef POLLED_XFERS
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "No irq configured\n");
+               return irq;
+       }
+       res = request_irq(irq, etx_irq, 0, "evatronix-nand", etx_info);
+       if (res < 0) {
+               dev_err(dev, "request_irq failed\n");
+               return res;
+       }
+       dev_info(dev, "Successfully registered IRQ %d\n", irq);
+#endif
+
+       return 0;
+}
+
+/* Write timing setup to controller */
+static void setup_etx_timing(struct etx_setup *etx_setup)
+{
+       etx_write(etx_setup->timings.time_seq_0, TIME_SEQ_0_REG);
+       etx_write(etx_setup->timings.time_seq_1, TIME_SEQ_1_REG);
+       etx_write(etx_setup->timings.timings_asyn, TIMINGS_ASYN_REG);
+       etx_write(etx_setup->timings.time_gen_seq_0, TIME_GEN_SEQ_0_REG);
+       etx_write(etx_setup->timings.time_gen_seq_1, TIME_GEN_SEQ_1_REG);
+       etx_write(etx_setup->timings.time_gen_seq_2, TIME_GEN_SEQ_2_REG);
+       etx_write(etx_setup->timings.time_gen_seq_3, TIME_GEN_SEQ_3_REG);
+}
+
+/* Write per-chip specific config to controller */
+static void config_etx(struct etx_config *etx_config, void *ref)
+{
+       static void *saved_ref;
+
+       /* To avoid rewriting these unnecessarily every time, we only do
+        * it when the ref has changed, or if ref == NULL (=> force). */
+       if (ref) {
+               if (ref == saved_ref)
+                       return;
+               saved_ref = ref; /* only save if non-null */
+       }
+
+       etx_write(etx_config->mem_ctrl, MEM_CTRL_REG);
+       etx_write(etx_config->control, CONTROL_REG);
+       etx_write(etx_config->ecc_ctrl, ECC_CTRL_REG);
+       etx_write(etx_config->ecc_offset, ECC_OFFSET_REG);
+}
+
+
+#ifndef POLLED_XFERS
+/* Set up interrupt and wq, with supplied interrupt mask */
+static void setup_int(uint32_t what)
+{
+       /* Flag waited on by wq */
+       etx_info->irq.done = 0;
+
+       /* clear interrupt status bits */
+       etx_write(0, INT_STATUS_REG);
+
+       /* set interrupt mask */
+       etx_write(what, INT_MASK_REG);
+
+       /* enable global NFC interrupt. Ooooh... */
+       etx_write(etx_read(CONTROL_REG) | CONTROL_INT_EN, CONTROL_REG);
+}
+#endif
+
+/* Set up interrupt, send command, then wait for (any bit of) expected state */
+/* Before issuing a command, we should check if the controller is ready.
+ * We can't check INT_STATUS_REG.MEM0_RDY_INT_FL as it is not a status bit,
+ * it is set on an nfc state transition after the completion of for
+ * instance a page program command, so we can use it as a command
+ * completed trigger however.
+ * (See NFC Design Spec (rev 1.15) figure 35 for illustration.)
+ * TODO: However, we could check STATUS.CTRL_STAT, which should always
+ * be 0 prior to issuing a command, indicating the controller is not
+ * busy. */
+static void command_and_wait(uint32_t etx_command, uint32_t int_state)
+#ifndef POLLED_XFERS
+{
+       long timeout;
+
+       /* Set up interrupt condition. Here we utilize the fact that the
+        * bits in INT_STATE are the same as in INT_MASK. */
+       setup_int(int_state);
+
+       /* Send command */
+       etx_write(etx_command, COMMAND_REG);
+
+       /* The timeout should only trigger in abnormal situations, so
+        * we leave it at one second for now. (nand_base uses 20ms for write
+        * and 400ms for erase, respectively.) */
+       /* TODO: A special case might be to consider the case of an
+        * unconnected flash chip during probe. If that causes the timeout
+        * to be triggered, we might want to lower it, and even make it
+        * dependent on the NAND flash command being executed. */
+       timeout = wait_event_timeout(etx_info->irq.wq, etx_info->irq.done,
+                                    1 * HZ);
+       if (timeout <= 0) {
+               dev_info(etx_info->dev,
+                        "Request 0x%08x timed out waiting for 0x%08x\n",
+                        etx_command, int_state);
+               /* TODO: Do something useful here? */
+       }
+}
+#else /* POLLED_XFERS */
+{
+       int cmd_loops = 0;
+       uint32_t read_status, read_int_status, dma_status;
+
+       /* Clear interrupt status bits */
+       etx_write(0, INT_STATUS_REG);
+
+       /* Send command */
+       etx_write(etx_command, COMMAND_REG);
+
+       /* Wait for command to complete */
+       MTD_TRACE("Waiting for 0x%08x bit(s) to be set in int_status\n",
+                 int_state);
+
+#define MAX_CMD_LOOPS 100000
+       do {
+               cmd_loops++;
+               read_status = etx_read(STATUS_REG);
+               read_int_status = etx_read(INT_STATUS_REG);
+               dma_status = etx_read(DMA_CTRL_REG);
+               MTD_TRACE("Wait for command done: 0x%08x/0x%08x/0x%08x (%d)\n",
+                         read_status, read_int_status, dma_status, cmd_loops);
+       } while (!(read_int_status & int_state) && cmd_loops < MAX_CMD_LOOPS);
+
+       if (cmd_loops >= MAX_CMD_LOOPS)
+               MTD_TRACE("Int wait for 0x%08x timed out after %d loops: "
+                         "STATUS = 0x%08x, INT_STATUS=0x%08x, "
+                         "DMA_CTRL = 0x%08x, command 0x%08x\n",
+                         cmd_loops, int_state, read_status, read_int_status,
+                         dma_status, etx_command);
+}
+#endif
+
+/* Initialize DMA, wq and interrupt status for upcoming transfer. */
+static void init_dma(uint64_t addr, int bytes)
+{
+       int dma_trig_level;
+
+       /* DMA control */
+
+       /* Start when COMMAND register written, set burst type/size */
+       etx_write(DMA_CTRL_DMA_START | DMA_CTRL_DMA_BURST_I_P_4, DMA_CTRL_REG);
+
+       /* DMA address and length */
+#ifdef EVATRONIX_DMA64BIT
+       /* The manual says this register does not 'occur' (sic) unless
+        * 64 bit DMA support is included. */
+       etx_write(addr >> 32, DMA_ADDR_H_REG);
+#endif
+       etx_write(addr, DMA_ADDR_L_REG);
+
+       /* Byte counter */
+       /* Round up to nearest 32-bit word */
+       etx_write((bytes + 3) & 0xfffffffc, DMA_CNT_REG);
+
+       /* Cap DMA trigger level at FIFO size */
+       dma_trig_level = bytes * 8 / 32; /* 32-bit entities */
+       if (dma_trig_level > DMA_TLVL_MAX)
+               dma_trig_level = DMA_TLVL_MAX;
+       etx_write(dma_trig_level, DMA_TLVL_REG);
+}
+
+/* Initialize transfer to or from DMA buffer */
+static void init_dmabuf(int bytes)
+{
+       etx_info->dma.ptr = etx_info->dma.buf;
+       etx_info->dma.bytes_left = bytes;
+}
+
+/* Initialize controller for DATA_REG readout */
+static void init_dreg_read(int bytes)
+{
+       /* Transfer to DATA_REG register */
+       etx_write(DATA_REG_SIZE_DATA_REG_SIZE(bytes), DATA_REG_SIZE_REG);
+}
+
+/* Set up CONTROL depending on whether we want ECC or not */
+static void setup_control(int enable_ecc)
+{
+       uint32_t control;
+
+       /* When reading the oob, we never want ECC, when reading the
+        * main area, it depends. */
+       control = etx_read(CONTROL_REG) & ~CONTROL_ECC_EN;
+       if (enable_ecc)
+               control |= CONTROL_ECC_EN;
+       etx_write(control, CONTROL_REG);
+}
+
+/* Read from flash using DMA */
+/* Assumes basic setup for DMA has been done previously. */
+/* The MTD framework never reads a complete page (main + oob) in one go
+ * when using HW ECC, so we don't need to support ETX_READ_ALL in this mode.
+ * For SW ECC we read the whole page on one go in ALL mode however. */
+static void read_dma(struct chip_info *info, int page, int column,
+                    enum etx_read_mode m)
+{
+       int size;
+       uint32_t command;
+
+       switch (m) {
+       case ETX_READ_OOB:
+               size = info->mtd.oobsize;
+               break;
+       case ETX_READ_ALL:
+               size = info->mtd.oobsize + info->mtd.writesize;
+               break;
+       case ETX_READ_STD:
+       case ETX_READ_RAW:
+               size = info->mtd.writesize;
+               break;
+       default:
+               BUG();
+       }
+
+       /* Set up ECC depending on mode */
+       setup_control(m == ETX_READ_STD);
+
+       /* Set up DMA and transfer size */
+
+       init_dmabuf(size);
+       init_dma(etx_info->dma.phys, size);
+       etx_write(size, DATA_SIZE_REG);
+
+       /* Set up addresses */
+
+       if (m == ETX_READ_OOB)
+               column += info->mtd.writesize;
+       etx_write(column, ADDR0_COL_REG);
+       etx_write(page, ADDR0_ROW_REG);
+
+       /* For devices > 128 MiB we have 5 address cycles and can use a
+        * standard NFC command sequence. For smaller devices we have
+        * 4 address cycles and need to use a Generic Command Sequence. */
+       if (info->chip.chipsize > (128 << 20)) {
+               command = COMMAND_READ_PAGE_DMA_STD;
+       } else {
+               etx_write(GEN_SEQ_CTRL_READ_PAGE_4CYCLE, GEN_SEQ_CTRL_REG);
+               command = COMMAND_READ_PAGE_DMA_GEN;
+       }
+
+       command_and_wait(command, INT_STATUS_DMA_INT_FL);
+}
+
+/* Write using DMA */
+/* Assumes DMA has been set up previously and buffer contains data. */
+/* Contrary to read, column is set to writesize when writing to oob, by mtd.
+ * oob is set when the caller wants to write oob data along with the main data.
+ */
+static void write_dma(struct chip_info *info, int page, int column,
+               int oob, int raw)
+{
+       int size;
+       uint32_t command;
+
+       /* Since the controller handles ECC on its own, raw mode doesn't
+        * come into the size calculations. */
+       if (column >= info->mtd.writesize) { /* oob write only */
+               size = info->mtd.oobsize;
+               raw = 1;
+       } else {
+               size = info->mtd.writesize;
+               if (oob) {
+                       size += info->mtd.oobsize;
+                       raw = 1;
+               }
+       }
+
+       setup_control(!raw);
+
+       /* Dump selected parts of buffer */
+       MTD_TRACE("Write %d bytes: 0x%08x 0x%08x .. 0x%08x\n", size,
+                 ((uint32_t *)(etx_info->dma.buf))[0],
+                 ((uint32_t *)(etx_info->dma.buf))[1],
+                 ((uint32_t *)(etx_info->dma.buf))[size / 4 - 1]);
+
+       /* Set up DMA and transfer size */
+       init_dma(etx_info->dma.phys, size);
+       etx_write(size, DATA_SIZE_REG);
+
+       /* Set up addresses */
+
+       etx_write(column, ADDR0_COL_REG);
+       etx_write(page, ADDR0_ROW_REG);
+
+
+       /* For devices > 128 MiB we have 5 address cycles and can use a
+        * standard NFC command sequence. For smaller devices we have
+        * 4 address cycles and need to use a Generic Command Sequence. */
+       if (info->chip.chipsize > (128 << 20)) {
+               command = COMMAND_WRITE_PAGE_DMA_STD;
+       } else {
+               etx_write(GEN_SEQ_CTRL_WRITE_PAGE_4CYCLE, GEN_SEQ_CTRL_REG);
+               command = COMMAND_WRITE_PAGE_DMA_GEN;
+       }
+
+       /* TODO: Use INT_STATUS_MEM0_RDY_INT_FL instead ? */
+       command_and_wait(command, INT_STATUS_DMA_INT_FL);
+
+       /* Wait for Ready from device */
+       /* TODO: ?needed ?how */
+       /* TODO: In the same way as for erase, we could check INT_STATUS_REG.
+        * STAT_ERR_INT0_FL, but nand_base will check the device by reading
+        * error status anyway after the write command. */
+
+       /* clear buffer so it doesn't contain the written data anymore */
+       /* TODO: remove this, just useful during development to verify
+        * that a subsequent read just doesn't read what happens to be
+        * lying around in the buffer. */
+       memset(etx_info->dma.buf, 0, DMA_BUF_SIZE);
+}
+
+/* Block erase */
+static void block_erase(int page)
+{
+       /* Set up addresses */
+       etx_write(page, ADDR0_ROW_REG);
+       MTD_TRACE("Erase block containing page %d\n", page);
+
+       /* Send 3 address cycle block erase command */
+       command_and_wait(COMMAND_BLOCK_ERASE, INT_STATUS_MEM0_RDY_INT_FL);
+
+#ifndef POLLED_XFERS
+       MTD_TRACE("Erase block: INT_STATUS 0x%08x\n", etx_info->irq.int_status);
+#endif
+       /* TODO: What to do if we get an error bit set here (i.e.INT_STATUS_REG.
+        * STAT_ERR_INT0_FL) ? Normally, error status is checked by nand_base
+        * by doing a status read after the erase command. So we can probably
+        * ignore STAT_ERR_INT0_FL here. If need be, we can save the
+        * status so a subsequent status right might use it for something.
+        * The err bit probably just indicates that the flash didn't pull
+        * R/_B low within tWB. */
+}
+
+/* Check for erased page.
+ * The prerequisite to calling this routine is: page has been read with
+ * HW ECC, which has returned an 'ecc uncorrectable' status, so either the
+ * page does in fact contain too many bitflips for the ECC algorithm to correct
+ * or the page is in fact erased, which results in the all-FF's ECC to
+ * be invalid relative to the all-FF's data on the page.
+ * Since with the Evatronix NFC we don't have access to either the ECC bytes
+ * or the oob area after a HW ECC read, the following algorithm is adopted:
+ * - Count the number of 0's in the main area. If there are more than
+ *   the ECC strength per ECC block we assume the page wasn't in fact erased,
+ *   and return with an error status.
+ * - If the main area appears erased, we still need to determine if the oob is
+ *   also erased, if not, it would appear that the page wasn't in fact erased,
+ *   and what we're looking at is a page of mostly-FF data with an invalid ECC.
+ *   - Thus we need to read the oob, leaving the main area at the start of the
+ *     DMA buffer in case someone actually wants to read the data later (e.g.
+ *     nanddump).
+ *   - We then count the number of non-zero bits in the oob. The accepted
+ *     number of zeros could be determined by figuring the the size ratio
+ *     of the oob compared to an ECC block. For instance, if the oob is 64
+ *     bytes, an ECC block 512 bytes, and the error correction capability
+ *     of 8 bits, then the accepted number of zeros for the oob to be
+ *     considered erased would be 64/512 * 8 = 1. Alternatively we could just
+ *     accept an error correction capability number of zeros.
+ *     If there are less than this threshold number of zero bits, the page
+ *     is considered erased. In this case we return an all-FF page to the user.
+ *     Otherwise, we consider ourselves to have an ECC error on our hands,
+ *     and we return the apropriate error status while at the same time leaving
+ *     original main area data in place, for potential scrutiny by a user space
+ *     application (e.g. nanddump).
+ * Caveat: It could be that there are some cases for which an almost-FF page
+ * yields an almost-FF ECC. If there are fewer than the error correction
+ * capability number of zero bits, we could conclude that such a page would
+ * be erased when in fact it actually contains data with too many bitflips.
+ * Experience will have to determine whether this can actually occur. From
+ * past experiences with ECC codes it seems unlikely that that trivial
+ * data will in fact result in a trivial ECC code. Even the fairly basic
+ * 1-bit error correction capability Hamming code does not on its own return
+ * an all-FF ECC for all-FF data.
+ *
+ * Function returns 1 if the page is in fact (considered) erased, 0 if not.
+ */
+static int check_erased_page(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+       struct nand_chip *chip = &info->chip;
+
+       /* We calculate the number of steps here rather than grabbing
+        * ecc.steps to handle the case of a subpage read where we
+        * haven't read a complete page. */
+       int eccsteps = len / chip->ecc.size;
+       int eccsize = chip->ecc.size;
+       int eccstrength = chip->ecc.strength;
+
+       int main_area_zeros = 0;
+
+       int step;
+       uint8_t *bufpos = buf;
+
+       MTD_TRACE("%s: %d byte page, ecc steps %d, size %d, strength %d\n",
+                 __func__, len, eccsteps, eccsize, eccstrength);
+
+       /* Check that main area appears erased. If not, return */
+
+       for (step = 0; step < eccsteps; step++) {
+               int zeros = count_zero_bits(bufpos, eccsize, eccstrength);
+
+               if (zeros > eccstrength)
+                       return 0;
+               bufpos += eccsize;
+               main_area_zeros += zeros;
+       }
+
+       /* Ok, main area seems erased. Read oob so we can check it too. */
+
+       /* Note that this will overwrite the DMA buffer with the oob data,
+        * which is ok since the main area data has already been copied
+        * to buf earlier. */
+       read_dma(info, info->cmd_cache.page, info->cmd_cache.column,
+                ETX_READ_OOB);
+
+       /* We go for the simple approach and accept eccstrength zero bits */
+       if (count_zero_bits(etx_info->dma.buf, mtd->oobsize, eccstrength) >
+           eccstrength)
+               return 0;
+
+       MTD_TRACE("%s: Page is erased.%s\n", __func__,
+                 main_area_zeros != 0 ? " Clearing main area to 0xff." : "");
+
+       if (main_area_zeros != 0)
+               memset(buf, 0xff, len);
+
+       return 1;
+}
+
+
+/**** MTD API ****/
+
+/* For cmd_ctrl (and possibly others) we need to do absolutely nothing, but the
+ * pointer is still required to point to a valid function. */
+static void etx_dummy_cmd_ctrl(struct mtd_info *mtd, int cmd,
+               unsigned int ctrl)
+{
+}
+
+/* Read state of ready pin */
+static int etx_dev_ready(struct mtd_info *mtd)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+       struct etx_config *etx_config = &info->etx_config;
+
+       MTD_TRACE("%p\n", mtd);
+
+       return !!(etx_read(STATUS_REG) & etx_config->mem_status_mask);
+
+}
+
+/* Read byte from DMA buffer */
+/* Not used directly, only via etx_read_byte */
+static uint8_t etx_read_dmabuf_byte(struct mtd_info *mtd)
+{
+       MTD_TRACE("%p\n", mtd);
+       if (etx_info->dma.bytes_left) {
+               etx_info->dma.bytes_left--;
+               return *etx_info->dma.ptr++;
+       } else
+               return 0; /* no data */
+}
+
+/* Read block of data from DMA buffer */
+static void etx_read_dmabuf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       MTD_TRACE("%p, buf %p, len %d\n", mtd, buf, len);
+       if (len > etx_info->dma.bytes_left) {
+               dev_crit(etx_info->dev,
+                        "Trying to read %d bytes with %d bytes remaining\n",
+                        len, etx_info->dma.bytes_left);
+               BUG();
+       }
+       memcpy(buf, etx_info->dma.ptr, len);
+       etx_info->dma.ptr += len;
+       etx_info->dma.bytes_left -= len;
+}
+
+/* Write block of data to DMA buffer */
+static void etx_write_dmabuf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       MTD_TRACE("%p, buf %p, len %d\n", mtd, buf, len);
+       /* TODO: Grab info pointer from mtd instead of using same always ? */
+       if (len > etx_info->dma.bytes_left) {
+               dev_crit(etx_info->dev,
+                        "Trying to write %d bytes with %d bytes remaining\n",
+                        len, etx_info->dma.bytes_left);
+               BUG();
+       }
+       memcpy(etx_info->dma.ptr, buf, len);
+       etx_info->dma.ptr += len;
+       etx_info->dma.bytes_left -= len;
+}
+
+/* Read byte from DMA buffer or DATA_REG, depending on previous command. */
+/* Used by MTD for reading ID bytes, and chip status */
+static uint8_t etx_read_byte(struct mtd_info *mtd)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+       uint8_t status_value;
+
+       if (info->cmd_cache.command != NAND_CMD_STATUS)
+               return etx_read_dmabuf_byte(mtd);
+
+       MTD_TRACE("Read status\n");
+
+       /* In order to read status, we need to send a READ_STATUS command
+        * to the NFC first, in order to get the data into the DATA_REG */
+       init_dreg_read(1);
+       /* We want to read all status bits from the device */
+       etx_write(STATUS_MASK_STATE_MASK(0xff), STATUS_MASK_REG);
+       command_and_wait(COMMAND_READ_STATUS, INT_STATUS_DATA_REG_FL);
+       status_value = etx_read(DATA_REG_REG) & 0xff;
+       status_value = 0xC0; /* Bit 7 : No write prot., Bit 6: Device ready */
+       MTD_TRACE("Status 0x%08x\n", status_value);
+       return status_value;
+}
+
+/* Do the dirty work for read_page_foo */
+static int etx_read_page_mode(struct mtd_info *mtd, struct nand_chip *chip,
+               uint8_t *buf, int oob_required, int page, enum etx_read_mode m)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+       unsigned int max_bitflips;
+       uint32_t ecc_status;
+
+       if (page != info->cmd_cache.page) {
+               MTD_TRACE("Warning: Read page has different page number than "
+                         "READ0: %d vs. %d\n", page, info->cmd_cache.page);
+       }
+
+       if (m == ETX_READ_STD) {
+               /* ECC error flags and counters are not cleared automatically
+                * so we do it here. */
+               /* Note that the design spec says nothing about having to
+                * zero ECC_STAT (although it explicitly says that ECC_CNT
+                * needs to be zeroed by software), but testing on actual
+                * hardware (RTL at this stage) reveals that this is in fact
+                * the case. */
+               etx_write(0, ECC_STAT_REG);
+               etx_write(0, ECC_CNT_REG);
+       }
+
+       read_dma(info, info->cmd_cache.page, info->cmd_cache.column, m);
+
+       /* This is actually etx_read_dmabuf */
+       chip->read_buf(mtd, buf, mtd->writesize);
+
+       if (m == ETX_READ_RAW)
+               return 0;
+
+       /* Get ECC status from controller */
+       ecc_status = etx_read(ECC_STAT_REG);
+       max_bitflips = etx_read(ECC_CNT_REG) & ECC_CNT_ERR_LVL_MASK;
+
+#ifdef WORKAROUND_NO_ECC_CNT
+       /* If we get an ERROR bit set, but ECC_CNT is 0, we assume
+        * a single bit flip has occurred for want of better information. */
+       if ((ecc_status & ECC_STAT_ERROR_0) && max_bitflips == 0)
+               max_bitflips = 1;
+#endif
+
+       if (ecc_status & ECC_STAT_UNC_0)
+               if (!check_erased_page(mtd, buf, mtd->writesize))
+                       mtd->ecc_stats.failed++;
+
+       /* The following is actually not really correct, as the stats should
+        * reflect _all_ bitflips, not just the largest one in the latest read.
+        * We could rectify this by reading chip->ecc.bytes at a time,
+        * and accumulating the statistics per read, but at least for now
+        * the additional overhead doesn't seem to warrant the increased
+        * accuracy of the statistics, since the important figure is the
+        * max number of bitflips in a single ECC block returned by this
+        * function. */
+       mtd->ecc_stats.corrected += max_bitflips;
+
+       MTD_TRACE("ECC read status: %s%s%s%s, correction count %d\n",
+                 ecc_status & ECC_STAT_UNC_0 ? "Uncorrected " : "",
+                 ecc_status & ECC_STAT_ERROR_0 ? "Corrected " : "",
+                 ecc_status & ECC_STAT_OVER_0 ? "Over limit " : "",
+                 ecc_status & (ECC_STAT_UNC_0 | ECC_STAT_ERROR_0 |
+                               ECC_STAT_OVER_0) ?  "" : "ok", max_bitflips);
+
+       /* We shouldn't see oob_required for ECC reads. */
+       if (oob_required) {
+               dev_crit(etx_info->dev, "Need separate read for the OOB\n");
+               BUG();
+       }
+
+       return max_bitflips;
+}
+
+/* Read page with HW ECC */
+static int etx_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+               uint8_t *buf, int oob_required, int page)
+{
+       MTD_TRACE("page %d, oobreq %d\n", page, oob_required);
+       return etx_read_page_mode(mtd, chip, buf, oob_required, page,
+                                 ETX_READ_STD);
+}
+
+/* Read page with no ECC */
+static int etx_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+               uint8_t *buf, int oob_required, int page)
+{
+       MTD_TRACE("page %d, oobreq %d\n", page, oob_required);
+       return etx_read_page_mode(mtd, chip, buf, oob_required, page,
+                                 ETX_READ_RAW);
+}
+
+/* Write page with HW ECC */
+/* This is the only place where we know we'll be writing w/ ECC */
+static int etx_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+               const uint8_t *buf, int oob_required)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+
+       MTD_TRACE("oob_required %d\n", oob_required);
+
+       /* The controller can't write data to the oob when ECC is enabled,
+        * so we set oob_required to 0 here and don't process the oob
+        * further even if requested. This could happen for instance if
+        * using nandwrite -o without -n . */
+       if (oob_required)
+               dev_warn(etx_info->dev, "Tried to write OOB with ECC!\n");
+       info->cmd_cache.oob_required = 0;
+       info->cmd_cache.write_raw = 0;
+
+       /* A bit silly this, this is actually etx_write_dmabuf */
+       chip->write_buf(mtd, buf, mtd->writesize);
+
+       return 0;
+}
+
+/* Write page with no ECC */
+/* This is the only place where we know we won't be writing w/ ECC */
+static int etx_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+               const uint8_t *buf, int oob_required)
+{
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+
+       MTD_TRACE("oob_required %d\n", oob_required);
+
+       /* We need this for the upcoming PAGEPROG command */
+       info->cmd_cache.oob_required = oob_required;
+       info->cmd_cache.write_raw = 1;
+
+       /* A bit silly this, this is actually etx_write_dmabuf */
+       chip->write_buf(mtd, buf, mtd->writesize);
+
+       if (oob_required)
+               chip->write_buf(mtd, info->chip.oob_poi, mtd->oobsize);
+
+       return 0;
+}
+
+
+/* Handle commands from MTD NAND layer */
+static void etx_nand_command(struct mtd_info *mtd, unsigned int command,
+                            int column, int page_addr)
+{
+       /* We know that an mtd belonging to us is actually only the first
+        * struct in a multi-struct structure. */
+       struct chip_info *info = TO_CHIP_INFO(mtd);
+
+       /* Save command so that other parts of the API can figure out
+        * what's actually going on. */
+       info->cmd_cache.command = command;
+
+       /* Configure the NFC for the flash chip in question. */
+       config_etx(&info->etx_config, info);
+
+       /* Some commands we execute immediately, while some need to be
+        * deferred until we have all the data needed, i.e. for page read,
+        * we can't initiate the read until we know if we are going to be
+        * using raw mode or not.
+        */
+       switch (command) {
+       case NAND_CMD_READ0:
+               MTD_TRACE("READ0 page %d, column %d\n", page_addr, column);
+               if (etx_info->setup->ecc_mode == NAND_ECC_HW) {
+                       /* We do not yet know if the caller wants to
+                        * read the page with or without ECC, so we
+                        * just store the page number and main/oob flag
+                        * here.
+                        * TODO: Since the page number arrives via the
+                        * read_page call, we don't really need to
+                        * store it. */
+                       info->cmd_cache.page = page_addr;
+                       info->cmd_cache.column = column;
+               } else {
+                       /* Read the whole page including oob */
+                       info->cmd_cache.oob_required = 1;
+                       read_dma(info, page_addr, column, ETX_READ_ALL);
+               }
+               break;
+       case NAND_CMD_READOOB:
+               MTD_TRACE("READOOB page %d, column %d\n", page_addr, column);
+               /* In contrast to READ0, where nand_base always calls
+                * a read_page_foo function before reading the data,
+                * for READOOB, read_buf is called instead.
+                * We don't want the actual read in read_buf, so
+                * we put it here. */
+               read_dma(info, page_addr, column, ETX_READ_OOB);
+               break;
+       case NAND_CMD_ERASE1:
+               MTD_TRACE("ERASE1 page %d\n", page_addr);
+               /* Just grab page parameter, wait until ERASE2 to do
+                * something. */
+               info->cmd_cache.page = page_addr;
+               break;
+       case NAND_CMD_ERASE2:
+               MTD_TRACE("ERASE2 page %d, do it\n", info->cmd_cache.page);
+               /* Off we go! */
+               block_erase(info->cmd_cache.page);
+               break;
+       case NAND_CMD_RESET:
+               MTD_TRACE("chip reset\n");
+
+               command_and_wait(COMMAND_RESET,
+                                INT_STATUS_CMD_END_INT_FL);
+               break;
+       case NAND_CMD_SEQIN:
+               MTD_TRACE("SEQIN column %d, page %d\n", column, page_addr);
+               /* Just grab some parameters, then wait until
+                * PAGEPROG to do the actual operation. */
+               info->cmd_cache.page = page_addr;
+               info->cmd_cache.column = column;
+               /* Prepare DMA buffer for data. We don't yet know
+                * how much data there is, so set size to max. */
+               init_dmabuf(DMA_BUF_SIZE);
+               break;
+       case NAND_CMD_PAGEPROG:
+               /* Used for both main area and oob */
+               MTD_TRACE("PAGEPROG page %d, column %d, w/oob %d, raw %d\n",
+                         info->cmd_cache.page, info->cmd_cache.column,
+                         info->cmd_cache.oob_required,
+                         info->cmd_cache.write_raw);
+               write_dma(info, info->cmd_cache.page,
+                         info->cmd_cache.column,
+                         info->cmd_cache.oob_required,
+                         info->cmd_cache.write_raw);
+               break;
+       case NAND_CMD_READID:
+               MTD_TRACE("READID (0x%02x)\n", column);
+
+               /* Read specified ID bytes */
+               /* 0x00 would be NAND_READ_ID_ADDR_STD
+                * 0x20 would be NAND_READ_ID_ADDR_ONFI,
+                * but NAND subsystem knows this and sends us the
+                * address values directly */
+               etx_write(column, ADDR0_COL_REG);
+
+               /* Set up expected number of returned bytes */
+               init_dmabuf(column == NAND_READ_ID_ADDR_STD ? 5 : 4);
+               init_dma(etx_info->dma.phys,
+                        column == NAND_READ_ID_ADDR_STD ? 5 : 4);
+
+               /* Send read id command */
+               command_and_wait(COMMAND_READ_ID,
+                                INT_STATUS_DMA_INT_FL);
+               break;
+       case NAND_CMD_STATUS:
+               MTD_TRACE("STATUS, defer to later read byte\n");
+               /* Don't do anything now, wait until we need to
+                * actually read status. */
+               break;
+       default:
+               MTD_TRACE("Unhandled command 0x%02x (col %d, page addr %d)\n",
+                         command, column, page_addr);
+               break;
+       }
+}
+
+
+/**** Top level probing and device management ****/
+
+/* Get configuration from device tree */
+#ifdef CONFIG_OF
+static int etx_get_dt_config(struct platform_device *pdev)
+{
+       struct etx_setup *etx_setup = dev_get_platdata(&pdev->dev);
+       struct device *dev = &pdev->dev;
+       struct device_node *np = pdev->dev.of_node;
+       int res, timings;
+
+       if (!np) {
+               dev_err(dev, "No configuration\n");
+               return -EINVAL;
+       }
+
+       /* ECC parameters */
+       res = of_get_nand_ecc_mode(np);
+       if (res == NAND_ECC_HW || res == NAND_ECC_SOFT_BCH)
+               etx_setup->ecc_mode = res;
+       else
+               dev_warn(dev, "Unsupported/unset ECC mode, using default\n");
+
+       res = of_get_nand_ecc_strength(np);
+       /* NFC can handle 2 bits but ECC_BYTES macro can't and it's
+        * highly unlikely we'd ever need to support 2 bits correction
+        * in practice, so don't allow that case here. */
+       if (res < 0 ||
+           (res != 4 && res != 8 && res != 16 && res != 24 && res != 32))
+               dev_warn(dev, "Unsupported ECC strength, using default\n");
+       else
+               etx_setup->ecc_strength = res;
+
+       res = of_get_nand_ecc_step_size(np);
+       if (res < 0 || (res != 256 && res != 512 && res != 1024))
+               dev_warn(dev, "Unsupported ECC step size, using default\n");
+       else
+               etx_setup->ecc_blksize = res;
+
+       etx_setup->on_flash_bbt = of_get_nand_on_flash_bbt(np);
+
+       timings = sizeof(etx_setup->timings) / sizeof(u32);
+       res = of_property_read_u32_array(np, "timings",
+                                        (u32 *)&etx_setup->timings, timings);
+       if (res < 0) {
+               dev_warn(dev, "NAND timing setup missing, using defaults\n");
+               /* Default values have been set, but we don't know what
+                * read_u32_array does if it fails during parsing, so reset
+                * them here again. */
+               memcpy(&etx_setup->timings, &default_mode0_pll_enabled,
+                      sizeof(etx_setup->timings));
+       }
+
+       return 0;
+}
+#else
+static int etx_get_dt_config(struct platform_device *pdev)
+{
+       return -ENOSYS;
+}
+#endif
+
+
+/* Per-NAND-chip initialization. */
+static __init
+struct mtd_info *etx_nand_flash_probe(struct platform_device *pdev,
+                                     unsigned bank_no)
+{
+       struct chip_info *this;
+       struct device *dev = &pdev->dev;
+       int pages_per_block, ecc_blksize, ecc_strength;
+
+       /* Allocate memory for MTD device structure and private data */
+       this = devm_kzalloc(dev, sizeof(struct chip_info), GFP_KERNEL);
+       if (!this) {
+               dev_err(dev, "Unable to allocate local device structure.\n");
+               return NULL;
+       }
+
+       /* Link the private data with the mtd structure */
+       this->mtd.priv = &this->chip;
+
+       /* Set up basic config for NAND controller hardware */
+
+       /* Device control. */
+#ifdef ETX_CE_BANK_SEL
+       /* Separate chips regarded as different banks. */
+       this->etx_config.mem_ctrl = MEM_CTRL_BANK_SEL(bank_no);
+#else
+       /* Separate chips regarded as different chip selects. */
+       this->etx_config.mem_ctrl = MEM_CTRL_MEM_CE(bank_no);
+#endif
+
+#ifdef ETX_RB_WIRED_AND
+       /* Ready/busy from nand flash arrives via wired-AND for device 0 */
+       this->etx_config.mem_status_mask = STATUS_MEM0_ST;
+#else
+       /* Ready/busy from nand flash as separate per-device signals */
+       this->etx_config.mem_status_mask = STATUS_MEM_ST(bank_no);
+#endif
+
+       /* Our interface to the mtd API */
+       this->chip.cmdfunc = etx_nand_command;
+       this->chip.cmd_ctrl = etx_dummy_cmd_ctrl;
+       this->chip.dev_ready = etx_dev_ready;
+       this->chip.read_byte = etx_read_byte;
+       this->chip.read_buf = etx_read_dmabuf;
+       this->chip.write_buf = etx_write_dmabuf;
+
+       /* Scan to find existence of the device */
+       /* Note that the NFC is not completely set up at this time, but
+        * that is ok as we only need to identify the device here. */
+       if (nand_scan_ident(&this->mtd, 1, NULL))
+               goto outta_here;
+
+       /* Set up rest of config for NAND controller hardware */
+
+       /* set ECC block size and pages per block */
+       pages_per_block = this->mtd.erasesize / this->mtd.writesize;
+       ecc_blksize = etx_info->setup->ecc_blksize;
+       ecc_strength = etx_info->setup->ecc_strength;
+       this->etx_config.control = CONTROL_ECC_BLOCK_SIZE(ecc_blksize) |
+                                  CONTROL_BLOCK_SIZE(pages_per_block);
+
+       /* Set up ECC control and offset of ECC data */
+       /* We don't use the threshold capability of the controller, as we
+        * let mtd handle that, so set the threshold to same as capability. */
+       this->etx_config.ecc_ctrl = ECC_CTRL_ECC_THRESHOLD(ecc_strength) |
+                                   ECC_CTRL_ECC_CAP(ecc_strength);
+       /* Put ECC bytes into OOB at an offset, to skip bad block marker */
+       this->etx_config.ecc_offset = this->mtd.writesize + ECC_OFFSET;
+
+       /* Since we've now completed the configuration, we need to force it to
+        * be written to the NFC, else the caching in config_etx will leave
+        * the etx_config values written since nand_scan_ident unwritten. */
+       config_etx(&this->etx_config, NULL);
+
+       /* ECC setup */
+
+       /* ECC API */
+       /* Override the following functions when using hardware ECC,
+        * otherwise we use the defaults set up by nand_base. */
+       if (etx_info->setup->ecc_mode == NAND_ECC_HW) {
+               this->chip.ecc.read_page = etx_read_page_hwecc;
+               this->chip.ecc.read_page_raw = etx_read_page_raw;
+               this->chip.ecc.write_page = etx_write_page_hwecc;
+               this->chip.ecc.write_page_raw = etx_write_page_raw;
+       }
+
+       this->chip.ecc.mode = etx_info->setup->ecc_mode;
+
+       /* TODO: Not sure if these really need to be set for HW ECC; if
+        * nothing else though we can use the values for our lower level
+        * driver to have a common point where it is all set up .*/
+       this->chip.ecc.size = ecc_blksize;
+       this->chip.ecc.strength = ecc_strength;
+       this->chip.ecc.bytes = ECC_BYTES(ecc_strength, ecc_blksize);
+
+       /* We set the bitflip_threshold at 75% of the error correction
+        * level to get some margin in case bitflips happen in parts of the
+        * flash that we don't read that often. */
+       /* We add 1 so that an ECC strength of 1 gives us a threshold of 1;
+        * rather academic though, as we only support BCH anyway... */
+       this->mtd.bitflip_threshold = (ecc_strength + 1) * 3 / 4;
+
+       if (etx_info->setup->on_flash_bbt)
+               /* Enable the use of a flash based bad block table.
+                * Since the OOB is not ECC protected we don't put BBT stuff
+                * there. We also don't mark user-detected badblocks as bad in
+                * their oob, only in the BBT, to avoid potential chip problems
+                * when attempting to write bad blocks (writing to bad blocks
+                * is not recommended according to flash manufacturers). */
+               this->chip.bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB |
+                                        NAND_BBT_NO_OOB_BBM;
+
+       this->chip.controller = etx_info->controller;
+
+       /* Finalize NAND scan, including BBT if requested */
+       if (nand_scan_tail(&this->mtd))
+               goto outta_here;
+
+       this->mtd.dev.parent = &pdev->dev;
+
+       return &this->mtd;
+
+outta_here:
+       kfree(this);
+       return NULL;
+}
+
+/* Main probe function. Called to probe and set up device. */
+static int __init etx_nand_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mtd_info *main_mtd = NULL;
+       struct mtd_info *mtds[CONFIG_MTD_NAND_EVATRONIX_CHIPS];
+       struct etx_setup *etx_setup;
+       struct nand_hw_control *controller;
+       int err = 0;
+
+       dev_info(dev, "Initializing Evatronix NANDFLASH-CTRL driver\n");
+
+       /* etx_info is where we keep runtime information about the NFC */
+       etx_info = devm_kzalloc(dev, sizeof(*etx_info), GFP_KERNEL);
+       if (!etx_info) {
+               dev_err(dev, "Unable to allocate device control structure.\n");
+               return -ENOMEM;
+       }
+       etx_info->dev = dev;
+       spin_lock_init(&etx_info->lock);
+
+       /* Set up a controller struct to act as shared lock for all devices */
+       controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
+       if (controller == NULL) {
+               dev_err(dev, "Unable to allocate controller structure.\n");
+               return -ENOMEM;
+       }
+       spin_lock_init(&controller->lock);
+       init_waitqueue_head(&controller->wq);
+       etx_info->controller = controller;
+
+       /* etx_setup is where we keep settings from DT, in digested form */
+       etx_setup = devm_kzalloc(dev, sizeof(*etx_setup), GFP_KERNEL);
+       if (!etx_setup) {
+               dev_err(dev, "Unable to allocate device setup structure.\n");
+               return -ENOMEM;
+       }
+       pdev->dev.platform_data = etx_setup;
+       etx_info->setup = etx_setup;
+
+       /* Default parameters, potentially overriden by DT */
+       etx_setup->ecc_mode = NAND_ECC_HW;
+       etx_setup->ecc_strength = 8;
+       etx_setup->ecc_blksize = 512;
+       memcpy(&etx_setup->timings, &default_mode0_pll_enabled,
+              sizeof(etx_setup->timings));
+
+       /* Get config from device tree. */
+       err = etx_get_dt_config(pdev);
+       if (err) {
+               dev_err(dev, "Can't retrieve dt config\n");
+               return err;
+       }
+
+       dev_info(dev, "ECC using %s mode with strength %i and block size %i.\n",
+               etx_setup->ecc_mode == NAND_ECC_HW ? "hardware" : "software",
+               etx_setup->ecc_strength, etx_setup->ecc_blksize);
+
+       /* Initialize interrupts and DMA etc. */
+       err = etx_init_resources(pdev);
+       if (err)
+               return err;
+
+       setup_etx_timing(etx_setup);
+
+#ifndef POLLED_XFERS
+       init_waitqueue_head(&etx_info->irq.wq);
+#endif
+
+       mtds[0] = etx_nand_flash_probe(pdev, 0);
+       if (mtds[0] == NULL)
+               return -ENXIO;
+
+#if CONFIG_MTD_NAND_EVATRONIX_CHIPS > 1
+       mtds[1] = etx_nand_flash_probe(pdev, 1);
+       if (mtds[1] != NULL) {
+               /* Two devices found, combine them into one */
+               main_mtd = mtd_concat_create(mtds, 2, "axisflash");
+
+               if (main_mtd == NULL) {
+                       dev_err(dev, "mtd concat failed, using first chip\n");
+                       main_mtd = mtds[0];
+               }
+       }
+#endif
+       if (main_mtd == NULL) {
+               /* No concat device, use first chip only */
+               main_mtd = mtds[0];
+       }
+
+       /* We have our mtd now, insert call to mapping driver here. */
+
+       return err;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id etx_nand_id_table[] = {
+       { .compatible = "evatronix,nandflash-ctrl" },
+       {} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, etx_nand_id_table);
+#endif
+
+static struct platform_driver etx_nand_driver = {
+       .driver = {
+               .name   = "evatronix-nand",
+               .owner  = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(etx_nand_id_table),
+#endif
+       },
+       .probe = etx_nand_probe,
+};
+
+module_platform_driver(etx_nand_driver);
+
+MODULE_AUTHOR("Ricard Wanderlof <rica...@axis.com>");
+MODULE_DESCRIPTION("Evatronix NANDFLASH-CTRL driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/evatronix_nand.h 
b/drivers/mtd/nand/evatronix_nand.h
new file mode 100644
index 0000000..115d1be
--- /dev/null
+++ b/drivers/mtd/nand/evatronix_nand.h
@@ -0,0 +1,401 @@
+/*
+ * evatronix_nand.h - NAND Flash Driver for Evatronix NANDFLASH-CTRL
+ * NAND Flash Controller IP.
+ *
+ * This implementation has been designed against Rev 1.15 of the
+ * NANDFLASH-CTRL Design Specification.
+ *
+ * Copyright (c) 2014 Axis Communication AB, Lund, Sweden.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _EVATRONIX_NAND_H_
+#define  _EVATRONIX_NAND_H_
+
+#include <linux/bitops.h> /* for ffs() */
+
+/* Register offsets for Evatronix NANDFLASH-CTRL IP */
+/* Register field shift values and masks are interespersed as it makes
+ * them easier to locate. */
+/* We use shift values rather than direct masks (e.g. 0x0000d000), as the
+ * hardware manual lists the bit number, making the definitions below
+ * easier to verify against the manual. */
+/* All (known) registers are here, but we only put in the bit fields
+ * for the fields we need. */
+/* We try to be consistent regarding _SIZE/_MASK/_value macros so as to
+ * get a consistent layout here, except for trivial cases where there is
+ * only a single bit or field in a register at bit offset 0. */
+
+#define COMMAND_REG            0x00
+/* The masks reflect the input data to the MAKE_COMMAND macro, rather than
+ * the bits in the register itself. These macros are not intended to be
+ * used by the user, who should use the MAKE_COMMAND et al macros. */
+#define _CMD_SEQ_SHIFT                 0
+#define _INPUT_SEL_SHIFT               6
+#define _DATA_SEL_SHIFT                        7
+#define _CMD_0_SHIFT                   8
+#define _CMD_1_3_SHIFT                 16
+#define _CMD_2_SHIFT                   24
+
+#define _CMD_SEQ_MASK                  0x3f
+#define _INPUT_SEL_MASK                        1
+#define _DATA_SEL_MASK                 1
+#define _CMD_MASK                      0xff /* for all CMD_foo */
+
+#define MAKE_COMMAND(CMD_SEQ, INPUT_SEL, DATA_SEL, CMD_0, CMD_1_3, CMD_2) \
+       ((((CMD_SEQ)    & _CMD_SEQ_MASK)        << _CMD_SEQ_SHIFT)      | \
+        (((INPUT_SEL)  & _INPUT_SEL_MASK)      << _INPUT_SEL_SHIFT)    | \
+        (((DATA_SEL)   & _DATA_SEL_MASK)       << _DATA_SEL_SHIFT)     | \
+        (((CMD_0)      & _CMD_MASK)            << _CMD_0_SHIFT)        | \
+        (((CMD_1_3)    & _CMD_MASK)            << _CMD_1_3_SHIFT)      | \
+        (((CMD_2)      & _CMD_MASK)            << _CMD_2_SHIFT))
+
+#define INPUT_SEL_SIU                  0
+#define INPUT_SEL_DMA                  1
+#define DATA_SEL_FIFO                  0
+#define DATA_SEL_DATA_REG              1
+
+#define CONTROL_REG            0x04
+#define CONTROL_BLOCK_SIZE_32          (0 << 6)
+#define CONTROL_BLOCK_SIZE_64          (1 << 6)
+#define CONTROL_BLOCK_SIZE_128         (2 << 6)
+#define CONTROL_BLOCK_SIZE_256         (3 << 6)
+#define CONTROL_BLOCK_SIZE(SIZE)       ((ffs(SIZE) - 6) << 6)
+#define CONTROL_ECC_EN                 (1 << 5)
+#define CONTROL_INT_EN                 (1 << 4)
+#define CONTROL_ECC_BLOCK_SIZE_256     (0 << 1)
+#define CONTROL_ECC_BLOCK_SIZE_512     (1 << 1)
+#define CONTROL_ECC_BLOCK_SIZE_1024    (2 << 1)
+#define CONTROL_ECC_BLOCK_SIZE(SIZE)   ((ffs(SIZE) - 9) << 1)
+#define STATUS_REG             0x08
+#define STATUS_MEM_ST(DEVICE)          (1 << (DEVICE))
+#define STATUS_MEM0_ST                 (1 << 0)
+#define STATUS_MEM1_ST                 (2 << 0)
+#define STATUS_CTRL_STAT               (1 << 8)
+#define STATUS_MASK_REG                0x0C
+#define STATE_MASK_SHIFT               0
+#define STATUS_MASK_STATE_MASK(MASK)   (((MASK) & 0xff) << STATE_MASK_SHIFT)
+#define ERROR_MASK_SHIFT               8
+#define STATUS_MASK_ERROR_MASK(MASK)   (((MASK) & 0xff) << ERROR_MASK_SHIFT)
+#define INT_MASK_REG           0x10
+#define INT_MASK_ECC_INT0_EN           (1 << 24)
+#define INT_MASK_STAT_ERR_INT0_EN      (1 << 16)
+#define INT_MASK_MEM0_RDY_INT_EN       (1 << 8)
+#define INT_MASK_DMA_INT_EN            (1 << 3)
+#define INT_MASK_DATA_REG_EN           (1 << 2)
+#define INT_MASK_CMD_END_INT_EN                (1 << 1)
+#define INT_STATUS_REG         0x14
+#define INT_STATUS_ECC_INT0_FL         (1 << 24)
+#define INT_STATUS_STAT_ERR_INT0_FL    (1 << 16)
+#define INT_STATUS_MEM0_RDY_INT_FL     (1 << 8)
+#define INT_STATUS_DMA_INT_FL          (1 << 3)
+#define INT_STATUS_DATA_REG_FL         (1 << 2)
+#define INT_STATUS_CMD_END_INT_FL      (1 << 1)
+#define ECC_CTRL_REG           0x18
+#define ECC_CTRL_ECC_CAP_2             (0 << 0)
+#define ECC_CTRL_ECC_CAP_4             (1 << 0)
+#define ECC_CTRL_ECC_CAP_8             (2 << 0)
+#define ECC_CTRL_ECC_CAP_16            (3 << 0)
+#define ECC_CTRL_ECC_CAP_24            (4 << 0)
+#define ECC_CTRL_ECC_CAP_32            (5 << 0)
+#define ECC_CTRL_ECC_CAP(B)            ((B) < 24 ? ffs(B) - 2 : (B) / 6)
+/* # ECC corrections that are acceptable during read before setting OVER flag 
*/
+#define ECC_CTRL_ECC_THRESHOLD(VAL)    (((VAL) & 0x3f) << 8)
+#define ECC_OFFSET_REG         0x1C
+#define ECC_STAT_REG           0x20
+/* Correctable error flag(s) */
+#define ECC_STAT_ERROR_0               (1 << 0)
+/* Uncorrectable error flag(s) */
+#define ECC_STAT_UNC_0                 (1 << 8)
+/* Acceptable errors level overflow flag(s) */
+#define ECC_STAT_OVER_0                        (1 << 16)
+#define ADDR0_COL_REG          0x24
+#define ADDR0_ROW_REG          0x28
+#define ADDR1_COL_REG          0x2C
+#define ADDR1_ROW_REG          0x30
+#define PROTECT_REG            0x34
+#define FIFO_DATA_REG          0x38
+#define DATA_REG_REG           0x3C
+#define DATA_REG_SIZE_REG      0x40
+#define DATA_REG_SIZE_DATA_REG_SIZE(SIZE) (((SIZE) - 1) & 3)
+#define DEV0_PTR_REG           0x44
+#define DEV1_PTR_REG           0x48
+#define DEV2_PTR_REG           0x4C
+#define DEV3_PTR_REG           0x50
+#define DEV4_PTR_REG           0x54
+#define DEV5_PTR_REG           0x58
+#define DEV6_PTR_REG           0x5C
+#define DEV7_PTR_REG           0x60
+#define DMA_ADDR_L_REG         0x64
+#define DMA_ADDR_H_REG         0x68
+#define DMA_CNT_REG            0x6C
+#define DMA_CTRL_REG           0x70
+#define DMA_CTRL_DMA_START             (1 << 7) /* start on command */
+#define DMA_CTRL_DMA_MODE_SG           (1 << 5) /* scatter/gather mode */
+#define DMA_CTRL_DMA_BURST_I_P_4       (0 << 2) /* incr. precise burst */
+#define DMA_CTRL_DMA_BURST_S_P_16      (1 << 2) /* stream precise burst */
+#define DMA_CTRL_DMA_BURST_SINGLE      (2 << 2) /* single transfer */
+#define DMA_CTRL_DMA_BURST_UNSPEC      (3 << 2) /* burst of unspec. length */
+#define DMA_CTRL_DMA_BURST_I_P_8       (4 << 2) /* incr. precise burst */
+#define DMA_CTRL_DMA_BURST_I_P_16      (5 << 2) /* incr. precise burst */
+#define DMA_CTRL_ERR_FLAG              (1 << 1) /* read only */
+#define DMA_CTRL_DMA_READY             (1 << 0) /* read only */
+#define BBM_CTRL_REG           0x74
+#define MEM_CTRL_REG           0x80
+#define MEM_CTRL_MEM_CE(CE)            (((CE) & 7) << 0)
+#define MEM_CTRL_BANK_SEL(BANK)                (((BANK) & 7) << 16)
+#define DATA_SIZE_REG          0x84
+#define TIMINGS_ASYN_REG       0x88
+#define TIMINGS_SYN_REG                0x8C
+#define TIME_SEQ_0_REG         0x90
+#define TIME_SEQ_1_REG         0x94
+#define TIME_GEN_SEQ_0_REG     0x98
+#define TIME_GEN_SEQ_1_REG     0x9C
+#define TIME_GEN_SEQ_2_REG     0xA0
+#define FIFO_INIT_REG          0xB0
+#define FIFO_INIT_FIFO_INIT                    1 /* Flush FIFO */
+#define FIFO_STATE_REG         0xB4
+#define FIFO_STATE_DF_W_EMPTY          (1 << 7)
+#define FIFO_STATE_DF_R_FULL           (1 << 6)
+#define FIFO_STATE_CF_ACCPT_W          (1 << 5)
+#define FIFO_STATE_CF_ACCPT_R          (1 << 4)
+#define FIFO_STATE_CF_FULL             (1 << 3)
+#define FIFO_STATE_CF_EMPTY            (1 << 2)
+#define FIFO_STATE_DF_W_FULL           (1 << 1)
+#define FIFO_STATE_DF_R_EMPTY          (1 << 0)
+#define GEN_SEQ_CTRL_REG       0xB8            /* aka GENERIC_SEQ_CTRL */
+#define _CMD0_EN_SHIFT                 0
+#define _CMD1_EN_SHIFT                 1
+#define _CMD2_EN_SHIFT                 2
+#define _CMD3_EN_SHIFT                 3
+#define _COL_A0_SHIFT                  4
+#define _COL_A1_SHIFT                  6
+#define _ROW_A0_SHIFT                  8
+#define _ROW_A1_SHIFT                  10
+#define _DATA_EN_SHIFT                 12
+#define _DELAY_EN_SHIFT                        13
+#define _IMD_SEQ_SHIFT                 15
+#define _CMD3_SHIFT                    16
+#define ECC_CNT_REG            0x14C
+#define ECC_CNT_ERR_LVL_MASK           0x3F
+
+#define _CMD0_EN_MASK                  1
+#define _CMD1_EN_MASK                  1
+#define _CMD2_EN_MASK                  1
+#define _CMD3_EN_MASK                  1
+#define _COL_A0_MASK                   3
+#define _COL_A1_MASK                   3
+#define _ROW_A0_MASK                   3
+#define _ROW_A1_MASK                   3
+#define _DATA_EN_MASK                  1
+#define _DELAY_EN_MASK                 3
+#define _IMD_SEQ_MASK                  1
+#define _CMD3_MASK                     0xff
+
+/* DELAY_EN field values, non-shifted */
+#define _BUSY_NONE                     0
+#define _BUSY_0                                1
+#define _BUSY_1                                2
+
+/* Slightly confusingly, the DELAYx_EN fields enable BUSY phases. */
+#define MAKE_GEN_CMD(CMD0_EN, CMD1_EN, CMD2_EN, CMD3_EN, \
+                    COL_A0, ROW_A0, COL_A1, ROW_A1, \
+                    DATA_EN, BUSY_EN, IMMEDIATE_SEQ, CMD3) \
+       ((((CMD0_EN)    & _CMD0_EN_MASK)        << _CMD0_EN_SHIFT)      | \
+        (((CMD1_EN)    & _CMD1_EN_MASK)        << _CMD1_EN_SHIFT)      | \
+        (((CMD2_EN)    & _CMD2_EN_MASK)        << _CMD2_EN_SHIFT)      | \
+        (((CMD3_EN)    & _CMD3_EN_MASK)        << _CMD3_EN_SHIFT)      | \
+        (((COL_A0)     & _COL_A0_MASK)         << _COL_A0_SHIFT)       | \
+        (((COL_A1)     & _COL_A1_MASK)         << _COL_A1_SHIFT)       | \
+        (((ROW_A0)     & _ROW_A0_MASK)         << _ROW_A0_SHIFT)       | \
+        (((ROW_A1)     & _ROW_A1_MASK)         << _ROW_A1_SHIFT)       | \
+        (((DATA_EN)    & _DATA_EN_MASK)        << _DATA_EN_SHIFT)      | \
+        (((BUSY_EN)    & _DELAY_EN_MASK)       << _DELAY_EN_SHIFT)     | \
+        (((IMMEDIATE_SEQ) & _IMD_SEQ_MASK)     << _IMD_SEQ_SHIFT)      | \
+        (((CMD3)       & _CMD3_MASK)           << _CMD3_SHIFT))
+
+/* The sequence encodings are not trivial. The ones we use are listed here. */
+#define _SEQ_0                 0x00 /* send one cmd, then wait for ready */
+#define _SEQ_1                 0x21 /* send one cmd, one addr, fetch data */
+#define _SEQ_4                 0x24 /* single cycle write then read */
+#define _SEQ_10                        0x2A /* read page */
+#define _SEQ_12                        0x0C /* write page, don't wait for R/B 
*/
+#define _SEQ_18                        0x32 /* read page using general cycle */
+#define _SEQ_19                        0x13 /* write page using general cycle 
*/
+#define _SEQ_14                        0x0E /* 3 address cycles, for block 
erase */
+
+#define MLUN_REG               0xBC
+#define DEV0_SIZE_REG          0xC0
+#define DEV1_SIZE_REG          0xC4
+#define DEV2_SIZE_REG          0xC8
+#define DEV3_SIZE_REG          0xCC
+#define DEV4_SIZE_REG          0xD0
+#define DEV5_SIZE_REG          0xD4
+#define DEV6_SIZE_REG          0xD8
+#define DEV7_SIZE_REG          0xDC
+#define SS_CCNT0_REG           0xE0
+#define SS_CCNT1_REG           0xE4
+#define SS_SCNT_REG            0xE8
+#define SS_ADDR_DEV_CTRL_REG   0xEC
+#define SS_CMD0_REG            0xF0
+#define SS_CMD1_REG            0xF4
+#define SS_CMD2_REG            0xF8
+#define SS_CMD3_REG            0xFC
+#define SS_ADDR_REG            0x100
+#define SS_MSEL_REG            0x104
+#define SS_REQ_REG             0x108
+#define SS_BRK_REG             0x10C
+#define DMA_TLVL_REG           0x114
+#define DMA_TLVL_MAX           0xFF
+#define AES_CTRL_REG           0x118
+#define AES_DATAW_REG          0x11C
+#define AES_SVECT_REG          0x120
+#define CMD_MARK_REG           0x124
+#define LUN_STATUS_0_REG       0x128
+#define LUN_STATUS_1_REG       0x12C
+#define TIMINGS_TOGGLE_REG     0x130
+#define TIME_GEN_SEQ_3_REG     0x134
+#define SQS_DELAY_REG          0x138
+#define CNE_MASK_REG           0x13C
+#define CNE_VAL_REG            0x140
+#define CNA_CTRL_REG           0x144
+#define INTERNAL_STATUS_REG    0x148
+#define ECC_CNT_REG            0x14C
+#define PARAM_REG_REG          0x150
+
+/* NAND flash command generation */
+
+/* NAND flash command codes */
+#define NAND_RESET             0xff
+#define NAND_READ_STATUS       0x70
+#define NAND_READ_ID           0x90
+#define NAND_READ_ID_ADDR_STD  0x00    /* address written to ADDR0_COL */
+#define NAND_READ_ID_ADDR_ONFI 0x20    /* address written to ADDR0_COL */
+#define NAND_PAGE_READ         0x00
+#define NAND_PAGE_READ_END     0x30
+#define NAND_BLOCK_ERASE       0x60
+#define NAND_BLOCK_ERASE_END   0xd0
+#define NAND_PAGE_WRITE                0x80
+#define NAND_PAGE_WRITE_END    0x10
+
+#define _DONT_CARE 0x00 /* When we don't have anything better to say */
+
+
+/* Assembled values for putting into COMMAND register */
+
+/* Reset NAND flash */
+
+/* Uses SEQ_0: non-directional sequence, single command, wait for ready */
+#define COMMAND_RESET \
+       MAKE_COMMAND(_SEQ_0, INPUT_SEL_SIU, DATA_SEL_FIFO, \
+               NAND_RESET, _DONT_CARE, _DONT_CARE)
+
+/* Read status */
+
+/* Uses SEQ_4: single command, then read data via DATA_REG */
+#define COMMAND_READ_STATUS \
+       MAKE_COMMAND(_SEQ_4, INPUT_SEL_SIU, DATA_SEL_DATA_REG, \
+               NAND_READ_STATUS, _DONT_CARE, _DONT_CARE)
+
+/* Read ID */
+
+/* Uses SEQ_1: single command, ADDR0_COL, then read data via FIFO */
+/* ADDR0_COL is set to NAND_READ_ID_ADDR_STD for non-ONFi, and
+ * NAND_READ_ID_ADDR_ONFI for ONFi.
+ * The controller reads 5 bytes in the non-ONFi case, and 4 bytes in the
+ * ONFi case, so the data reception (DMA or FIFO_REG) needs to be set up
+ * accordingly. */
+#define COMMAND_READ_ID \
+       MAKE_COMMAND(_SEQ_1, INPUT_SEL_DMA, DATA_SEL_FIFO, \
+               NAND_READ_ID, _DONT_CARE, _DONT_CARE)
+
+/* Page read via slave interface (FIFO_DATA register) */
+
+/* Standard 5-cycle read command, with 0x30 end-of-cycle marker */
+/* Uses SEQ_10: CMD0 + 5 address cycles + CMD2, read data */
+#define COMMAND_READ_PAGE_STD \
+       MAKE_COMMAND(_SEQ_10, INPUT_SEL_SIU, DATA_SEL_FIFO, \
+               NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
+
+/* 4-cycle read command, together with GEN_SEQ_CTRL_READ_PAGE_4CYCLE */
+/* Uses SEQ_18 (generic command sequence, see GEN_SEQ_ECTRL_READ_PAGE_4CYCLE)):
+   CMD0 + 2+2 address cycles + CMD2, read data */
+#define COMMAND_READ_PAGE_GEN \
+       MAKE_COMMAND(_SEQ_18, INPUT_SEL_SIU, DATA_SEL_FIFO, \
+               NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
+
+/* Page read via master interface (DMA) */
+
+/* Standard 5-cycle read command, with 0x30 end-of-cycle marker */
+/* Uses SEQ_10: CMD0 + 5 address cycles + CMD2, read data */
+#define COMMAND_READ_PAGE_DMA_STD \
+       MAKE_COMMAND(_SEQ_10, INPUT_SEL_DMA, DATA_SEL_FIFO, \
+               NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
+
+/* 4-cycle read command, together with GEN_SEQ_CTRL_READ_PAGE_4CYCLE */
+/* Uses SEQ_18 (generic command sequence, see GEN_SEQ_ECTRL_READ_PAGE_4CYCLE)):
+   CMD0 + 2+2 address cycles + CMD2, read data */
+#define COMMAND_READ_PAGE_DMA_GEN \
+       MAKE_COMMAND(_SEQ_18, INPUT_SEL_DMA, DATA_SEL_FIFO, \
+               NAND_PAGE_READ, _DONT_CARE, NAND_PAGE_READ_END)
+
+/* Page write via master interface (DMA) */
+
+/* Uses SEQ_12: CMD0 + 5 address cycles + write data + CMD1 */
+#define COMMAND_WRITE_PAGE_DMA_STD \
+       MAKE_COMMAND(_SEQ_12, INPUT_SEL_DMA, DATA_SEL_FIFO, \
+               NAND_PAGE_WRITE, NAND_PAGE_WRITE_END, _DONT_CARE)
+
+/* Uses SEQ_19: CMD0 + 4 address cycles + write data + CMD1 */
+#define COMMAND_WRITE_PAGE_DMA_GEN \
+       MAKE_COMMAND(_SEQ_19, INPUT_SEL_DMA, DATA_SEL_FIFO, \
+               NAND_PAGE_WRITE, NAND_PAGE_WRITE_END, _DONT_CARE)
+
+/* Block erase */
+
+/* Uses SEQ_14: CMD0 + 3 address cycles + CMD1 */
+#define COMMAND_BLOCK_ERASE \
+       MAKE_COMMAND(_SEQ_14, INPUT_SEL_SIU, DATA_SEL_FIFO, \
+               NAND_BLOCK_ERASE, NAND_BLOCK_ERASE_END, _DONT_CARE)
+
+/* Assembled values for putting into GEN_SEQ_CTRL register */
+
+/* General command sequence specification for 4 cycle PAGE_READ command */
+#define GEN_SEQ_CTRL_READ_PAGE_4CYCLE \
+       MAKE_GEN_CMD(1, 0, 1, 0,        /* enable command 0 and 2 phases */ \
+                    2, 2,              /* col A0 2 cycles, row A0 2 cycles */ \
+                    0, 0,              /* col A1, row A1 not used */ \
+                    1,                 /* data phase enabled */ \
+                    _BUSY_0,           /* busy0 phase enabled */ \
+                    0,                 /* immediate cmd execution disabled */ \
+                    _DONT_CARE)        /* command 3 code not needed */
+
+/* General command sequence specification for 4 cycle PAGE_PROGRAM command */
+#define GEN_SEQ_CTRL_WRITE_PAGE_4CYCLE \
+       MAKE_GEN_CMD(1, 1, 0, 0,        /* enable command 0 and 1 phases */ \
+                    2, 2,              /* col A0 2 cycles, row A0 2 cycles */ \
+                    0, 0,              /* col A1, row A1 not used */ \
+                    1,                 /* data phase enabled */ \
+                    _BUSY_1,           /* busy1 phase enabled */ \
+                    0,                 /* immediate cmd execution disabled */ \
+                    _DONT_CARE)        /* command 3 code not needed */
+
+/* BCH ECC size calculations. Should really go somewhere else? */
+/* From "Mr. NAND's Wild Ride: Warning: Suprises Ahead", by Robert Pierce,
+ * Denali Software Inc. 2009, table on page 5 */
+/* Use 8 bit correction as base. */
+#define ECC8_BYTES(BLKSIZE) (ffs(BLKSIZE) + 3)
+/* The following is valid for 4..24 bits of correction. */
+#define ECC_BYTES(CAP, BLKSIZE) ((ECC8_BYTES(BLKSIZE) * (CAP) + 7) / 8)
+
+#endif /* _EVATRONIX_NAND_H_ */

-- 
Ricard Wolf Wanderlöf                           ricardw(at)axis.com
Axis Communications AB, Lund, Sweden            www.axis.com
Phone +46 46 272 2016                           Fax +46 46 13 61 30
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to