[RFC PATCH 2/2] nand: cavium: Nand flash controller for Cavium ARM64 SOCs

Jan Glauber jglauber at cavium.com
Mon Mar 27 09:05:24 PDT 2017


Add a driver for the nand flash controller as found on Cavium's
ARM64 SOCs.

The nand flash controller can support up to 8 chips and is presented
as a PCI device. It uses a DMA engine to transfer data between the nand
and L2/DRAM and a programmable command queue to issue multiple
nand flash commands together.

Signed-off-by: Jan Glauber <jglauber at cavium.com>
---
 drivers/mtd/nand/Kconfig       |    6 +
 drivers/mtd/nand/Makefile      |    1 +
 drivers/mtd/nand/cavium_nand.c | 1160 ++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/cavium_nand.h |  231 ++++++++
 4 files changed, 1398 insertions(+)
 create mode 100644 drivers/mtd/nand/cavium_nand.c
 create mode 100644 drivers/mtd/nand/cavium_nand.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 6d4d567..354e88f 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -575,4 +575,10 @@ config MTD_NAND_MTK
 	  Enables support for NAND controller on MTK SoCs.
 	  This controller is found on mt27xx, mt81xx, mt65xx SoCs.
 
+config MTD_NAND_CAVIUM
+        tristate "Support for NAND on Cavium Octeon TX SOCs"
+        depends on PCI && HAS_DMA && (ARM64 || COMPILE_TEST)
+        help
+          Enables support for NAND Flash found on Cavium Octeon TX SOCs.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 19a66e4..0ac23ef 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -60,5 +60,6 @@ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
 obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
 obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_nand.o mtk_ecc.o
