[OpenWrt-Devel] [PATCH 2/3] ipq806x: add NAND flash controller support

Mathieu Olivari mathieu at codeaurora.org
Thu Aug 6 22:28:41 EDT 2015


These patches add support for ipq806x NAND flash controller. Most of
these are cherry-picked & backported from LKML:
*https://lkml.org/lkml/2015/8/3/16

This patch just modifies the kernel code, but doesn't change the config.
It should be harmless.

Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
---
 .../160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch |   74 +
 ...g-to-access-bad-block-markers-in-raw-mode.patch |   84 +
 ...-mtd-nand-Qualcomm-NAND-controller-driver.patch | 2024 ++++++++++++++++++++
 ...63-dt-bindings-qcom_nandc-Add-DT-bindings.patch |   82 +
 ...-dts-Add-NAND-controller-node-for-ipq806x.patch |   50 +
 ...nable-NAND-node-on-IPQ8064-AP148-platform.patch |   79 +
 ...dd-support-for-NSS-GMAC-clocks-and-resets.patch |   20 +-
 ...RM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch |   11 +-
 ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch |    7 +-
 ...g-to-access-bad-block-markers-in-raw-mode.patch |   84 +
 ...-mtd-nand-Qualcomm-NAND-controller-driver.patch | 2024 ++++++++++++++++++++
 ...63-dt-bindings-qcom_nandc-Add-DT-bindings.patch |   82 +
 ...-dts-Add-NAND-controller-node-for-ipq806x.patch |   50 +
 ...nable-NAND-node-on-IPQ8064-AP148-platform.patch |   76 +
 ...RM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch |   11 +-
 ...-qcom-add-gmac-nodes-to-ipq806x-platforms.patch |    7 +-
 16 files changed, 4739 insertions(+), 26 deletions(-)
 create mode 100644 target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch
 create mode 100644 target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
 create mode 100644 target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
 create mode 100644 target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
 create mode 100644 target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
 create mode 100644 target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
 create mode 100644 target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
 create mode 100644 target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
 create mode 100644 target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
 create mode 100644 target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
 create mode 100644 target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch

