lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <alpine.DEB.2.02.1410021408380.19786@lnxricardw1.se.axis.com>
Date:	Thu, 2 Oct 2014 14:14:12 +0200
From:	Ricard Wanderlof <ricard.wanderlof@...s.com>
To:	Linux mtd <linux-mtd@...ts.infradead.org>
CC:	<linux-kernel@...r.kernel.org>
Subject: [PATCH RFC]: mtd: nand: Driver for Evatronix NAND flash controller


From: Ricard Wanderlof <ricardw@...s.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 <ricardw@...s.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@...1e000 {
+	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 <ricardw@...s.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 majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