+obj-$(CONFIG_MTD_NAND_CAVIUM)		+= cavium_nand.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/cavium_nand.c b/drivers/mtd/nand/cavium_nand.c
new file mode 100644
index 0000000..b3ab447
--- /dev/null
+++ b/drivers/mtd/nand/cavium_nand.c
@@ -0,0 +1,1160 @@
+/*
+ * Cavium cn8xxx NAND flash controller (NDF) driver.
+ *
+ * Copyright (C) 2017 Cavium Inc.
+ * Authors: Jan Glauber <jglauber at cavium.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include "cavium_nand.h"
+
+#define MAX_NAND_NAME_LEN	64
+#define NAND_MAX_PAGESIZE	2048
+#define NAND_MAX_OOBSIZE	64
+
+/* NAND chip related information */
+struct cvm_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+	int cs;					/* chip select 0..7 */
+	struct ndf_set_tm_par_cmd timings;	/* timing parameters */
+	int selected_page;
+	bool oob_access;
+};
+
+struct cvm_nand_buf {
+	int dmabuflen;
+	u8 *dmabuf;
+	dma_addr_t dmaaddr;
+
+	int data_len;           /* Number of bytes in the data buffer */
+	int data_index;         /* Current read index */
+};
+
+/* NAND flash controller (NDF) related information */
+struct cvm_nfc {
+	struct nand_hw_control controller;
+	struct device *dev;
+	void __iomem *base;
+	struct list_head chips;
+	int selected_chip;      /* Currently selected NAND chip number */
+	struct clk *clk;	/* System clock */
+
+	/*
+	 * Status is separate from cvm_nand_buf because
+	 * it can be used in parallel and during init.
+	 */
+	u8 *stat;
+	dma_addr_t stat_addr;
+	bool use_status;
+
+	struct cvm_nand_buf buf;
+};
+
+static inline struct cvm_nand_chip *to_cvm_nand(struct nand_chip *nand)
+{
+	return container_of(nand, struct cvm_nand_chip, nand);
+}
+
+static inline struct cvm_nfc *to_cvm_nfc(struct nand_hw_control *ctrl)
+{
+	return container_of(ctrl, struct cvm_nfc, controller);
+}
+
+/* default parameters used for probing chips */
+static int default_onfi_timing = 0;
+static int default_width = 1; /* 8 bit */
+static int default_page_size = 2048;
+static struct ndf_set_tm_par_cmd default_timing_parms;
+
+/*
+ * Get the number of bits required to encode the column bits. This
+ * does not include bits required for the OOB area.
+ */
+static int ndf_get_column_bits(struct nand_chip *nand)
+{
+	int page_size;
+
+	if (!nand)
+		page_size = default_page_size;
+	else
+		page_size = nand->onfi_params.byte_per_page;
+	return get_bitmask_order(page_size - 1);
+}
+
+irqreturn_t cvm_nfc_isr(int irq, void *dev_id)
+{
+	struct cvm_nfc *tn = dev_id;
+
+	wake_up(&tn->controller.wq);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Read a single byte from the temporary buffer. Used after READID
+ * to get the NAND information and for STATUS.
+ */
+static u8 cvm_nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct cvm_nfc *tn = to_cvm_nfc(nand->controller);
+
+	if (tn->use_status)
+		return *tn->stat;
+
+	if (tn->buf.data_index < tn->buf.data_len)
+		return tn->buf.dmabuf[tn->buf.data_index++];
+	else
+		dev_err(tn->dev, "No data to read\n");
+
+	return 0xff;
+}
+
+/*
+ * Read a number of pending bytes from the temporary buffer. Used
+ * to get page and OOB data.
+ */
+static void cvm_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct cvm_nfc *tn = to_cvm_nfc(nand->controller);
+
+	if (len > tn->buf.data_len - tn->buf.data_index) {
+		dev_err(tn->dev, "Not enough data for read of %d bytes\n", len);
+		return;
+	}
+
+	memcpy(buf, tn->buf.dmabuf + tn->buf.data_index, len);
+	tn->buf.data_index += len;
+}
+
+static void cvm_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct cvm_nfc *tn = to_cvm_nfc(nand->controller);
+
+	memcpy(tn->buf.dmabuf + tn->buf.data_len, buf, len);
+	tn->buf.data_len += len;
+}
+
+/* Overwrite default function to avoid sync abort on chip = -1. */
+static void cvm_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+	return;
+}
+
+static inline int timing_to_cycle(u32 timing, unsigned long clock)
+{
+	unsigned int ns;
+	int margin = 2;
+
+	ns = DIV_ROUND_UP(timing, 1000);
+
+	clock /= 1000000; /* no rounding needed since clock is multiple of 1MHz */
+	ns *= clock;
+	return DIV_ROUND_UP(ns, 1000) + margin;
+}
+
+static void set_timings(struct ndf_set_tm_par_cmd *tp,
+			const struct nand_sdr_timings *timings,
+			unsigned long sclk)
+{
+	/* scaled coprocessor-cycle values */
+	u32 sWH, sCLS, sCLH, sALS, sALH, sRP, sREH, sWB, sWC;
+
+	tp->tim_mult = 0;
+	sWH = timing_to_cycle(timings->tWH_min, sclk);
+	sCLS = timing_to_cycle(timings->tCLS_min, sclk);
+	sCLH = timing_to_cycle(timings->tCLH_min, sclk);
+	sALS = timing_to_cycle(timings->tALS_min, sclk);
+	sALH = timing_to_cycle(timings->tALH_min, sclk);
+	sRP = timing_to_cycle(timings->tRP_min, sclk);
+	sREH = timing_to_cycle(timings->tREH_min, sclk);
+	sWB = timing_to_cycle(timings->tWB_max, sclk);
+	sWC = timing_to_cycle(timings->tWC_min, sclk);
+
+	tp->tm_par1 = sWH;
+	tp->tm_par2 = sCLH;
+	tp->tm_par3 = sRP + 1;
+	tp->tm_par4 = sCLS - sWH;
+	tp->tm_par5 = sWC - sWH + 1;
+	tp->tm_par6 = sWB;
+	tp->tm_par7 = 0;
+
+	/* TODO: comment paramter re-use */
+
+	pr_debug("%s: tim_par: mult: %d  p1: %d  p2: %d  p3: %d\n",
+		__func__, tp->tim_mult, tp->tm_par1, tp->tm_par2, tp->tm_par3);
+	pr_debug("                 p4: %d  p5: %d  p6: %d  p7: %d\n",
+		tp->tm_par4, tp->tm_par5, tp->tm_par6, tp->tm_par7);
+
+}
+
+static int set_default_timings(struct cvm_nfc *tn,
+			       const struct nand_sdr_timings *timings)
+{
+	unsigned long sclk = clk_get_rate(tn->clk);
+
+	set_timings(&default_timing_parms, timings, sclk);
+	return 0;
+}
+
+static int cvm_nfc_chip_set_timings(struct cvm_nand_chip *chip,
+					 const struct nand_sdr_timings *timings)
+{
+	struct cvm_nfc *tn = to_cvm_nfc(chip->nand.controller);
+	unsigned long sclk = clk_get_rate(tn->clk);
+
+	set_timings(&chip->timings, timings, sclk);
+	return 0;
+}
+
+/* How many bytes are free in the NFD_CMD queue? */
+static int ndf_cmd_queue_free(struct cvm_nfc *tn)
+{
+	u64 ndf_misc;
+
+	ndf_misc = readq(tn->base + NDF_MISC);
+	return FIELD_GET(NDF_MISC_FR_BYTE, ndf_misc);
+}
+
+/* Submit a command to the NAND command queue. */
+static int ndf_submit(struct cvm_nfc *tn, union ndf_cmd *cmd)
+{
+	int opcode = cmd->val[0] & 0xf;
+
+	switch (opcode) {
+        /* All these commands fit in one 64bit word */
+	case NDF_OP_NOP:
+	case NDF_OP_SET_TM_PAR:
+	case NDF_OP_WAIT:
+	case NDF_OP_CHIP_EN_DIS:
+	case NDF_OP_CLE_CMD:
+	case NDF_OP_WR_CMD:
+	case NDF_OP_RD_CMD:
+	case NDF_OP_RD_EDO_CMD:
+	case NDF_OP_BUS_ACQ_REL:
+	        if (ndf_cmd_queue_free(tn) < 8)
+			goto full;
+		writeq(cmd->val[0], tn->base + NDF_CMD);
+		break;
+	case NDF_OP_ALE_CMD: /* ALE commands take either one or two 64bit words */
+		if (cmd->u.ale_cmd.adr_byte_num < 5) {
+			if (ndf_cmd_queue_free(tn) < 8)
+				goto full;
+			writeq(cmd->val[0], tn->base + NDF_CMD);
+		} else {
+			if (ndf_cmd_queue_free(tn) < 16)
+				goto full;
+			writeq(cmd->val[0], tn->base + NDF_CMD);
+			writeq(cmd->val[1], tn->base + NDF_CMD);
+		}
+		break;
+	case NDF_OP_WAIT_STATUS: /* Wait status commands take two 64bit words */
+		if (ndf_cmd_queue_free(tn) < 16)
+			goto full;
+		writeq(cmd->val[0], tn->base + NDF_CMD);
+		writeq(cmd->val[1], tn->base + NDF_CMD);
+		break;
+	default:
+		dev_err(tn->dev, "ndf_submit: unknown command: %u\n", opcode);
+		return -EINVAL;
+	}
+	return 0;
+full:
+	dev_err(tn->dev, "ndf_submit: no space left in command queue\n");
+	return -ENOMEM;
+}
+
+/*
+ * Wait for the ready/busy signal. First wait for busy to be valid,
+ * then wait for busy to de-assert.
+ */
+static int ndf_wait_for_busy_done(struct cvm_nfc *tn)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.wait.opcode = NDF_OP_WAIT;
+	cmd.u.wait.r_b = 1;
+	cmd.u.wait.wlen = 6;
+
+	if (ndf_submit(tn, &cmd))
+		return -ENOMEM;
+	return 0;
+}
+
+static bool ndf_dma_done(struct cvm_nfc *tn)
+{
+	u64 dma_cfg, ndf_int;
+
+	/* Check DMA done bit */
+	ndf_int = readq(tn->base + NDF_INT);
+	if (!(ndf_int & NDF_INT_DMA_DONE))
+		return false;
+
+	/* Enable bit should be clear after a transfer */
+	dma_cfg = readq(tn->base + NDF_DMA_CFG);
+	if (dma_cfg & NDF_DMA_CFG_EN)
+		return false;
+	return true;
+}
+
+static int ndf_wait(struct cvm_nfc *tn)
+{
+	long time_left;
+
+	/* enable all IRQ types */
+	writeq(0xff, tn->base + NDF_INT_ENA_W1S);
+	time_left = wait_event_timeout(tn->controller.wq,
+				       ndf_dma_done(tn), 250);
+	writeq(0xff, tn->base + NDF_INT_ENA_W1C);
+
+	if (!time_left) {
+		dev_err(tn->dev, "ndf_wait: timeout error\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static int ndf_wait_idle(struct cvm_nfc *tn)
+{
+	u64 val;
+
+	return readq_poll_timeout(tn->base + NDF_ST_REG, val,
+				  val & NDF_ST_REG_EXE_IDLE, 100, 100000);
+}
+
+/* Issue set timing parameters */
+static int ndf_queue_cmd_timing(struct cvm_nfc *tn,
+				struct ndf_set_tm_par_cmd *timings)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.set_tm_par.opcode = NDF_OP_SET_TM_PAR;
+	cmd.u.set_tm_par.tim_mult = timings->tim_mult;
+	cmd.u.set_tm_par.tm_par1 = timings->tm_par1;
+	cmd.u.set_tm_par.tm_par2 = timings->tm_par2;
+	cmd.u.set_tm_par.tm_par3 = timings->tm_par3;
+	cmd.u.set_tm_par.tm_par4 = timings->tm_par4;
+	cmd.u.set_tm_par.tm_par5 = timings->tm_par5;
+	cmd.u.set_tm_par.tm_par6 = timings->tm_par6;
+	cmd.u.set_tm_par.tm_par7 = timings->tm_par7;
+	return ndf_submit(tn, &cmd);
+}
+
+/* Issue bus acquire or release */
+static int ndf_queue_cmd_bus(struct cvm_nfc *tn, int direction)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.bus_acq_rel.opcode = NDF_OP_BUS_ACQ_REL;
+	cmd.u.bus_acq_rel.direction = direction;
+	return ndf_submit(tn, &cmd);
+}
+
+/* Issue chip select or deselect */
+static int ndf_queue_cmd_chip(struct cvm_nfc *tn, int enable, int chip,
+			      int width)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.chip_en_dis.opcode = NDF_OP_CHIP_EN_DIS;
+	cmd.u.chip_en_dis.chip = chip;
+	cmd.u.chip_en_dis.enable = enable;
+	cmd.u.chip_en_dis.bus_width = width;
+	return ndf_submit(tn, &cmd);
+}
+
+static int ndf_queue_cmd_wait(struct cvm_nfc *tn, int parm)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.wait.opcode = NDF_OP_WAIT;
+	cmd.u.wait.wlen = parm;
+	return ndf_submit(tn, &cmd);
+}
+
+static int ndf_queue_cmd_cle(struct cvm_nfc *tn, int command)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.cle_cmd.opcode = NDF_OP_CLE_CMD;
+	cmd.u.cle_cmd.cmd_data = command;
+	cmd.u.cle_cmd.clen1 = 4;
+	cmd.u.cle_cmd.clen2 = 1;
+	cmd.u.cle_cmd.clen3 = 2;
+	return ndf_submit(tn, &cmd);
+}
+
+static int ndf_queue_cmd_ale(struct cvm_nfc *tn, int addr_bytes,
+			     struct nand_chip *nand, u64 addr, int page_size)
+{
+	struct cvm_nand_chip *cvm_nand = (nand) ? to_cvm_nand(nand) : NULL;
+	int column = addr & (page_size - 1);
+	u64 row = addr >> ndf_get_column_bits(nand);
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.ale_cmd.opcode = NDF_OP_ALE_CMD;
+	cmd.u.ale_cmd.adr_byte_num = addr_bytes;
+
+	/* set column bit for OOB area, assume OOB follows page */
+	if (cvm_nand && cvm_nand->oob_access)
+		column |= page_size;
+
+	if (addr_bytes == 1) {
+		cmd.u.ale_cmd.adr_byt1 = addr & 0xff;
+	} else if (addr_bytes == 2) {
+		cmd.u.ale_cmd.adr_byt1 = addr & 0xff;
+		cmd.u.ale_cmd.adr_byt2 = (addr >> 8) & 0xff;
+	} else if (addr_bytes == 4) {
+		cmd.u.ale_cmd.adr_byt1 =  column & 0xff;
+		cmd.u.ale_cmd.adr_byt2 = (column >> 8) & 0xff;
+		cmd.u.ale_cmd.adr_byt3 = row & 0xff;
+		cmd.u.ale_cmd.adr_byt4 = (row >> 8) & 0xff;
+	} else if (addr_bytes > 4) {
+		cmd.u.ale_cmd.adr_byt1 =  column & 0xff;
+		cmd.u.ale_cmd.adr_byt2 = (column >> 8) & 0xff;
+		cmd.u.ale_cmd.adr_byt3 = row & 0xff;
+		cmd.u.ale_cmd.adr_byt4 = (row >> 8) & 0xff;
+		/* row bits above 16 */
+		cmd.u.ale_cmd.adr_byt5 = (row >> 16) & 0xff;
+		cmd.u.ale_cmd.adr_byt6 = (row >> 24) & 0xff;
+		cmd.u.ale_cmd.adr_byt7 = (row >> 32) & 0xff;
+		cmd.u.ale_cmd.adr_byt8 = (row >> 40) & 0xff;
+	}
+
+	cmd.u.ale_cmd.alen1 = 3;
+	cmd.u.ale_cmd.alen2 = 1;
+	cmd.u.ale_cmd.alen3 = 5;
+	cmd.u.ale_cmd.alen4 = 2;
+	return ndf_submit(tn, &cmd);
+}
+
+static int ndf_queue_cmd_write(struct cvm_nfc *tn, int len)
+{
+	union ndf_cmd cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.wr_cmd.opcode = NDF_OP_WR_CMD;
+	cmd.u.wr_cmd.data = len;
+	cmd.u.wr_cmd.wlen1 = 3;
+	cmd.u.wr_cmd.wlen2 = 1;
+	return ndf_submit(tn, &cmd);
+}
+
+static int ndf_build_pre_cmd(struct cvm_nfc *tn, int cmd1,
+			     int addr_bytes, u64 addr, int cmd2)
+{
+	struct nand_chip *nand = tn->controller.active;
+	struct cvm_nand_chip *cvm_nand;
+	struct ndf_set_tm_par_cmd *timings;
+	int width, page_size, rc;
+
+	/* Also called before chip probing is finished */
+	if (!nand) {
+		timings = &default_timing_parms;
+		page_size = default_page_size;
+		width = default_width;
+	} else {
+		cvm_nand = to_cvm_nand(nand);
+		timings = &cvm_nand->timings;
+		page_size = nand->onfi_params.byte_per_page;
+		if (nand->onfi_params.features & ONFI_FEATURE_16_BIT_BUS)
+			width = 2;
+		else
+			width = 1;
+	}
+
+	rc = ndf_queue_cmd_timing(tn, timings);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_bus(tn, NDF_BUS_ACQUIRE);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_chip(tn, 1, tn->selected_chip, width);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_wait(tn, 1);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_cle(tn, cmd1);
+	if (rc)
+		return rc;
+
+	if (addr_bytes) {
+		rc = ndf_queue_cmd_ale(tn, addr_bytes, nand, addr, page_size);
+		if (rc)
+			return rc;
+	}
+
+	/* CLE 2 */
+	if (cmd2) {
+		rc = ndf_queue_cmd_cle(tn, cmd2);
+		if (rc)
+			return rc;
+	}
+	return 0;
+}
+
+static int ndf_build_post_cmd(struct cvm_nfc *tn)
+{
+	int rc;
+
+	/* Deselect chip */
+	rc = ndf_queue_cmd_chip(tn, 0, 0, 0);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_wait(tn, 2);
+	if (rc)
+		return rc;
+
+	/* Release bus */
+	rc = ndf_queue_cmd_bus(tn, 0);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_wait(tn, 2);
+	if (rc)
+		return rc;
+
+	/* Write 1 to clear all interrupt bits before starting DMA */
+	writeq(0xff, tn->base + NDF_INT);
+
+	/*
+	 * Last action is ringing the doorbell with number of bus
+	 * acquire-releases cycles (currently 1).
+	 */
+	writeq(1, tn->base + NDF_DRBELL);
+	return 0;
+}
+
+/* Setup the NAND DMA engine for a transfer. */
+static void ndf_setup_dma(struct cvm_nfc *tn, int is_write,
+			  dma_addr_t bus_addr, int len)
+{
+	u64 dma_cfg;
+
+	dma_cfg = FIELD_PREP(NDF_DMA_CFG_RW, is_write) |
+		  FIELD_PREP(NDF_DMA_CFG_SIZE, (len >> 3) - 1);
+	dma_cfg |= NDF_DMA_CFG_EN;
+	writeq(dma_cfg, tn->base + NDF_DMA_CFG);
+	writeq(bus_addr, tn->base + NDF_DMA_ADR);
+}
+
+static int cvm_nand_reset(struct cvm_nfc *tn)
+{
+	int rc;
+
+	rc = ndf_build_pre_cmd(tn, NAND_CMD_RESET, 0, 0, 0);
+	if (rc)
+		return rc;
+
+	rc = ndf_wait_for_busy_done(tn);
+	if (rc)
+		return rc;
+
+	rc = ndf_build_post_cmd(tn);
+	if (rc)
+		return rc;
+	return 0;
+}
+
+static int cvm_nand_set_features(struct mtd_info *mtd,
+				      struct nand_chip *chip, int feature_addr,
+				      u8 *subfeature_para)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct cvm_nfc *tn = to_cvm_nfc(nand->controller);
+	int rc;
+
+	rc = ndf_build_pre_cmd(tn, NAND_CMD_SET_FEATURES, 1, feature_addr, 0);
+	if (rc)
+		return rc;
+
+	memcpy(tn->buf.dmabuf, subfeature_para, 4);
+	memset(tn->buf.dmabuf + 4, 0, 4);
+
+	rc = ndf_queue_cmd_write(tn, 8);
+	if (rc)
+		return rc;
+
+	ndf_setup_dma(tn, 0, tn->buf.dmaaddr, 8);
+
+	rc = ndf_wait_for_busy_done(tn);
+	if (rc)
+		return rc;
+
+	rc = ndf_build_post_cmd(tn);
+	if (rc)
+		return rc;
+	return 0;
+}
+
+static int ndf_read(struct cvm_nfc *tn, int cmd1, int addr_bytes, u64 addr,
+		    int cmd2, int len)
+{
+	dma_addr_t bus_addr = (cmd1 != NAND_CMD_STATUS) ?
+			      tn->buf.dmaaddr : tn->stat_addr;
+	struct nand_chip *nand = tn->controller.active;
+	int timing_mode, bytes, rc;
+	union ndf_cmd cmd;
+	u64 start, end;
+
+	if (!nand)
+		timing_mode = default_onfi_timing;
+	else
+		timing_mode = nand->onfi_params.async_timing_mode;
+
+	/* Build the command and address cycles */
+	rc = ndf_build_pre_cmd(tn, cmd1, addr_bytes, addr, cmd2);
+	if (rc)
+		return rc;
+
+	/* This waits for some time, then waits for busy to be de-asserted. */
+	rc = ndf_wait_for_busy_done(tn);
+	if (rc)
+		return rc;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.u.wait.opcode = NDF_OP_WAIT;
+	cmd.u.wait.wlen = 3;	/* tRR is 15 cycles, this is 16 so its ok */
+	rc = ndf_submit(tn, &cmd);
+	if (rc)
+		return rc;
+	rc = ndf_submit(tn, &cmd);
+	if (rc)
+		return rc;
+
+	memset(&cmd, 0, sizeof(cmd));
+	if (timing_mode == ONFI_TIMING_MODE_4 ||
+	    timing_mode == ONFI_TIMING_MODE_5)
+		cmd.u.rd_cmd.opcode = NDF_OP_RD_EDO_CMD;
+	else
+		cmd.u.rd_cmd.opcode = NDF_OP_RD_CMD;
+	cmd.u.rd_cmd.data = len;
+	cmd.u.rd_cmd.rlen1 = 7;
+	cmd.u.rd_cmd.rlen2 = 3;
+	cmd.u.rd_cmd.rlen3 = 1;
+	cmd.u.rd_cmd.rlen4 = 7;
+	rc = ndf_submit(tn, &cmd);
+	if (rc)
+		return rc;
+
+	start = (u64) bus_addr;
+	ndf_setup_dma(tn, 0, bus_addr, len);
+
+	rc = ndf_build_post_cmd(tn);
+	if (rc)
+		return rc;
+
+	/* Wait for the DMA to complete */
+	rc = ndf_wait(tn);
+	if (rc)
+		return rc;
+
+	end = readq(tn->base + NDF_DMA_ADR);
+	bytes = end - start;
+
+	/* Make sure NDF is really done */
+	rc = ndf_wait_idle(tn);
+	if (rc) {
+		dev_err(tn->dev, "poll idle failed\n");
+		return rc;
+	}
+	return bytes;
+}
+
+/*
+ * Read a page from NAND. If the buffer has room, the out of band
+ * data will be included.
+ */
+int ndf_page_read(struct cvm_nfc *tn, u64 addr, int len)
+{
+	int rc;
+
+	memset(tn->buf.dmabuf, 0xff, len);
+	rc = ndf_read(tn, NAND_CMD_READ0, 4, addr, NAND_CMD_READSTART, len);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+/* Erase a NAND block */
+static int ndf_block_erase(struct cvm_nfc *tn, u64 addr)
+{
+	struct nand_chip *nand = tn->controller.active;
+	int row, rc;
+
+	row = addr >> ndf_get_column_bits(nand);
+	rc = ndf_build_pre_cmd(tn, NAND_CMD_ERASE1, 2, row, NAND_CMD_ERASE2);
+	if (rc)
+		return rc;
+
+	/* Wait for R_B to signal erase is complete  */
+        rc = ndf_wait_for_busy_done(tn);
+        if (rc)
+                return rc;
+
+        rc = ndf_build_post_cmd(tn);
+        if (rc)
+                return rc;
+
+        /* Wait until the command queue is idle */
+	return ndf_wait_idle(tn);
+}
+
+/*
+ * Write a page (or less) to NAND.
+ */
+static int ndf_page_write(struct cvm_nfc *tn, u64 addr)
+{
+	int len, rc;
+
+	len = tn->buf.data_len - tn->buf.data_index;
+	WARN_ON_ONCE(len & 0x7);
+
+	ndf_setup_dma(tn, 1, tn->buf.dmaaddr + tn->buf.data_index, len);
+	rc = ndf_build_pre_cmd(tn, NAND_CMD_SEQIN, 4, addr, 0);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_write(tn, len);
+	if (rc)
+		return rc;
+
+	rc = ndf_queue_cmd_cle(tn, NAND_CMD_PAGEPROG);
+	if (rc)
+		return rc;
+
+	/* Wait for R_B to signal program is complete  */
+	rc = ndf_wait_for_busy_done(tn);
+	if (rc)
+		return rc;
+
+	rc = ndf_build_post_cmd(tn);
+	if (rc)
+		return rc;
+
+	/* Wait for the DMA to complete */
+	rc = ndf_wait(tn);
+	if (rc)
+		return rc;
+
+	/* Data transfer is done but NDF is not, it is waiting for R/B# */
+	return ndf_wait_idle(tn);
+}
+
+static void cvm_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+				  int column, int page_addr)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct cvm_nand_chip *cvm_nand = to_cvm_nand(nand);
+	struct cvm_nfc *tn = to_cvm_nfc(nand->controller);
+	int rc;
+
+	tn->selected_chip = cvm_nand->cs;
+	if (tn->selected_chip < 0 || tn->selected_chip >= NAND_MAX_CHIPS) {
+		dev_err(tn->dev, "invalid chip select\n");
+		return;
+	}
+
+	tn->use_status = false;
+	cvm_nand->oob_access = false;
+
+	switch (command) {
+	case NAND_CMD_READID:
+		tn->buf.data_index = 0;
+		memset(tn->buf.dmabuf, 0xff, 8);
+		rc = ndf_read(tn, command, 1, column, 0, 8);
+		if (rc < 0)
+			dev_err(tn->dev, "READID failed with %d\n", rc);
+		else
+			tn->buf.data_len = rc;
+		break;
+
+	case NAND_CMD_READOOB:
+		cvm_nand->oob_access = true;
+		tn->buf.data_index = 0;
+		tn->buf.data_len = ndf_page_read(tn,
+				(page_addr << nand->page_shift) + 0x800,
+				mtd->oobsize);
+
+		if (tn->buf.data_len < mtd->oobsize) {
+			dev_err(tn->dev, "READOOB failed with %d\n",
+				tn->buf.data_len);
+			tn->buf.data_len = 0;
+		}
+		break;
+
+	case NAND_CMD_READ0:
+		tn->buf.data_index = 0;
+		tn->buf.data_len = ndf_page_read(tn,
+				column + (page_addr << nand->page_shift),
+				(1 << nand->page_shift) + mtd->oobsize);
+		if (tn->buf.data_len < (1 << nand->page_shift) + mtd->oobsize) {
+			dev_err(tn->dev, "READ0 failed with %d\n",
+				tn->buf.data_len);
+			tn->buf.data_len = 0;
+		}
+		break;
+
+	case NAND_CMD_STATUS:
+		tn->use_status = true;
+		memset(tn->stat, 0xff, 8);
+		rc = ndf_read(tn, command, 0, 0, 0, 8);
+		if (rc < 0)
+			dev_err(tn->dev, "STATUS failed with %d\n", rc);
+		break;
+
+	case NAND_CMD_RESET:
+		tn->buf.data_index = 0;
+		tn->buf.data_len = 0;
+		memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen);
+		rc = cvm_nand_reset(tn);
+		if (rc < 0)
+			dev_err(tn->dev, "RESET failed with %d\n", rc);
+                break;
+
+	case NAND_CMD_PARAM:
+		tn->buf.data_index = column;
+		memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen);
+		rc = ndf_read(tn, command, 1, 0, 0, 2048);
+		if (rc < 0)
+			dev_err(tn->dev, "PARAM failed with %d\n", rc);
+		else
+			tn->buf.data_len = rc;
+                break;
+
+	case NAND_CMD_RNDOUT:
+		tn->buf.data_index = column;
+		break;
+
+	case NAND_CMD_ERASE1:
+		if (ndf_block_erase(tn, page_addr << nand->page_shift))
+			dev_err(tn->dev, "ERASE1 failed\n");
+		break;
+
+	case NAND_CMD_ERASE2:
+		/* We do all erase processing in the first command, so ignore
+		 * this one.
+		 */
+		break;
+
+	case NAND_CMD_SEQIN:
+		if (column == mtd->writesize)
+			cvm_nand->oob_access = true;
+		tn->buf.data_index = column;
+		tn->buf.data_len = column;
+		cvm_nand->selected_page = page_addr;
+		break;
+
+	case NAND_CMD_PAGEPROG:
+		rc = ndf_page_write(tn,
+			cvm_nand->selected_page << nand->page_shift);
+		if (rc)
+			dev_err(tn->dev, "PAGEPROG failed with %d\n", rc);
+		break;
+
+	default:
+		WARN_ON_ONCE(1);
+		dev_err(tn->dev, "unhandled nand cmd: %x\n", command);
+	}
+}
+
+static int cvm_nfc_chip_init_timings(struct cvm_nand_chip *chip,
+					   struct device_node *np)
+{
+	const struct nand_sdr_timings *timings;
+	int ret, mode;
+
+	mode = onfi_get_async_timing_mode(&chip->nand);
+	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
+		mode = chip->nand.onfi_timing_mode_default;
+	} else {
+		u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
+
+		mode = fls(mode) - 1;
+		if (mode < 0)
+			mode = 0;
+
+		feature[0] = mode;
+		ret = chip->nand.onfi_set_features(&chip->nand.mtd, &chip->nand,
+						ONFI_FEATURE_ADDR_TIMING_MODE,
+						feature);
+		if (ret)
+			return ret;
+	}
+
+	timings = onfi_async_timing_mode_to_sdr_timings(mode);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+
+	return cvm_nfc_chip_set_timings(chip, timings);
+}
+
+static int cvm_nfc_chip_init(struct cvm_nfc *tn, struct device *dev,
+				   struct device_node *np)
+{
+	struct cvm_nand_chip *chip;
+	struct nand_chip *nand;
+	struct mtd_info *mtd;
+	int ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "reg", &chip->cs);
+	if (ret) {
+		dev_err(dev, "could not retrieve reg property: %d\n", ret);
+		return ret;
+	}
+
+	if (chip->cs >= NAND_MAX_CHIPS) {
+		dev_err(dev, "invalid reg value: %u (max CS = 7)\n", chip->cs);
+		return -EINVAL;
+	}
+
+	nand = &chip->nand;
+	nand->controller = &tn->controller;
+
+	nand_set_flash_node(nand, np);
+
+	nand->select_chip = cvm_nand_select_chip;
+	nand->cmdfunc = cvm_nand_cmdfunc;
+	nand->onfi_set_features = cvm_nand_set_features;
+	nand->read_byte = cvm_nand_read_byte;
+	nand->read_buf = cvm_nand_read_buf;
+	nand->write_buf = cvm_nand_write_buf;
+
+	mtd = nand_to_mtd(nand);
+	mtd->dev.parent = dev;
+
+	/* TODO: support more then 1 chip */
+	ret = nand_scan_ident(mtd, 1, NULL);
+	if (ret)
+		return ret;
+
+	ret = cvm_nfc_chip_init_timings(chip, np);
+	if (ret) {
+		dev_err(dev, "could not configure chip timings: %d\n", ret);
+		return ret;
+	}
+
+	ret = nand_scan_tail(mtd);
+	if (ret) {
+		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device: %d\n", ret);
+		nand_release(mtd);
+		return ret;
+	}
+
+	list_add_tail(&chip->node, &tn->chips);
+	return 0;
+}
+
+static int cvm_nfc_chips_init(struct cvm_nfc *tn, struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int nr_chips = of_get_child_count(np);
+	int ret;
+
+	if (nr_chips > NAND_MAX_CHIPS) {
+		dev_err(dev, "too many NAND chips: %d\n", nr_chips);
+		return -EINVAL;
+	}
+
+	if (!nr_chips) {
+		dev_err(dev, "no DT NAND chips found\n");
+		return -ENODEV;
+	}
+
+	pr_info("%s: scanning %d chips DTs\n", __func__, nr_chips);
+
+	for_each_child_of_node(np, nand_np) {
+		ret = cvm_nfc_chip_init(tn, dev, nand_np);
+		if (ret) {
+			of_node_put(nand_np);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+/* Reset NFC and initialize registers. */
+static int cvm_nfc_init(struct cvm_nfc *tn)
+{
+	const struct nand_sdr_timings *timings;
+	u64 ndf_misc;
+	int rc;
+
+	/* Initialize values and reset the fifo */
+	ndf_misc = readq(tn->base + NDF_MISC);
+
+	ndf_misc &= ~NDF_MISC_EX_DIS;
+	ndf_misc |= (NDF_MISC_BT_DIS | NDF_MISC_RST_FF);
+	writeq(ndf_misc, tn->base + NDF_MISC);
+
+	/* Bring the fifo out of reset */
+	ndf_misc &= ~(NDF_MISC_RST_FF);
+
+	/* Maximum of co-processor cycles for glitch filtering */
+	ndf_misc |= FIELD_PREP(NDF_MISC_WAIT_CNT, 0x3f);
+
+	writeq(ndf_misc, tn->base + NDF_MISC);
+
+	/* Set timing paramters to onfi mode 0 for probing */
+	timings = onfi_async_timing_mode_to_sdr_timings(0);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+	rc = set_default_timings(tn, timings);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int cvm_nfc_probe(struct pci_dev *pdev,
+			      const struct pci_device_id *ent)
+{
+	struct device *dev = &pdev->dev;
+	struct cvm_nfc *tn;
+	int ret;
+
+	tn = devm_kzalloc(dev, sizeof(*tn), GFP_KERNEL);
+	if (!tn)
+		return -ENOMEM;
+
+	tn->dev = dev;
+	spin_lock_init(&tn->controller.lock);
+	init_waitqueue_head(&tn->controller.wq);
+	INIT_LIST_HEAD(&tn->chips);
+
+	memset(tn->buf.dmabuf, 0xff, tn->buf.dmabuflen);
+
+	pci_set_drvdata(pdev, tn);
+	ret = pcim_enable_device(pdev);
+	if (ret)
+		return ret;
+	ret = pci_request_regions(pdev, KBUILD_MODNAME);
+	if (ret)
+		return ret;
+	tn->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
+	if (!tn->base)
+		return -EINVAL;
+
+	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
+	if (ret < 0)
+		return ret;
+	ret = devm_request_irq(dev, pci_irq_vector(pdev, 0),
+			       cvm_nfc_isr, 0, "nand-flash-controller", tn);
+	if (ret)
+		return ret;
+
+	tn->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(tn->clk))
+		return PTR_ERR(tn->clk);
+
+	ret = clk_prepare_enable(tn->clk);
+	if (ret)
+		return ret;
+
+	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
+		dev_err(dev, "64 bit DMA mask not available\n");
+
+	tn->buf.dmabuflen = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE;
+	tn->buf.dmabuf = dmam_alloc_coherent(dev, tn->buf.dmabuflen,
+					     &tn->buf.dmaaddr, GFP_KERNEL);
+	if (!tn->buf.dmabuf) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	tn->stat = dmam_alloc_coherent(dev, 8, &tn->stat_addr, GFP_KERNEL);
+	if (!tn->stat) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	cvm_nfc_init(tn);
+	ret = cvm_nfc_chips_init(tn, dev);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto error;
+	}
+	dev_info(&pdev->dev, "probed\n");
+	return 0;
+
+error:
+	clk_disable_unprepare(tn->clk);
+	return ret;
+}
+
+static void cvm_nfc_remove(struct pci_dev *pdev)
+{
+	struct cvm_nfc *tn = pci_get_drvdata(pdev);
+	struct cvm_nand_chip *chip;
+
+	while (!list_empty(&tn->chips)) {
+		chip = list_first_entry(&tn->chips, struct cvm_nand_chip,
+					node);
+		nand_release(&chip->nand.mtd);
+		list_del(&chip->node);
+	}
+	clk_disable_unprepare(tn->clk);
+}
+
+static const struct pci_device_id cvm_nfc_pci_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa04f) },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cvm_nfc_pci_id_table);
+
+static struct pci_driver cvm_nfc_pci_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= cvm_nfc_pci_id_table,
+	.probe		= cvm_nfc_probe,
+	.remove		= cvm_nfc_remove,
+};
+
+module_pci_driver(cvm_nfc_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jan Glauber <jglauber at cavium.com>");
+MODULE_DESCRIPTION("Cavium Inc. cvm NAND driver");
diff --git a/drivers/mtd/nand/cavium_nand.h b/drivers/mtd/nand/cavium_nand.h
new file mode 100644
index 0000000..7030c57
--- /dev/null
+++ b/drivers/mtd/nand/cavium_nand.h
@@ -0,0 +1,231 @@
+#ifndef _CAVIUM_NAND_H
+#define _CAVIUM_NAND_H
+
+#include <linux/bitops.h>
+
+/*
+ * The NDF_CMD queue takes commands between 16 - 128 bit.
+ * All commands must be 16 bit aligned and are little endian.
+ * WAIT_STATUS commands must be 64 bit aligned.
+ * Commands are selected by the 4 bit opcode.
+ *
+ * Available Commands:
+ *
+ * 16 Bit:
+ *   NOP
+ *   WAIT
+ *   BUS_ACQ, BUS_REL
+ *   CHIP_EN, CHIP_DIS
+ *
+ * 32 Bit:
+ *   CLE_CMD
+ *   RD_CMD, RD_EDO_CMD
+ *   WR_CMD
+ *
+ * 64 Bit:
+ *   SET_TM_PAR
+ *
+ * 96 Bit:
+ *   ALE_CMD
+ *
+ * 128 Bit:
+ *   WAIT_STATUS, WAIT_STATUS_ALE
+ */
+
+/* NDF Register offsets */
+#define NDF_CMD			0x0
+#define NDF_MISC		0x8
+#define NDF_ECC_CNT		0x10
+#define NDF_DRBELL		0x30
+#define NDF_ST_REG		0x38	/* status */
+#define NDF_INT			0x40
+#define NDF_INT_W1S		0x48
+#define NDF_DMA_CFG		0x50
+#define NDF_DMA_ADR		0x58
+#define NDF_INT_ENA_W1C		0x60
+#define NDF_INT_ENA_W1S		0x68
+
+/* NDF command opcodes */
+#define NDF_OP_NOP		0x0
+#define NDF_OP_SET_TM_PAR	0x1
+#define NDF_OP_WAIT		0x2
+#define NDF_OP_CHIP_EN_DIS	0x3
+#define NDF_OP_CLE_CMD		0x4
+#define NDF_OP_ALE_CMD		0x5
+#define NDF_OP_WR_CMD		0x8
+#define NDF_OP_RD_CMD		0x9
+#define NDF_OP_RD_EDO_CMD	0xa
+#define NDF_OP_WAIT_STATUS	0xb	/* same opcode for WAIT_STATUS_ALE */
+#define NDF_OP_BUS_ACQ_REL	0xf
+
+#define NDF_BUS_ACQUIRE		1
+#define NDF_BUS_RELEASE		0
+
+struct ndf_nop_cmd {
+	u16 opcode	: 4;
+	u16 nop		: 12;
+};
+
+struct ndf_wait_cmd {
+	u16 opcode	: 4;
+	u16 r_b		: 1;	/* wait for one cycle or PBUS_WAIT deassert */
+	u16		: 3;
+	u16 wlen	: 3;	/* timing parameter select */
+	u16		: 5;
+};
+
+struct ndf_bus_cmd {
+	u16 opcode	: 4;
+	u16 direction	: 4;	/* 1 = acquire, 0 = release */
+	u16		: 8;
+};
+
+struct ndf_chip_cmd {
+	u16 opcode	: 4;
+	u16 chip	: 3;	/* select chip, 0 = disable */
+	u16 enable	: 1;	/* 1 = enable, 0 = disable */
+	u16 bus_width	: 2;	/* 10 = 16 bit, 01 = 8 bit */
+	u16		: 6;
+};
+
+struct ndf_cle_cmd {
+	u32 opcode	: 4;
+	u32		: 4;
+	u32 cmd_data	: 8;	/* command sent to the PBUS AD pins */
+	u32 clen1	: 3;	/* time between PBUS CLE and WE asserts */
+	u32 clen2	: 3;	/* time WE remains asserted */
+	u32 clen3	: 3;	/* time between WE deassert and CLE */
+	u32		: 7;
+};
+
+/* RD_EDO_CMD uses the same layout as RD_CMD */
+struct ndf_rd_cmd {
+	u32 opcode	: 4;
+	u32 data	: 16;	/* data bytes */
+	u32 rlen1	: 3;
+	u32 rlen2	: 3;
+	u32 rlen3	: 3;
+	u32 rlen4	: 3;
+};
+
+struct ndf_wr_cmd {
+	u32 opcode	: 4;
+	u32 data	: 16;	/* data bytes */
+	u32		: 4;
+	u32 wlen1	: 3;
+	u32 wlen2	: 3;
+	u32		: 3;
+};
+
+struct ndf_set_tm_par_cmd {
+	u64 opcode	: 4;
+	u64 tim_mult	: 4;	/* multiplier for the seven paramters */
+	u64 tm_par1	: 8;	/* --> Following are the 7 timing parameters that */
+	u64 tm_par2	: 8;	/*     specify the number of coprocessor cycles.  */
+	u64 tm_par3	: 8;	/*     A value of zero means one cycle.		  */
+	u64 tm_par4	: 8;	/*     All values are scaled by tim_mult	  */
+	u64 tm_par5	: 8;	/*     using tim_par * (2 ^ tim_mult).		  */
+	u64 tm_par6	: 8;
+	u64 tm_par7	: 8;
+};
+
+struct ndf_ale_cmd {
+	u32 opcode	: 4;
+	u32		: 4;
+	u32 adr_byte_num: 4;	/* number of address bytes to be sent */
+	u32		: 4;
+	u32 alen1	: 3;
+	u32 alen2	: 3;
+	u32 alen3	: 3;
+	u32 alen4	: 3;
+	u32		: 4;
+	u8 adr_byt1;
+	u8 adr_byt2;
+	u8 adr_byt3;
+	u8 adr_byt4;
+	u8 adr_byt5;
+	u8 adr_byt6;
+	u8 adr_byt7;
+	u8 adr_byt8;
+};
+
+struct ndf_wait_status_cmd {
+	u32 opcode	: 4;
+	u32		: 4;
+	u32 data	: 8;	/* data */
+	u32 clen1	: 3;
+	u32 clen2	: 3;
+	u32 clen3	: 3;
+	u32		: 8;
+	u32 ale_ind	: 8;	/* set to 5 to select WAIT_STATUS_ALE command */
+	u32 adr_byte_num: 4;	/* ALE only: number of address bytes to be sent */
+	u32		: 4;
+	u32 alen1	: 3;	/* ALE only */
+	u32 alen2	: 3;	/* ALE only */
+	u32 alen3	: 3;	/* ALE only */
+	u32 alen4	: 3;	/* ALE only */
+	u32		: 4;
+	u8 adr_byt[4];		/* ALE only */
+	u32 nine	: 4;	/* set to 9 */
+	u32 and_mask	: 8;
+	u32 comp_byte	: 8;
+	u32 rlen1	: 3;
+	u32 rlen2	: 3;
+	u32 rlen3	: 3;
+	u32 rlen4	: 3;
+};
+
+union ndf_cmd {
+	u64 val[2];
+	union {
+		struct ndf_nop_cmd		nop;
+		struct ndf_wait_cmd		wait;
+		struct ndf_bus_cmd		bus_acq_rel;
+		struct ndf_chip_cmd		chip_en_dis;
+		struct ndf_cle_cmd		cle_cmd;
+		struct ndf_rd_cmd		rd_cmd;
+		struct ndf_wr_cmd		wr_cmd;
+		struct ndf_set_tm_par_cmd	set_tm_par;
+		struct ndf_ale_cmd		ale_cmd;
+		struct ndf_wait_status_cmd	wait_status;
+	} u;
+};
+
+#define NDF_MISC_MB_DIS		BIT_ULL(27)	/* Disable multi-bit error hangs */
+#define NDF_MISC_NBR_HWM	GENMASK_ULL(26, 24) /* High watermark for NBR FIFO or load/store operations */
+#define NDF_MISC_WAIT_CNT	GENMASK_ULL(23, 18) /* Wait input filter count */
+#define NDF_MISC_FR_BYTE	GENMASK_ULL(17, 7) /* Unfilled NFD_CMD queue bytes */
+#define NDF_MISC_RD_DONE	BIT_ULL(6)	/* Set by HW when it reads the last 8 bytes of NDF_CMD */
+#define NDF_MISC_RD_VAL		BIT_ULL(5)	/* Set by HW when it reads. SW read of NDF_CMD clears it */
+#define NDF_MISC_RD_CMD		BIT_ULL(4)	/* Let HW read NDF_CMD queue. Cleared on SW NDF_CMD write */
+#define NDF_MISC_BT_DIS		BIT_ULL(2)	/* Boot disable */
+#define NDF_MISC_EX_DIS		BIT_ULL(1)	/* Stop comand execution after completing command queue */
+#define NDF_MISC_RST_FF		BIT_ULL(0)	/* Reset fifo */
+
+#define NDF_INT_DMA_DONE	BIT_ULL(7)	/* DMA request complete */
+#define NDF_INT_OVFR		BIT_ULL(6)	/* NDF_CMD write when queue is full */
+#define NDF_INT_ECC_MULT	BIT_ULL(5)	/* Multi-bit ECC error detected */
+#define NDF_INT_ECC_1BIT	BIT_ULL(4)	/* Single-bit ECC error detected and fixed */
+#define NDF_INT_SM_BAD		BIT_ULL(3)	/* State machine is in bad state */
+#define NDF_INT_WDOG		BIT_ULL(2)	/* Watchdog timer expired during command execution */
+#define NDF_INT_FULL		BIT_ULL(1)	/* NDF_CMD queue is full */
+#define NDF_INT_EMPTY		BIT_ULL(0)	/* NDF_CMD queue is empty */
+
+#define NDF_DMA_CFG_EN		BIT_ULL(63)	/* DMA engine enable */
+#define NDF_DMA_CFG_RW		BIT_ULL(62)	/* Read or write */
+#define NDF_DMA_CFG_CLR		BIT_ULL(61)	/* Terminates DMA and clears enable bit */
+#define NDF_DMA_CFG_SWAP32	BIT_ULL(59)	/* 32-bit swap enable */
+#define NDF_DMA_CFG_SWAP16	BIT_ULL(58)	/* 16-bit swap enable */
+#define NDF_DMA_CFG_SWAP8	BIT_ULL(57)	/* 8-bit swap enable */
+#define NDF_DMA_CFG_CMD_BE	BIT_ULL(56)	/* Endian mode */
+#define NDF_DMA_CFG_SIZE	GENMASK_ULL(55, 36) /* Number of 64 bit transfers */
+
+#define NDF_ST_REG_EXE_IDLE	BIT_ULL(15)	/* Command execution status idle */
+#define NDF_ST_REG_EXE_SM	GENMASK_ULL(14, 11) /* Command execution SM states */
+#define NDF_ST_REG_BT_SM	GENMASK_ULL(10, 7) /* DMA and load SM states */
+#define NDF_ST_REG_RD_FF_BAD	BIT_ULL(6)	/* Queue read-back SM bad state */
+#define NDF_ST_REG_RD_FF	GENMASK_ULL(5, 4) /* Queue read-back SM states */
+#define NDF_ST_REG_MAIN_BAD	BIT_ULL(3)	/* Main SM is in a bad state */
+#define NDF_ST_REG_MAIN_SM	GENMASK_ULL(2, 0) /* Main SM states */
+
+#endif
-- 
2.9.0.rc0.21.g7777322




More information about the linux-mtd mailing list