diff --git a/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch b/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch
new file mode 100644
index 0000000..77d29d8
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/160-clk-qcom-Add-EBI2-clocks-for-IPQ806x.patch
@@ -0,0 +1,74 @@
+From 4c385b25fab119144bffb255ad77712fe586ac10 Mon Sep 17 00:00:00 2001
+From: Archit Taneja <architt at codeaurora.org>
+Date: Thu, 2 Apr 2015 11:20:41 +0530
+Subject: [PATCH] clk: qcom: Add EBI2 clocks for IPQ806x
+
+The NAND controller within EBI2 requires EBI2_CLK and
+EBI2_ALWAYS_ON_CLK clocks.  Create structs for these clocks so
+that they can be used by the NAND controller driver. Add an entry
+for EBI2_AON_CLK in the gcc-ipq806x DT binding document.
+
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+---
+ drivers/clk/qcom/gcc-ipq806x.c               | 32 ++++++++++++++++++++++++++++
+ include/dt-bindings/clock/qcom,gcc-ipq806x.h |  1 +
+ 2 files changed, 33 insertions(+)
+
+--- a/drivers/clk/qcom/gcc-ipq806x.c
++++ b/drivers/clk/qcom/gcc-ipq806x.c
+@@ -2239,6 +2239,36 @@ static struct clk_branch usb_fs1_h_clk =
+ 	},
+ };
+ 
++static struct clk_branch ebi2_clk = {
++	.hwcg_reg = 0x3b00,
++	.hwcg_bit = 6,
++	.halt_reg = 0x2fcc,
++	.halt_bit = 1,
++	.clkr = {
++		.enable_reg = 0x3b00,
++		.enable_mask = BIT(4),
++		.hw.init = &(struct clk_init_data){
++			.name = "ebi2_clk",
++			.ops = &clk_branch_ops,
++			.flags = CLK_IS_ROOT,
++		},
++	},
++};
++
++static struct clk_branch ebi2_aon_clk = {
++	.halt_reg = 0x2fcc,
++	.halt_bit = 0,
++	.clkr = {
++		.enable_reg = 0x3b00,
++		.enable_mask = BIT(8),
++		.hw.init = &(struct clk_init_data){
++			.name = "ebi2_always_on_clk",
++			.ops = &clk_branch_ops,
++			.flags = CLK_IS_ROOT,
++		},
++	},
++};
++
+ static struct clk_regmap *gcc_ipq806x_clks[] = {
+ 	[PLL0] = &pll0.clkr,
+ 	[PLL0_VOTE] = &pll0_vote,
+@@ -2341,6 +2371,8 @@ static struct clk_regmap *gcc_ipq806x_cl
+ 	[USB_FS1_XCVR_SRC] = &usb_fs1_xcvr_clk_src.clkr,
+ 	[USB_FS1_XCVR_CLK] = &usb_fs1_xcvr_clk.clkr,
+ 	[USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr,
++	[EBI2_CLK] = &ebi2_clk.clkr,
++	[EBI2_AON_CLK] = &ebi2_aon_clk.clkr,
+ 	[PLL9] = &hfpll0.clkr,
+ 	[PLL10] = &hfpll1.clkr,
+ 	[PLL12] = &hfpll_l2.clkr,
+--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
+@@ -289,5 +289,6 @@
+ #define UBI32_CORE2_CLK_SRC			278
+ #define UBI32_CORE1_CLK				279
+ #define UBI32_CORE2_CLK				280
++#define EBI2_AON_CLK				281
+ 
+ #endif
diff --git a/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
new file mode 100644
index 0000000..3fb4785
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
@@ -0,0 +1,84 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,
+	1/5] mtd: nand: Create a BBT flag to access bad block markers in raw
+	mode
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927081
+Message-Id: <1438578498-32254-2-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>
+Date: Mon,  3 Aug 2015 10:38:14 +0530
+
+Some controllers can access the factory bad block marker from OOB only
+when they read it in raw mode. When ECC is enabled, these controllers
+discard reading/writing bad block markers, preventing access to them
+altogether.
+
+The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
+This results in the nand driver's ecc->read_oob() op to be called, which
+works with ECC enabled.
+
+Create a new BBT option flag that tells nand_bbt to force the mode to
+MTD_OPS_RAW. This would result in the correct op being called for the
+underlying nand controller driver.
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+drivers/mtd/nand/nand_base.c | 6 +++++-
+ drivers/mtd/nand/nand_bbt.c  | 6 +++++-
+ include/linux/mtd/bbm.h      | 7 +++++++
+ 3 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -396,7 +396,11 @@ static int nand_default_block_markbad(st
+ 	} else {
+ 		ops.len = ops.ooblen = 1;
+ 	}
+-	ops.mode = MTD_OPS_PLACE_OOB;
++
++	if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
++		ops.mode = MTD_OPS_RAW;
++	else
++		ops.mode = MTD_OPS_PLACE_OOB;
+ 
+ 	/* Write to first/last page(s) if necessary */
+ 	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -423,7 +423,11 @@ static int scan_block_fast(struct mtd_in
+ 	ops.oobbuf = buf;
+ 	ops.ooboffs = 0;
+ 	ops.datbuf = NULL;
+-	ops.mode = MTD_OPS_PLACE_OOB;
++
++	if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
++		ops.mode = MTD_OPS_RAW;
++	else
++		ops.mode = MTD_OPS_PLACE_OOB;
+ 
+ 	for (j = 0; j < numpages; j++) {
+ 		/*
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -116,6 +116,13 @@ struct nand_bbt_descr {
+ #define NAND_BBT_NO_OOB_BBM	0x00080000
+ 
+ /*
++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
++ * when in RAW access mode
++ */
++#define NAND_BBT_ACCESS_BBM_RAW	0x00100000
++
++/*
+  * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+  * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+  * in nand_chip.bbt_options.
diff --git a/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
new file mode 100644
index 0000000..6172f7d
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
@@ -0,0 +1,2024 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927101
+Message-Id: <1438578498-32254-3-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>
+Date: Mon,  3 Aug 2015 10:38:15 +0530
+
+The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx,
+MDM9x15 series.
+
+It exists as a sub block inside the IPs EBI2 (External Bus Interface 2)
+and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a
+broader interface for external slow peripheral devices such as LCD and
+NAND/NOR flash memory or SRAM like interfaces.
+
+We add support for the NAND controller found within EBI2. For the SoCs
+of our interest, we only use the NAND controller within EBI2. Therefore,
+it's safe for us to assume that the NAND controller is a standalone block
+within the SoC.
+
+The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND
+flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and
+16 bit correction/step) and RS ECC(4 bit correction/step) that covers main
+and spare data. The controller contains an internal 512 byte page buffer
+to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA
+for register read/write and data transfers. The controller performs page
+reads and writes at a codeword/step level of 512 bytes. It can support up
+to 2 external chips of different configurations.
+
+The driver prepares register read and write configuration descriptors for
+each codeword, followed by data descriptors to read or write data from the
+controller's internal buffer. It uses a single ADM DMA channel that we get
+via dmaengine API. The controller requires 2 ADM CRCIs for command and
+data flow control. These are passed via DT.
+
+The ecc layout used by the controller is syndrome like, but we can't use
+the standard syndrome ecc ops because of several reasons. First, the amount
+of data bytes covered by ecc isn't same in each step. Second, writing to
+free oob space requires us writing to the entire step in which the oob
+lies. This forces us to create our own ecc ops.
+
+One more difference is how the controller accesses the bad block marker.
+The controller ignores reading the marker when ECC is enabled. ECC needs
+to be explicity disabled to read or write to the bad block marker. For
+this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to
+read the factory provided bad block markers.
+
+v3:
+- Refactor dma functions for maximum reuse
+- Use dma_slave_confing on stack
+- optimize and clean upempty_page_fixup using memchr_inv
+- ensure portability with dma register reads using le32_* funcs
+- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves
+- fix handling of return values of dmaengine funcs
+- constify wherever possible
+- Remove dependency on ADM DMA in Kconfig
+- Misc fixes and clean ups
+
+v2:
+- Use new BBT flag that allows us to read BBM in raw mode
+- reduce memcpy-s in the driver
+- some refactor and clean ups because of above changes
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+drivers/mtd/nand/Kconfig      |    7 +
+ drivers/mtd/nand/Makefile     |    1 +
+ drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1921 insertions(+)
+ create mode 100644 drivers/mtd/nand/qcom_nandc.c
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -516,4 +516,11 @@ config MTD_NAND_XWAY
+ 	  Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
+ 	  to the External Bus Unit (EBU).
+ 
++config MTD_NAND_QCOM
++	tristate "Support for NAND on QCOM SoCs"
++	depends on ARCH_QCOM
++	help
++	  Enables support for NAND flash chips on SoCs containing the EBI2 NAND
++	  controller. This controller is found on IPQ806x SoC.
++
+ endif # MTD_NAND
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nandc.c
+@@ -0,0 +1,1918 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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 <linux/clk.h>
++#include <linux/slab.h>
++#include <linux/bitops.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmaengine.h>
++#include <linux/module.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_mtd.h>
++#include <linux/delay.h>
++
++/* NANDc reg offsets */
++#define NAND_FLASH_CMD			0x00
++#define NAND_ADDR0			0x04
++#define NAND_ADDR1			0x08
++#define NAND_FLASH_CHIP_SELECT		0x0c
++#define NAND_EXEC_CMD			0x10
++#define NAND_FLASH_STATUS		0x14
++#define NAND_BUFFER_STATUS		0x18
++#define NAND_DEV0_CFG0			0x20
++#define NAND_DEV0_CFG1			0x24
++#define NAND_DEV0_ECC_CFG		0x28
++#define NAND_DEV1_ECC_CFG		0x2c
++#define NAND_DEV1_CFG0			0x30
++#define NAND_DEV1_CFG1			0x34
++#define NAND_READ_ID			0x40
++#define NAND_READ_STATUS		0x44
++#define NAND_DEV_CMD0			0xa0
++#define NAND_DEV_CMD1			0xa4
++#define NAND_DEV_CMD2			0xa8
++#define NAND_DEV_CMD_VLD		0xac
++#define SFLASHC_BURST_CFG		0xe0
++#define NAND_ERASED_CW_DETECT_CFG	0xe8
++#define NAND_ERASED_CW_DETECT_STATUS	0xec
++#define NAND_EBI2_ECC_BUF_CFG		0xf0
++#define FLASH_BUF_ACC			0x100
++
++#define NAND_CTRL			0xf00
++#define NAND_VERSION			0xf08
++#define NAND_READ_LOCATION_0		0xf20
++#define NAND_READ_LOCATION_1		0xf24
++
++/* dummy register offsets, used by write_reg_dma */
++#define NAND_DEV_CMD1_RESTORE		0xdead
++#define NAND_DEV_CMD_VLD_RESTORE	0xbeef
++
++/* NAND_FLASH_CMD bits */
++#define PAGE_ACC			BIT(4)
++#define LAST_PAGE			BIT(5)
++
++/* NAND_FLASH_CHIP_SELECT bits */
++#define NAND_DEV_SEL			0
++#define DM_EN				BIT(2)
++
++/* NAND_FLASH_STATUS bits */
++#define FS_OP_ERR			BIT(4)
++#define FS_READY_BSY_N			BIT(5)
++#define FS_MPU_ERR			BIT(8)
++#define FS_DEVICE_STS_ERR		BIT(16)
++#define FS_DEVICE_WP			BIT(23)
++
++/* NAND_BUFFER_STATUS bits */
++#define BS_UNCORRECTABLE_BIT		BIT(8)
++#define BS_CORRECTABLE_ERR_MSK		0x1f
++
++/* NAND_DEVn_CFG0 bits */
++#define DISABLE_STATUS_AFTER_WRITE	4
++#define CW_PER_PAGE			6
++#define UD_SIZE_BYTES			9
++#define ECC_PARITY_SIZE_BYTES_RS	19
++#define SPARE_SIZE_BYTES		23
++#define NUM_ADDR_CYCLES			27
++#define STATUS_BFR_READ			30
++#define SET_RD_MODE_AFTER_STATUS	31
++
++/* NAND_DEVn_CFG0 bits */
++#define DEV0_CFG1_ECC_DISABLE		0
++#define WIDE_FLASH			1
++#define NAND_RECOVERY_CYCLES		2
++#define CS_ACTIVE_BSY			5
++#define BAD_BLOCK_BYTE_NUM		6
++#define BAD_BLOCK_IN_SPARE_AREA		16
++#define WR_RD_BSY_GAP			17
++#define ENABLE_BCH_ECC			27
++
++/* NAND_DEV0_ECC_CFG bits */
++#define ECC_CFG_ECC_DISABLE		0
++#define ECC_SW_RESET			1
++#define ECC_MODE			4
++#define ECC_PARITY_SIZE_BYTES_BCH	8
++#define ECC_NUM_DATA_BYTES		16
++#define ECC_FORCE_CLK_OPEN		30
++
++/* NAND_DEV_CMD1 bits */
++#define READ_ADDR			0
++
++/* NAND_DEV_CMD_VLD bits */
++#define READ_START_VLD			0
++
++/* NAND_EBI2_ECC_BUF_CFG bits */
++#define NUM_STEPS			0
++
++/* NAND_ERASED_CW_DETECT_CFG bits */
++#define ERASED_CW_ECC_MASK		1
++#define AUTO_DETECT_RES			0
++#define MASK_ECC			(1 << ERASED_CW_ECC_MASK)
++#define RESET_ERASED_DET		(1 << AUTO_DETECT_RES)
++#define ACTIVE_ERASED_DET		(0 << AUTO_DETECT_RES)
++#define CLR_ERASED_PAGE_DET		(RESET_ERASED_DET | MASK_ECC)
++#define SET_ERASED_PAGE_DET		(ACTIVE_ERASED_DET | MASK_ECC)
++
++/* NAND_ERASED_CW_DETECT_STATUS bits */
++#define PAGE_ALL_ERASED			BIT(7)
++#define CODEWORD_ALL_ERASED		BIT(6)
++#define PAGE_ERASED			BIT(5)
++#define CODEWORD_ERASED			BIT(4)
++#define ERASED_PAGE			(PAGE_ALL_ERASED | PAGE_ERASED)
++#define ERASED_CW			(CODEWORD_ALL_ERASED | CODEWORD_ERASED)
++
++/* Version Mask */
++#define NAND_VERSION_MAJOR_MASK		0xf0000000
++#define NAND_VERSION_MAJOR_SHIFT	28
++#define NAND_VERSION_MINOR_MASK		0x0fff0000
++#define NAND_VERSION_MINOR_SHIFT	16
++
++/* NAND OP_CMDs */
++#define PAGE_READ			0x2
++#define PAGE_READ_WITH_ECC		0x3
++#define PAGE_READ_WITH_ECC_SPARE	0x4
++#define PROGRAM_PAGE			0x6
++#define PAGE_PROGRAM_WITH_ECC		0x7
++#define PROGRAM_PAGE_SPARE		0x9
++#define BLOCK_ERASE			0xa
++#define FETCH_ID			0xb
++#define RESET_DEVICE			0xd
++
++/*
++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
++ * the driver calls the chunks 'step' or 'codeword' interchangeably
++ */
++#define NANDC_STEP_SIZE			512
++
++/*
++ * the largest page size we support is 8K, this will have 16 steps/codewords
++ * of 512 bytes each
++ */
++#define	MAX_NUM_STEPS			(SZ_8K / NANDC_STEP_SIZE)
++
++/* we read at most 3 registers per codeword scan */
++#define MAX_REG_RD			(3 * MAX_NUM_STEPS)
++
++/* ECC modes */
++#define ECC_NONE	BIT(0)
++#define ECC_RS_4BIT	BIT(1)
++#define	ECC_BCH_4BIT	BIT(2)
++#define	ECC_BCH_8BIT	BIT(3)
++
++struct desc_info {
++	struct list_head list;
++
++	enum dma_transfer_direction dir;
++	struct scatterlist sgl;
++	struct dma_async_tx_descriptor *dma_desc;
++};
++
++/*
++ * holds the current register values that we want to write. acts as a contiguous
++ * chunk of memory which we use to write the controller registers through DMA.
++ */
++struct nandc_regs {
++	u32 cmd;
++	u32 addr0;
++	u32 addr1;
++	u32 chip_sel;
++	u32 exec;
++
++	u32 cfg0;
++	u32 cfg1;
++	u32 ecc_bch_cfg;
++
++	u32 clrflashstatus;
++	u32 clrreadstatus;
++
++	u32 cmd1;
++	u32 vld;
++
++	u32 orig_cmd1;
++	u32 orig_vld;
++
++	u32 ecc_buf_cfg;
++};
++
++/*
++ * @cmd_crci:			ADM DMA CRCI for command flow control
++ * @data_crci:			ADM DMA CRCI for data flow control
++ * @list:			DMA descriptor list (list of desc_infos)
++ * @dma_done:			completion param to denote end of last
++ *				descriptor in the list
++ * @data_buffer:		our local DMA buffer for page read/writes,
++ *				used when we can't use the buffer provided
++ *				by upper layers directly
++ * @buf_size/count/start:	markers for chip->read_buf/write_buf functions
++ * @reg_read_buf:		buffer for reading register data via DMA
++ * @reg_read_pos:		marker for data read in reg_read_buf
++ * @cfg0, cfg1, cfg0_raw..:	NANDc register configurations needed for
++ *				ecc/non-ecc mode for the current nand flash
++ *				device
++ * @regs:			a contiguous chunk of memory for DMA register
++ *				writes
++ * @ecc_strength:		4 bit or 8 bit ecc, received via DT
++ * @bus_width:			8 bit or 16 bit NAND bus width, received via DT
++ * @ecc_modes:			supported ECC modes by the current controller,
++ *				initialized via DT match data
++ * @cw_size:			the number of bytes in a single step/codeword
++ *				of a page, consisting of all data, ecc, spare
++ *				and reserved bytes
++ * @cw_data:			the number of bytes within a codeword protected
++ *				by ECC
++ * @bch_enabled:		flag to tell whether BCH or RS ECC mode is used
++ * @status:			value to be returned if NAND_CMD_STATUS command
++ *				is executed
++ */
++struct qcom_nandc_data {
++	struct platform_device *pdev;
++	struct device *dev;
++
++	void __iomem *base;
++	struct resource *res;
++
++	struct clk *core_clk;
++	struct clk *aon_clk;
++
++	/* DMA stuff */
++	struct dma_chan *chan;
++	struct dma_slave_config	slave_conf;
++	unsigned int cmd_crci;
++	unsigned int data_crci;
++	struct list_head list;
++	struct completion dma_done;
++
++	/* MTD stuff */
++	struct nand_chip chip;
++	struct mtd_info mtd;
++
++	/* local data buffer and markers */
++	u8		*data_buffer;
++	int		buf_size;
++	int		buf_count;
++	int		buf_start;
++
++	/* local buffer to read back registers */
++	u32 *reg_read_buf;
++	int reg_read_pos;
++
++	/* required configs */
++	u32 cfg0, cfg1;
++	u32 cfg0_raw, cfg1_raw;
++	u32 ecc_buf_cfg;
++	u32 ecc_bch_cfg;
++	u32 clrflashstatus;
++	u32 clrreadstatus;
++	u32 sflashc_burst_cfg;
++	u32 cmd1, vld;
++
++	/* register state */
++	struct nandc_regs *regs;
++
++	/* things we get from DT */
++	int ecc_strength;
++	int bus_width;
++
++	u32 ecc_modes;
++
++	/* misc params */
++	int cw_size;
++	int cw_data;
++	bool use_ecc;
++	bool bch_enabled;
++	u8 status;
++	int last_command;
++};
++
++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset)
++{
++	return ioread32(this->base + offset);
++}
++
++static inline void nandc_write(struct qcom_nandc_data *this, int offset,
++			       u32 val)
++{
++	iowrite32(val, this->base + offset);
++}
++
++/* helper to configure address register values */
++static void set_address(struct qcom_nandc_data *this, u16 column, int page)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nandc_regs *regs = this->regs;
++
++	if (chip->options & NAND_BUSWIDTH_16)
++		column >>= 1;
++
++	regs->addr0 = page << 16 | column;
++	regs->addr1 = page >> 16 & 0xff;
++}
++
++/*
++ * update_rw_regs:	set up read/write register values, these will be
++ *			written to the NAND controller registers via DMA
++ *
++ * @num_cw:		number of steps for the read/write operation
++ * @read:		read or write operation
++ */
++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read)
++{
++	struct nandc_regs *regs = this->regs;
++
++	if (read) {
++		if (this->use_ecc)
++			regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
++		else
++			regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++	} else {
++			regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
++	}
++
++	if (this->use_ecc) {
++		regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) |
++				(num_cw - 1) << CW_PER_PAGE;
++
++		regs->cfg1 = this->cfg1;
++		regs->ecc_bch_cfg = this->ecc_bch_cfg;
++	} else {
++		regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++				(num_cw - 1) << CW_PER_PAGE;
++
++		regs->cfg1 = this->cfg1_raw;
++		regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++	}
++
++	regs->ecc_buf_cfg = this->ecc_buf_cfg;
++	regs->clrflashstatus = this->clrflashstatus;
++	regs->clrreadstatus = this->clrreadstatus;
++	regs->exec = 1;
++}
++
++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off,
++			 const void *vaddr, int size, bool flow_control)
++{
++	struct desc_info *desc;
++	struct dma_async_tx_descriptor *dma_desc;
++	struct scatterlist *sgl;
++	struct dma_slave_config slave_conf;
++	int r;
++
++	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++	if (!desc)
++		return -ENOMEM;
++
++	list_add_tail(&desc->list, &this->list);
++
++	sgl = &desc->sgl;
++
++	sg_init_one(sgl, vaddr, size);
++
++	desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
++
++	r = dma_map_sg(this->dev, sgl, 1, desc->dir);
++	if (r == 0) {
++		r = -ENOMEM;
++		goto err;
++	}
++
++	memset(&slave_conf, 0x00, sizeof(slave_conf));
++
++	slave_conf.device_fc = flow_control;
++	if (read) {
++		slave_conf.src_maxburst = 16;
++		slave_conf.src_addr = this->res->start + reg_off;
++		slave_conf.slave_id = this->data_crci;
++	} else {
++		slave_conf.dst_maxburst = 16;
++		slave_conf.dst_addr = this->res->start + reg_off;
++		slave_conf.slave_id = this->cmd_crci;
++	}
++
++	r = dmaengine_slave_config(this->chan, &slave_conf);
++	if (r) {
++		dev_err(this->dev, "failed to configure dma channel\n");
++		goto err;
++	}
++
++	dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0);
++	if (!dma_desc) {
++		dev_err(this->dev, "failed to prepare desc\n");
++		r = -EINVAL;
++		goto err;
++	}
++
++	desc->dma_desc = dma_desc;
++
++	return 0;
++err:
++	kfree(desc);
++
++	return r;
++}
++
++/*
++ * read_reg_dma:	prepares a descriptor to read a given number of
++ *			contiguous registers to the reg_read_buf pointer
++ *
++ * @first:		offset of the first register in the contiguous block
++ * @num_regs:		number of registers to read
++ */
++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++	bool flow_control = false;
++	void *vaddr;
++	int size;
++
++	if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
++		flow_control = true;
++
++	size = num_regs * sizeof(u32);
++	vaddr = this->reg_read_buf + this->reg_read_pos;
++	this->reg_read_pos += num_regs;
++
++	return prep_dma_desc(this, true, first, vaddr, size, flow_control);
++}
++
++/*
++ * write_reg_dma:	prepares a descriptor to write a given number of
++ *			contiguous registers
++ *
++ * @first:		offset of the first register in the contiguous block
++ * @num_regs:		number of registers to write
++ */
++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++	bool flow_control = false;
++	struct nandc_regs *regs = this->regs;
++	void *vaddr;
++	int size;
++
++	switch (first) {
++	case NAND_FLASH_CMD:
++		vaddr = &regs->cmd;
++		flow_control = true;
++		break;
++	case NAND_EXEC_CMD:
++		vaddr = &regs->exec;
++		break;
++	case NAND_FLASH_STATUS:
++		vaddr = &regs->clrflashstatus;
++		break;
++	case NAND_DEV0_CFG0:
++		vaddr = &regs->cfg0;
++		break;
++	case NAND_READ_STATUS:
++		vaddr = &regs->clrreadstatus;
++		break;
++	case NAND_DEV_CMD1:
++		vaddr = &regs->cmd1;
++		break;
++	case NAND_DEV_CMD1_RESTORE:
++		first = NAND_DEV_CMD1;
++		vaddr = &regs->orig_cmd1;
++		break;
++	case NAND_DEV_CMD_VLD:
++		vaddr = &regs->vld;
++		break;
++	case NAND_DEV_CMD_VLD_RESTORE:
++		first = NAND_DEV_CMD_VLD;
++		vaddr = &regs->orig_vld;
++		break;
++	case NAND_EBI2_ECC_BUF_CFG:
++		vaddr = &regs->ecc_buf_cfg;
++		break;
++	default:
++		dev_err(this->dev, "invalid starting register\n");
++		return -EINVAL;
++	}
++
++	size = num_regs * sizeof(u32);
++
++	return prep_dma_desc(this, false, first, vaddr, size, flow_control);
++}
++
++/*
++ * read_data_dma:	prepares a DMA descriptor to transfer data from the
++ *			controller's internal buffer to the buffer 'vaddr'
++ *
++ * @reg_off:		offset within the controller's data buffer
++ * @vaddr:		virtual address of the buffer we want to write to
++ * @size:		DMA transaction size in bytes
++ */
++static int read_data_dma(struct qcom_nandc_data *this, int reg_off,
++			 const u8 *vaddr, int size)
++{
++	return prep_dma_desc(this, true, reg_off, vaddr, size, false);
++}
++
++/*
++ * write_data_dma:	prepares a DMA descriptor to transfer data from
++ *			'vaddr' to the controller's internal buffer
++ *
++ * @reg_off:		offset within the controller's data buffer
++ * @vaddr:		virtual address of the buffer we want to read from
++ * @size:		DMA transaction size in bytes
++ */
++static int write_data_dma(struct qcom_nandc_data *this, int reg_off,
++			  const u8 *vaddr, int size)
++{
++	return prep_dma_desc(this, false, reg_off, vaddr, size, false);
++}
++
++/*
++ * helper to prepare dma descriptors to configure registers needed for reading a
++ * codeword/step in a page
++ */
++static void config_cw_read(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 3);
++	write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 2);
++	read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1);
++}
++
++/*
++ * helpers to prepare dma descriptors used to configure registers needed for
++ * writing a codeword/step in a page
++ */
++static void config_cw_write_pre(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 3);
++	write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++}
++
++static void config_cw_write_post(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	write_reg_dma(this, NAND_FLASH_STATUS, 1);
++	write_reg_dma(this, NAND_READ_STATUS, 1);
++}
++
++/*
++ * the following functions are used within chip->cmdfunc() to perform different
++ * NAND_CMD_* commands
++ */
++
++/* sets up descriptors for NAND_CMD_PARAM */
++static int nandc_param(struct qcom_nandc_data *this)
++{
++	struct nandc_regs *regs = this->regs;
++
++	/*
++	 * NAND_CMD_PARAM is called before we know much about the FLASH chip
++	 * in use. we configure the controller to perform a raw read of 512
++	 * bytes to read onfi params
++	 */
++	regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++	regs->addr0 = 0;
++	regs->addr1 = 0;
++	regs->cfg0 =  0 << CW_PER_PAGE
++			| 512 << UD_SIZE_BYTES
++			| 5 << NUM_ADDR_CYCLES
++			| 0 << SPARE_SIZE_BYTES;
++
++	regs->cfg1 =  7 << NAND_RECOVERY_CYCLES
++			| 0 << CS_ACTIVE_BSY
++			| 17 << BAD_BLOCK_BYTE_NUM
++			| 1 << BAD_BLOCK_IN_SPARE_AREA
++			| 2 << WR_RD_BSY_GAP
++			| 0 << WIDE_FLASH
++			| 1 << DEV0_CFG1_ECC_DISABLE;
++
++	regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++
++	/* configure CMD1 and VLD for ONFI param probing */
++	regs->vld = (this->vld & ~(1 << READ_START_VLD))
++			| 0 << READ_START_VLD;
++
++	regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR))
++			| NAND_CMD_PARAM << READ_ADDR;
++
++	regs->exec = 1;
++
++	regs->orig_cmd1 = this->cmd1;
++	regs->orig_vld = this->vld;
++
++	write_reg_dma(this, NAND_DEV_CMD_VLD, 1);
++	write_reg_dma(this, NAND_DEV_CMD1, 1);
++
++	this->buf_count = 512;
++	memset(this->data_buffer, 0xff, this->buf_count);
++
++	config_cw_read(this);
++
++	read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count);
++
++	/* restore CMD1 and VLD regs */
++	write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1);
++	write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_ERASE1 */
++static int erase_block(struct qcom_nandc_data *this, int page_addr)
++{
++	struct nandc_regs *regs = this->regs;
++
++	regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE;
++	regs->addr0 = page_addr;
++	regs->addr1 = 0;
++	regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE);
++	regs->cfg1 = this->cfg1_raw;
++	regs->exec = 1;
++	regs->clrflashstatus = this->clrflashstatus;
++	regs->clrreadstatus = this->clrreadstatus;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 2);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	write_reg_dma(this, NAND_FLASH_STATUS, 1);
++	write_reg_dma(this, NAND_READ_STATUS, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_READID */
++static int read_id(struct qcom_nandc_data *this, int column)
++{
++	struct nandc_regs *regs = this->regs;
++
++	if (column == -1)
++		return 0;
++
++	regs->cmd = FETCH_ID;
++	regs->addr0 = column;
++	regs->addr1 = 0;
++	regs->chip_sel = DM_EN;
++	regs->exec = 1;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 4);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_READ_ID, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_RESET */
++static int reset(struct qcom_nandc_data *this)
++{
++	struct nandc_regs *regs = this->regs;
++
++	regs->cmd = RESET_DEVICE;
++	regs->exec = 1;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 1);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	return 0;
++}
++
++/* helpers to submit/free our list of dma descriptors */
++static void dma_callback(void *param)
++{
++	struct qcom_nandc_data *this = param;
++	struct completion *c = &this->dma_done;
++
++	complete(c);
++}
++
++static int submit_descs(struct qcom_nandc_data *this)
++{
++	struct completion *c = &this->dma_done;
++	struct desc_info *desc;
++	int r;
++
++	init_completion(c);
++
++	list_for_each_entry(desc, &this->list, list) {
++		/*
++		 * we add a callback to the last descriptor in our list to
++		 * notify completion of command
++		 */
++		if (list_is_last(&desc->list, &this->list)) {
++			desc->dma_desc->callback = dma_callback;
++			desc->dma_desc->callback_param = this;
++		}
++
++		dmaengine_submit(desc->dma_desc);
++	}
++
++	dma_async_issue_pending(this->chan);
++
++	r = wait_for_completion_timeout(c, msecs_to_jiffies(500));
++	if (!r)
++		return -ETIMEDOUT;
++
++	return 0;
++}
++
++static void free_descs(struct qcom_nandc_data *this)
++{
++	struct desc_info *desc, *n;
++
++	list_for_each_entry_safe(desc, n, &this->list, list) {
++		list_del(&desc->list);
++		dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir);
++		kfree(desc);
++	}
++}
++
++/* reset the register read buffer for next NAND operation */
++static void clear_read_regs(struct qcom_nandc_data *this)
++{
++	this->reg_read_pos = 0;
++	memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf));
++}
++
++static void pre_command(struct qcom_nandc_data *this, int command)
++{
++	this->buf_count = 0;
++	this->buf_start = 0;
++	this->use_ecc = false;
++	this->last_command = command;
++
++	clear_read_regs(this);
++}
++
++/*
++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
++ * privately maintained status byte, this status byte can be read after
++ * NAND_CMD_STATUS is called
++ */
++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int num_cw;
++	int i;
++
++	num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
++
++	for (i = 0; i < num_cw; i++) {
++		__le32 flash_status = le32_to_cpu(this->reg_read_buf[i]);
++
++		if (flash_status & FS_MPU_ERR)
++			this->status &= ~NAND_STATUS_WP;
++
++		if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
++				(flash_status & FS_DEVICE_STS_ERR)))
++			this->status |= NAND_STATUS_FAIL;
++	}
++}
++
++static void post_command(struct qcom_nandc_data *this, int command)
++{
++	switch (command) {
++	case NAND_CMD_READID:
++		memcpy(this->data_buffer, this->reg_read_buf, this->buf_count);
++		break;
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_ERASE1:
++		parse_erase_write_errors(this, command);
++		break;
++	default:
++		break;
++	}
++}
++
++/*
++ * Implements chip->cmdfunc. It's  only used for a limited set of commands.
++ * The rest of the commands wouldn't be called by upper layers. For example,
++ * NAND_CMD_READOOB would never be called because we have our own versions
++ * of read_oob ops for nand_ecc_ctrl.
++ */
++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
++			 int column, int page_addr)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	struct qcom_nandc_data *this = chip->priv;
++	bool wait = false;
++	int r = 0;
++
++	pre_command(this, command);
++
++	switch (command) {
++	case NAND_CMD_RESET:
++		r = reset(this);
++		wait = true;
++		break;
++
++	case NAND_CMD_READID:
++		this->buf_count = 4;
++		r = read_id(this, column);
++		wait = true;
++		break;
++
++	case NAND_CMD_PARAM:
++		r = nandc_param(this);
++		wait = true;
++		break;
++
++	case NAND_CMD_ERASE1:
++		r = erase_block(this, page_addr);
++		wait = true;
++		break;
++
++	case NAND_CMD_READ0:
++		/* we read the entire page for now */
++		WARN_ON(column != 0);
++
++		this->use_ecc = true;
++		set_address(this, 0, page_addr);
++		update_rw_regs(this, ecc->steps, true);
++		break;
++
++	case NAND_CMD_SEQIN:
++		WARN_ON(column != 0);
++		set_address(this, 0, page_addr);
++		break;
++
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_STATUS:
++	case NAND_CMD_NONE:
++	default:
++		break;
++	}
++
++	if (r) {
++		dev_err(this->dev, "failure executing command %d\n",
++			command);
++		free_descs(this);
++		return;
++	}
++
++	if (wait) {
++		r = submit_descs(this);
++		if (r)
++			dev_err(this->dev,
++				"failure submitting descs for command %d\n",
++				command);
++	}
++
++	free_descs(this);
++
++	post_command(this, command);
++}
++
++/*
++ * when using RS ECC, the NAND controller flags an error when reading an
++ * erased page. however, there are special characters at certain offsets when
++ * we read the erased page. we check here if the page is really empty. if so,
++ * we replace the magic characters with 0xffs
++ */
++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = ecc->steps;
++	u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS];
++	int i, j;
++
++	/* if BCH is enabled, HW will take care of detecting erased pages */
++	if (this->bch_enabled || !this->use_ecc)
++		return false;
++
++	for (i = 0; i < cwperpage; i++) {
++		u8 *empty1, *empty2;
++		__le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]);
++
++		/*
++		 * an erased page flags an error in NAND_FLASH_STATUS, check if
++		 * the page is erased by looking for 0x54s at offsets 3 and 175
++		 * from the beginning of each codeword
++		 */
++		if (!(flash_status & FS_OP_ERR))
++			break;
++
++		empty1 = &data_buf[3 + i * this->cw_data];
++		empty2 = &data_buf[175 + i * this->cw_data];
++
++		/*
++		 * if the error wasn't because of an erased page, bail out and
++		 * and let someone else do the error checking
++		 */
++		if ((*empty1 == 0x54 && *empty2 == 0xff) ||
++				(*empty1 == 0xff && *empty2 == 0x54)) {
++			orig1[i] = *empty1;
++			orig2[i] = *empty2;
++
++			*empty1 = 0xff;
++			*empty2 = 0xff;
++		} else {
++			break;
++		}
++	}
++
++	if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize))
++		goto not_empty;
++
++	/*
++	 * tell the caller that the page was empty and is fixed up, so that
++	 * parse_read_errors() doesn't think it's an error
++	 */
++	return true;
++
++not_empty:
++	/* restore original values if not empty*/
++	for (j = 0; j < i; j++) {
++		data_buf[3 + j * this->cw_data] = orig1[j];
++		data_buf[175 + j * this->cw_data] = orig2[j];
++	}
++
++	return false;
++}
++
++struct read_stats {
++	__le32 flash;
++	__le32 buffer;
++	__le32 erased_cw;
++};
++
++/*
++ * reads back status registers set by the controller to notify page read
++ * errors. this is equivalent to what 'ecc->correct()' would do.
++ */
++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = ecc->steps;
++	unsigned int max_bitflips = 0;
++	int i;
++
++	for (i = 0; i < cwperpage; i++) {
++		int stat;
++		struct read_stats *buf;
++
++		buf = (struct read_stats *) (this->reg_read_buf + 3 * i);
++
++		buf->flash = le32_to_cpu(buf->flash);
++		buf->buffer = le32_to_cpu(buf->buffer);
++		buf->erased_cw = le32_to_cpu(buf->erased_cw);
++
++		if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) {
++
++			/* ignore erased codeword errors */
++			if (this->bch_enabled) {
++				if ((buf->erased_cw & ERASED_CW) == ERASED_CW)
++					continue;
++			} else if (erased_page) {
++				continue;
++			}
++
++			if (buf->buffer & BS_UNCORRECTABLE_BIT) {
++				mtd->ecc_stats.failed++;
++				continue;
++			}
++		}
++
++		stat = buf->buffer & BS_CORRECTABLE_ERR_MSK;
++		mtd->ecc_stats.corrected += stat;
++
++		max_bitflips = max_t(unsigned int, max_bitflips, stat);
++	}
++
++	return max_bitflips;
++}
++
++/*
++ * helper to perform the actual page read operation, used by ecc->read_page()
++ * and ecc->read_oob()
++ */
++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf,
++			 u8 *oob_buf)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int i, r;
++
++	/* queue cmd descs for each codeword */
++	for (i = 0; i < ecc->steps; i++) {
++		int data_size, oob_size;
++
++		if (i == (ecc->steps - 1)) {
++			data_size = ecc->size - ((ecc->steps - 1) << 2);
++			oob_size = (ecc->steps << 2) + ecc->bytes;
++		} else {
++			data_size = this->cw_data;
++			oob_size = ecc->bytes;
++		}
++
++		config_cw_read(this);
++
++		if (data_buf)
++			read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++		if (oob_buf)
++			read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf,
++					oob_size);
++
++		if (data_buf)
++			data_buf += data_size;
++		if (oob_buf)
++			oob_buf += oob_size;
++	}
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failure to read page/oob\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/*
++ * a helper that copies the last step/codeword of a page (containing free oob)
++ * into our local buffer
++ */
++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int size;
++	int r;
++
++	clear_read_regs(this);
++
++	size = use_ecc ? this->cw_data : this->cw_size;
++
++	/* prepare a clean read buffer */
++	memset(this->data_buffer, 0xff, size);
++
++	this->use_ecc = use_ecc;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, true);
++
++	config_cw_read(this);
++
++	read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size);
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failed to copy last codeword\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/* implements ecc->read_page() */
++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
++				uint8_t *buf, int oob_required, int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	u8 *data_buf, *oob_buf = NULL;
++	bool erased_page;
++	int r;
++
++	data_buf = buf;
++	oob_buf = oob_required ? chip->oob_poi : NULL;
++
++	r = read_page_low(this, data_buf, oob_buf);
++	if (r) {
++		dev_err(this->dev, "failure to read page\n");
++		return r;
++	}
++
++	erased_page = empty_page_fixup(this, data_buf);
++
++	return parse_read_errors(this, erased_page);
++}
++
++/* implements ecc->read_oob() */
++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++			       int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int r;
++
++	clear_read_regs(this);
++
++	this->use_ecc = true;
++	set_address(this, 0, page);
++	update_rw_regs(this, ecc->steps, true);
++
++	r = read_page_low(this, NULL, chip->oob_poi);
++	if (r)
++		dev_err(this->dev, "failure to read oob\n");
++
++	return r;
++}
++
++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */
++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				   int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int start, length;
++	int r;
++
++	/*
++	 * configure registers for a raw page read, the address is set to the
++	 * beginning of the last codeword, we don't care about reading ecc
++	 * portion of oob, just the free stuff
++	 */
++	r = copy_last_cw(this, false, page);
++	if (r)
++		return r;
++
++	/*
++	 * reading raw oob has 2 parts, first the bad block byte, then the
++	 * actual free oob region. perform a memcpy in two steps
++	 */
++	start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++	length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++	memcpy(oob, this->data_buffer + start, length);
++
++	oob += length;
++
++	start = this->cw_data - (ecc->steps << 2) + 1;
++	length = ecc->steps << 2;
++
++	memcpy(oob, this->data_buffer + start, length);
++
++	return 0;
++}
++
++/* implements ecc->write_page() */
++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++				 const uint8_t *buf, int oob_required)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	u8 *data_buf, *oob_buf;
++	int i, r = 0;
++
++	clear_read_regs(this);
++
++	data_buf = (u8 *) buf;
++	oob_buf = chip->oob_poi;
++
++	this->use_ecc = true;
++	update_rw_regs(this, ecc->steps, false);
++
++	for (i = 0; i < ecc->steps; i++) {
++		int data_size, oob_size;
++
++		if (i == (ecc->steps - 1)) {
++			data_size = ecc->size - ((ecc->steps - 1) << 2);
++			oob_size = (ecc->steps << 2) + ecc->bytes;
++		} else {
++			data_size = this->cw_data;
++			oob_size = ecc->bytes;
++		}
++
++		config_cw_write_pre(this);
++		write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++		/*
++		 * we don't really need to write anything to oob for the
++		 * first n - 1 codewords since these oob regions just
++		 * contain ecc that's written by the controller itself
++		 */
++		if (i == (ecc->steps - 1))
++			write_data_dma(this, FLASH_BUF_ACC + data_size,
++					oob_buf, oob_size);
++		config_cw_write_post(this);
++
++		data_buf += data_size;
++		oob_buf += oob_size;
++	}
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failure to write page\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/*
++ * implements ecc->write_oob()
++ *
++ * the NAND controller cannot write only data or only oob within a codeword,
++ * since ecc is calculated for the combined codeword. we first copy the
++ * entire contents for the last codeword(data + oob), replace the old oob
++ * with the new one in chip->oob_poi, and then write the entire codeword.
++ * this read-copy-write operation results in a slight perormance loss.
++ */
++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int free_boff;
++	int data_size, oob_size;
++	int r, status = 0;
++
++	r = copy_last_cw(this, true, page);
++	if (r)
++		return r;
++
++	clear_read_regs(this);
++
++	/* calculate the data and oob size for the last codeword/step */
++	data_size = ecc->size - ((ecc->steps - 1) << 2);
++	oob_size = (ecc->steps << 2) + ecc->bytes;
++
++	/*
++	 * the location of spare data in the oob buffer, we could also use
++	 * ecc->layout.oobfree here
++	 */
++	free_boff = ecc->bytes * (ecc->steps - 1);
++
++	/* override new oob content to last codeword */
++	memcpy(this->data_buffer + data_size, oob + free_boff, oob_size);
++
++	this->use_ecc = true;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, false);
++
++	config_cw_write_pre(this);
++	write_data_dma(this, FLASH_BUF_ACC, this->data_buffer,
++		data_size + oob_size);
++	config_cw_write_post(this);
++
++	r = submit_descs(this);
++
++	free_descs(this);
++
++	if (r) {
++		dev_err(this->dev, "failure to write oob\n");
++		return -EIO;
++	}
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++	status = chip->waitfunc(mtd, chip);
++
++	return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/* implements ecc->write_oob_raw(), used to write bad block marker flag */
++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd,
++				    struct nand_chip *chip, int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int start, length;
++	int r, status = 0;
++
++	r = copy_last_cw(this, false, page);
++	if (r)
++		return r;
++
++	clear_read_regs(this);
++
++	/*
++	 * writing raw oob has 2 parts, first the bad block region, then the
++	 * actual free region
++	 */
++	start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++	length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++	memcpy(this->data_buffer + start, oob, length);
++
++	oob += length;
++
++	start = this->cw_data - (ecc->steps << 2) + 1;
++	length = ecc->steps << 2;
++
++	memcpy(this->data_buffer + start, oob, length);
++
++	/* prepare write */
++	this->use_ecc = false;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, false);
++
++	config_cw_write_pre(this);
++	write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size);
++	config_cw_write_post(this);
++
++	r = submit_descs(this);
++
++	free_descs(this);
++
++	if (r) {
++		dev_err(this->dev, "failure to write updated oob\n");
++		return -EIO;
++	}
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++	status = chip->waitfunc(mtd, chip);
++
++	return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/*
++ * the three functions below implement chip->read_byte(), chip->read_buf()
++ * and chip->write_buf() respectively. these aren't used for
++ * reading/writing page data, they are used for smaller data like reading
++ * id, status etc
++ */
++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	uint8_t *buf = this->data_buffer;
++	uint8_t ret = 0x0;
++
++	if (this->last_command == NAND_CMD_STATUS) {
++		ret = this->status;
++
++		this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++
++		return ret;
++	}
++
++	if (this->buf_start < this->buf_count)
++		ret = buf[this->buf_start++];
++
++	return ret;
++}
++
++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++	memcpy(buf, this->data_buffer + this->buf_start, real_len);
++	this->buf_start += real_len;
++}
++
++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
++		int len)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++	memcpy(this->data_buffer + this->buf_start, buf, real_len);
++
++	this->buf_start += real_len;
++}
++
++/* we support only one external chip for now */
++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++
++	if (chipnr <= 0)
++		return;
++
++	dev_warn(this->dev, "invalid chip select\n");
++}
++
++/*
++ * NAND controller page layout info
++ *
++ * |-----------------------|	  |---------------------------------|
++ * |		xx.......xx|	  |		*********xx.......xx|
++ * |	DATA	xx..ECC..xx|	  |	DATA	**SPARE**xx..ECC..xx|
++ * |   (516)	xx.......xx|	  |  (516-n*4)	**(n*4)**xx.......xx|
++ * |		xx.......xx|	  |		*********xx.......xx|
++ * |-----------------------|	  |---------------------------------|
++ *     codeword 1,2..n-1			codeword n
++ *  <---(528/532 Bytes)---->	   <-------(528/532 Bytes)---------->
++ *
++ * n = number of codewords in the page
++ * . = ECC bytes
++ * * = spare bytes
++ * x = unused/reserved bytes
++ *
++ * 2K page: n = 4, spare = 16 bytes
++ * 4K page: n = 8, spare = 32 bytes
++ * 8K page: n = 16, spare = 64 bytes
++ *
++ * the qcom nand controller operates at a sub page/codeword level. each
++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
++ * the number of ECC bytes vary based on the ECC strength and the bus width.
++ *
++ * the first n - 1 codewords contains 516 bytes of user data, the remaining
++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
++ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
++ *
++ * the layout described above is used by the controller when the ECC block is
++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are
++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout
++ * layouts defined below doesn't consider the positions occupied by the reserved
++ * bytes
++ *
++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width)
++ * in the last codeword is the position of bad block marker. the bad block
++ * marker cannot be accessed when ECC is enabled.
++ *
++ */
++
++/*
++ * Layouts for different page sizes and ecc modes. We skip the eccpos field
++ * since it isn't needed for this driver
++ */
++
++/* 2K page, 4 bit ECC */
++static struct nand_ecclayout layout_oob_64 = {
++	.eccbytes	= 40,
++	.oobfree	= {
++				{ 30, 16 },
++			  },
++};
++
++/* 4K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_128 = {
++	.eccbytes	= 80,
++	.oobfree	= {
++				{ 70, 32 },
++			  },
++};
++
++/* 4K page, 8 bit ECC, 8 bit bus width */
++static struct nand_ecclayout layout_oob_224_x8 = {
++	.eccbytes	= 104,
++	.oobfree	= {
++				{ 91, 32 },
++			  },
++};
++
++/* 4K page, 8 bit ECC, 16 bit bus width */
++static struct nand_ecclayout layout_oob_224_x16 = {
++	.eccbytes	= 112,
++	.oobfree	= {
++				{ 98, 32 },
++			  },
++};
++
++/* 8K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_256 = {
++	.eccbytes	= 160,
++	.oobfree	= {
++				{ 151, 64 },
++			  },
++};
++
++/*
++ * this is called before scan_ident, we do some minimal configurations so
++ * that reading ID and ONFI params work
++ */
++static void qcom_nandc_pre_init(struct qcom_nandc_data *this)
++{
++	/* kill onenand */
++	nandc_write(this, SFLASHC_BURST_CFG, 0);
++
++	/* enable ADM DMA */
++	nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN);
++
++	/* save the original values of these registers */
++	this->cmd1 = nandc_read(this, NAND_DEV_CMD1);
++	this->vld = nandc_read(this, NAND_DEV_CMD_VLD);
++
++	/* initial status value */
++	this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++}
++
++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage;
++	bool wide_bus;
++
++	/* the nand controller fetches codewords/chunks of 512 bytes */
++	cwperpage = mtd->writesize >> 9;
++
++	ecc->strength = this->ecc_strength;
++
++	wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++	if (ecc->strength >= 8) {
++		/* 8 bit ECC defaults to BCH ECC on all platforms */
++		ecc->bytes = wide_bus ? 14 : 13;
++	} else {
++		/*
++		 * if the controller supports BCH for 4 bit ECC, the controller
++		 * uses lesser bytes for ECC. If RS is used, the ECC bytes is
++		 * always 10 bytes
++		 */
++		if (this->ecc_modes & ECC_BCH_4BIT)
++			ecc->bytes = wide_bus ? 8 : 7;
++		else
++			ecc->bytes = 10;
++	}
++
++	/* each step consists of 512 bytes of data */
++	ecc->size = NANDC_STEP_SIZE;
++
++	ecc->read_page		= qcom_nandc_read_page;
++	ecc->read_oob		= qcom_nandc_read_oob;
++	ecc->write_page		= qcom_nandc_write_page;
++	ecc->write_oob		= qcom_nandc_write_oob;
++
++	/*
++	 * the bad block marker is readable only when we read the page with ECC
++	 * disabled. all the ops above run with ECC enabled. We need raw read
++	 * and write function for oob in order to access bad block marker.
++	 */
++	ecc->read_oob_raw	= qcom_nandc_read_oob_raw;
++	ecc->write_oob_raw	= qcom_nandc_write_oob_raw;
++
++	switch (mtd->oobsize) {
++	case 64:
++		ecc->layout = &layout_oob_64;
++		break;
++	case 128:
++		ecc->layout = &layout_oob_128;
++		break;
++	case 224:
++		if (wide_bus)
++			ecc->layout = &layout_oob_224_x16;
++		else
++			ecc->layout = &layout_oob_224_x8;
++		break;
++	case 256:
++		ecc->layout = &layout_oob_256;
++		break;
++	default:
++		dev_err(this->dev, "unsupported NAND device, oobsize %d\n",
++			mtd->oobsize);
++		return -ENODEV;
++	}
++
++	ecc->mode = NAND_ECC_HW;
++
++	/* enable ecc by default */
++	this->use_ecc = true;
++
++	return 0;
++}
++
++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = mtd->writesize / ecc->size;
++	int spare_bytes, bad_block_byte;
++	bool wide_bus;
++	int ecc_mode = 0;
++
++	wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++	if (ecc->strength >= 8) {
++		this->cw_size = 532;
++
++		spare_bytes = wide_bus ? 0 : 2;
++
++		this->bch_enabled = true;
++		ecc_mode = 1;
++	} else {
++		this->cw_size = 528;
++
++		if (this->ecc_modes & ECC_BCH_4BIT) {
++			spare_bytes = wide_bus ? 2 : 4;
++
++			this->bch_enabled = true;
++			ecc_mode = 0;
++		} else {
++			spare_bytes = wide_bus ? 0 : 1;
++		}
++	}
++
++	/*
++	 * DATA_UD_BYTES varies based on whether the read/write command protects
++	 * spare data with ECC too. We protect spare data by default, so we set
++	 * it to main + spare data, which are 512 and 4 bytes respectively.
++	 */
++	this->cw_data = 516;
++
++	bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1;
++
++	this->cfg0 = (cwperpage - 1) << CW_PER_PAGE
++				| this->cw_data << UD_SIZE_BYTES
++				| 0 << DISABLE_STATUS_AFTER_WRITE
++				| 5 << NUM_ADDR_CYCLES
++				| ecc->bytes << ECC_PARITY_SIZE_BYTES_RS
++				| 0 << STATUS_BFR_READ
++				| 1 << SET_RD_MODE_AFTER_STATUS
++				| spare_bytes << SPARE_SIZE_BYTES;
++
++	this->cfg1 = 7 << NAND_RECOVERY_CYCLES
++				| 0 <<  CS_ACTIVE_BSY
++				| bad_block_byte << BAD_BLOCK_BYTE_NUM
++				| 0 << BAD_BLOCK_IN_SPARE_AREA
++				| 2 << WR_RD_BSY_GAP
++				| wide_bus << WIDE_FLASH
++				| this->bch_enabled << ENABLE_BCH_ECC;
++
++	this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
++				| this->cw_size << UD_SIZE_BYTES
++				| 5 << NUM_ADDR_CYCLES
++				| 0 << SPARE_SIZE_BYTES;
++
++	this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
++				| 0 << CS_ACTIVE_BSY
++				| 17 << BAD_BLOCK_BYTE_NUM
++				| 1 << BAD_BLOCK_IN_SPARE_AREA
++				| 2 << WR_RD_BSY_GAP
++				| wide_bus << WIDE_FLASH
++				| 1 << DEV0_CFG1_ECC_DISABLE;
++
++	this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE
++				| 0 << ECC_SW_RESET
++				| this->cw_data << ECC_NUM_DATA_BYTES
++				| 1 << ECC_FORCE_CLK_OPEN
++				| ecc_mode << ECC_MODE
++				| ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH;
++
++	this->ecc_buf_cfg = 0x203 << NUM_STEPS;
++
++	this->clrflashstatus = FS_READY_BSY_N;
++	this->clrreadstatus = 0xc0;
++
++	dev_dbg(this->dev,
++		"cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
++		this->cfg0, this->cfg1, this->ecc_buf_cfg,
++		this->ecc_bch_cfg, this->cw_size, this->cw_data,
++		ecc->strength, ecc->bytes, cwperpage);
++}
++
++static int qcom_nandc_alloc(struct qcom_nandc_data *this)
++{
++	int r;
++
++	r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32));
++	if (r) {
++		dev_err(this->dev, "failed to set DMA mask\n");
++		return r;
++	}
++
++	/*
++	 * we use the internal buffer for reading ONFI params, reading small
++	 * data like ID and status, and preforming read-copy-write operations
++	 * when writing to a codeword partially. 532 is the maximum possible
++	 * size of a codeword for our nand controller
++	 */
++	this->buf_size = 532;
++
++	this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL);
++	if (!this->data_buffer)
++		return -ENOMEM;
++
++	this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL);
++	if (!this->regs)
++		return -ENOMEM;
++
++	this->reg_read_buf = devm_kzalloc(this->dev,
++				MAX_REG_RD * sizeof(*this->reg_read_buf),
++				GFP_KERNEL);
++	if (!this->reg_read_buf)
++		return -ENOMEM;
++
++	INIT_LIST_HEAD(&this->list);
++
++	this->chan = dma_request_slave_channel(this->dev, "rxtx");
++	if (!this->chan) {
++		dev_err(this->dev, "failed to request slave channel\n");
++		return -ENODEV;
++	}
++
++	return 0;
++}
++
++static void qcom_nandc_unalloc(struct qcom_nandc_data *this)
++{
++	dma_release_channel(this->chan);
++}
++
++static int qcom_nandc_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct device_node *np = this->dev->of_node;
++	struct mtd_part_parser_data ppdata = { .of_node = np };
++	int r;
++
++	mtd->priv = chip;
++	mtd->name = "qcom-nandc";
++	mtd->owner = THIS_MODULE;
++
++	chip->priv = this;
++
++	chip->cmdfunc		= qcom_nandc_command;
++	chip->select_chip	= qcom_nandc_select_chip;
++	chip->read_byte		= qcom_nandc_read_byte;
++	chip->read_buf		= qcom_nandc_read_buf;
++	chip->write_buf		= qcom_nandc_write_buf;
++
++	chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
++	if (this->bus_width == 16)
++		chip->options |= NAND_BUSWIDTH_16;
++
++	chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW;
++	if (of_get_nand_on_flash_bbt(np))
++		chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++	qcom_nandc_pre_init(this);
++
++	r = nand_scan_ident(mtd, 1, NULL);
++	if (r)
++		return r;
++
++	r = qcom_nandc_ecc_init(this);
++	if (r)
++		return r;
++
++	qcom_nandc_hw_post_init(this);
++
++	r = nand_scan_tail(mtd);
++	if (r)
++		return r;
++
++	return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
++}
++
++static int qcom_nandc_parse_dt(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++	struct device_node *np = this->dev->of_node;
++	int r;
++
++	this->ecc_strength = of_get_nand_ecc_strength(np);
++	if (this->ecc_strength < 0) {
++		dev_warn(this->dev,
++			"incorrect ecc strength, setting to 4 bits/step\n");
++		this->ecc_strength = 4;
++	}
++
++	this->bus_width = of_get_nand_bus_width(np);
++	if (this->bus_width < 0) {
++		dev_warn(this->dev, "incorrect bus width, setting to 8\n");
++		this->bus_width = 8;
++	}
++
++	r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci);
++	if (r) {
++		dev_err(this->dev, "command CRCI unspecified\n");
++		return r;
++	}
++
++	r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci);
++	if (r) {
++		dev_err(this->dev, "data CRCI unspecified\n");
++		return r;
++	}
++
++	return 0;
++}
++
++static int qcom_nandc_probe(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this;
++	const struct of_device_id *match;
++	int r;
++
++	this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
++	if (!this)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, this);
++
++	this->pdev = pdev;
++	this->dev  = &pdev->dev;
++
++	match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
++	if (!match) {
++		dev_err(&pdev->dev, "failed to match device\n");
++		return -ENODEV;
++	}
++
++	if (!match->data) {
++		dev_err(&pdev->dev, "failed to get device data\n");
++		return -ENODEV;
++	}
++
++	this->ecc_modes = (u32) match->data;
++
++	this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	this->base = devm_ioremap_resource(&pdev->dev, this->res);
++	if (IS_ERR(this->base))
++		return PTR_ERR(this->base);
++
++	this->core_clk = devm_clk_get(&pdev->dev, "core");
++	if (IS_ERR(this->core_clk))
++		return PTR_ERR(this->core_clk);
++
++	this->aon_clk = devm_clk_get(&pdev->dev, "aon");
++	if (IS_ERR(this->aon_clk))
++		return PTR_ERR(this->aon_clk);
++
++	r = qcom_nandc_parse_dt(pdev);
++	if (r)
++		return r;
++
++	r = qcom_nandc_alloc(this);
++	if (r)
++		return r;
++
++	r = clk_prepare_enable(this->core_clk);
++	if (r)
++		goto err_core_clk;
++
++	r = clk_prepare_enable(this->aon_clk);
++	if (r)
++		goto err_aon_clk;
++
++	r = qcom_nandc_init(this);
++	if (r)
++		goto err_init;
++
++	return 0;
++
++err_init:
++	clk_disable_unprepare(this->aon_clk);
++err_aon_clk:
++	clk_disable_unprepare(this->core_clk);
++err_core_clk:
++	qcom_nandc_unalloc(this);
++
++	return r;
++}
++
++static int qcom_nandc_remove(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++
++	qcom_nandc_unalloc(this);
++
++	clk_disable_unprepare(this->aon_clk);
++	clk_disable_unprepare(this->core_clk);
++
++	return 0;
++}
++
++#define EBI2_NANDC_ECC_MODES	(ECC_RS_4BIT | ECC_BCH_8BIT)
++
++/*
++ * data will hold a struct pointer containing more differences once we support
++ * more IPs
++ */
++static const struct of_device_id qcom_nandc_of_match[] = {
++	{	.compatible = "qcom,ebi2-nandc",
++		.data = (void *) EBI2_NANDC_ECC_MODES,
++	},
++	{}
++};
++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
++
++static struct platform_driver qcom_nandc_driver = {
++	.driver = {
++		.name = "qcom-nandc",
++		.of_match_table = qcom_nandc_of_match,
++	},
++	.probe   = qcom_nandc_probe,
++	.remove  = qcom_nandc_remove,
++};
++module_platform_driver(qcom_nandc_driver);
++
++MODULE_AUTHOR("Archit Taneja <architt at codeaurora.org>");
++MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740
+ obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
+ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
++obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
+ 
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
new file mode 100644
index 0000000..6530eb1
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927141
+Message-Id: <1438578498-32254-4-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:16 +0530
+
+Add DT bindings document for the Qualcomm NAND controller driver.
+
+Cc: devicetree at vger.kernel.org
+
+v3:
+- Don't use '0x' when specifying nand controller address space
+- Add optional property for on-flash bbt usage
+
+Acked-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+.../devicetree/bindings/mtd/qcom_nandc.txt         | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+@@ -0,0 +1,49 @@
++* Qualcomm NAND controller
++
++Required properties:
++- compatible:		should be "qcom,ebi2-nand" for IPQ806x
++- reg:			MMIO address range
++- clocks:		must contain core clock and always on clock
++- clock-names:		must contain "core" for the core clock and "aon" for the
++			always on clock
++- dmas:			DMA specifier, consisting of a phandle to the ADM DMA
++			controller node and the channel number to be used for
++			NAND. Refer to dma.txt and qcom_adm.txt for more details
++- dma-names:		must be "rxtx"
++- qcom,cmd-crci:	must contain the ADM command type CRCI block instance
++			number specified for the NAND controller on the given
++			platform
++- qcom,data-crci:	must contain the ADM data type CRCI block instance
++			number specified for the NAND controller on the given
++			platform
++
++Optional properties:
++- nand-bus-width:	bus width. Must be 8 or 16. If not present, 8 is chosen
++			as default
++
++- nand-ecc-strength:	number of bits to correct per ECC step. Must be 4 or 8
++			bits. If not present, 4 is chosen as default
++- nand-on-flash-bbt:	Create/use on-flash bad block table
++
++The device tree may optionally contain sub-nodes describing partitions of the
++address space. See partition.txt for more detail.
++
++Example:
++
++nand at 1ac00000 {
++	compatible = "qcom,ebi2-nandc";
++	reg = <0x1ac00000 0x800>;
++
++	clocks = <&gcc EBI2_CLK>,
++		 <&gcc EBI2_AON_CLK>;
++	clock-names = "core", "aon";
++
++	dmas = <&adm_dma 3>;
++	dma-names = "rxtx";
++	qcom,cmd-crci = <15>;
++	qcom,data-crci = <3>;
++
++	partition at 0 {
++	...
++	};
++};
diff --git a/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..a2742b7
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,50 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927121
+Message-Id: <1438578498-32254-5-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:17 +0530
+
+The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding
+compatible string.
+
+Cc: devicetree at vger.kernel.org
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -677,5 +677,21 @@
+ 			status = "disabled";
+ 		};
+ 
++		nand at 1ac00000 {
++			compatible = "qcom,ebi2-nandc";
++			reg = <0x1ac00000 0x800>;
++
++			clocks = <&gcc EBI2_CLK>,
++				 <&gcc EBI2_AON_CLK>;
++			clock-names = "core", "aon";
++
++			dmas = <&adm_dma 3>;
++			dma-names = "rxtx";
++			qcom,cmd-crci = <15>;
++			qcom,data-crci = <3>;
++
++			status = "disabled";
++		};
++
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
new file mode 100644
index 0000000..94e4e6b
--- /dev/null
+++ b/target/linux/ipq806x/patches-3.18/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
@@ -0,0 +1,79 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927091
+Message-Id: <1438578498-32254-6-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:18 +0530
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Cc: devicetree at vger.kernel.org
+
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -61,6 +61,31 @@
+ 					bias-none;
+ 				};
+ 			};
++
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
+ 		};
+ 
+ 		gsbi at 16300000 {
+@@ -174,5 +199,19 @@
+ 			pinctrl-0 = <&pcie1_pins>;
+ 			pinctrl-names = "default";
+ 		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++		};
+ 	};
+ };
++
++&adm_dma {
++	status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
index d7d6b69..22d28ac 100644
--- a/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
+++ b/target/linux/ipq806x/patches-3.18/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch
@@ -86,7 +86,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  static struct freq_tbl clk_tbl_gsbi_uart[] = {
  	{  1843200, P_PLL8, 2,  6, 625 },
  	{  3686400, P_PLL8, 2, 12, 625 },
-@@ -2239,6 +2290,472 @@ static struct clk_branch usb_fs1_h_clk =
+@@ -2269,6 +2320,472 @@ static struct clk_branch ebi2_aon_clk =
  	},
  };
  
@@ -559,7 +559,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  static struct clk_regmap *gcc_ipq806x_clks[] = {
  	[PLL0] = &pll0.clkr,
  	[PLL0_VOTE] = &pll0_vote,
-@@ -2247,6 +2764,7 @@ static struct clk_regmap *gcc_ipq806x_cl
+@@ -2277,6 +2794,7 @@ static struct clk_regmap *gcc_ipq806x_cl
  	[PLL8_VOTE] = &pll8_vote,
  	[PLL14] = &pll14.clkr,
  	[PLL14_VOTE] = &pll14_vote,
@@ -567,7 +567,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  	[GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
  	[GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
  	[GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
-@@ -2344,6 +2862,18 @@ static struct clk_regmap *gcc_ipq806x_cl
+@@ -2376,6 +2894,18 @@ static struct clk_regmap *gcc_ipq806x_cl
  	[PLL9] = &hfpll0.clkr,
  	[PLL10] = &hfpll1.clkr,
  	[PLL12] = &hfpll_l2.clkr,
@@ -586,7 +586,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  };
  
  static const struct qcom_reset_map gcc_ipq806x_resets[] = {
-@@ -2462,6 +2992,48 @@ static const struct qcom_reset_map gcc_i
+@@ -2494,6 +3024,48 @@ static const struct qcom_reset_map gcc_i
  	[USB30_1_PHY_RESET] = { 0x3b58, 0 },
  	[NSSFB0_RESET] = { 0x3b60, 6 },
  	[NSSFB1_RESET] = { 0x3b60, 7 },
@@ -635,7 +635,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  };
  
  static const struct regmap_config gcc_ipq806x_regmap_config = {
-@@ -2490,6 +3062,8 @@ static int gcc_ipq806x_probe(struct plat
+@@ -2522,6 +3094,8 @@ static int gcc_ipq806x_probe(struct plat
  {
  	struct clk *clk;
  	struct device *dev = &pdev->dev;
@@ -644,7 +644,7 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  
  	/* Temporary until RPM clocks supported */
  	clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000);
-@@ -2500,7 +3074,25 @@ static int gcc_ipq806x_probe(struct plat
+@@ -2532,7 +3106,25 @@ static int gcc_ipq806x_probe(struct plat
  	if (IS_ERR(clk))
  		return PTR_ERR(clk);
  
@@ -673,12 +673,12 @@ Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
  static int gcc_ipq806x_remove(struct platform_device *pdev)
 --- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h
 +++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h
-@@ -289,5 +289,7 @@
- #define UBI32_CORE2_CLK_SRC			278
+@@ -290,5 +290,7 @@
  #define UBI32_CORE1_CLK				279
  #define UBI32_CORE2_CLK				280
-+#define NSSTCM_CLK_SRC				281
-+#define NSSTCM_CLK				282
+ #define EBI2_AON_CLK				281
++#define NSSTCM_CLK_SRC				282
++#define NSSTCM_CLK				283
  
  #endif
 --- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h
diff --git a/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
index 73b8edf..75ebfe6 100644
--- a/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
+++ b/target/linux/ipq806x/patches-3.18/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
@@ -22,8 +22,8 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  	};
  
  	chosen {
-@@ -61,6 +62,15 @@
- 					bias-none;
+@@ -86,6 +87,15 @@
+ 					bias-bus-hold;
  				};
  			};
 +
@@ -38,9 +38,9 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  		};
  
  		gsbi at 16300000 {
-@@ -174,5 +184,33 @@
- 			pinctrl-0 = <&pcie1_pins>;
- 			pinctrl-names = "default";
+@@ -209,6 +219,34 @@
+ 			nand-ecc-strength = <4>;
+ 			nand-bus-width = <8>;
  		};
 +
 +		mdio0: mdio {
@@ -72,6 +72,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 +		};
  	};
  };
+ 
 --- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 @@ -16,6 +16,7 @@
diff --git a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
index 5213d62..72a9f8b 100644
--- a/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
+++ b/target/linux/ipq806x/patches-3.18/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -12,7 +12,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 
 --- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
-@@ -71,6 +71,16 @@
+@@ -96,6 +96,16 @@
  					bias-disable;
  				};
  			};
@@ -29,7 +29,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  		};
  
  		gsbi at 16300000 {
-@@ -212,5 +222,26 @@
+@@ -247,6 +257,27 @@
  				reg = <4>;
  			};
  		};
@@ -56,6 +56,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 +		};
  	};
  };
+ 
 --- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 @@ -75,6 +75,14 @@
@@ -116,7 +117,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  };
 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
-@@ -677,5 +677,91 @@
+@@ -693,5 +693,91 @@
  			status = "disabled";
  		};
  
diff --git a/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch b/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
new file mode 100644
index 0000000..088b8cb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/161-mtd-nand-Create-a-BBT-flag-to-access-bad-block-markers-in-raw-mode.patch
@@ -0,0 +1,84 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,
+	1/5] mtd: nand: Create a BBT flag to access bad block markers in raw
+	mode
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927081
+Message-Id: <1438578498-32254-2-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>
+Date: Mon,  3 Aug 2015 10:38:14 +0530
+
+Some controllers can access the factory bad block marker from OOB only
+when they read it in raw mode. When ECC is enabled, these controllers
+discard reading/writing bad block markers, preventing access to them
+altogether.
+
+The bbt driver assumes MTD_OPS_PLACE_OOB when scanning for bad blocks.
+This results in the nand driver's ecc->read_oob() op to be called, which
+works with ECC enabled.
+
+Create a new BBT option flag that tells nand_bbt to force the mode to
+MTD_OPS_RAW. This would result in the correct op being called for the
+underlying nand controller driver.
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+drivers/mtd/nand/nand_base.c | 6 +++++-
+ drivers/mtd/nand/nand_bbt.c  | 6 +++++-
+ include/linux/mtd/bbm.h      | 7 +++++++
+ 3 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -395,7 +395,11 @@ static int nand_default_block_markbad(st
+ 	} else {
+ 		ops.len = ops.ooblen = 1;
+ 	}
+-	ops.mode = MTD_OPS_PLACE_OOB;
++
++	if (unlikely(chip->bbt_options & NAND_BBT_ACCESS_BBM_RAW))
++		ops.mode = MTD_OPS_RAW;
++	else
++		ops.mode = MTD_OPS_PLACE_OOB;
+ 
+ 	/* Write to first/last page(s) if necessary */
+ 	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
+--- a/drivers/mtd/nand/nand_bbt.c
++++ b/drivers/mtd/nand/nand_bbt.c
+@@ -423,7 +423,11 @@ static int scan_block_fast(struct mtd_in
+ 	ops.oobbuf = buf;
+ 	ops.ooboffs = 0;
+ 	ops.datbuf = NULL;
+-	ops.mode = MTD_OPS_PLACE_OOB;
++
++	if (unlikely(bd->options & NAND_BBT_ACCESS_BBM_RAW))
++		ops.mode = MTD_OPS_RAW;
++	else
++		ops.mode = MTD_OPS_PLACE_OOB;
+ 
+ 	for (j = 0; j < numpages; j++) {
+ 		/*
+--- a/include/linux/mtd/bbm.h
++++ b/include/linux/mtd/bbm.h
+@@ -116,6 +116,13 @@ struct nand_bbt_descr {
+ #define NAND_BBT_NO_OOB_BBM	0x00080000
+ 
+ /*
++ * Force MTD_OPS_RAW mode when trying to access bad block markes from OOB. To
++ * be used by controllers which can access BBM only when ECC is disabled, i.e,
++ * when in RAW access mode
++ */
++#define NAND_BBT_ACCESS_BBM_RAW	0x00100000
++
++/*
+  * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr
+  * was allocated dynamicaly and must be freed in nand_release(). Has no meaning
+  * in nand_chip.bbt_options.
diff --git a/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch b/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
new file mode 100644
index 0000000..4be7ecb
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/162-mtd-nand-Qualcomm-NAND-controller-driver.patch
@@ -0,0 +1,2024 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,2/5] mtd: nand: Qualcomm NAND controller driver
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927101
+Message-Id: <1438578498-32254-3-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>
+Date: Mon,  3 Aug 2015 10:38:15 +0530
+
+The Qualcomm NAND controller is found in SoCs like IPQ806x, MSM7xx,
+MDM9x15 series.
+
+It exists as a sub block inside the IPs EBI2 (External Bus Interface 2)
+and QPIC (Qualcomm Parallel Interface Controller). These IPs provide a
+broader interface for external slow peripheral devices such as LCD and
+NAND/NOR flash memory or SRAM like interfaces.
+
+We add support for the NAND controller found within EBI2. For the SoCs
+of our interest, we only use the NAND controller within EBI2. Therefore,
+it's safe for us to assume that the NAND controller is a standalone block
+within the SoC.
+
+The controller supports 512B, 2kB, 4kB and 8kB page 8-bit and 16-bit NAND
+flash devices. It contains a HW ECC block that supports BCH ECC (4, 8 and
+16 bit correction/step) and RS ECC(4 bit correction/step) that covers main
+and spare data. The controller contains an internal 512 byte page buffer
+to which we read/write via DMA. The EBI2 type NAND controller uses ADM DMA
+for register read/write and data transfers. The controller performs page
+reads and writes at a codeword/step level of 512 bytes. It can support up
+to 2 external chips of different configurations.
+
+The driver prepares register read and write configuration descriptors for
+each codeword, followed by data descriptors to read or write data from the
+controller's internal buffer. It uses a single ADM DMA channel that we get
+via dmaengine API. The controller requires 2 ADM CRCIs for command and
+data flow control. These are passed via DT.
+
+The ecc layout used by the controller is syndrome like, but we can't use
+the standard syndrome ecc ops because of several reasons. First, the amount
+of data bytes covered by ecc isn't same in each step. Second, writing to
+free oob space requires us writing to the entire step in which the oob
+lies. This forces us to create our own ecc ops.
+
+One more difference is how the controller accesses the bad block marker.
+The controller ignores reading the marker when ECC is enabled. ECC needs
+to be explicity disabled to read or write to the bad block marker. For
+this reason, we use the newly created flag NAND_BBT_ACCESS_BBM_RAW to
+read the factory provided bad block markers.
+
+v3:
+- Refactor dma functions for maximum reuse
+- Use dma_slave_confing on stack
+- optimize and clean upempty_page_fixup using memchr_inv
+- ensure portability with dma register reads using le32_* funcs
+- use NAND_USE_BOUNCE_BUFFER instead of doing it ourselves
+- fix handling of return values of dmaengine funcs
+- constify wherever possible
+- Remove dependency on ADM DMA in Kconfig
+- Misc fixes and clean ups
+
+v2:
+- Use new BBT flag that allows us to read BBM in raw mode
+- reduce memcpy-s in the driver
+- some refactor and clean ups because of above changes
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+drivers/mtd/nand/Kconfig      |    7 +
+ drivers/mtd/nand/Makefile     |    1 +
+ drivers/mtd/nand/qcom_nandc.c | 1913 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1921 insertions(+)
+ create mode 100644 drivers/mtd/nand/qcom_nandc.c
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -530,4 +530,11 @@ config MTD_NAND_HISI504
+ 	help
+ 	  Enables support for NAND controller on Hisilicon SoC Hip04.
+ 
++config MTD_NAND_QCOM
++	tristate "Support for NAND on QCOM SoCs"
++	depends on ARCH_QCOM
++	help
++	  Enables support for NAND flash chips on SoCs containing the EBI2 NAND
++	  controller. This controller is found on IPQ806x SoC.
++
+ endif # MTD_NAND
+--- /dev/null
++++ b/drivers/mtd/nand/qcom_nandc.c
+@@ -0,0 +1,1918 @@
++/*
++ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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 <linux/clk.h>
++#include <linux/slab.h>
++#include <linux/bitops.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmaengine.h>
++#include <linux/module.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_mtd.h>
++#include <linux/delay.h>
++
++/* NANDc reg offsets */
++#define NAND_FLASH_CMD			0x00
++#define NAND_ADDR0			0x04
++#define NAND_ADDR1			0x08
++#define NAND_FLASH_CHIP_SELECT		0x0c
++#define NAND_EXEC_CMD			0x10
++#define NAND_FLASH_STATUS		0x14
++#define NAND_BUFFER_STATUS		0x18
++#define NAND_DEV0_CFG0			0x20
++#define NAND_DEV0_CFG1			0x24
++#define NAND_DEV0_ECC_CFG		0x28
++#define NAND_DEV1_ECC_CFG		0x2c
++#define NAND_DEV1_CFG0			0x30
++#define NAND_DEV1_CFG1			0x34
++#define NAND_READ_ID			0x40
++#define NAND_READ_STATUS		0x44
++#define NAND_DEV_CMD0			0xa0
++#define NAND_DEV_CMD1			0xa4
++#define NAND_DEV_CMD2			0xa8
++#define NAND_DEV_CMD_VLD		0xac
++#define SFLASHC_BURST_CFG		0xe0
++#define NAND_ERASED_CW_DETECT_CFG	0xe8
++#define NAND_ERASED_CW_DETECT_STATUS	0xec
++#define NAND_EBI2_ECC_BUF_CFG		0xf0
++#define FLASH_BUF_ACC			0x100
++
++#define NAND_CTRL			0xf00
++#define NAND_VERSION			0xf08
++#define NAND_READ_LOCATION_0		0xf20
++#define NAND_READ_LOCATION_1		0xf24
++
++/* dummy register offsets, used by write_reg_dma */
++#define NAND_DEV_CMD1_RESTORE		0xdead
++#define NAND_DEV_CMD_VLD_RESTORE	0xbeef
++
++/* NAND_FLASH_CMD bits */
++#define PAGE_ACC			BIT(4)
++#define LAST_PAGE			BIT(5)
++
++/* NAND_FLASH_CHIP_SELECT bits */
++#define NAND_DEV_SEL			0
++#define DM_EN				BIT(2)
++
++/* NAND_FLASH_STATUS bits */
++#define FS_OP_ERR			BIT(4)
++#define FS_READY_BSY_N			BIT(5)
++#define FS_MPU_ERR			BIT(8)
++#define FS_DEVICE_STS_ERR		BIT(16)
++#define FS_DEVICE_WP			BIT(23)
++
++/* NAND_BUFFER_STATUS bits */
++#define BS_UNCORRECTABLE_BIT		BIT(8)
++#define BS_CORRECTABLE_ERR_MSK		0x1f
++
++/* NAND_DEVn_CFG0 bits */
++#define DISABLE_STATUS_AFTER_WRITE	4
++#define CW_PER_PAGE			6
++#define UD_SIZE_BYTES			9
++#define ECC_PARITY_SIZE_BYTES_RS	19
++#define SPARE_SIZE_BYTES		23
++#define NUM_ADDR_CYCLES			27
++#define STATUS_BFR_READ			30
++#define SET_RD_MODE_AFTER_STATUS	31
++
++/* NAND_DEVn_CFG0 bits */
++#define DEV0_CFG1_ECC_DISABLE		0
++#define WIDE_FLASH			1
++#define NAND_RECOVERY_CYCLES		2
++#define CS_ACTIVE_BSY			5
++#define BAD_BLOCK_BYTE_NUM		6
++#define BAD_BLOCK_IN_SPARE_AREA		16
++#define WR_RD_BSY_GAP			17
++#define ENABLE_BCH_ECC			27
++
++/* NAND_DEV0_ECC_CFG bits */
++#define ECC_CFG_ECC_DISABLE		0
++#define ECC_SW_RESET			1
++#define ECC_MODE			4
++#define ECC_PARITY_SIZE_BYTES_BCH	8
++#define ECC_NUM_DATA_BYTES		16
++#define ECC_FORCE_CLK_OPEN		30
++
++/* NAND_DEV_CMD1 bits */
++#define READ_ADDR			0
++
++/* NAND_DEV_CMD_VLD bits */
++#define READ_START_VLD			0
++
++/* NAND_EBI2_ECC_BUF_CFG bits */
++#define NUM_STEPS			0
++
++/* NAND_ERASED_CW_DETECT_CFG bits */
++#define ERASED_CW_ECC_MASK		1
++#define AUTO_DETECT_RES			0
++#define MASK_ECC			(1 << ERASED_CW_ECC_MASK)
++#define RESET_ERASED_DET		(1 << AUTO_DETECT_RES)
++#define ACTIVE_ERASED_DET		(0 << AUTO_DETECT_RES)
++#define CLR_ERASED_PAGE_DET		(RESET_ERASED_DET | MASK_ECC)
++#define SET_ERASED_PAGE_DET		(ACTIVE_ERASED_DET | MASK_ECC)
++
++/* NAND_ERASED_CW_DETECT_STATUS bits */
++#define PAGE_ALL_ERASED			BIT(7)
++#define CODEWORD_ALL_ERASED		BIT(6)
++#define PAGE_ERASED			BIT(5)
++#define CODEWORD_ERASED			BIT(4)
++#define ERASED_PAGE			(PAGE_ALL_ERASED | PAGE_ERASED)
++#define ERASED_CW			(CODEWORD_ALL_ERASED | CODEWORD_ERASED)
++
++/* Version Mask */
++#define NAND_VERSION_MAJOR_MASK		0xf0000000
++#define NAND_VERSION_MAJOR_SHIFT	28
++#define NAND_VERSION_MINOR_MASK		0x0fff0000
++#define NAND_VERSION_MINOR_SHIFT	16
++
++/* NAND OP_CMDs */
++#define PAGE_READ			0x2
++#define PAGE_READ_WITH_ECC		0x3
++#define PAGE_READ_WITH_ECC_SPARE	0x4
++#define PROGRAM_PAGE			0x6
++#define PAGE_PROGRAM_WITH_ECC		0x7
++#define PROGRAM_PAGE_SPARE		0x9
++#define BLOCK_ERASE			0xa
++#define FETCH_ID			0xb
++#define RESET_DEVICE			0xd
++
++/*
++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
++ * the driver calls the chunks 'step' or 'codeword' interchangeably
++ */
++#define NANDC_STEP_SIZE			512
++
++/*
++ * the largest page size we support is 8K, this will have 16 steps/codewords
++ * of 512 bytes each
++ */
++#define	MAX_NUM_STEPS			(SZ_8K / NANDC_STEP_SIZE)
++
++/* we read at most 3 registers per codeword scan */
++#define MAX_REG_RD			(3 * MAX_NUM_STEPS)
++
++/* ECC modes */
++#define ECC_NONE	BIT(0)
++#define ECC_RS_4BIT	BIT(1)
++#define	ECC_BCH_4BIT	BIT(2)
++#define	ECC_BCH_8BIT	BIT(3)
++
++struct desc_info {
++	struct list_head list;
++
++	enum dma_transfer_direction dir;
++	struct scatterlist sgl;
++	struct dma_async_tx_descriptor *dma_desc;
++};
++
++/*
++ * holds the current register values that we want to write. acts as a contiguous
++ * chunk of memory which we use to write the controller registers through DMA.
++ */
++struct nandc_regs {
++	u32 cmd;
++	u32 addr0;
++	u32 addr1;
++	u32 chip_sel;
++	u32 exec;
++
++	u32 cfg0;
++	u32 cfg1;
++	u32 ecc_bch_cfg;
++
++	u32 clrflashstatus;
++	u32 clrreadstatus;
++
++	u32 cmd1;
++	u32 vld;
++
++	u32 orig_cmd1;
++	u32 orig_vld;
++
++	u32 ecc_buf_cfg;
++};
++
++/*
++ * @cmd_crci:			ADM DMA CRCI for command flow control
++ * @data_crci:			ADM DMA CRCI for data flow control
++ * @list:			DMA descriptor list (list of desc_infos)
++ * @dma_done:			completion param to denote end of last
++ *				descriptor in the list
++ * @data_buffer:		our local DMA buffer for page read/writes,
++ *				used when we can't use the buffer provided
++ *				by upper layers directly
++ * @buf_size/count/start:	markers for chip->read_buf/write_buf functions
++ * @reg_read_buf:		buffer for reading register data via DMA
++ * @reg_read_pos:		marker for data read in reg_read_buf
++ * @cfg0, cfg1, cfg0_raw..:	NANDc register configurations needed for
++ *				ecc/non-ecc mode for the current nand flash
++ *				device
++ * @regs:			a contiguous chunk of memory for DMA register
++ *				writes
++ * @ecc_strength:		4 bit or 8 bit ecc, received via DT
++ * @bus_width:			8 bit or 16 bit NAND bus width, received via DT
++ * @ecc_modes:			supported ECC modes by the current controller,
++ *				initialized via DT match data
++ * @cw_size:			the number of bytes in a single step/codeword
++ *				of a page, consisting of all data, ecc, spare
++ *				and reserved bytes
++ * @cw_data:			the number of bytes within a codeword protected
++ *				by ECC
++ * @bch_enabled:		flag to tell whether BCH or RS ECC mode is used
++ * @status:			value to be returned if NAND_CMD_STATUS command
++ *				is executed
++ */
++struct qcom_nandc_data {
++	struct platform_device *pdev;
++	struct device *dev;
++
++	void __iomem *base;
++	struct resource *res;
++
++	struct clk *core_clk;
++	struct clk *aon_clk;
++
++	/* DMA stuff */
++	struct dma_chan *chan;
++	struct dma_slave_config	slave_conf;
++	unsigned int cmd_crci;
++	unsigned int data_crci;
++	struct list_head list;
++	struct completion dma_done;
++
++	/* MTD stuff */
++	struct nand_chip chip;
++	struct mtd_info mtd;
++
++	/* local data buffer and markers */
++	u8		*data_buffer;
++	int		buf_size;
++	int		buf_count;
++	int		buf_start;
++
++	/* local buffer to read back registers */
++	u32 *reg_read_buf;
++	int reg_read_pos;
++
++	/* required configs */
++	u32 cfg0, cfg1;
++	u32 cfg0_raw, cfg1_raw;
++	u32 ecc_buf_cfg;
++	u32 ecc_bch_cfg;
++	u32 clrflashstatus;
++	u32 clrreadstatus;
++	u32 sflashc_burst_cfg;
++	u32 cmd1, vld;
++
++	/* register state */
++	struct nandc_regs *regs;
++
++	/* things we get from DT */
++	int ecc_strength;
++	int bus_width;
++
++	u32 ecc_modes;
++
++	/* misc params */
++	int cw_size;
++	int cw_data;
++	bool use_ecc;
++	bool bch_enabled;
++	u8 status;
++	int last_command;
++};
++
++static inline u32 nandc_read(struct qcom_nandc_data *this, int offset)
++{
++	return ioread32(this->base + offset);
++}
++
++static inline void nandc_write(struct qcom_nandc_data *this, int offset,
++			       u32 val)
++{
++	iowrite32(val, this->base + offset);
++}
++
++/* helper to configure address register values */
++static void set_address(struct qcom_nandc_data *this, u16 column, int page)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nandc_regs *regs = this->regs;
++
++	if (chip->options & NAND_BUSWIDTH_16)
++		column >>= 1;
++
++	regs->addr0 = page << 16 | column;
++	regs->addr1 = page >> 16 & 0xff;
++}
++
++/*
++ * update_rw_regs:	set up read/write register values, these will be
++ *			written to the NAND controller registers via DMA
++ *
++ * @num_cw:		number of steps for the read/write operation
++ * @read:		read or write operation
++ */
++static void update_rw_regs(struct qcom_nandc_data *this, int num_cw, bool read)
++{
++	struct nandc_regs *regs = this->regs;
++
++	if (read) {
++		if (this->use_ecc)
++			regs->cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
++		else
++			regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++	} else {
++			regs->cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
++	}
++
++	if (this->use_ecc) {
++		regs->cfg0 = (this->cfg0 & ~(7U << CW_PER_PAGE)) |
++				(num_cw - 1) << CW_PER_PAGE;
++
++		regs->cfg1 = this->cfg1;
++		regs->ecc_bch_cfg = this->ecc_bch_cfg;
++	} else {
++		regs->cfg0 = (this->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++				(num_cw - 1) << CW_PER_PAGE;
++
++		regs->cfg1 = this->cfg1_raw;
++		regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++	}
++
++	regs->ecc_buf_cfg = this->ecc_buf_cfg;
++	regs->clrflashstatus = this->clrflashstatus;
++	regs->clrreadstatus = this->clrreadstatus;
++	regs->exec = 1;
++}
++
++static int prep_dma_desc(struct qcom_nandc_data *this, bool read, int reg_off,
++			 const void *vaddr, int size, bool flow_control)
++{
++	struct desc_info *desc;
++	struct dma_async_tx_descriptor *dma_desc;
++	struct scatterlist *sgl;
++	struct dma_slave_config slave_conf;
++	int r;
++
++	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++	if (!desc)
++		return -ENOMEM;
++
++	list_add_tail(&desc->list, &this->list);
++
++	sgl = &desc->sgl;
++
++	sg_init_one(sgl, vaddr, size);
++
++	desc->dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
++
++	r = dma_map_sg(this->dev, sgl, 1, desc->dir);
++	if (r == 0) {
++		r = -ENOMEM;
++		goto err;
++	}
++
++	memset(&slave_conf, 0x00, sizeof(slave_conf));
++
++	slave_conf.device_fc = flow_control;
++	if (read) {
++		slave_conf.src_maxburst = 16;
++		slave_conf.src_addr = this->res->start + reg_off;
++		slave_conf.slave_id = this->data_crci;
++	} else {
++		slave_conf.dst_maxburst = 16;
++		slave_conf.dst_addr = this->res->start + reg_off;
++		slave_conf.slave_id = this->cmd_crci;
++	}
++
++	r = dmaengine_slave_config(this->chan, &slave_conf);
++	if (r) {
++		dev_err(this->dev, "failed to configure dma channel\n");
++		goto err;
++	}
++
++	dma_desc = dmaengine_prep_slave_sg(this->chan, sgl, 1, desc->dir, 0);
++	if (!dma_desc) {
++		dev_err(this->dev, "failed to prepare desc\n");
++		r = -EINVAL;
++		goto err;
++	}
++
++	desc->dma_desc = dma_desc;
++
++	return 0;
++err:
++	kfree(desc);
++
++	return r;
++}
++
++/*
++ * read_reg_dma:	prepares a descriptor to read a given number of
++ *			contiguous registers to the reg_read_buf pointer
++ *
++ * @first:		offset of the first register in the contiguous block
++ * @num_regs:		number of registers to read
++ */
++static int read_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++	bool flow_control = false;
++	void *vaddr;
++	int size;
++
++	if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
++		flow_control = true;
++
++	size = num_regs * sizeof(u32);
++	vaddr = this->reg_read_buf + this->reg_read_pos;
++	this->reg_read_pos += num_regs;
++
++	return prep_dma_desc(this, true, first, vaddr, size, flow_control);
++}
++
++/*
++ * write_reg_dma:	prepares a descriptor to write a given number of
++ *			contiguous registers
++ *
++ * @first:		offset of the first register in the contiguous block
++ * @num_regs:		number of registers to write
++ */
++static int write_reg_dma(struct qcom_nandc_data *this, int first, int num_regs)
++{
++	bool flow_control = false;
++	struct nandc_regs *regs = this->regs;
++	void *vaddr;
++	int size;
++
++	switch (first) {
++	case NAND_FLASH_CMD:
++		vaddr = &regs->cmd;
++		flow_control = true;
++		break;
++	case NAND_EXEC_CMD:
++		vaddr = &regs->exec;
++		break;
++	case NAND_FLASH_STATUS:
++		vaddr = &regs->clrflashstatus;
++		break;
++	case NAND_DEV0_CFG0:
++		vaddr = &regs->cfg0;
++		break;
++	case NAND_READ_STATUS:
++		vaddr = &regs->clrreadstatus;
++		break;
++	case NAND_DEV_CMD1:
++		vaddr = &regs->cmd1;
++		break;
++	case NAND_DEV_CMD1_RESTORE:
++		first = NAND_DEV_CMD1;
++		vaddr = &regs->orig_cmd1;
++		break;
++	case NAND_DEV_CMD_VLD:
++		vaddr = &regs->vld;
++		break;
++	case NAND_DEV_CMD_VLD_RESTORE:
++		first = NAND_DEV_CMD_VLD;
++		vaddr = &regs->orig_vld;
++		break;
++	case NAND_EBI2_ECC_BUF_CFG:
++		vaddr = &regs->ecc_buf_cfg;
++		break;
++	default:
++		dev_err(this->dev, "invalid starting register\n");
++		return -EINVAL;
++	}
++
++	size = num_regs * sizeof(u32);
++
++	return prep_dma_desc(this, false, first, vaddr, size, flow_control);
++}
++
++/*
++ * read_data_dma:	prepares a DMA descriptor to transfer data from the
++ *			controller's internal buffer to the buffer 'vaddr'
++ *
++ * @reg_off:		offset within the controller's data buffer
++ * @vaddr:		virtual address of the buffer we want to write to
++ * @size:		DMA transaction size in bytes
++ */
++static int read_data_dma(struct qcom_nandc_data *this, int reg_off,
++			 const u8 *vaddr, int size)
++{
++	return prep_dma_desc(this, true, reg_off, vaddr, size, false);
++}
++
++/*
++ * write_data_dma:	prepares a DMA descriptor to transfer data from
++ *			'vaddr' to the controller's internal buffer
++ *
++ * @reg_off:		offset within the controller's data buffer
++ * @vaddr:		virtual address of the buffer we want to read from
++ * @size:		DMA transaction size in bytes
++ */
++static int write_data_dma(struct qcom_nandc_data *this, int reg_off,
++			  const u8 *vaddr, int size)
++{
++	return prep_dma_desc(this, false, reg_off, vaddr, size, false);
++}
++
++/*
++ * helper to prepare dma descriptors to configure registers needed for reading a
++ * codeword/step in a page
++ */
++static void config_cw_read(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 3);
++	write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 2);
++	read_reg_dma(this, NAND_ERASED_CW_DETECT_STATUS, 1);
++}
++
++/*
++ * helpers to prepare dma descriptors used to configure registers needed for
++ * writing a codeword/step in a page
++ */
++static void config_cw_write_pre(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 3);
++	write_reg_dma(this, NAND_EBI2_ECC_BUF_CFG, 1);
++}
++
++static void config_cw_write_post(struct qcom_nandc_data *this)
++{
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	write_reg_dma(this, NAND_FLASH_STATUS, 1);
++	write_reg_dma(this, NAND_READ_STATUS, 1);
++}
++
++/*
++ * the following functions are used within chip->cmdfunc() to perform different
++ * NAND_CMD_* commands
++ */
++
++/* sets up descriptors for NAND_CMD_PARAM */
++static int nandc_param(struct qcom_nandc_data *this)
++{
++	struct nandc_regs *regs = this->regs;
++
++	/*
++	 * NAND_CMD_PARAM is called before we know much about the FLASH chip
++	 * in use. we configure the controller to perform a raw read of 512
++	 * bytes to read onfi params
++	 */
++	regs->cmd = PAGE_READ | PAGE_ACC | LAST_PAGE;
++	regs->addr0 = 0;
++	regs->addr1 = 0;
++	regs->cfg0 =  0 << CW_PER_PAGE
++			| 512 << UD_SIZE_BYTES
++			| 5 << NUM_ADDR_CYCLES
++			| 0 << SPARE_SIZE_BYTES;
++
++	regs->cfg1 =  7 << NAND_RECOVERY_CYCLES
++			| 0 << CS_ACTIVE_BSY
++			| 17 << BAD_BLOCK_BYTE_NUM
++			| 1 << BAD_BLOCK_IN_SPARE_AREA
++			| 2 << WR_RD_BSY_GAP
++			| 0 << WIDE_FLASH
++			| 1 << DEV0_CFG1_ECC_DISABLE;
++
++	regs->ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++
++	/* configure CMD1 and VLD for ONFI param probing */
++	regs->vld = (this->vld & ~(1 << READ_START_VLD))
++			| 0 << READ_START_VLD;
++
++	regs->cmd1 = (this->cmd1 & ~(0xFF << READ_ADDR))
++			| NAND_CMD_PARAM << READ_ADDR;
++
++	regs->exec = 1;
++
++	regs->orig_cmd1 = this->cmd1;
++	regs->orig_vld = this->vld;
++
++	write_reg_dma(this, NAND_DEV_CMD_VLD, 1);
++	write_reg_dma(this, NAND_DEV_CMD1, 1);
++
++	this->buf_count = 512;
++	memset(this->data_buffer, 0xff, this->buf_count);
++
++	config_cw_read(this);
++
++	read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->buf_count);
++
++	/* restore CMD1 and VLD regs */
++	write_reg_dma(this, NAND_DEV_CMD1_RESTORE, 1);
++	write_reg_dma(this, NAND_DEV_CMD_VLD_RESTORE, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_ERASE1 */
++static int erase_block(struct qcom_nandc_data *this, int page_addr)
++{
++	struct nandc_regs *regs = this->regs;
++
++	regs->cmd = BLOCK_ERASE | PAGE_ACC | LAST_PAGE;
++	regs->addr0 = page_addr;
++	regs->addr1 = 0;
++	regs->cfg0 = this->cfg0_raw & ~(7 << CW_PER_PAGE);
++	regs->cfg1 = this->cfg1_raw;
++	regs->exec = 1;
++	regs->clrflashstatus = this->clrflashstatus;
++	regs->clrreadstatus = this->clrreadstatus;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 3);
++	write_reg_dma(this, NAND_DEV0_CFG0, 2);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	write_reg_dma(this, NAND_FLASH_STATUS, 1);
++	write_reg_dma(this, NAND_READ_STATUS, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_READID */
++static int read_id(struct qcom_nandc_data *this, int column)
++{
++	struct nandc_regs *regs = this->regs;
++
++	if (column == -1)
++		return 0;
++
++	regs->cmd = FETCH_ID;
++	regs->addr0 = column;
++	regs->addr1 = 0;
++	regs->chip_sel = DM_EN;
++	regs->exec = 1;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 4);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_READ_ID, 1);
++
++	return 0;
++}
++
++/* sets up descriptors for NAND_CMD_RESET */
++static int reset(struct qcom_nandc_data *this)
++{
++	struct nandc_regs *regs = this->regs;
++
++	regs->cmd = RESET_DEVICE;
++	regs->exec = 1;
++
++	write_reg_dma(this, NAND_FLASH_CMD, 1);
++	write_reg_dma(this, NAND_EXEC_CMD, 1);
++
++	read_reg_dma(this, NAND_FLASH_STATUS, 1);
++
++	return 0;
++}
++
++/* helpers to submit/free our list of dma descriptors */
++static void dma_callback(void *param)
++{
++	struct qcom_nandc_data *this = param;
++	struct completion *c = &this->dma_done;
++
++	complete(c);
++}
++
++static int submit_descs(struct qcom_nandc_data *this)
++{
++	struct completion *c = &this->dma_done;
++	struct desc_info *desc;
++	int r;
++
++	init_completion(c);
++
++	list_for_each_entry(desc, &this->list, list) {
++		/*
++		 * we add a callback to the last descriptor in our list to
++		 * notify completion of command
++		 */
++		if (list_is_last(&desc->list, &this->list)) {
++			desc->dma_desc->callback = dma_callback;
++			desc->dma_desc->callback_param = this;
++		}
++
++		dmaengine_submit(desc->dma_desc);
++	}
++
++	dma_async_issue_pending(this->chan);
++
++	r = wait_for_completion_timeout(c, msecs_to_jiffies(500));
++	if (!r)
++		return -ETIMEDOUT;
++
++	return 0;
++}
++
++static void free_descs(struct qcom_nandc_data *this)
++{
++	struct desc_info *desc, *n;
++
++	list_for_each_entry_safe(desc, n, &this->list, list) {
++		list_del(&desc->list);
++		dma_unmap_sg(this->dev, &desc->sgl, 1, desc->dir);
++		kfree(desc);
++	}
++}
++
++/* reset the register read buffer for next NAND operation */
++static void clear_read_regs(struct qcom_nandc_data *this)
++{
++	this->reg_read_pos = 0;
++	memset(this->reg_read_buf, 0, MAX_REG_RD * sizeof(*this->reg_read_buf));
++}
++
++static void pre_command(struct qcom_nandc_data *this, int command)
++{
++	this->buf_count = 0;
++	this->buf_start = 0;
++	this->use_ecc = false;
++	this->last_command = command;
++
++	clear_read_regs(this);
++}
++
++/*
++ * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our
++ * privately maintained status byte, this status byte can be read after
++ * NAND_CMD_STATUS is called
++ */
++static void parse_erase_write_errors(struct qcom_nandc_data *this, int command)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int num_cw;
++	int i;
++
++	num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1;
++
++	for (i = 0; i < num_cw; i++) {
++		__le32 flash_status = le32_to_cpu(this->reg_read_buf[i]);
++
++		if (flash_status & FS_MPU_ERR)
++			this->status &= ~NAND_STATUS_WP;
++
++		if (flash_status & FS_OP_ERR || (i == (num_cw - 1) &&
++				(flash_status & FS_DEVICE_STS_ERR)))
++			this->status |= NAND_STATUS_FAIL;
++	}
++}
++
++static void post_command(struct qcom_nandc_data *this, int command)
++{
++	switch (command) {
++	case NAND_CMD_READID:
++		memcpy(this->data_buffer, this->reg_read_buf, this->buf_count);
++		break;
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_ERASE1:
++		parse_erase_write_errors(this, command);
++		break;
++	default:
++		break;
++	}
++}
++
++/*
++ * Implements chip->cmdfunc. It's  only used for a limited set of commands.
++ * The rest of the commands wouldn't be called by upper layers. For example,
++ * NAND_CMD_READOOB would never be called because we have our own versions
++ * of read_oob ops for nand_ecc_ctrl.
++ */
++static void qcom_nandc_command(struct mtd_info *mtd, unsigned int command,
++			 int column, int page_addr)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	struct qcom_nandc_data *this = chip->priv;
++	bool wait = false;
++	int r = 0;
++
++	pre_command(this, command);
++
++	switch (command) {
++	case NAND_CMD_RESET:
++		r = reset(this);
++		wait = true;
++		break;
++
++	case NAND_CMD_READID:
++		this->buf_count = 4;
++		r = read_id(this, column);
++		wait = true;
++		break;
++
++	case NAND_CMD_PARAM:
++		r = nandc_param(this);
++		wait = true;
++		break;
++
++	case NAND_CMD_ERASE1:
++		r = erase_block(this, page_addr);
++		wait = true;
++		break;
++
++	case NAND_CMD_READ0:
++		/* we read the entire page for now */
++		WARN_ON(column != 0);
++
++		this->use_ecc = true;
++		set_address(this, 0, page_addr);
++		update_rw_regs(this, ecc->steps, true);
++		break;
++
++	case NAND_CMD_SEQIN:
++		WARN_ON(column != 0);
++		set_address(this, 0, page_addr);
++		break;
++
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_STATUS:
++	case NAND_CMD_NONE:
++	default:
++		break;
++	}
++
++	if (r) {
++		dev_err(this->dev, "failure executing command %d\n",
++			command);
++		free_descs(this);
++		return;
++	}
++
++	if (wait) {
++		r = submit_descs(this);
++		if (r)
++			dev_err(this->dev,
++				"failure submitting descs for command %d\n",
++				command);
++	}
++
++	free_descs(this);
++
++	post_command(this, command);
++}
++
++/*
++ * when using RS ECC, the NAND controller flags an error when reading an
++ * erased page. however, there are special characters at certain offsets when
++ * we read the erased page. we check here if the page is really empty. if so,
++ * we replace the magic characters with 0xffs
++ */
++static bool empty_page_fixup(struct qcom_nandc_data *this, u8 *data_buf)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = ecc->steps;
++	u8 orig1[MAX_NUM_STEPS], orig2[MAX_NUM_STEPS];
++	int i, j;
++
++	/* if BCH is enabled, HW will take care of detecting erased pages */
++	if (this->bch_enabled || !this->use_ecc)
++		return false;
++
++	for (i = 0; i < cwperpage; i++) {
++		u8 *empty1, *empty2;
++		__le32 flash_status = le32_to_cpu(this->reg_read_buf[3 * i]);
++
++		/*
++		 * an erased page flags an error in NAND_FLASH_STATUS, check if
++		 * the page is erased by looking for 0x54s at offsets 3 and 175
++		 * from the beginning of each codeword
++		 */
++		if (!(flash_status & FS_OP_ERR))
++			break;
++
++		empty1 = &data_buf[3 + i * this->cw_data];
++		empty2 = &data_buf[175 + i * this->cw_data];
++
++		/*
++		 * if the error wasn't because of an erased page, bail out and
++		 * and let someone else do the error checking
++		 */
++		if ((*empty1 == 0x54 && *empty2 == 0xff) ||
++				(*empty1 == 0xff && *empty2 == 0x54)) {
++			orig1[i] = *empty1;
++			orig2[i] = *empty2;
++
++			*empty1 = 0xff;
++			*empty2 = 0xff;
++		} else {
++			break;
++		}
++	}
++
++	if (i < cwperpage || memchr_inv(data_buf, 0xff, mtd->writesize))
++		goto not_empty;
++
++	/*
++	 * tell the caller that the page was empty and is fixed up, so that
++	 * parse_read_errors() doesn't think it's an error
++	 */
++	return true;
++
++not_empty:
++	/* restore original values if not empty*/
++	for (j = 0; j < i; j++) {
++		data_buf[3 + j * this->cw_data] = orig1[j];
++		data_buf[175 + j * this->cw_data] = orig2[j];
++	}
++
++	return false;
++}
++
++struct read_stats {
++	__le32 flash;
++	__le32 buffer;
++	__le32 erased_cw;
++};
++
++/*
++ * reads back status registers set by the controller to notify page read
++ * errors. this is equivalent to what 'ecc->correct()' would do.
++ */
++static int parse_read_errors(struct qcom_nandc_data *this, bool erased_page)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = ecc->steps;
++	unsigned int max_bitflips = 0;
++	int i;
++
++	for (i = 0; i < cwperpage; i++) {
++		int stat;
++		struct read_stats *buf;
++
++		buf = (struct read_stats *) (this->reg_read_buf + 3 * i);
++
++		buf->flash = le32_to_cpu(buf->flash);
++		buf->buffer = le32_to_cpu(buf->buffer);
++		buf->erased_cw = le32_to_cpu(buf->erased_cw);
++
++		if (buf->flash & (FS_OP_ERR | FS_MPU_ERR)) {
++
++			/* ignore erased codeword errors */
++			if (this->bch_enabled) {
++				if ((buf->erased_cw & ERASED_CW) == ERASED_CW)
++					continue;
++			} else if (erased_page) {
++				continue;
++			}
++
++			if (buf->buffer & BS_UNCORRECTABLE_BIT) {
++				mtd->ecc_stats.failed++;
++				continue;
++			}
++		}
++
++		stat = buf->buffer & BS_CORRECTABLE_ERR_MSK;
++		mtd->ecc_stats.corrected += stat;
++
++		max_bitflips = max_t(unsigned int, max_bitflips, stat);
++	}
++
++	return max_bitflips;
++}
++
++/*
++ * helper to perform the actual page read operation, used by ecc->read_page()
++ * and ecc->read_oob()
++ */
++static int read_page_low(struct qcom_nandc_data *this, u8 *data_buf,
++			 u8 *oob_buf)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int i, r;
++
++	/* queue cmd descs for each codeword */
++	for (i = 0; i < ecc->steps; i++) {
++		int data_size, oob_size;
++
++		if (i == (ecc->steps - 1)) {
++			data_size = ecc->size - ((ecc->steps - 1) << 2);
++			oob_size = (ecc->steps << 2) + ecc->bytes;
++		} else {
++			data_size = this->cw_data;
++			oob_size = ecc->bytes;
++		}
++
++		config_cw_read(this);
++
++		if (data_buf)
++			read_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++		if (oob_buf)
++			read_data_dma(this, FLASH_BUF_ACC + data_size, oob_buf,
++					oob_size);
++
++		if (data_buf)
++			data_buf += data_size;
++		if (oob_buf)
++			oob_buf += oob_size;
++	}
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failure to read page/oob\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/*
++ * a helper that copies the last step/codeword of a page (containing free oob)
++ * into our local buffer
++ */
++static int copy_last_cw(struct qcom_nandc_data *this, bool use_ecc, int page)
++{
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int size;
++	int r;
++
++	clear_read_regs(this);
++
++	size = use_ecc ? this->cw_data : this->cw_size;
++
++	/* prepare a clean read buffer */
++	memset(this->data_buffer, 0xff, size);
++
++	this->use_ecc = use_ecc;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, true);
++
++	config_cw_read(this);
++
++	read_data_dma(this, FLASH_BUF_ACC, this->data_buffer, size);
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failed to copy last codeword\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/* implements ecc->read_page() */
++static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
++				uint8_t *buf, int oob_required, int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	u8 *data_buf, *oob_buf = NULL;
++	bool erased_page;
++	int r;
++
++	data_buf = buf;
++	oob_buf = oob_required ? chip->oob_poi : NULL;
++
++	r = read_page_low(this, data_buf, oob_buf);
++	if (r) {
++		dev_err(this->dev, "failure to read page\n");
++		return r;
++	}
++
++	erased_page = empty_page_fixup(this, data_buf);
++
++	return parse_read_errors(this, erased_page);
++}
++
++/* implements ecc->read_oob() */
++static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++			       int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int r;
++
++	clear_read_regs(this);
++
++	this->use_ecc = true;
++	set_address(this, 0, page);
++	update_rw_regs(this, ecc->steps, true);
++
++	r = read_page_low(this, NULL, chip->oob_poi);
++	if (r)
++		dev_err(this->dev, "failure to read oob\n");
++
++	return r;
++}
++
++/* implements ecc->read_oob_raw(), used to read the bad block marker flag */
++static int qcom_nandc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				   int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int start, length;
++	int r;
++
++	/*
++	 * configure registers for a raw page read, the address is set to the
++	 * beginning of the last codeword, we don't care about reading ecc
++	 * portion of oob, just the free stuff
++	 */
++	r = copy_last_cw(this, false, page);
++	if (r)
++		return r;
++
++	/*
++	 * reading raw oob has 2 parts, first the bad block byte, then the
++	 * actual free oob region. perform a memcpy in two steps
++	 */
++	start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++	length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++	memcpy(oob, this->data_buffer + start, length);
++
++	oob += length;
++
++	start = this->cw_data - (ecc->steps << 2) + 1;
++	length = ecc->steps << 2;
++
++	memcpy(oob, this->data_buffer + start, length);
++
++	return 0;
++}
++
++/* implements ecc->write_page() */
++static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++				 const uint8_t *buf, int oob_required)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	u8 *data_buf, *oob_buf;
++	int i, r = 0;
++
++	clear_read_regs(this);
++
++	data_buf = (u8 *) buf;
++	oob_buf = chip->oob_poi;
++
++	this->use_ecc = true;
++	update_rw_regs(this, ecc->steps, false);
++
++	for (i = 0; i < ecc->steps; i++) {
++		int data_size, oob_size;
++
++		if (i == (ecc->steps - 1)) {
++			data_size = ecc->size - ((ecc->steps - 1) << 2);
++			oob_size = (ecc->steps << 2) + ecc->bytes;
++		} else {
++			data_size = this->cw_data;
++			oob_size = ecc->bytes;
++		}
++
++		config_cw_write_pre(this);
++		write_data_dma(this, FLASH_BUF_ACC, data_buf, data_size);
++
++		/*
++		 * we don't really need to write anything to oob for the
++		 * first n - 1 codewords since these oob regions just
++		 * contain ecc that's written by the controller itself
++		 */
++		if (i == (ecc->steps - 1))
++			write_data_dma(this, FLASH_BUF_ACC + data_size,
++					oob_buf, oob_size);
++		config_cw_write_post(this);
++
++		data_buf += data_size;
++		oob_buf += oob_size;
++	}
++
++	r = submit_descs(this);
++	if (r)
++		dev_err(this->dev, "failure to write page\n");
++
++	free_descs(this);
++
++	return r;
++}
++
++/*
++ * implements ecc->write_oob()
++ *
++ * the NAND controller cannot write only data or only oob within a codeword,
++ * since ecc is calculated for the combined codeword. we first copy the
++ * entire contents for the last codeword(data + oob), replace the old oob
++ * with the new one in chip->oob_poi, and then write the entire codeword.
++ * this read-copy-write operation results in a slight perormance loss.
++ */
++static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int free_boff;
++	int data_size, oob_size;
++	int r, status = 0;
++
++	r = copy_last_cw(this, true, page);
++	if (r)
++		return r;
++
++	clear_read_regs(this);
++
++	/* calculate the data and oob size for the last codeword/step */
++	data_size = ecc->size - ((ecc->steps - 1) << 2);
++	oob_size = (ecc->steps << 2) + ecc->bytes;
++
++	/*
++	 * the location of spare data in the oob buffer, we could also use
++	 * ecc->layout.oobfree here
++	 */
++	free_boff = ecc->bytes * (ecc->steps - 1);
++
++	/* override new oob content to last codeword */
++	memcpy(this->data_buffer + data_size, oob + free_boff, oob_size);
++
++	this->use_ecc = true;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, false);
++
++	config_cw_write_pre(this);
++	write_data_dma(this, FLASH_BUF_ACC, this->data_buffer,
++		data_size + oob_size);
++	config_cw_write_post(this);
++
++	r = submit_descs(this);
++
++	free_descs(this);
++
++	if (r) {
++		dev_err(this->dev, "failure to write oob\n");
++		return -EIO;
++	}
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++	status = chip->waitfunc(mtd, chip);
++
++	return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/* implements ecc->write_oob_raw(), used to write bad block marker flag */
++static int qcom_nandc_write_oob_raw(struct mtd_info *mtd,
++				    struct nand_chip *chip, int page)
++{
++	struct qcom_nandc_data *this = chip->priv;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	uint8_t *oob = chip->oob_poi;
++	int start, length;
++	int r, status = 0;
++
++	r = copy_last_cw(this, false, page);
++	if (r)
++		return r;
++
++	clear_read_regs(this);
++
++	/*
++	 * writing raw oob has 2 parts, first the bad block region, then the
++	 * actual free region
++	 */
++	start = mtd->writesize - (this->cw_size * (ecc->steps - 1));
++	length = chip->options & NAND_BUSWIDTH_16 ? 2 : 1;
++
++	memcpy(this->data_buffer + start, oob, length);
++
++	oob += length;
++
++	start = this->cw_data - (ecc->steps << 2) + 1;
++	length = ecc->steps << 2;
++
++	memcpy(this->data_buffer + start, oob, length);
++
++	/* prepare write */
++	this->use_ecc = false;
++	set_address(this, this->cw_size * (ecc->steps - 1), page);
++	update_rw_regs(this, 1, false);
++
++	config_cw_write_pre(this);
++	write_data_dma(this, FLASH_BUF_ACC, this->data_buffer, this->cw_size);
++	config_cw_write_post(this);
++
++	r = submit_descs(this);
++
++	free_descs(this);
++
++	if (r) {
++		dev_err(this->dev, "failure to write updated oob\n");
++		return -EIO;
++	}
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++	status = chip->waitfunc(mtd, chip);
++
++	return status & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++/*
++ * the three functions below implement chip->read_byte(), chip->read_buf()
++ * and chip->write_buf() respectively. these aren't used for
++ * reading/writing page data, they are used for smaller data like reading
++ * id, status etc
++ */
++static uint8_t qcom_nandc_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	uint8_t *buf = this->data_buffer;
++	uint8_t ret = 0x0;
++
++	if (this->last_command == NAND_CMD_STATUS) {
++		ret = this->status;
++
++		this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++
++		return ret;
++	}
++
++	if (this->buf_start < this->buf_count)
++		ret = buf[this->buf_start++];
++
++	return ret;
++}
++
++static void qcom_nandc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++	memcpy(buf, this->data_buffer + this->buf_start, real_len);
++	this->buf_start += real_len;
++}
++
++static void qcom_nandc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
++		int len)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++	int real_len = min_t(size_t, len, this->buf_count - this->buf_start);
++
++	memcpy(this->data_buffer + this->buf_start, buf, real_len);
++
++	this->buf_start += real_len;
++}
++
++/* we support only one external chip for now */
++static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct qcom_nandc_data *this = chip->priv;
++
++	if (chipnr <= 0)
++		return;
++
++	dev_warn(this->dev, "invalid chip select\n");
++}
++
++/*
++ * NAND controller page layout info
++ *
++ * |-----------------------|	  |---------------------------------|
++ * |		xx.......xx|	  |		*********xx.......xx|
++ * |	DATA	xx..ECC..xx|	  |	DATA	**SPARE**xx..ECC..xx|
++ * |   (516)	xx.......xx|	  |  (516-n*4)	**(n*4)**xx.......xx|
++ * |		xx.......xx|	  |		*********xx.......xx|
++ * |-----------------------|	  |---------------------------------|
++ *     codeword 1,2..n-1			codeword n
++ *  <---(528/532 Bytes)---->	   <-------(528/532 Bytes)---------->
++ *
++ * n = number of codewords in the page
++ * . = ECC bytes
++ * * = spare bytes
++ * x = unused/reserved bytes
++ *
++ * 2K page: n = 4, spare = 16 bytes
++ * 4K page: n = 8, spare = 32 bytes
++ * 8K page: n = 16, spare = 64 bytes
++ *
++ * the qcom nand controller operates at a sub page/codeword level. each
++ * codeword is 528 and 532 bytes for 4 bit and 8 bit ECC modes respectively.
++ * the number of ECC bytes vary based on the ECC strength and the bus width.
++ *
++ * the first n - 1 codewords contains 516 bytes of user data, the remaining
++ * 12/16 bytes consist of ECC and reserved data. The nth codeword contains
++ * both user data and spare(oobavail) bytes that sum up to 516 bytes.
++ *
++ * the layout described above is used by the controller when the ECC block is
++ * enabled. When we read a page with ECC enabled, the unused/reserved bytes are
++ * skipped and not copied to our internal buffer. therefore, the nand_ecclayout
++ * layouts defined below doesn't consider the positions occupied by the reserved
++ * bytes
++ *
++ * when the ECC block is disabled, one unused byte (or two for 16 bit bus width)
++ * in the last codeword is the position of bad block marker. the bad block
++ * marker cannot be accessed when ECC is enabled.
++ *
++ */
++
++/*
++ * Layouts for different page sizes and ecc modes. We skip the eccpos field
++ * since it isn't needed for this driver
++ */
++
++/* 2K page, 4 bit ECC */
++static struct nand_ecclayout layout_oob_64 = {
++	.eccbytes	= 40,
++	.oobfree	= {
++				{ 30, 16 },
++			  },
++};
++
++/* 4K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_128 = {
++	.eccbytes	= 80,
++	.oobfree	= {
++				{ 70, 32 },
++			  },
++};
++
++/* 4K page, 8 bit ECC, 8 bit bus width */
++static struct nand_ecclayout layout_oob_224_x8 = {
++	.eccbytes	= 104,
++	.oobfree	= {
++				{ 91, 32 },
++			  },
++};
++
++/* 4K page, 8 bit ECC, 16 bit bus width */
++static struct nand_ecclayout layout_oob_224_x16 = {
++	.eccbytes	= 112,
++	.oobfree	= {
++				{ 98, 32 },
++			  },
++};
++
++/* 8K page, 4 bit ECC, 8/16 bit bus width */
++static struct nand_ecclayout layout_oob_256 = {
++	.eccbytes	= 160,
++	.oobfree	= {
++				{ 151, 64 },
++			  },
++};
++
++/*
++ * this is called before scan_ident, we do some minimal configurations so
++ * that reading ID and ONFI params work
++ */
++static void qcom_nandc_pre_init(struct qcom_nandc_data *this)
++{
++	/* kill onenand */
++	nandc_write(this, SFLASHC_BURST_CFG, 0);
++
++	/* enable ADM DMA */
++	nandc_write(this, NAND_FLASH_CHIP_SELECT, DM_EN);
++
++	/* save the original values of these registers */
++	this->cmd1 = nandc_read(this, NAND_DEV_CMD1);
++	this->vld = nandc_read(this, NAND_DEV_CMD_VLD);
++
++	/* initial status value */
++	this->status = NAND_STATUS_READY | NAND_STATUS_WP;
++}
++
++static int qcom_nandc_ecc_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage;
++	bool wide_bus;
++
++	/* the nand controller fetches codewords/chunks of 512 bytes */
++	cwperpage = mtd->writesize >> 9;
++
++	ecc->strength = this->ecc_strength;
++
++	wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++	if (ecc->strength >= 8) {
++		/* 8 bit ECC defaults to BCH ECC on all platforms */
++		ecc->bytes = wide_bus ? 14 : 13;
++	} else {
++		/*
++		 * if the controller supports BCH for 4 bit ECC, the controller
++		 * uses lesser bytes for ECC. If RS is used, the ECC bytes is
++		 * always 10 bytes
++		 */
++		if (this->ecc_modes & ECC_BCH_4BIT)
++			ecc->bytes = wide_bus ? 8 : 7;
++		else
++			ecc->bytes = 10;
++	}
++
++	/* each step consists of 512 bytes of data */
++	ecc->size = NANDC_STEP_SIZE;
++
++	ecc->read_page		= qcom_nandc_read_page;
++	ecc->read_oob		= qcom_nandc_read_oob;
++	ecc->write_page		= qcom_nandc_write_page;
++	ecc->write_oob		= qcom_nandc_write_oob;
++
++	/*
++	 * the bad block marker is readable only when we read the page with ECC
++	 * disabled. all the ops above run with ECC enabled. We need raw read
++	 * and write function for oob in order to access bad block marker.
++	 */
++	ecc->read_oob_raw	= qcom_nandc_read_oob_raw;
++	ecc->write_oob_raw	= qcom_nandc_write_oob_raw;
++
++	switch (mtd->oobsize) {
++	case 64:
++		ecc->layout = &layout_oob_64;
++		break;
++	case 128:
++		ecc->layout = &layout_oob_128;
++		break;
++	case 224:
++		if (wide_bus)
++			ecc->layout = &layout_oob_224_x16;
++		else
++			ecc->layout = &layout_oob_224_x8;
++		break;
++	case 256:
++		ecc->layout = &layout_oob_256;
++		break;
++	default:
++		dev_err(this->dev, "unsupported NAND device, oobsize %d\n",
++			mtd->oobsize);
++		return -ENODEV;
++	}
++
++	ecc->mode = NAND_ECC_HW;
++
++	/* enable ecc by default */
++	this->use_ecc = true;
++
++	return 0;
++}
++
++static void qcom_nandc_hw_post_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct nand_ecc_ctrl *ecc = &chip->ecc;
++	int cwperpage = mtd->writesize / ecc->size;
++	int spare_bytes, bad_block_byte;
++	bool wide_bus;
++	int ecc_mode = 0;
++
++	wide_bus = chip->options & NAND_BUSWIDTH_16 ? true : false;
++
++	if (ecc->strength >= 8) {
++		this->cw_size = 532;
++
++		spare_bytes = wide_bus ? 0 : 2;
++
++		this->bch_enabled = true;
++		ecc_mode = 1;
++	} else {
++		this->cw_size = 528;
++
++		if (this->ecc_modes & ECC_BCH_4BIT) {
++			spare_bytes = wide_bus ? 2 : 4;
++
++			this->bch_enabled = true;
++			ecc_mode = 0;
++		} else {
++			spare_bytes = wide_bus ? 0 : 1;
++		}
++	}
++
++	/*
++	 * DATA_UD_BYTES varies based on whether the read/write command protects
++	 * spare data with ECC too. We protect spare data by default, so we set
++	 * it to main + spare data, which are 512 and 4 bytes respectively.
++	 */
++	this->cw_data = 516;
++
++	bad_block_byte = mtd->writesize - this->cw_size * (cwperpage - 1) + 1;
++
++	this->cfg0 = (cwperpage - 1) << CW_PER_PAGE
++				| this->cw_data << UD_SIZE_BYTES
++				| 0 << DISABLE_STATUS_AFTER_WRITE
++				| 5 << NUM_ADDR_CYCLES
++				| ecc->bytes << ECC_PARITY_SIZE_BYTES_RS
++				| 0 << STATUS_BFR_READ
++				| 1 << SET_RD_MODE_AFTER_STATUS
++				| spare_bytes << SPARE_SIZE_BYTES;
++
++	this->cfg1 = 7 << NAND_RECOVERY_CYCLES
++				| 0 <<  CS_ACTIVE_BSY
++				| bad_block_byte << BAD_BLOCK_BYTE_NUM
++				| 0 << BAD_BLOCK_IN_SPARE_AREA
++				| 2 << WR_RD_BSY_GAP
++				| wide_bus << WIDE_FLASH
++				| this->bch_enabled << ENABLE_BCH_ECC;
++
++	this->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
++				| this->cw_size << UD_SIZE_BYTES
++				| 5 << NUM_ADDR_CYCLES
++				| 0 << SPARE_SIZE_BYTES;
++
++	this->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
++				| 0 << CS_ACTIVE_BSY
++				| 17 << BAD_BLOCK_BYTE_NUM
++				| 1 << BAD_BLOCK_IN_SPARE_AREA
++				| 2 << WR_RD_BSY_GAP
++				| wide_bus << WIDE_FLASH
++				| 1 << DEV0_CFG1_ECC_DISABLE;
++
++	this->ecc_bch_cfg = this->bch_enabled << ECC_CFG_ECC_DISABLE
++				| 0 << ECC_SW_RESET
++				| this->cw_data << ECC_NUM_DATA_BYTES
++				| 1 << ECC_FORCE_CLK_OPEN
++				| ecc_mode << ECC_MODE
++				| ecc->bytes << ECC_PARITY_SIZE_BYTES_BCH;
++
++	this->ecc_buf_cfg = 0x203 << NUM_STEPS;
++
++	this->clrflashstatus = FS_READY_BSY_N;
++	this->clrreadstatus = 0xc0;
++
++	dev_dbg(this->dev,
++		"cfg0 %x cfg1 %x ecc_buf_cfg %x ecc_bch cfg %x cw_size %d cw_data %d strength %d parity_bytes %d steps %d\n",
++		this->cfg0, this->cfg1, this->ecc_buf_cfg,
++		this->ecc_bch_cfg, this->cw_size, this->cw_data,
++		ecc->strength, ecc->bytes, cwperpage);
++}
++
++static int qcom_nandc_alloc(struct qcom_nandc_data *this)
++{
++	int r;
++
++	r = dma_set_coherent_mask(this->dev, DMA_BIT_MASK(32));
++	if (r) {
++		dev_err(this->dev, "failed to set DMA mask\n");
++		return r;
++	}
++
++	/*
++	 * we use the internal buffer for reading ONFI params, reading small
++	 * data like ID and status, and preforming read-copy-write operations
++	 * when writing to a codeword partially. 532 is the maximum possible
++	 * size of a codeword for our nand controller
++	 */
++	this->buf_size = 532;
++
++	this->data_buffer = devm_kzalloc(this->dev, this->buf_size, GFP_KERNEL);
++	if (!this->data_buffer)
++		return -ENOMEM;
++
++	this->regs = devm_kzalloc(this->dev, sizeof(*this->regs), GFP_KERNEL);
++	if (!this->regs)
++		return -ENOMEM;
++
++	this->reg_read_buf = devm_kzalloc(this->dev,
++				MAX_REG_RD * sizeof(*this->reg_read_buf),
++				GFP_KERNEL);
++	if (!this->reg_read_buf)
++		return -ENOMEM;
++
++	INIT_LIST_HEAD(&this->list);
++
++	this->chan = dma_request_slave_channel(this->dev, "rxtx");
++	if (!this->chan) {
++		dev_err(this->dev, "failed to request slave channel\n");
++		return -ENODEV;
++	}
++
++	return 0;
++}
++
++static void qcom_nandc_unalloc(struct qcom_nandc_data *this)
++{
++	dma_release_channel(this->chan);
++}
++
++static int qcom_nandc_init(struct qcom_nandc_data *this)
++{
++	struct mtd_info *mtd = &this->mtd;
++	struct nand_chip *chip = &this->chip;
++	struct device_node *np = this->dev->of_node;
++	struct mtd_part_parser_data ppdata = { .of_node = np };
++	int r;
++
++	mtd->priv = chip;
++	mtd->name = "qcom-nandc";
++	mtd->owner = THIS_MODULE;
++
++	chip->priv = this;
++
++	chip->cmdfunc		= qcom_nandc_command;
++	chip->select_chip	= qcom_nandc_select_chip;
++	chip->read_byte		= qcom_nandc_read_byte;
++	chip->read_buf		= qcom_nandc_read_buf;
++	chip->write_buf		= qcom_nandc_write_buf;
++
++	chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
++	if (this->bus_width == 16)
++		chip->options |= NAND_BUSWIDTH_16;
++
++	chip->bbt_options = NAND_BBT_ACCESS_BBM_RAW;
++	if (of_get_nand_on_flash_bbt(np))
++		chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++	qcom_nandc_pre_init(this);
++
++	r = nand_scan_ident(mtd, 1, NULL);
++	if (r)
++		return r;
++
++	r = qcom_nandc_ecc_init(this);
++	if (r)
++		return r;
++
++	qcom_nandc_hw_post_init(this);
++
++	r = nand_scan_tail(mtd);
++	if (r)
++		return r;
++
++	return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
++}
++
++static int qcom_nandc_parse_dt(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++	struct device_node *np = this->dev->of_node;
++	int r;
++
++	this->ecc_strength = of_get_nand_ecc_strength(np);
++	if (this->ecc_strength < 0) {
++		dev_warn(this->dev,
++			"incorrect ecc strength, setting to 4 bits/step\n");
++		this->ecc_strength = 4;
++	}
++
++	this->bus_width = of_get_nand_bus_width(np);
++	if (this->bus_width < 0) {
++		dev_warn(this->dev, "incorrect bus width, setting to 8\n");
++		this->bus_width = 8;
++	}
++
++	r = of_property_read_u32(np, "qcom,cmd-crci", &this->cmd_crci);
++	if (r) {
++		dev_err(this->dev, "command CRCI unspecified\n");
++		return r;
++	}
++
++	r = of_property_read_u32(np, "qcom,data-crci", &this->data_crci);
++	if (r) {
++		dev_err(this->dev, "data CRCI unspecified\n");
++		return r;
++	}
++
++	return 0;
++}
++
++static int qcom_nandc_probe(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this;
++	const struct of_device_id *match;
++	int r;
++
++	this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
++	if (!this)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, this);
++
++	this->pdev = pdev;
++	this->dev  = &pdev->dev;
++
++	match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev);
++	if (!match) {
++		dev_err(&pdev->dev, "failed to match device\n");
++		return -ENODEV;
++	}
++
++	if (!match->data) {
++		dev_err(&pdev->dev, "failed to get device data\n");
++		return -ENODEV;
++	}
++
++	this->ecc_modes = (u32) match->data;
++
++	this->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	this->base = devm_ioremap_resource(&pdev->dev, this->res);
++	if (IS_ERR(this->base))
++		return PTR_ERR(this->base);
++
++	this->core_clk = devm_clk_get(&pdev->dev, "core");
++	if (IS_ERR(this->core_clk))
++		return PTR_ERR(this->core_clk);
++
++	this->aon_clk = devm_clk_get(&pdev->dev, "aon");
++	if (IS_ERR(this->aon_clk))
++		return PTR_ERR(this->aon_clk);
++
++	r = qcom_nandc_parse_dt(pdev);
++	if (r)
++		return r;
++
++	r = qcom_nandc_alloc(this);
++	if (r)
++		return r;
++
++	r = clk_prepare_enable(this->core_clk);
++	if (r)
++		goto err_core_clk;
++
++	r = clk_prepare_enable(this->aon_clk);
++	if (r)
++		goto err_aon_clk;
++
++	r = qcom_nandc_init(this);
++	if (r)
++		goto err_init;
++
++	return 0;
++
++err_init:
++	clk_disable_unprepare(this->aon_clk);
++err_aon_clk:
++	clk_disable_unprepare(this->core_clk);
++err_core_clk:
++	qcom_nandc_unalloc(this);
++
++	return r;
++}
++
++static int qcom_nandc_remove(struct platform_device *pdev)
++{
++	struct qcom_nandc_data *this = platform_get_drvdata(pdev);
++
++	qcom_nandc_unalloc(this);
++
++	clk_disable_unprepare(this->aon_clk);
++	clk_disable_unprepare(this->core_clk);
++
++	return 0;
++}
++
++#define EBI2_NANDC_ECC_MODES	(ECC_RS_4BIT | ECC_BCH_8BIT)
++
++/*
++ * data will hold a struct pointer containing more differences once we support
++ * more IPs
++ */
++static const struct of_device_id qcom_nandc_of_match[] = {
++	{	.compatible = "qcom,ebi2-nandc",
++		.data = (void *) EBI2_NANDC_ECC_MODES,
++	},
++	{}
++};
++MODULE_DEVICE_TABLE(of, qcom_nandc_of_match);
++
++static struct platform_driver qcom_nandc_driver = {
++	.driver = {
++		.name = "qcom-nandc",
++		.of_match_table = qcom_nandc_of_match,
++	},
++	.probe   = qcom_nandc_probe,
++	.remove  = qcom_nandc_remove,
++};
++module_platform_driver(qcom_nandc_driver);
++
++MODULE_AUTHOR("Archit Taneja <architt at codeaurora.org>");
++MODULE_DESCRIPTION("Qualcomm NAND Controller driver");
++MODULE_LICENSE("GPL v2");
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nan
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
+ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
++obj-$(CONFIG_MTD_NAND_QCOM)	        += qcom_nandc.o
+ 
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch b/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
new file mode 100644
index 0000000..6530eb1
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/163-dt-bindings-qcom_nandc-Add-DT-bindings.patch
@@ -0,0 +1,82 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,3/5] dt/bindings: qcom_nandc: Add DT bindings
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927141
+Message-Id: <1438578498-32254-4-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:16 +0530
+
+Add DT bindings document for the Qualcomm NAND controller driver.
+
+Cc: devicetree at vger.kernel.org
+
+v3:
+- Don't use '0x' when specifying nand controller address space
+- Add optional property for on-flash bbt usage
+
+Acked-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+.../devicetree/bindings/mtd/qcom_nandc.txt         | 49 ++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/qcom_nandc.txt
+@@ -0,0 +1,49 @@
++* Qualcomm NAND controller
++
++Required properties:
++- compatible:		should be "qcom,ebi2-nand" for IPQ806x
++- reg:			MMIO address range
++- clocks:		must contain core clock and always on clock
++- clock-names:		must contain "core" for the core clock and "aon" for the
++			always on clock
++- dmas:			DMA specifier, consisting of a phandle to the ADM DMA
++			controller node and the channel number to be used for
++			NAND. Refer to dma.txt and qcom_adm.txt for more details
++- dma-names:		must be "rxtx"
++- qcom,cmd-crci:	must contain the ADM command type CRCI block instance
++			number specified for the NAND controller on the given
++			platform
++- qcom,data-crci:	must contain the ADM data type CRCI block instance
++			number specified for the NAND controller on the given
++			platform
++
++Optional properties:
++- nand-bus-width:	bus width. Must be 8 or 16. If not present, 8 is chosen
++			as default
++
++- nand-ecc-strength:	number of bits to correct per ECC step. Must be 4 or 8
++			bits. If not present, 4 is chosen as default
++- nand-on-flash-bbt:	Create/use on-flash bad block table
++
++The device tree may optionally contain sub-nodes describing partitions of the
++address space. See partition.txt for more detail.
++
++Example:
++
++nand at 1ac00000 {
++	compatible = "qcom,ebi2-nandc";
++	reg = <0x1ac00000 0x800>;
++
++	clocks = <&gcc EBI2_CLK>,
++		 <&gcc EBI2_AON_CLK>;
++	clock-names = "core", "aon";
++
++	dmas = <&adm_dma 3>;
++	dma-names = "rxtx";
++	qcom,cmd-crci = <15>;
++	qcom,data-crci = <3>;
++
++	partition at 0 {
++	...
++	};
++};
diff --git a/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
new file mode 100644
index 0000000..27eae8f
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/164-arm-qcom-dts-Add-NAND-controller-node-for-ipq806x.patch
@@ -0,0 +1,50 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,4/5] arm: qcom: dts: Add NAND controller node for ipq806x
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927121
+Message-Id: <1438578498-32254-5-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:17 +0530
+
+The nand controller in IPQ806x is of the 'EBI2 type'. Use the corresponding
+compatible string.
+
+Cc: devicetree at vger.kernel.org
+
+Reviewed-by: Andy Gross <agross at codeaurora.org>
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064.dtsi | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
+@@ -615,5 +615,21 @@
+ 
+ 			status = "disabled";
+ 		};
++
++		nand at 1ac00000 {
++			compatible = "qcom,ebi2-nandc";
++			reg = <0x1ac00000 0x800>;
++
++			clocks = <&gcc EBI2_CLK>,
++				 <&gcc EBI2_AON_CLK>;
++			clock-names = "core", "aon";
++
++			dmas = <&adm_dma 3>;
++			dma-names = "rxtx";
++			qcom,cmd-crci = <15>;
++			qcom,data-crci = <3>;
++
++			status = "disabled";
++		};
+ 	};
+ };
diff --git a/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch b/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
new file mode 100644
index 0000000..2231d2d
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.1/165-arm-qcom-dts-Enable-NAND-node-on-IPQ8064-AP148-platform.patch
@@ -0,0 +1,76 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3,5/5] arm: qcom: dts: Enable NAND node on IPQ8064 AP148 platform
+From: Archit Taneja <architt at codeaurora.org>
+X-Patchwork-Id: 6927091
+Message-Id: <1438578498-32254-6-git-send-email-architt at codeaurora.org>
+To: linux-mtd at lists.infradead.org, dehrenberg at google.com,
+	cernekee at gmail.com, computersforpeace at gmail.com
+Cc: linux-arm-msm at vger.kernel.org, agross at codeaurora.org,
+	sboyd at codeaurora.org, linux-kernel at vger.kernel.org,
+	Archit Taneja <architt at codeaurora.org>, devicetree at vger.kernel.org
+Date: Mon,  3 Aug 2015 10:38:18 +0530
+
+Enable the NAND controller node on the AP148 platform. Provide pinmux
+information.
+
+Cc: devicetree at vger.kernel.org
+
+Signed-off-by: Archit Taneja <architt at codeaurora.org>
+
+---
+arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 36 ++++++++++++++++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+@@ -61,6 +61,28 @@
+ 					bias-none;
+ 				};
+ 			};
++			nand_pins: nand_pins {
++				mux {
++					pins = "gpio34", "gpio35", "gpio36",
++					       "gpio37", "gpio38", "gpio39",
++					       "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					function = "nand";
++					drive-strength = <10>;
++					bias-disable;
++				};
++				pullups {
++					pins = "gpio39";
++					bias-pull-up;
++				};
++				hold {
++					pins = "gpio40", "gpio41", "gpio42",
++					       "gpio43", "gpio44", "gpio45",
++					       "gpio46", "gpio47";
++					bias-bus-hold;
++				};
++			};
+ 		};
+ 
+ 		gsbi at 16300000 {
+@@ -150,5 +172,19 @@
+ 			pinctrl-0 = <&pcie1_pins>;
+ 			pinctrl-names = "default";
+ 		};
++
++		nand at 1ac00000 {
++			status = "ok";
++
++			pinctrl-0 = <&nand_pins>;
++			pinctrl-names = "default";
++
++			nand-ecc-strength = <4>;
++			nand-bus-width = <8>;
++		};
+ 	};
+ };
++
++&adm_dma {
++	status = "ok";
++};
diff --git a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
index 1af0660..c1e267a 100644
--- a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
+++ b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch
@@ -22,8 +22,8 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  	};
  
  	chosen {
-@@ -61,6 +62,15 @@
- 					bias-none;
+@@ -83,6 +84,15 @@
+ 					bias-bus-hold;
  				};
  			};
 +
@@ -38,9 +38,9 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  		};
  
  		gsbi at 16300000 {
-@@ -150,5 +160,33 @@
- 			pinctrl-0 = <&pcie1_pins>;
- 			pinctrl-names = "default";
+@@ -182,6 +192,34 @@
+ 			nand-ecc-strength = <4>;
+ 			nand-bus-width = <8>;
  		};
 +
 +		mdio0: mdio {
@@ -72,6 +72,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 +		};
  	};
  };
+ 
 --- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 @@ -16,6 +16,7 @@
diff --git a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
index c8dafab..4a43ae4 100644
--- a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
+++ b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch
@@ -12,7 +12,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 
 --- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
-@@ -71,6 +71,16 @@
+@@ -93,6 +93,16 @@
  					bias-disable;
  				};
  			};
@@ -29,7 +29,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  		};
  
  		gsbi at 16300000 {
-@@ -188,5 +198,26 @@
+@@ -220,6 +230,27 @@
  				reg = <4>;
  			};
  		};
@@ -56,6 +56,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
 +		};
  	};
  };
+ 
 --- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 +++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts
 @@ -75,6 +75,14 @@
@@ -116,7 +117,7 @@ Signed-off-by: Mathieu Olivari <mathieu at codeaurora.org>
  };
 --- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
 +++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
-@@ -615,5 +615,91 @@
+@@ -631,5 +631,91 @@
  
  			status = "disabled";
  		};
-- 
2.1.4

_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list