linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support
@ 2018-11-16 16:40 Jianxin Pan
  2018-11-16 16:40 ` [PATCH v7 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver Jianxin Pan
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Jianxin Pan @ 2018-11-16 16:40 UTC (permalink / raw)
  To: Boris Brezillon, linux-mtd
  Cc: Jianxin Pan, David Woodhouse, Miquel Raynal, Brian Norris,
	Marek Vasut, Richard Weinberger, Jerome Brunet, Neil Armstrong,
	Martin Blumenstingl, Carlo Caione, Kevin Hilman, Rob Herring,
	Yixun Lan, Liang Yang, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel, devicetree

These two patches try to add initial NAND driver support for Amlogic Meson
SoCs, current it has been tested on GXL(p212) and AXG(s400) platform.

Changes since V6 at [7]
 - use timings->tBERS_max as the maximum time out, delete NFC_CMD_RB_TIMEOUT
 - fix nand_rw_cmd and support small block flash and which row address less than 3
 - fix coding style
 - replace readl/writel_* with readl/writel_relaxed*
 - delete ECC_SET_PROTECTED_OOB_BYTE and ECC_GET_PROTECTED_OOB_BYTE
 - implement dma access for read_buf and write_buf, more efficient.
 - delete waiting dma finish in write process and let NAND_CMD_PAGEPROG and
RB command go on queuing
 - add waiting the completed flag of last ecc page be set, for more strict

Changes since v5 at [6]:
 - use instr->delay_ns in exec_op() to caculate the delay cycle
 - delete struct meson_nfc_info_format and use macros instead
 - delete "is_scramble" in struct meson_nfc_nand_chip
 - add WARN_ON_ONCE() for chip > MAX_CE_NUM
 - drop param *mtd* or struct *nfc* if param *nand exist
 - substitute set_data_oob and get_data_oob for prase_data_oob and format_data_oob
 - split timings caculating and setting timings
 - move info_buf and data_buf to struct meson_nfc_nand_chip

Changes since v4 at [5]:
 - remove the initial default divider(CLK_DIV_MASK) in meson_nfc_clk_init()
 - dt-bindings: remove staus, add "rx" and "tx" clock, and node rename

Changes since v3 at [4]:
 - remove partition table and some used props from dt-bindings 

Changes since v2 at [3]:
 - remove some hardcode time value, like twb
 - use dev wait R/B instead of nand_soft_waitrdy
 - implement nfc ecc init by ecc helper
 - rework nfc buffer init to register the maximun buffer when several chips
 - free nfc buffer when error and cleanup
 - add variable to teack all the already assigned CS lines
 - fix mtd->name use the first cs line only
 - remove dt "nand-enable-scrambler" and use NAND_NEED_SCRAMBLING instead.
 - move setuping ECC fileds after the identification phase
 - use nand_scan() and attach_chip()
 - check one event to return IRQ_NONE
 - delete cast when of_device_get_match_data
 - use nand_controller_init() helper
 - remove nfc driver complains when calling devm_ioremap_resource
 - clear irqs before setting up irq handler

Changes since v1 at [1]:
  - adopt property amlogic,nand-enable-scrambler - thanks Martin
  - drop nand pins in DT
  - convert clk access to emmc clkc model 
  - fix regiser field definition alignment
  - drop nand-user-mode 
  - parse cs id from DT
  - rework n2m, m2n function
  - explain why insert two "IDLE" command
  - implement exec_op()
  - drop meson_nfc_get_nand_chip_dts()
  - release resource once error occur in meson_nfc_nand_chips_init(), 
  - call nand_cleanup(nand) once mtd_device_register fail

Items not addressed ( or confirmed ) in this version:
  - convert to ECC conf helper() 
  - convert to dma coherent API
  - how to construct mtd->name

[1] https://lkml.kernel.org/r/20180613161314.14894-1-yixun.lan@amlogic.com
[2] https://lkml.kernel.org/r/20180712211244.11428-1-yixun.lan@amlogic.com
[3] https://lkml.kernel.org/r/20180719094612.5833-1-yixun.lan@amlogic.com
[4] https://lkml.kernel.org/r/1536317831-58056-1-git-send-email-jianxin.pan@amlogic.com/
[5] https://lore.kernel.org/r/1537433449-65213-2-git-send-email-jianxin.pan@amlogic.com/
[6] https://lore.kernel.org/r/1539839345-14021-1-git-send-email-jianxin.pan@amlogic.com
[7] https://lore.kernel.org/r/1541090542-19618-1-git-send-email-jianxin.pan@amlogic.com
Liang Yang (2):
  dt-bindings: nand: meson: add Amlogic NAND controller driver
  mtd: rawnand: meson: add support for Amlogic NAND flash controller

 .../devicetree/bindings/mtd/amlogic,meson-nand.txt |   60 +
 drivers/mtd/nand/raw/Kconfig                       |   10 +
 drivers/mtd/nand/raw/Makefile                      |    1 +
 drivers/mtd/nand/raw/meson_nand.c                  | 1417 ++++++++++++++++++++
 4 files changed, 1488 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
 create mode 100644 drivers/mtd/nand/raw/meson_nand.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v7 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver
  2018-11-16 16:40 [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Jianxin Pan
@ 2018-11-16 16:40 ` Jianxin Pan
  2018-11-16 16:40 ` [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller Jianxin Pan
  2018-11-26  6:39 ` [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Liang Yang
  2 siblings, 0 replies; 19+ messages in thread
From: Jianxin Pan @ 2018-11-16 16:40 UTC (permalink / raw)
  To: Boris Brezillon, linux-mtd
  Cc: Liang Yang, Yixun Lan, Jianxin Pan, David Woodhouse,
	Miquel Raynal, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel, devicetree

From: Liang Yang <liang.yang@amlogic.com>

Add Amlogic NAND controller dt-bindings for Meson SoC,
Current this driver support GXBB/GXL/AXG platform.

Signed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/mtd/amlogic,meson-nand.txt | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
new file mode 100644
index 0000000..3983c11
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
@@ -0,0 +1,60 @@
+Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
+
+This file documents the properties in addition to those available in
+the MTD NAND bindings.
+
+Required properties:
+- compatible : contains one of:
+  - "amlogic,meson-gxl-nfc"
+  - "amlogic,meson-axg-nfc"
+- clocks     :
+	A list of phandle + clock-specifier pairs for the clocks listed
+	in clock-names.
+
+- clock-names: Should contain the following:
+	"core" - NFC module gate clock
+	"device" - device clock from eMMC sub clock controller
+	"rx" - rx clock phase
+	"tx" - tx clock phase
+
+- amlogic,mmc-syscon	: Required for NAND clocks, it's shared with SD/eMMC
+				controller port C
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Other properties:
+see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
+
+Example demonstrate on AXG SoC:
+
+	sd_emmc_c_clkc: mmc@7000 {
+		compatible = "amlogic,meson-axg-mmc-clkc", "syscon";
+		reg = <0x0 0x7000 0x0 0x800>;
+	};
+
+	nand-controller@7800 {
+		compatible = "amlogic,meson-axg-nfc";
+		reg = <0x0 0x7800 0x0 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
+
+		clocks = <&clkc CLKID_SD_EMMC_C>,
+			<&sd_emmc_c_clkc CLKID_MMC_DIV>,
+			<&sd_emmc_c_clkc CLKID_MMC_PHASE_RX>,
+			<&sd_emmc_c_clkc CLKID_MMC_PHASE_TX>;
+		clock-names = "core", "device", "rx", "tx";
+		amlogic,mmc-syscon = <&sd_emmc_c_clkc>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&nand_pins>;
+
+		nand@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			nand-on-flash-bbt;
+		};
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-11-16 16:40 [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Jianxin Pan
  2018-11-16 16:40 ` [PATCH v7 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver Jianxin Pan
@ 2018-11-16 16:40 ` Jianxin Pan
  2018-12-07  9:24   ` Miquel Raynal
  2018-11-26  6:39 ` [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Liang Yang
  2 siblings, 1 reply; 19+ messages in thread
From: Jianxin Pan @ 2018-11-16 16:40 UTC (permalink / raw)
  To: Boris Brezillon, linux-mtd
  Cc: Liang Yang, Yixun Lan, Jianxin Pan, David Woodhouse,
	Miquel Raynal, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

From: Liang Yang <liang.yang@amlogic.com>

Add initial support for the Amlogic NAND flash controller which found
in the Meson-GXBB/GXL/AXG SoCs.

Signed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
---
 drivers/mtd/nand/raw/Kconfig      |   10 +
 drivers/mtd/nand/raw/Makefile     |    1 +
 drivers/mtd/nand/raw/meson_nand.c | 1417 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1428 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/meson_nand.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index c7efc31..223b041 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -541,4 +541,14 @@ config MTD_NAND_TEGRA
 	  is supported. Extra OOB bytes when using HW ECC are currently
 	  not supported.
 
+config MTD_NAND_MESON
+	tristate "Support for NAND controller on Amlogic's Meson SoCs"
+	depends on ARCH_MESON || COMPILE_TEST
+	depends on COMMON_CLK_AMLOGIC
+	select COMMON_CLK_REGMAP_MESON
+	select MFD_SYSCON
+	help
+	  Enables support for NAND controller on Amlogic's Meson SoCs.
+	  This controller is found on Meson GXBB, GXL, AXG SoCs.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 57159b3..a2cc2fe 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
 obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
 obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
+obj-$(CONFIG_MTD_NAND_MESON)		+= meson_nand.o
 
 nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
new file mode 100644
index 0000000..c566636
--- /dev/null
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -0,0 +1,1417 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson Nand Flash Controller Driver
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Liang Yang <liang.yang@amlogic.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define NFC_REG_CMD		0x00
+#define NFC_CMD_DRD		(0x8 << 14)
+#define NFC_CMD_IDLE		(0xc << 14)
+#define NFC_CMD_DWR		(0x4 << 14)
+#define NFC_CMD_CLE		(0x5 << 14)
+#define NFC_CMD_ALE		(0x6 << 14)
+#define NFC_CMD_ADL		((0 << 16) | (3 << 20))
+#define NFC_CMD_ADH		((1 << 16) | (3 << 20))
+#define NFC_CMD_AIL		((2 << 16) | (3 << 20))
+#define NFC_CMD_AIH		((3 << 16) | (3 << 20))
+#define NFC_CMD_SEED		((8 << 16) | (3 << 20))
+#define NFC_CMD_M2N		((0 << 17) | (2 << 20))
+#define NFC_CMD_N2M		((1 << 17) | (2 << 20))
+#define NFC_CMD_RB		BIT(20)
+#define NFC_CMD_IO6		((0xb << 10) | (1 << 18))
+#define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
+#define NFC_CMD_RB_INT		BIT(14)
+
+#define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
+
+#define NFC_REG_CFG		0x04
+#define NFC_REG_DADR		0x08
+#define NFC_REG_IADR		0x0c
+#define NFC_REG_BUF		0x10
+#define NFC_REG_INFO		0x14
+#define NFC_REG_DC		0x18
+#define NFC_REG_ADR		0x1c
+#define NFC_REG_DL		0x20
+#define NFC_REG_DH		0x24
+#define NFC_REG_CADR		0x28
+#define NFC_REG_SADR		0x2c
+#define NFC_REG_PINS		0x30
+#define NFC_REG_VER		0x38
+
+#define NFC_RB_IRQ_EN		BIT(21)
+#define NFC_INT_MASK		(3 << 20)
+
+#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
+	(								\
+		(cmd_dir)			|			\
+		((ran) << 19)			|			\
+		((bch) << 14)			|			\
+		((short_mode) << 13)		|			\
+		(((page_size) & 0x7f) << 6)	|			\
+		((pages) & 0x3f)					\
+	)
+
+#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
+#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
+#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
+#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
+
+#define RB_STA(x)		(1 << (26 + (x)))
+#define DMA_DIR(dir)		((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
+
+#define ECC_CHECK_RETURN_FF	(-1)
+
+#define NAND_CE0		(0xe << 10)
+#define NAND_CE1		(0xd << 10)
+
+#define DMA_BUSY_TIMEOUT	0x100000
+#define CMD_FIFO_EMPTY_TIMEOUT	1000
+
+#define MAX_CE_NUM		2
+
+/* eMMC clock register, misc control */
+#define SD_EMMC_CLOCK		0x00
+#define CLK_ALWAYS_ON		BIT(28)
+#define CLK_SELECT_NAND		BIT(31)
+#define CLK_DIV_MASK		GENMASK(5, 0)
+
+#define NFC_CLK_CYCLE		6
+
+/* nand flash controller delay 3 ns */
+#define NFC_DEFAULT_DELAY	3000
+
+#define MAX_ECC_INDEX		10
+
+#define MUX_CLK_NUM_PARENTS	2
+
+#define ROW_ADDER(page, index)	(((page) >> (8 * (index))) & 0xff)
+#define MAX_CYCLE_ADDRS		5
+#define DIRREAD			1
+#define DIRWRITE		0
+
+#define ECC_PARITY_BCH8_512B	14
+
+#define PER_INFO_BYTE		8
+
+#define ECC_COMPLETE            BIT(31)
+#define ECC_ERR_CNT(x)		(((x) >> 24) & GENMASK(5, 0))
+#define ECC_ZERO_CNT(x)		(((x) >> 16) & GENMASK(5, 0))
+
+struct meson_nfc_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+	unsigned long clk_rate;
+	unsigned long level1_divider;
+	u32 bus_timing;
+	u32 twb;
+	u32 tadl;
+	u32 tbers_max;
+
+	u32 bch_mode;
+	u8 *data_buf;
+	__le64 *info_buf;
+	u32 nsels;
+	u8 sels[0];
+};
+
+struct meson_nand_ecc {
+	u32 bch;
+	u32 strength;
+};
+
+struct meson_nfc_data {
+	const struct nand_ecc_caps *ecc_caps;
+};
+
+struct meson_nfc_param {
+	u32 chip_select;
+	u32 rb_select;
+};
+
+struct nand_rw_cmd {
+	u32 cmd0;
+	u32 addrs[MAX_CYCLE_ADDRS];
+	u32 cmd1;
+};
+
+struct nand_timing {
+	u32 twb;
+	u32 tadl;
+	u32 tbers_max;
+};
+
+struct meson_nfc {
+	struct nand_controller controller;
+	struct clk *core_clk;
+	struct clk *device_clk;
+	struct clk *phase_tx;
+	struct clk *phase_rx;
+
+	unsigned long clk_rate;
+	u32 bus_timing;
+
+	struct device *dev;
+	void __iomem *reg_base;
+	struct regmap *reg_clk;
+	struct completion completion;
+	struct list_head chips;
+	const struct meson_nfc_data *data;
+	struct meson_nfc_param param;
+	struct nand_timing timing;
+	union {
+		int cmd[32];
+		struct nand_rw_cmd rw;
+	} cmdfifo;
+
+	dma_addr_t daddr;
+	dma_addr_t iaddr;
+
+	unsigned long assigned_cs;
+};
+
+enum {
+	NFC_ECC_BCH8_1K		= 2,
+	NFC_ECC_BCH24_1K,
+	NFC_ECC_BCH30_1K,
+	NFC_ECC_BCH40_1K,
+	NFC_ECC_BCH50_1K,
+	NFC_ECC_BCH60_1K,
+};
+
+#define MESON_ECC_DATA(b, s)	{ .bch = (b),	.strength = (s)}
+
+static int meson_nand_calc_ecc_bytes(int step_size, int strength)
+{
+	int ecc_bytes;
+
+	if (step_size == 512 && strength == 8)
+		return ECC_PARITY_BCH8_512B;
+
+	ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
+	ecc_bytes = ALIGN(ecc_bytes, 2);
+
+	return ecc_bytes;
+}
+
+NAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps,
+		     meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60);
+NAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps,
+		     meson_nand_calc_ecc_bytes, 1024, 8);
+
+static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
+{
+	return container_of(nand, struct meson_nfc_nand_chip, nand);
+}
+
+static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	int ret, value;
+
+	if (chip < 0 || WARN_ON_ONCE(chip > MAX_CE_NUM))
+		return;
+
+	nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
+	nfc->param.rb_select = nfc->param.chip_select;
+	nfc->timing.twb = meson_chip->twb;
+	nfc->timing.tadl = meson_chip->tadl;
+	nfc->timing.tbers_max = meson_chip->tbers_max;
+
+	if (chip >= 0) {
+		if (nfc->clk_rate != meson_chip->clk_rate) {
+			ret = clk_set_rate(nfc->device_clk,
+					   meson_chip->clk_rate);
+			if (ret) {
+				dev_err(nfc->dev, "failed to set clock rate\n");
+				return;
+			}
+			nfc->clk_rate = meson_chip->clk_rate;
+		}
+		if (nfc->bus_timing != meson_chip->bus_timing) {
+			value = (NFC_CLK_CYCLE - 1)
+				| (meson_chip->bus_timing << 5);
+			writel(value, nfc->reg_base + NFC_REG_CFG);
+			writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
+			nfc->bus_timing =  meson_chip->bus_timing;
+		}
+	}
+}
+
+static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
+{
+	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
+	       nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
+{
+	writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
+	       nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	u32 bch = meson_chip->bch_mode, cmd;
+	int len = mtd->writesize, pagesize, pages;
+	int scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;
+
+	pagesize = nand->ecc.size;
+
+	if (raw) {
+		len = mtd->writesize + mtd->oobsize;
+		cmd = (len & 0x3fff) | (scramble << 19) | DMA_DIR(dir);
+		writel(cmd, nfc->reg_base + NFC_REG_CMD);
+		return;
+	}
+
+	pages = len / nand->ecc.size;
+
+	cmd = CMDRWGEN(DMA_DIR(dir), scramble, bch, 0, pagesize, pages);
+
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
+{
+	/*
+	 * Insert two commands to make sure all valid commands are finished.
+	 *
+	 * The Nand flash controller is designed as two stages pipleline -
+	 *  a) fetch and b) excute.
+	 * There might be cases when the driver see command queue is empty,
+	 * but the Nand flash controller still has two commands buffered,
+	 * one is fetched into NFC request queue (ready to run), and another
+	 * is actively executing. So pushing 2 "IDLE" commands guarantees that
+	 * the pipeline is emptied.
+	 */
+	meson_nfc_cmd_idle(nfc, 0);
+	meson_nfc_cmd_idle(nfc, 0);
+}
+
+static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
+				     unsigned int timeout_ms)
+{
+	u32 cmd_size = 0;
+	int ret;
+
+	/* wait cmd fifo is empty */
+	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
+					 !NFC_CMD_GET_SIZE(cmd_size),
+					 10, timeout_ms * 1000);
+	if (ret)
+		dev_err(nfc->dev, "wait for empty cmd FIFO time out\n");
+
+	return ret;
+}
+
+static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
+{
+	meson_nfc_drain_cmd(nfc);
+
+	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
+}
+
+static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	int len;
+
+	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+
+	return meson_chip->data_buf + len;
+}
+
+static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	int len, temp;
+
+	temp = nand->ecc.size + nand->ecc.bytes;
+	len = (temp + 2) * i;
+
+	return meson_chip->data_buf + len;
+}
+
+static void meson_nfc_get_data_oob(struct nand_chip *nand,
+				   u8 *buf, u8 *oobbuf)
+{
+	int i, oob_len = 0;
+	u8 *dsrc, *osrc;
+
+	oob_len = nand->ecc.bytes + 2;
+	for (i = 0; i < nand->ecc.steps; i++) {
+		if (buf) {
+			dsrc = meson_nfc_data_ptr(nand, i);
+			memcpy(buf, dsrc, nand->ecc.size);
+			buf += nand->ecc.size;
+		}
+		osrc = meson_nfc_oob_ptr(nand, i);
+		memcpy(oobbuf, osrc, oob_len);
+		oobbuf += oob_len;
+	}
+}
+
+static void meson_nfc_set_data_oob(struct nand_chip *nand,
+				   const u8 *buf, u8 *oobbuf)
+{
+	int i, oob_len = 0;
+	u8 *dsrc, *osrc;
+
+	oob_len = nand->ecc.bytes + 2;
+	for (i = 0; i < nand->ecc.steps; i++) {
+		if (buf) {
+			dsrc = meson_nfc_data_ptr(nand, i);
+			memcpy(dsrc, buf, nand->ecc.size);
+			buf += nand->ecc.size;
+		}
+		osrc = meson_nfc_oob_ptr(nand, i);
+		memcpy(osrc, oobbuf, oob_len);
+		oobbuf += oob_len;
+	}
+}
+
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+{
+	u32 cmd, cfg;
+	int ret = 0;
+
+	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+	cfg = readl(nfc->reg_base + NFC_REG_CFG);
+	cfg |= (1 << 21);
+	writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+	init_completion(&nfc->completion);
+
+	/* use the max erase time as the maximum clock for waiting R/B */
+	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
+		| nfc->param.chip_select | nfc->timing.tbers_max;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	ret = wait_for_completion_timeout(&nfc->completion,
+					  msecs_to_jiffies(timeout_ms));
+	if (ret == 0)
+		ret = -1;
+
+	return ret;
+}
+
+static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	__le64 *info;
+	int i, count;
+
+	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+		info = &meson_chip->info_buf[i];
+		*info |= oob_buf[count];
+		*info |= oob_buf[count + 1] << 8;
+	}
+}
+
+static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	__le64 *info;
+	int i, count;
+
+	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+		info = &meson_chip->info_buf[i];
+		oob_buf[count] = *info;
+		oob_buf[count + 1] = *info >> 8;
+	}
+}
+
+static int meson_nfc_ecc_correct(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	__le64 *info;
+	u32 bitflips = 0, i;
+	int scramble;
+	u8 zero_cnt;
+
+	scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;
+
+	for (i = 0; i < nand->ecc.steps; i++) {
+		info = &meson_chip->info_buf[i];
+		if (ECC_ERR_CNT(*info) == 0x3f) {
+			zero_cnt = ECC_ZERO_CNT(*info);
+			if (scramble && zero_cnt < nand->ecc.strength)
+				return ECC_CHECK_RETURN_FF;
+			mtd->ecc_stats.failed++;
+			continue;
+		}
+		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
+		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
+	}
+
+	return bitflips;
+}
+
+static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, u8 *databuf,
+				      int datalen, u8 *infobuf, int infolen,
+				      enum dma_data_direction dir)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	u32 cmd;
+	int ret = 0;
+
+	nfc->daddr = dma_map_single(nfc->dev, (void *)databuf, datalen, dir);
+	ret = dma_mapping_error(nfc->dev, nfc->daddr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return ret;
+	}
+	cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	if (infobuf) {
+		nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir);
+		ret = dma_mapping_error(nfc->dev, nfc->iaddr);
+		if (ret) {
+			dev_err(nfc->dev, "dma mapping error\n");
+			dma_unmap_single(nfc->dev,
+					 nfc->daddr, datalen, dir);
+			return ret;
+		}
+		cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
+		writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+		cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
+		writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	}
+
+	return ret;
+}
+
+static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
+					 int infolen, int datalen,
+					 enum dma_data_direction dir)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+	dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir);
+	if (infolen)
+		dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir);
+}
+
+static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	int ret = 0;
+	u32 cmd;
+	u8 *info;
+
+	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
+	ret = meson_nfc_dma_buffer_setup(nand, buf, len, info,
+					 PER_INFO_BYTE, DMA_FROM_DEVICE);
+	if (ret)
+		return ret;
+
+	cmd = NFC_CMD_N2M | (len & 0x3fff);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, 1000);
+	meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE);
+	kfree(info);
+
+	return ret;
+}
+
+static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	int ret = 0;
+	u32 cmd;
+
+	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
+					 0, DMA_TO_DEVICE);
+	if (ret)
+		return ret;
+
+	cmd = NFC_CMD_M2N | (len & 0x3fff);
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, 1000);
+	meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE);
+
+	return ret;
+}
+
+static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
+						int page, bool in)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	const struct nand_sdr_timings *sdr =
+		nand_get_sdr_timings(&nand->data_interface);
+	u32 *addrs = nfc->cmdfifo.rw.addrs;
+	u32 cs = nfc->param.chip_select;
+	u32 cmd0, cmd_num, row_start;
+	int ret = 0, i;
+
+	cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int);
+
+	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
+	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
+
+	addrs[0] = cs | NFC_CMD_ALE | 0;
+	if (mtd->writesize <= 512) {
+		cmd_num--;
+		row_start = 1;
+	} else {
+		addrs[1] = cs | NFC_CMD_ALE | 0;
+		row_start = 2;
+	}
+
+	addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0);
+	addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1);
+
+	if (nand->options & NAND_ROW_ADDR_3)
+		addrs[row_start + 2] =
+			cs | NFC_CMD_ALE | ROW_ADDER(page, 2);
+	else
+		cmd_num--;
+
+	/* subtract cmd1 */
+	cmd_num--;
+
+	for (i = 0; i < cmd_num; i++)
+		writel_relaxed(nfc->cmdfifo.cmd[i],
+			       nfc->reg_base + NFC_REG_CMD);
+
+	if (in) {
+		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
+		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
+		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+	} else {
+		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
+	}
+
+	return ret;
+}
+
+static int meson_nfc_write_page_sub(struct nand_chip *nand,
+				    int page, int raw)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	const struct nand_sdr_timings *sdr =
+		nand_get_sdr_timings(&nand->data_interface);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	int data_len, info_len;
+	u32 cmd;
+	int ret;
+
+	data_len =  mtd->writesize + mtd->oobsize;
+	info_len = nand->ecc.steps * PER_INFO_BYTE;
+
+	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE);
+	if (ret)
+		return ret;
+
+	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
+					 data_len, (u8 *)meson_chip->info_buf,
+					 info_len, DMA_TO_DEVICE);
+	if (ret)
+		return ret;
+
+	meson_nfc_cmd_seed(nfc, page);
+	meson_nfc_cmd_access(nand, raw, DIRWRITE);
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+
+	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
+
+	return ret;
+}
+
+static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
+				    int oob_required, int page)
+{
+	u8 *oob_buf = nand->oob_poi;
+
+	meson_nfc_set_data_oob(nand, buf, oob_buf);
+
+	return meson_nfc_write_page_sub(nand, page, 1);
+}
+
+static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
+				      const u8 *buf, int oob_required, int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	u8 *oob_buf = nand->oob_poi;
+
+	memcpy(meson_chip->data_buf, buf, mtd->writesize);
+	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
+	meson_nfc_set_user_byte(nand, oob_buf);
+
+	return meson_nfc_write_page_sub(nand, page, 0);
+}
+
+static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
+					    struct nand_chip *nand, int raw)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	__le64 *info;
+	u32 neccpages;
+	int ret;
+
+	neccpages = raw ? 1 : nand->ecc.steps;
+	info = &meson_chip->info_buf[neccpages - 1];
+	do {
+		usleep_range(10, 15);
+		/* info is updated by nfc dma engine*/
+		smp_rmb();
+		ret = *info & ECC_COMPLETE;
+	} while (!ret);
+}
+
+static int meson_nfc_read_page_sub(struct nand_chip *nand,
+				   int page, int raw)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	int data_len, info_len;
+	int ret;
+
+	data_len =  mtd->writesize + mtd->oobsize;
+	info_len = nand->ecc.steps * PER_INFO_BYTE;
+
+	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
+	if (ret)
+		return ret;
+
+	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
+					 data_len, (u8 *)meson_chip->info_buf,
+					 info_len, DMA_FROM_DEVICE);
+	if (ret)
+		return ret;
+
+	meson_nfc_cmd_seed(nfc, page);
+	meson_nfc_cmd_access(nand, raw, DIRREAD);
+	ret = meson_nfc_wait_dma_finish(nfc);
+	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
+
+	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
+
+	return ret;
+}
+
+static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
+				   int oob_required, int page)
+{
+	u8 *oob_buf = nand->oob_poi;
+	int ret;
+
+	ret = meson_nfc_read_page_sub(nand, page, 1);
+	if (ret)
+		return ret;
+
+	meson_nfc_get_data_oob(nand, buf, oob_buf);
+
+	return 0;
+}
+
+static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
+				     int oob_required, int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	u8 *oob_buf = nand->oob_poi;
+	int ret;
+
+	ret = meson_nfc_read_page_sub(nand, page, 0);
+	if (ret)
+		return ret;
+
+	meson_nfc_get_user_byte(nand, oob_buf);
+
+	ret = meson_nfc_ecc_correct(nand);
+	if (ret == ECC_CHECK_RETURN_FF) {
+		if (buf)
+			memset(buf, 0xff, mtd->writesize);
+
+		memset(oob_buf, 0xff, mtd->oobsize);
+		return 0;
+	}
+
+	if (buf && buf != meson_chip->data_buf)
+		memcpy(buf, meson_chip->data_buf, mtd->writesize);
+
+	return ret;
+}
+
+static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
+{
+	return meson_nfc_read_page_raw(nand, NULL, 1, page);
+}
+
+static int meson_nfc_read_oob(struct nand_chip *nand, int page)
+{
+	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
+}
+
+void *
+meson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr)
+{
+	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR))
+		return NULL;
+	if (virt_addr_valid(instr->ctx.data.buf.in) &&
+	    !object_is_on_stack(instr->ctx.data.buf.in))
+		return instr->ctx.data.buf.in;
+
+	return kzalloc(instr->ctx.data.len, GFP_KERNEL);
+}
+
+void
+meson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr,
+				     void *buf)
+{
+	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) ||
+	    WARN_ON(!buf))
+		return;
+	if (buf == instr->ctx.data.buf.in)
+		return;
+
+	memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len);
+	kfree(buf);
+}
+
+const void *
+meson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
+{
+	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
+		return NULL;
+
+	if (virt_addr_valid(instr->ctx.data.buf.out) &&
+	    !object_is_on_stack(instr->ctx.data.buf.out))
+		return instr->ctx.data.buf.out;
+
+	return kmemdup(instr->ctx.data.buf.out,
+		       instr->ctx.data.len, GFP_KERNEL);
+}
+
+void
+meson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr,
+				      const void *buf)
+{
+	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) ||
+	    WARN_ON(!buf))
+		return;
+
+	if (buf != instr->ctx.data.buf.out)
+		kfree(buf);
+}
+
+static int meson_nfc_exec_op(struct nand_chip *nand,
+			     const struct nand_operation *op, bool check_only)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	const struct nand_op_instr *instr = NULL;
+	void *buf;
+	u32 op_id, delay_idle, cmd;
+	int i;
+
+	for (op_id = 0; op_id < op->ninstrs; op_id++) {
+		instr = &op->instrs[op_id];
+		delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns),
+					  meson_chip->level1_divider *
+					  NFC_CLK_CYCLE);
+		switch (instr->type) {
+		case NAND_OP_CMD_INSTR:
+			cmd = nfc->param.chip_select | NFC_CMD_CLE;
+			cmd |= instr->ctx.cmd.opcode & 0xff;
+			writel(cmd, nfc->reg_base + NFC_REG_CMD);
+			meson_nfc_cmd_idle(nfc, delay_idle);
+			break;
+
+		case NAND_OP_ADDR_INSTR:
+			for (i = 0; i < instr->ctx.addr.naddrs; i++) {
+				cmd = nfc->param.chip_select | NFC_CMD_ALE;
+				cmd |= instr->ctx.addr.addrs[i] & 0xff;
+				writel(cmd, nfc->reg_base + NFC_REG_CMD);
+			}
+			meson_nfc_cmd_idle(nfc, delay_idle);
+			break;
+
+		case NAND_OP_DATA_IN_INSTR:
+			buf = meson_nand_op_get_dma_safe_input_buf(instr);
+			meson_nfc_read_buf(nand, buf,
+					   instr->ctx.data.len);
+			meson_nand_op_put_dma_safe_input_buf(instr, buf);
+			break;
+
+		case NAND_OP_DATA_OUT_INSTR:
+			buf =
+			(void *)meson_nand_op_get_dma_safe_output_buf(instr);
+			meson_nfc_write_buf(nand, buf,
+					    instr->ctx.data.len);
+			meson_nand_op_put_dma_safe_output_buf(instr, buf);
+			break;
+
+		case NAND_OP_WAITRDY_INSTR:
+			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
+			if (instr->delay_ns)
+				meson_nfc_cmd_idle(nfc, delay_idle);
+			break;
+		}
+	}
+	meson_nfc_wait_cmd_finish(nfc, 1000);
+	return 0;
+}
+
+static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
+			       struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+
+	if (section >= nand->ecc.steps)
+		return -ERANGE;
+
+	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
+	oobregion->length = nand->ecc.bytes;
+
+	return 0;
+}
+
+static int meson_ooblayout_free(struct mtd_info *mtd, int section,
+				struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+
+	if (section >= nand->ecc.steps)
+		return -ERANGE;
+
+	oobregion->offset = section * (2 + nand->ecc.bytes);
+	oobregion->length = 2;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
+	.ecc = meson_ooblayout_ecc,
+	.free = meson_ooblayout_free,
+};
+
+static int meson_nfc_clk_init(struct meson_nfc *nfc)
+{
+	int ret;
+
+	/* request core clock */
+	nfc->core_clk = devm_clk_get(nfc->dev, "core");
+	if (IS_ERR(nfc->core_clk)) {
+		dev_err(nfc->dev, "failed to get core clk\n");
+		return PTR_ERR(nfc->core_clk);
+	}
+
+	nfc->device_clk = devm_clk_get(nfc->dev, "device");
+	if (IS_ERR(nfc->device_clk)) {
+		dev_err(nfc->dev, "failed to get device clk\n");
+		return PTR_ERR(nfc->device_clk);
+	}
+
+	nfc->phase_tx = devm_clk_get(nfc->dev, "tx");
+	if (IS_ERR(nfc->phase_tx)) {
+		dev_err(nfc->dev, "failed to get tx clk\n");
+		return PTR_ERR(nfc->phase_tx);
+	}
+
+	nfc->phase_rx = devm_clk_get(nfc->dev, "rx");
+	if (IS_ERR(nfc->phase_rx)) {
+		dev_err(nfc->dev, "failed to get rx clk\n");
+		return PTR_ERR(nfc->phase_rx);
+	}
+
+	/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
+	regmap_update_bits(nfc->reg_clk,
+			   0, CLK_SELECT_NAND, CLK_SELECT_NAND);
+
+	ret = clk_prepare_enable(nfc->core_clk);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable core clk\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(nfc->device_clk);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable device clk\n");
+		clk_disable_unprepare(nfc->core_clk);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(nfc->phase_tx);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable tx clk\n");
+		clk_disable_unprepare(nfc->core_clk);
+		clk_disable_unprepare(nfc->device_clk);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(nfc->phase_rx);
+	if (ret) {
+		dev_err(nfc->dev, "failed to enable rx clk\n");
+		clk_disable_unprepare(nfc->core_clk);
+		clk_disable_unprepare(nfc->device_clk);
+		clk_disable_unprepare(nfc->phase_tx);
+		return ret;
+	}
+
+	ret = clk_set_rate(nfc->device_clk, 24000000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void meson_nfc_disable_clk(struct meson_nfc *nfc)
+{
+	clk_disable_unprepare(nfc->phase_rx);
+	clk_disable_unprepare(nfc->phase_tx);
+	clk_disable_unprepare(nfc->device_clk);
+	clk_disable_unprepare(nfc->core_clk);
+}
+
+static void meson_nfc_free_buffer(struct nand_chip *nand)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+	kfree(meson_chip->info_buf);
+	kfree(meson_chip->data_buf);
+}
+
+static int meson_chip_buffer_init(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	u32 page_bytes, info_bytes, nsectors;
+
+	nsectors = mtd->writesize / nand->ecc.size;
+
+	page_bytes =  mtd->writesize + mtd->oobsize;
+	info_bytes = nsectors * PER_INFO_BYTE;
+
+	meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL);
+	if (!meson_chip->data_buf)
+		return -ENOMEM;
+
+	meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL);
+	if (!meson_chip->info_buf) {
+		kfree(meson_chip->data_buf);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static
+int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline,
+				   const struct nand_data_interface *conf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	const struct nand_sdr_timings *timings;
+	u32 div, bt_min, bt_max, tbers_clocks;
+
+	timings = nand_get_sdr_timings(conf);
+	if (IS_ERR(timings))
+		return -ENOTSUPP;
+
+	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+		return 0;
+
+	div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE);
+	bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div;
+	bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min +
+		  timings->tRC_min / 2) / div;
+
+	meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max),
+				       div * NFC_CLK_CYCLE);
+	meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min),
+					div * NFC_CLK_CYCLE);
+	tbers_clocks = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tBERS_max),
+				    div * NFC_CLK_CYCLE);
+	meson_chip->tbers_max = ilog2(tbers_clocks);
+	if (!is_power_of_2(tbers_clocks))
+		meson_chip->tbers_max++;
+
+	bt_min = DIV_ROUND_UP(bt_min, 1000);
+	bt_max = DIV_ROUND_UP(bt_max, 1000);
+
+	if (bt_max < bt_min)
+		return -EINVAL;
+
+	meson_chip->level1_divider = div;
+	meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider;
+	meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1;
+
+	return 0;
+}
+
+static int meson_nand_bch_mode(struct nand_chip *nand)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct meson_nand_ecc meson_ecc[] = {
+		MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8),
+		MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24),
+		MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30),
+		MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40),
+		MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50),
+		MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60),
+	};
+	int i;
+
+	if (nand->ecc.strength > 60 || nand->ecc.strength < 8)
+		return -EINVAL;
+
+	for (i = 0; i < sizeof(meson_ecc); i++) {
+		if (meson_ecc[i].strength == nand->ecc.strength) {
+			meson_chip->bch_mode = meson_ecc[i].bch;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int meson_nand_attach_chip(struct nand_chip *nand)
+{
+	struct meson_nfc *nfc = nand_get_controller_data(nand);
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	int nsectors = mtd->writesize / 1024;
+	int ret;
+
+	if (!mtd->name) {
+		mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
+					   "%s:nand%d",
+					   dev_name(nfc->dev),
+					   meson_chip->sels[0]);
+		if (!mtd->name)
+			return -ENOMEM;
+	}
+
+	if (nand->bbt_options & NAND_BBT_USE_FLASH)
+		nand->bbt_options |= NAND_BBT_NO_OOB;
+
+	nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+	ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps,
+				   mtd->oobsize - 2 * nsectors);
+	if (ret) {
+		dev_err(nfc->dev, "failed to ecc init\n");
+		return -EINVAL;
+	}
+
+	ret = meson_nand_bch_mode(nand);
+	if (ret)
+		return -EINVAL;
+
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
+	nand->ecc.write_page = meson_nfc_write_page_hwecc;
+	nand->ecc.write_oob_raw = nand_write_oob_std;
+	nand->ecc.write_oob = nand_write_oob_std;
+
+	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
+	nand->ecc.read_page = meson_nfc_read_page_hwecc;
+	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
+	nand->ecc.read_oob = meson_nfc_read_oob;
+
+	if (nand->options & NAND_BUSWIDTH_16) {
+		dev_err(nfc->dev, "16bits buswidth not supported");
+		return -EINVAL;
+	}
+	meson_chip_buffer_init(nand);
+	if (ret)
+		return -ENOMEM;
+
+	return ret;
+}
+
+static const struct nand_controller_ops meson_nand_controller_ops = {
+	.attach_chip = meson_nand_attach_chip,
+};
+
+static int
+meson_nfc_nand_chip_init(struct device *dev,
+			 struct meson_nfc *nfc, struct device_node *np)
+{
+	struct meson_nfc_nand_chip *meson_chip;
+	struct nand_chip *nand;
+	struct mtd_info *mtd;
+	int ret, i;
+	u32 tmp, nsels;
+
+	if (!of_get_property(np, "reg", &nsels))
+		return -EINVAL;
+
+	nsels /= sizeof(u32);
+	if (!nsels || nsels > MAX_CE_NUM) {
+		dev_err(dev, "invalid reg property size\n");
+		return -EINVAL;
+	}
+
+	meson_chip = devm_kzalloc(dev,
+				  sizeof(*meson_chip) + (nsels * sizeof(u8)),
+				  GFP_KERNEL);
+	if (!meson_chip)
+		return -ENOMEM;
+
+	meson_chip->nsels = nsels;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(np, "reg", i, &tmp);
+		if (ret) {
+			dev_err(dev, "could not retrieve reg property: %d\n",
+				ret);
+			return ret;
+		}
+
+		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+			dev_err(dev, "CS %d already assigned\n", tmp);
+			return -EINVAL;
+		}
+	}
+
+	nand = &meson_chip->nand;
+	nand->controller = &nfc->controller;
+	nand->controller->ops = &meson_nand_controller_ops;
+	nand_set_flash_node(nand, np);
+	nand_set_controller_data(nand, nfc);
+
+	nand->options |= NAND_USE_BOUNCE_BUFFER;
+	nand->select_chip = meson_nfc_select_chip;
+	nand->exec_op = meson_nfc_exec_op;
+	nand->setup_data_interface = meson_nfc_setup_data_interface;
+	mtd = nand_to_mtd(nand);
+	mtd->owner = THIS_MODULE;
+	mtd->dev.parent = dev;
+
+	ret = nand_scan(nand, nsels);
+	if (ret)
+		return ret;
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device: %d\n", ret);
+		nand_cleanup(nand);
+		return ret;
+	}
+
+	list_add_tail(&meson_chip->node, &nfc->chips);
+
+	return 0;
+}
+
+static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
+{
+	struct meson_nfc_nand_chip *meson_chip;
+	struct mtd_info *mtd;
+	int ret;
+
+	while (!list_empty(&nfc->chips)) {
+		meson_chip = list_first_entry(&nfc->chips,
+					      struct meson_nfc_nand_chip, node);
+		mtd = nand_to_mtd(&meson_chip->nand);
+		ret = mtd_device_unregister(mtd);
+		if (ret)
+			return ret;
+
+		meson_nfc_free_buffer(&meson_chip->nand);
+		nand_cleanup(&meson_chip->nand);
+		list_del(&meson_chip->node);
+	}
+
+	return 0;
+}
+
+static int meson_nfc_nand_chips_init(struct device *dev,
+				     struct meson_nfc *nfc)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int ret;
+
+	for_each_child_of_node(np, nand_np) {
+		ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
+		if (ret) {
+			meson_nfc_nand_chip_cleanup(nfc);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t meson_nfc_irq(int irq, void *id)
+{
+	struct meson_nfc *nfc = id;
+	u32 cfg;
+
+	cfg = readl(nfc->reg_base + NFC_REG_CFG);
+	if (!(cfg & NFC_RB_IRQ_EN))
+		return IRQ_NONE;
+
+	cfg &= ~(NFC_RB_IRQ_EN);
+	writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+	complete(&nfc->completion);
+	return IRQ_HANDLED;
+}
+
+static const struct meson_nfc_data meson_gxl_data = {
+	.ecc_caps = &meson_gxl_ecc_caps,
+};
+
+static const struct meson_nfc_data meson_axg_data = {
+	.ecc_caps = &meson_axg_ecc_caps,
+};
+
+static const struct of_device_id meson_nfc_id_table[] = {
+	{
+		.compatible = "amlogic,meson-gxl-nfc",
+		.data = &meson_gxl_data,
+	}, {
+		.compatible = "amlogic,meson-axg-nfc",
+		.data = &meson_axg_data,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
+
+static int meson_nfc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct meson_nfc *nfc;
+	struct resource *res;
+	int ret, irq;
+
+	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return -ENOMEM;
+
+	nfc->data = of_device_get_match_data(&pdev->dev);
+	if (!nfc->data)
+		return -ENODEV;
+
+	nand_controller_init(&nfc->controller);
+	INIT_LIST_HEAD(&nfc->chips);
+
+	nfc->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	nfc->reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(nfc->reg_base))
+		return PTR_ERR(nfc->reg_base);
+
+	nfc->reg_clk =
+		syscon_regmap_lookup_by_phandle(dev->of_node,
+						"amlogic,mmc-syscon");
+	if (IS_ERR(nfc->reg_clk)) {
+		dev_err(dev, "Failed to lookup clock base\n");
+		return PTR_ERR(nfc->reg_clk);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no nfi irq resource\n");
+		return -EINVAL;
+	}
+
+	ret = meson_nfc_clk_init(nfc);
+	if (ret) {
+		dev_err(dev, "failed to initialize nand clk\n");
+		goto err;
+	}
+
+	writel(0, nfc->reg_base + NFC_REG_CFG);
+	ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
+	if (ret) {
+		dev_err(dev, "failed to request nfi irq\n");
+		ret = -EINVAL;
+		goto err_clk;
+	}
+
+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "failed to set dma mask\n");
+		goto err_clk;
+	}
+
+	platform_set_drvdata(pdev, nfc);
+
+	ret = meson_nfc_nand_chips_init(dev, nfc);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto err_clk;
+	}
+
+	return 0;
+
+err_clk:
+	meson_nfc_disable_clk(nfc);
+err:
+	return ret;
+}
+
+static int meson_nfc_remove(struct platform_device *pdev)
+{
+	struct meson_nfc *nfc = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = meson_nfc_nand_chip_cleanup(nfc);
+	if (ret)
+		return ret;
+
+	meson_nfc_disable_clk(nfc);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver meson_nfc_driver = {
+	.probe  = meson_nfc_probe,
+	.remove = meson_nfc_remove,
+	.driver = {
+		.name  = "meson-nand",
+		.of_match_table = meson_nfc_id_table,
+	},
+};
+module_platform_driver(meson_nfc_driver);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support
  2018-11-16 16:40 [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Jianxin Pan
  2018-11-16 16:40 ` [PATCH v7 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver Jianxin Pan
  2018-11-16 16:40 ` [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller Jianxin Pan
@ 2018-11-26  6:39 ` Liang Yang
  2 siblings, 0 replies; 19+ messages in thread
From: Liang Yang @ 2018-11-26  6:39 UTC (permalink / raw)
  To: Jianxin Pan, Boris Brezillon, linux-mtd
  Cc: David Woodhouse, Miquel Raynal, Brian Norris, Marek Vasut,
	Richard Weinberger, Jerome Brunet, Neil Armstrong,
	Martin Blumenstingl, Carlo Caione, Kevin Hilman, Rob Herring,
	Yixun Lan, Jian Hu, Hanjie Lin, Victor Wan, linux-amlogic,
	linux-arm-kernel, linux-kernel, devicetree

Hi Boris and Miquel,

How about the v7 patch?

On 2018/11/17 0:40, Jianxin Pan wrote:
> These two patches try to add initial NAND driver support for Amlogic Meson
> SoCs, current it has been tested on GXL(p212) and AXG(s400) platform.
> 
> Changes since V6 at [7]
>   - use timings->tBERS_max as the maximum time out, delete NFC_CMD_RB_TIMEOUT
>   - fix nand_rw_cmd and support small block flash and which row address less than 3
>   - fix coding style
>   - replace readl/writel_* with readl/writel_relaxed*
>   - delete ECC_SET_PROTECTED_OOB_BYTE and ECC_GET_PROTECTED_OOB_BYTE
>   - implement dma access for read_buf and write_buf, more efficient.
>   - delete waiting dma finish in write process and let NAND_CMD_PAGEPROG and
> RB command go on queuing
>   - add waiting the completed flag of last ecc page be set, for more strict
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-11-16 16:40 ` [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller Jianxin Pan
@ 2018-12-07  9:24   ` Miquel Raynal
  2018-12-07  9:42     ` Miquel Raynal
  2018-12-10 11:23     ` Liang Yang
  0 siblings, 2 replies; 19+ messages in thread
From: Miquel Raynal @ 2018-12-07  9:24 UTC (permalink / raw)
  To: Jianxin Pan
  Cc: Boris Brezillon, linux-mtd, Liang Yang, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Jianxin,

Looks good to me overall, a few comments inline.

Jianxin Pan <jianxin.pan@amlogic.com> wrote on Sat, 17 Nov 2018
00:40:38 +0800:

> From: Liang Yang <liang.yang@amlogic.com>
> 
> Add initial support for the Amlogic NAND flash controller which found
> in the Meson-GXBB/GXL/AXG SoCs.
> 
> Signed-off-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
> Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
> ---
>  drivers/mtd/nand/raw/Kconfig      |   10 +
>  drivers/mtd/nand/raw/Makefile     |    1 +
>  drivers/mtd/nand/raw/meson_nand.c | 1417 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 1428 insertions(+)
>  create mode 100644 drivers/mtd/nand/raw/meson_nand.c
> 
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index c7efc31..223b041 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -541,4 +541,14 @@ config MTD_NAND_TEGRA
>  	  is supported. Extra OOB bytes when using HW ECC are currently
>  	  not supported.
>  
> +config MTD_NAND_MESON
> +	tristate "Support for NAND controller on Amlogic's Meson SoCs"
> +	depends on ARCH_MESON || COMPILE_TEST
> +	depends on COMMON_CLK_AMLOGIC
> +	select COMMON_CLK_REGMAP_MESON
> +	select MFD_SYSCON
> +	help
> +	  Enables support for NAND controller on Amlogic's Meson SoCs.
> +	  This controller is found on Meson GXBB, GXL, AXG SoCs.
> +
>  endif # MTD_NAND
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index 57159b3..a2cc2fe 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
>  obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
>  obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
>  obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
> +obj-$(CONFIG_MTD_NAND_MESON)		+= meson_nand.o
>  
>  nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
>  nand-objs += nand_onfi.o
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> new file mode 100644
> index 0000000..c566636
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -0,0 +1,1417 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Amlogic Meson Nand Flash Controller Driver
> + *
> + * Copyright (c) 2018 Amlogic, inc.
> + * Author: Liang Yang <liang.yang@amlogic.com>
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/mtd/rawnand.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/iopoll.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +
> +#define NFC_REG_CMD		0x00
> +#define NFC_CMD_DRD		(0x8 << 14)
> +#define NFC_CMD_IDLE		(0xc << 14)
> +#define NFC_CMD_DWR		(0x4 << 14)
> +#define NFC_CMD_CLE		(0x5 << 14)
> +#define NFC_CMD_ALE		(0x6 << 14)
> +#define NFC_CMD_ADL		((0 << 16) | (3 << 20))
> +#define NFC_CMD_ADH		((1 << 16) | (3 << 20))
> +#define NFC_CMD_AIL		((2 << 16) | (3 << 20))
> +#define NFC_CMD_AIH		((3 << 16) | (3 << 20))
> +#define NFC_CMD_SEED		((8 << 16) | (3 << 20))
> +#define NFC_CMD_M2N		((0 << 17) | (2 << 20))
> +#define NFC_CMD_N2M		((1 << 17) | (2 << 20))
> +#define NFC_CMD_RB		BIT(20)
> +#define NFC_CMD_IO6		((0xb << 10) | (1 << 18))
> +#define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
> +#define NFC_CMD_RB_INT		BIT(14)
> +
> +#define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
> +
> +#define NFC_REG_CFG		0x04
> +#define NFC_REG_DADR		0x08
> +#define NFC_REG_IADR		0x0c
> +#define NFC_REG_BUF		0x10
> +#define NFC_REG_INFO		0x14
> +#define NFC_REG_DC		0x18
> +#define NFC_REG_ADR		0x1c
> +#define NFC_REG_DL		0x20
> +#define NFC_REG_DH		0x24
> +#define NFC_REG_CADR		0x28
> +#define NFC_REG_SADR		0x2c
> +#define NFC_REG_PINS		0x30
> +#define NFC_REG_VER		0x38
> +
> +#define NFC_RB_IRQ_EN		BIT(21)
> +#define NFC_INT_MASK		(3 << 20)
> +
> +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
> +	(								\
> +		(cmd_dir)			|			\
> +		((ran) << 19)			|			\
> +		((bch) << 14)			|			\
> +		((short_mode) << 13)		|			\
> +		(((page_size) & 0x7f) << 6)	|			\
> +		((pages) & 0x3f)					\
> +	)
> +
> +#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
> +#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
> +#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
> +#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
> +
> +#define RB_STA(x)		(1 << (26 + (x)))
> +#define DMA_DIR(dir)		((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
> +
> +#define ECC_CHECK_RETURN_FF	(-1)
> +
> +#define NAND_CE0		(0xe << 10)
> +#define NAND_CE1		(0xd << 10)
> +
> +#define DMA_BUSY_TIMEOUT	0x100000
> +#define CMD_FIFO_EMPTY_TIMEOUT	1000
> +
> +#define MAX_CE_NUM		2
> +
> +/* eMMC clock register, misc control */
> +#define SD_EMMC_CLOCK		0x00
> +#define CLK_ALWAYS_ON		BIT(28)
> +#define CLK_SELECT_NAND		BIT(31)
> +#define CLK_DIV_MASK		GENMASK(5, 0)
> +
> +#define NFC_CLK_CYCLE		6
> +
> +/* nand flash controller delay 3 ns */
> +#define NFC_DEFAULT_DELAY	3000
> +
> +#define MAX_ECC_INDEX		10
> +
> +#define MUX_CLK_NUM_PARENTS	2
> +
> +#define ROW_ADDER(page, index)	(((page) >> (8 * (index))) & 0xff)
> +#define MAX_CYCLE_ADDRS		5
> +#define DIRREAD			1
> +#define DIRWRITE		0
> +
> +#define ECC_PARITY_BCH8_512B	14
> +
> +#define PER_INFO_BYTE		8
> +
> +#define ECC_COMPLETE            BIT(31)
> +#define ECC_ERR_CNT(x)		(((x) >> 24) & GENMASK(5, 0))
> +#define ECC_ZERO_CNT(x)		(((x) >> 16) & GENMASK(5, 0))
> +
> +struct meson_nfc_nand_chip {
> +	struct list_head node;
> +	struct nand_chip nand;
> +	unsigned long clk_rate;
> +	unsigned long level1_divider;
> +	u32 bus_timing;
> +	u32 twb;
> +	u32 tadl;
> +	u32 tbers_max;
> +
> +	u32 bch_mode;
> +	u8 *data_buf;
> +	__le64 *info_buf;
> +	u32 nsels;
> +	u8 sels[0];
> +};
> +
> +struct meson_nand_ecc {
> +	u32 bch;
> +	u32 strength;
> +};
> +
> +struct meson_nfc_data {
> +	const struct nand_ecc_caps *ecc_caps;
> +};
> +
> +struct meson_nfc_param {
> +	u32 chip_select;
> +	u32 rb_select;
> +};
> +
> +struct nand_rw_cmd {
> +	u32 cmd0;
> +	u32 addrs[MAX_CYCLE_ADDRS];
> +	u32 cmd1;
> +};
> +
> +struct nand_timing {
> +	u32 twb;
> +	u32 tadl;
> +	u32 tbers_max;
> +};
> +
> +struct meson_nfc {
> +	struct nand_controller controller;
> +	struct clk *core_clk;
> +	struct clk *device_clk;
> +	struct clk *phase_tx;
> +	struct clk *phase_rx;
> +
> +	unsigned long clk_rate;
> +	u32 bus_timing;
> +
> +	struct device *dev;
> +	void __iomem *reg_base;
> +	struct regmap *reg_clk;
> +	struct completion completion;
> +	struct list_head chips;
> +	const struct meson_nfc_data *data;
> +	struct meson_nfc_param param;
> +	struct nand_timing timing;
> +	union {
> +		int cmd[32];
> +		struct nand_rw_cmd rw;
> +	} cmdfifo;
> +
> +	dma_addr_t daddr;
> +	dma_addr_t iaddr;
> +
> +	unsigned long assigned_cs;
> +};
> +
> +enum {
> +	NFC_ECC_BCH8_1K		= 2,
> +	NFC_ECC_BCH24_1K,
> +	NFC_ECC_BCH30_1K,
> +	NFC_ECC_BCH40_1K,
> +	NFC_ECC_BCH50_1K,
> +	NFC_ECC_BCH60_1K,
> +};
> +
> +#define MESON_ECC_DATA(b, s)	{ .bch = (b),	.strength = (s)}
> +
> +static int meson_nand_calc_ecc_bytes(int step_size, int strength)
> +{
> +	int ecc_bytes;
> +
> +	if (step_size == 512 && strength == 8)
> +		return ECC_PARITY_BCH8_512B;
> +
> +	ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
> +	ecc_bytes = ALIGN(ecc_bytes, 2);
> +
> +	return ecc_bytes;
> +}
> +
> +NAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps,
> +		     meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60);
> +NAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps,
> +		     meson_nand_calc_ecc_bytes, 1024, 8);
> +
> +static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
> +{
> +	return container_of(nand, struct meson_nfc_nand_chip, nand);
> +}
> +
> +static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	int ret, value;
> +
> +	if (chip < 0 || WARN_ON_ONCE(chip > MAX_CE_NUM))
> +		return;
> +
> +	nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
> +	nfc->param.rb_select = nfc->param.chip_select;
> +	nfc->timing.twb = meson_chip->twb;
> +	nfc->timing.tadl = meson_chip->tadl;
> +	nfc->timing.tbers_max = meson_chip->tbers_max;
> +
> +	if (chip >= 0) {
> +		if (nfc->clk_rate != meson_chip->clk_rate) {
> +			ret = clk_set_rate(nfc->device_clk,
> +					   meson_chip->clk_rate);
> +			if (ret) {
> +				dev_err(nfc->dev, "failed to set clock rate\n");
> +				return;
> +			}
> +			nfc->clk_rate = meson_chip->clk_rate;
> +		}
> +		if (nfc->bus_timing != meson_chip->bus_timing) {
> +			value = (NFC_CLK_CYCLE - 1)
> +				| (meson_chip->bus_timing << 5);
> +			writel(value, nfc->reg_base + NFC_REG_CFG);
> +			writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
> +			nfc->bus_timing =  meson_chip->bus_timing;
> +		}
> +	}

Don't you have timing registers to set?

> +}
> +
> +static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
> +{
> +	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
> +	       nfc->reg_base + NFC_REG_CMD);
> +}
> +
> +static void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
> +{
> +	writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
> +	       nfc->reg_base + NFC_REG_CMD);
> +}
> +
> +static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	u32 bch = meson_chip->bch_mode, cmd;
> +	int len = mtd->writesize, pagesize, pages;
> +	int scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;

There are quite a few places where you use hardcoded values, I would
have preferred preprocessor defines for that. In this case, something
link:


        // naming is just as a reference, use whatever you want
        +#define CMD_SCRAMBLE BIT(19) 
        [...]
        +int scramble = nand->options & NAND_NEED_SCRAMBLING) ? CMD_SCRAMBLE : 0;

would be better (you can extend to other places as well).

> +
> +	pagesize = nand->ecc.size;
> +
> +	if (raw) {
> +		len = mtd->writesize + mtd->oobsize;
> +		cmd = (len & 0x3fff) | (scramble << 19) | DMA_DIR(dir);
> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +		return;
> +	}
> +
> +	pages = len / nand->ecc.size;
> +
> +	cmd = CMDRWGEN(DMA_DIR(dir), scramble, bch, 0, pagesize, pages);
> +
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +}
> +
> +static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
> +{
> +	/*
> +	 * Insert two commands to make sure all valid commands are finished.
> +	 *
> +	 * The Nand flash controller is designed as two stages pipleline -
> +	 *  a) fetch and b) excute.
> +	 * There might be cases when the driver see command queue is empty,
> +	 * but the Nand flash controller still has two commands buffered,
> +	 * one is fetched into NFC request queue (ready to run), and another
> +	 * is actively executing. So pushing 2 "IDLE" commands guarantees that
> +	 * the pipeline is emptied.
> +	 */
> +	meson_nfc_cmd_idle(nfc, 0);
> +	meson_nfc_cmd_idle(nfc, 0);
> +}
> +
> +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
> +				     unsigned int timeout_ms)
> +{
> +	u32 cmd_size = 0;
> +	int ret;
> +
> +	/* wait cmd fifo is empty */
> +	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
> +					 !NFC_CMD_GET_SIZE(cmd_size),
> +					 10, timeout_ms * 1000);
> +	if (ret)
> +		dev_err(nfc->dev, "wait for empty cmd FIFO time out\n");
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
> +{
> +	meson_nfc_drain_cmd(nfc);
> +
> +	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
> +}
> +
> +static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	int len;
> +
> +	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> +
> +	return meson_chip->data_buf + len;
> +}
> +
> +static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	int len, temp;
> +
> +	temp = nand->ecc.size + nand->ecc.bytes;
> +	len = (temp + 2) * i;
> +
> +	return meson_chip->data_buf + len;
> +}
> +
> +static void meson_nfc_get_data_oob(struct nand_chip *nand,
> +				   u8 *buf, u8 *oobbuf)
> +{
> +	int i, oob_len = 0;
> +	u8 *dsrc, *osrc;
> +
> +	oob_len = nand->ecc.bytes + 2;
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		if (buf) {
> +			dsrc = meson_nfc_data_ptr(nand, i);
> +			memcpy(buf, dsrc, nand->ecc.size);
> +			buf += nand->ecc.size;
> +		}
> +		osrc = meson_nfc_oob_ptr(nand, i);
> +		memcpy(oobbuf, osrc, oob_len);
> +		oobbuf += oob_len;
> +	}
> +}
> +
> +static void meson_nfc_set_data_oob(struct nand_chip *nand,
> +				   const u8 *buf, u8 *oobbuf)
> +{
> +	int i, oob_len = 0;
> +	u8 *dsrc, *osrc;
> +
> +	oob_len = nand->ecc.bytes + 2;
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		if (buf) {
> +			dsrc = meson_nfc_data_ptr(nand, i);
> +			memcpy(dsrc, buf, nand->ecc.size);
> +			buf += nand->ecc.size;
> +		}
> +		osrc = meson_nfc_oob_ptr(nand, i);
> +		memcpy(osrc, oobbuf, oob_len);
> +		oobbuf += oob_len;
> +	}
> +}
> +
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +{
> +	u32 cmd, cfg;
> +	int ret = 0;
> +
> +	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +	cfg = readl(nfc->reg_base + NFC_REG_CFG);
> +	cfg |= (1 << 21);
> +	writel(cfg, nfc->reg_base + NFC_REG_CFG);
> +
> +	init_completion(&nfc->completion);
> +
> +	/* use the max erase time as the maximum clock for waiting R/B */
> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> +		| nfc->param.chip_select | nfc->timing.tbers_max;

Nit: I think the '|' should be on the previous line.

> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +	ret = wait_for_completion_timeout(&nfc->completion,
> +					  msecs_to_jiffies(timeout_ms));
> +	if (ret == 0)
> +		ret = -1;
> +
> +	return ret;
> +}
> +
> +static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	__le64 *info;
> +	int i, count;
> +
> +	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +		info = &meson_chip->info_buf[i];
> +		*info |= oob_buf[count];
> +		*info |= oob_buf[count + 1] << 8;
> +	}
> +}
> +
> +static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	__le64 *info;
> +	int i, count;
> +
> +	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +		info = &meson_chip->info_buf[i];
> +		oob_buf[count] = *info;
> +		oob_buf[count + 1] = *info >> 8;
> +	}
> +}
> +
> +static int meson_nfc_ecc_correct(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	__le64 *info;
> +	u32 bitflips = 0, i;
> +	int scramble;
> +	u8 zero_cnt;
> +
> +	scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;
> +
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		info = &meson_chip->info_buf[i];
> +		if (ECC_ERR_CNT(*info) == 0x3f) {
> +			zero_cnt = ECC_ZERO_CNT(*info);
> +			if (scramble && zero_cnt < nand->ecc.strength)
> +				return ECC_CHECK_RETURN_FF;

This and what you do later with ECC_CHECK_RETURN_FF is pretty unclear
to me.

> +			mtd->ecc_stats.failed++;
> +			continue;
> +		}
> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> +	}

Are you sure you handle correctly empty pages with bf?

> +
> +	return bitflips;
> +}
> +
> +static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, u8 *databuf,
> +				      int datalen, u8 *infobuf, int infolen,
> +				      enum dma_data_direction dir)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	u32 cmd;
> +	int ret = 0;
> +
> +	nfc->daddr = dma_map_single(nfc->dev, (void *)databuf, datalen, dir);
> +	ret = dma_mapping_error(nfc->dev, nfc->daddr);
> +	if (ret) {
> +		dev_err(nfc->dev, "dma mapping error\n");
> +		return ret;
> +	}
> +	cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +	cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +	if (infobuf) {
> +		nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir);
> +		ret = dma_mapping_error(nfc->dev, nfc->iaddr);
> +		if (ret) {
> +			dev_err(nfc->dev, "dma mapping error\n");
> +			dma_unmap_single(nfc->dev,
> +					 nfc->daddr, datalen, dir);
> +			return ret;
> +		}
> +		cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +		cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	}
> +
> +	return ret;
> +}
> +
> +static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
> +					 int infolen, int datalen,
> +					 enum dma_data_direction dir)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +
> +	dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir);
> +	if (infolen)
> +		dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir);
> +}
> +
> +static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	int ret = 0;
> +	u32 cmd;
> +	u8 *info;
> +
> +	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
> +	ret = meson_nfc_dma_buffer_setup(nand, buf, len, info,
> +					 PER_INFO_BYTE, DMA_FROM_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	cmd = NFC_CMD_N2M | (len & 0x3fff);
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, 1000);
> +	meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE);
> +	kfree(info);
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	int ret = 0;
> +	u32 cmd;
> +
> +	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
> +					 0, DMA_TO_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	cmd = NFC_CMD_M2N | (len & 0x3fff);
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, 1000);
> +	meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE);
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
> +						int page, bool in)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	const struct nand_sdr_timings *sdr =
> +		nand_get_sdr_timings(&nand->data_interface);
> +	u32 *addrs = nfc->cmdfifo.rw.addrs;
> +	u32 cs = nfc->param.chip_select;
> +	u32 cmd0, cmd_num, row_start;
> +	int ret = 0, i;
> +
> +	cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int);
> +
> +	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
> +	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
> +
> +	addrs[0] = cs | NFC_CMD_ALE | 0;
> +	if (mtd->writesize <= 512) {
> +		cmd_num--;
> +		row_start = 1;
> +	} else {
> +		addrs[1] = cs | NFC_CMD_ALE | 0;
> +		row_start = 2;
> +	}
> +
> +	addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0);
> +	addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1);
> +
> +	if (nand->options & NAND_ROW_ADDR_3)
> +		addrs[row_start + 2] =
> +			cs | NFC_CMD_ALE | ROW_ADDER(page, 2);
> +	else
> +		cmd_num--;
> +
> +	/* subtract cmd1 */
> +	cmd_num--;
> +
> +	for (i = 0; i < cmd_num; i++)
> +		writel_relaxed(nfc->cmdfifo.cmd[i],
> +			       nfc->reg_base + NFC_REG_CMD);
> +
> +	if (in) {
> +		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
> +		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +	} else {
> +		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
> +	}
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_write_page_sub(struct nand_chip *nand,
> +				    int page, int raw)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	const struct nand_sdr_timings *sdr =
> +		nand_get_sdr_timings(&nand->data_interface);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	int data_len, info_len;
> +	u32 cmd;
> +	int ret;
> +
> +	data_len =  mtd->writesize + mtd->oobsize;
> +	info_len = nand->ecc.steps * PER_INFO_BYTE;
> +
> +	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE);
> +	if (ret)
> +		return ret;
> +
> +	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
> +					 data_len, (u8 *)meson_chip->info_buf,
> +					 info_len, DMA_TO_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	meson_nfc_cmd_seed(nfc, page);
> +	meson_nfc_cmd_access(nand, raw, DIRWRITE);
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +
> +	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
> +				    int oob_required, int page)
> +{
> +	u8 *oob_buf = nand->oob_poi;
> +
> +	meson_nfc_set_data_oob(nand, buf, oob_buf);
> +
> +	return meson_nfc_write_page_sub(nand, page, 1);
> +}
> +
> +static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
> +				      const u8 *buf, int oob_required, int page)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	u8 *oob_buf = nand->oob_poi;
> +
> +	memcpy(meson_chip->data_buf, buf, mtd->writesize);
> +	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
> +	meson_nfc_set_user_byte(nand, oob_buf);
> +
> +	return meson_nfc_write_page_sub(nand, page, 0);
> +}
> +
> +static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> +					    struct nand_chip *nand, int raw)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	__le64 *info;
> +	u32 neccpages;
> +	int ret;
> +
> +	neccpages = raw ? 1 : nand->ecc.steps;
> +	info = &meson_chip->info_buf[neccpages - 1];
> +	do {
> +		usleep_range(10, 15);
> +		/* info is updated by nfc dma engine*/
> +		smp_rmb();
> +		ret = *info & ECC_COMPLETE;
> +	} while (!ret);
> +}
> +
> +static int meson_nfc_read_page_sub(struct nand_chip *nand,
> +				   int page, int raw)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	int data_len, info_len;
> +	int ret;
> +
> +	data_len =  mtd->writesize + mtd->oobsize;
> +	info_len = nand->ecc.steps * PER_INFO_BYTE;
> +
> +	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
> +	if (ret)
> +		return ret;
> +
> +	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
> +					 data_len, (u8 *)meson_chip->info_buf,
> +					 info_len, DMA_FROM_DEVICE);
> +	if (ret)
> +		return ret;
> +
> +	meson_nfc_cmd_seed(nfc, page);
> +	meson_nfc_cmd_access(nand, raw, DIRREAD);
> +	ret = meson_nfc_wait_dma_finish(nfc);
> +	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
> +
> +	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
> +				   int oob_required, int page)
> +{
> +	u8 *oob_buf = nand->oob_poi;
> +	int ret;
> +
> +	ret = meson_nfc_read_page_sub(nand, page, 1);
> +	if (ret)
> +		return ret;
> +
> +	meson_nfc_get_data_oob(nand, buf, oob_buf);
> +
> +	return 0;
> +}
> +
> +static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> +				     int oob_required, int page)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	u8 *oob_buf = nand->oob_poi;
> +	int ret;
> +
> +	ret = meson_nfc_read_page_sub(nand, page, 0);
> +	if (ret)
> +		return ret;
> +
> +	meson_nfc_get_user_byte(nand, oob_buf);
> +
> +	ret = meson_nfc_ecc_correct(nand);
> +	if (ret == ECC_CHECK_RETURN_FF) {
> +		if (buf)
> +			memset(buf, 0xff, mtd->writesize);
> +
> +		memset(oob_buf, 0xff, mtd->oobsize);
> +		return 0;
> +	}
> +
> +	if (buf && buf != meson_chip->data_buf)
> +		memcpy(buf, meson_chip->data_buf, mtd->writesize);
> +
> +	return ret;
> +}
> +
> +static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
> +{
> +	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> +}
> +
> +static int meson_nfc_read_oob(struct nand_chip *nand, int page)
> +{
> +	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> +}
> +
> +void *
> +meson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr)
> +{
> +	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR))
> +		return NULL;
> +	if (virt_addr_valid(instr->ctx.data.buf.in) &&
> +	    !object_is_on_stack(instr->ctx.data.buf.in))
> +		return instr->ctx.data.buf.in;
> +
> +	return kzalloc(instr->ctx.data.len, GFP_KERNEL);

I think allocating memory and using it without ever testing the
allocation succeeded is wrong. You do that in many places. I would like
to see allocations properly handled.

> +}
> +
> +void
> +meson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr,
> +				     void *buf)
> +{
> +	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) ||
> +	    WARN_ON(!buf))
> +		return;
> +	if (buf == instr->ctx.data.buf.in)
> +		return;
> +
> +	memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len);
> +	kfree(buf);
> +}
> +
> +const void *
> +meson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
> +{
> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
> +		return NULL;
> +
> +	if (virt_addr_valid(instr->ctx.data.buf.out) &&
> +	    !object_is_on_stack(instr->ctx.data.buf.out))

Can you please create helpers for that? I guess it will help removing
these checks once the core will have a DMA-safe approach.

> +		return instr->ctx.data.buf.out;
> +
> +	return kmemdup(instr->ctx.data.buf.out,
> +		       instr->ctx.data.len, GFP_KERNEL);
> +}
> +
> +void
> +meson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr,
> +				      const void *buf)
> +{
> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) ||
> +	    WARN_ON(!buf))
> +		return;
> +
> +	if (buf != instr->ctx.data.buf.out)
> +		kfree(buf);
> +}
> +
> +static int meson_nfc_exec_op(struct nand_chip *nand,
> +			     const struct nand_operation *op, bool check_only)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	const struct nand_op_instr *instr = NULL;
> +	void *buf;
> +	u32 op_id, delay_idle, cmd;
> +	int i;
> +
> +	for (op_id = 0; op_id < op->ninstrs; op_id++) {
> +		instr = &op->instrs[op_id];
> +		delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns),
> +					  meson_chip->level1_divider *
> +					  NFC_CLK_CYCLE);
> +		switch (instr->type) {
> +		case NAND_OP_CMD_INSTR:
> +			cmd = nfc->param.chip_select | NFC_CMD_CLE;
> +			cmd |= instr->ctx.cmd.opcode & 0xff;
> +			writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +			meson_nfc_cmd_idle(nfc, delay_idle);
> +			break;
> +
> +		case NAND_OP_ADDR_INSTR:
> +			for (i = 0; i < instr->ctx.addr.naddrs; i++) {
> +				cmd = nfc->param.chip_select | NFC_CMD_ALE;
> +				cmd |= instr->ctx.addr.addrs[i] & 0xff;
> +				writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +			}
> +			meson_nfc_cmd_idle(nfc, delay_idle);
> +			break;
> +
> +		case NAND_OP_DATA_IN_INSTR:
> +			buf = meson_nand_op_get_dma_safe_input_buf(instr);
> +			meson_nfc_read_buf(nand, buf,
> +					   instr->ctx.data.len);
> +			meson_nand_op_put_dma_safe_input_buf(instr, buf);
> +			break;
> +
> +		case NAND_OP_DATA_OUT_INSTR:
> +			buf =
> +			(void *)meson_nand_op_get_dma_safe_output_buf(instr);
> +			meson_nfc_write_buf(nand, buf,
> +					    instr->ctx.data.len);
> +			meson_nand_op_put_dma_safe_output_buf(instr, buf);
> +			break;
> +
> +		case NAND_OP_WAITRDY_INSTR:
> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +			if (instr->delay_ns)
> +				meson_nfc_cmd_idle(nfc, delay_idle);
> +			break;
> +		}
> +	}
> +	meson_nfc_wait_cmd_finish(nfc, 1000);
> +	return 0;
> +}
> +
> +static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
> +			       struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +
> +	if (section >= nand->ecc.steps)
> +		return -ERANGE;
> +
> +	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> +	oobregion->length = nand->ecc.bytes;
> +
> +	return 0;
> +}
> +
> +static int meson_ooblayout_free(struct mtd_info *mtd, int section,
> +				struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +
> +	if (section >= nand->ecc.steps)
> +		return -ERANGE;
> +
> +	oobregion->offset = section * (2 + nand->ecc.bytes);
> +	oobregion->length = 2;
> +
> +	return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
> +	.ecc = meson_ooblayout_ecc,
> +	.free = meson_ooblayout_free,
> +};
> +
> +static int meson_nfc_clk_init(struct meson_nfc *nfc)
> +{
> +	int ret;
> +
> +	/* request core clock */
> +	nfc->core_clk = devm_clk_get(nfc->dev, "core");
> +	if (IS_ERR(nfc->core_clk)) {
> +		dev_err(nfc->dev, "failed to get core clk\n");
> +		return PTR_ERR(nfc->core_clk);
> +	}
> +
> +	nfc->device_clk = devm_clk_get(nfc->dev, "device");
> +	if (IS_ERR(nfc->device_clk)) {
> +		dev_err(nfc->dev, "failed to get device clk\n");
> +		return PTR_ERR(nfc->device_clk);
> +	}
> +
> +	nfc->phase_tx = devm_clk_get(nfc->dev, "tx");
> +	if (IS_ERR(nfc->phase_tx)) {
> +		dev_err(nfc->dev, "failed to get tx clk\n");
> +		return PTR_ERR(nfc->phase_tx);
> +	}
> +
> +	nfc->phase_rx = devm_clk_get(nfc->dev, "rx");
> +	if (IS_ERR(nfc->phase_rx)) {
> +		dev_err(nfc->dev, "failed to get rx clk\n");
> +		return PTR_ERR(nfc->phase_rx);
> +	}
> +
> +	/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
> +	regmap_update_bits(nfc->reg_clk,
> +			   0, CLK_SELECT_NAND, CLK_SELECT_NAND);
> +
> +	ret = clk_prepare_enable(nfc->core_clk);
> +	if (ret) {
> +		dev_err(nfc->dev, "failed to enable core clk\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(nfc->device_clk);
> +	if (ret) {
> +		dev_err(nfc->dev, "failed to enable device clk\n");
> +		clk_disable_unprepare(nfc->core_clk);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(nfc->phase_tx);
> +	if (ret) {
> +		dev_err(nfc->dev, "failed to enable tx clk\n");
> +		clk_disable_unprepare(nfc->core_clk);
> +		clk_disable_unprepare(nfc->device_clk);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(nfc->phase_rx);
> +	if (ret) {
> +		dev_err(nfc->dev, "failed to enable rx clk\n");
> +		clk_disable_unprepare(nfc->core_clk);
> +		clk_disable_unprepare(nfc->device_clk);
> +		clk_disable_unprepare(nfc->phase_tx);

This error case is a good candidate to a goto statement.

> +		return ret;
> +	}
> +
> +	ret = clk_set_rate(nfc->device_clk, 24000000);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void meson_nfc_disable_clk(struct meson_nfc *nfc)
> +{
> +	clk_disable_unprepare(nfc->phase_rx);
> +	clk_disable_unprepare(nfc->phase_tx);
> +	clk_disable_unprepare(nfc->device_clk);
> +	clk_disable_unprepare(nfc->core_clk);
> +}
> +
> +static void meson_nfc_free_buffer(struct nand_chip *nand)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +
> +	kfree(meson_chip->info_buf);
> +	kfree(meson_chip->data_buf);
> +}
> +
> +static int meson_chip_buffer_init(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	u32 page_bytes, info_bytes, nsectors;
> +
> +	nsectors = mtd->writesize / nand->ecc.size;
> +
> +	page_bytes =  mtd->writesize + mtd->oobsize;
> +	info_bytes = nsectors * PER_INFO_BYTE;
> +
> +	meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL);
> +	if (!meson_chip->data_buf)
> +		return -ENOMEM;
> +
> +	meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL);
> +	if (!meson_chip->info_buf) {
> +		kfree(meson_chip->data_buf);
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static
> +int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline,
> +				   const struct nand_data_interface *conf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	const struct nand_sdr_timings *timings;
> +	u32 div, bt_min, bt_max, tbers_clocks;
> +
> +	timings = nand_get_sdr_timings(conf);
> +	if (IS_ERR(timings))
> +		return -ENOTSUPP;
> +
> +	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
> +		return 0;
> +
> +	div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE);
> +	bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div;
> +	bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min +
> +		  timings->tRC_min / 2) / div;
> +
> +	meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max),
> +				       div * NFC_CLK_CYCLE);
> +	meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min),
> +					div * NFC_CLK_CYCLE);
> +	tbers_clocks = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tBERS_max),
> +				    div * NFC_CLK_CYCLE);
> +	meson_chip->tbers_max = ilog2(tbers_clocks);
> +	if (!is_power_of_2(tbers_clocks))
> +		meson_chip->tbers_max++;
> +
> +	bt_min = DIV_ROUND_UP(bt_min, 1000);
> +	bt_max = DIV_ROUND_UP(bt_max, 1000);
> +
> +	if (bt_max < bt_min)
> +		return -EINVAL;
> +
> +	meson_chip->level1_divider = div;
> +	meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider;
> +	meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1;
> +
> +	return 0;
> +}
> +
> +static int meson_nand_bch_mode(struct nand_chip *nand)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct meson_nand_ecc meson_ecc[] = {
> +		MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8),
> +		MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24),
> +		MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30),
> +		MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40),
> +		MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50),
> +		MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60),
> +	};

Maybe this array could be static?

> +	int i;
> +
> +	if (nand->ecc.strength > 60 || nand->ecc.strength < 8)
> +		return -EINVAL;
> +
> +	for (i = 0; i < sizeof(meson_ecc); i++) {
> +		if (meson_ecc[i].strength == nand->ecc.strength) {
> +			meson_chip->bch_mode = meson_ecc[i].bch;
> +			return 0;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int meson_nand_attach_chip(struct nand_chip *nand)
> +{
> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	int nsectors = mtd->writesize / 1024;
> +	int ret;
> +
> +	if (!mtd->name) {
> +		mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
> +					   "%s:nand%d",
> +					   dev_name(nfc->dev),
> +					   meson_chip->sels[0]);
> +		if (!mtd->name)
> +			return -ENOMEM;
> +	}
> +
> +	if (nand->bbt_options & NAND_BBT_USE_FLASH)
> +		nand->bbt_options |= NAND_BBT_NO_OOB;
> +
> +	nand->options |= NAND_NO_SUBPAGE_WRITE;
> +
> +	ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps,
> +				   mtd->oobsize - 2 * nsectors);
> +	if (ret) {
> +		dev_err(nfc->dev, "failed to ecc init\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = meson_nand_bch_mode(nand);
> +	if (ret)
> +		return -EINVAL;
> +
> +	nand->ecc.mode = NAND_ECC_HW;
> +	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
> +	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> +	nand->ecc.write_oob_raw = nand_write_oob_std;
> +	nand->ecc.write_oob = nand_write_oob_std;
> +
> +	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
> +	nand->ecc.read_page = meson_nfc_read_page_hwecc;
> +	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> +	nand->ecc.read_oob = meson_nfc_read_oob;
> +
> +	if (nand->options & NAND_BUSWIDTH_16) {
> +		dev_err(nfc->dev, "16bits buswidth not supported");
> +		return -EINVAL;
> +	}
> +	meson_chip_buffer_init(nand);
> +	if (ret)
> +		return -ENOMEM;
> +
> +	return ret;
> +}
> +
> +static const struct nand_controller_ops meson_nand_controller_ops = {
> +	.attach_chip = meson_nand_attach_chip,

Don't you need a ->detach_chip hook to free data_buf/info_buf
buffers?

> +};
> +
> +static int
> +meson_nfc_nand_chip_init(struct device *dev,
> +			 struct meson_nfc *nfc, struct device_node *np)
> +{
> +	struct meson_nfc_nand_chip *meson_chip;
> +	struct nand_chip *nand;
> +	struct mtd_info *mtd;
> +	int ret, i;
> +	u32 tmp, nsels;
> +
> +	if (!of_get_property(np, "reg", &nsels))
> +		return -EINVAL;
> +
> +	nsels /= sizeof(u32);
> +	if (!nsels || nsels > MAX_CE_NUM) {
> +		dev_err(dev, "invalid reg property size\n");
> +		return -EINVAL;
> +	}
> +
> +	meson_chip = devm_kzalloc(dev,
> +				  sizeof(*meson_chip) + (nsels * sizeof(u8)),
> +				  GFP_KERNEL);
> +	if (!meson_chip)
> +		return -ENOMEM;
> +
> +	meson_chip->nsels = nsels;
> +
> +	for (i = 0; i < nsels; i++) {
> +		ret = of_property_read_u32_index(np, "reg", i, &tmp);
> +		if (ret) {
> +			dev_err(dev, "could not retrieve reg property: %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
> +			dev_err(dev, "CS %d already assigned\n", tmp);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	nand = &meson_chip->nand;
> +	nand->controller = &nfc->controller;
> +	nand->controller->ops = &meson_nand_controller_ops;
> +	nand_set_flash_node(nand, np);
> +	nand_set_controller_data(nand, nfc);
> +
> +	nand->options |= NAND_USE_BOUNCE_BUFFER;
> +	nand->select_chip = meson_nfc_select_chip;
> +	nand->exec_op = meson_nfc_exec_op;
> +	nand->setup_data_interface = meson_nfc_setup_data_interface;
> +	mtd = nand_to_mtd(nand);
> +	mtd->owner = THIS_MODULE;
> +	mtd->dev.parent = dev;
> +
> +	ret = nand_scan(nand, nsels);
> +	if (ret)
> +		return ret;
> +
> +	ret = mtd_device_register(mtd, NULL, 0);
> +	if (ret) {
> +		dev_err(dev, "failed to register mtd device: %d\n", ret);
> +		nand_cleanup(nand);
> +		return ret;
> +	}
> +
> +	list_add_tail(&meson_chip->node, &nfc->chips);
> +
> +	return 0;
> +}
> +
> +static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
> +{
> +	struct meson_nfc_nand_chip *meson_chip;
> +	struct mtd_info *mtd;
> +	int ret;
> +
> +	while (!list_empty(&nfc->chips)) {
> +		meson_chip = list_first_entry(&nfc->chips,
> +					      struct meson_nfc_nand_chip, node);
> +		mtd = nand_to_mtd(&meson_chip->nand);
> +		ret = mtd_device_unregister(mtd);
> +		if (ret)
> +			return ret;
> +
> +		meson_nfc_free_buffer(&meson_chip->nand);
> +		nand_cleanup(&meson_chip->nand);
> +		list_del(&meson_chip->node);
> +	}
> +
> +	return 0;
> +}
> +
> +static int meson_nfc_nand_chips_init(struct device *dev,
> +				     struct meson_nfc *nfc)
> +{
> +	struct device_node *np = dev->of_node;
> +	struct device_node *nand_np;
> +	int ret;
> +
> +	for_each_child_of_node(np, nand_np) {
> +		ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
> +		if (ret) {
> +			meson_nfc_nand_chip_cleanup(nfc);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t meson_nfc_irq(int irq, void *id)
> +{
> +	struct meson_nfc *nfc = id;
> +	u32 cfg;
> +
> +	cfg = readl(nfc->reg_base + NFC_REG_CFG);
> +	if (!(cfg & NFC_RB_IRQ_EN))
> +		return IRQ_NONE;
> +
> +	cfg &= ~(NFC_RB_IRQ_EN);
> +	writel(cfg, nfc->reg_base + NFC_REG_CFG);
> +
> +	complete(&nfc->completion);
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct meson_nfc_data meson_gxl_data = {
> +	.ecc_caps = &meson_gxl_ecc_caps,
> +};
> +
> +static const struct meson_nfc_data meson_axg_data = {
> +	.ecc_caps = &meson_axg_ecc_caps,
> +};
> +
> +static const struct of_device_id meson_nfc_id_table[] = {
> +	{
> +		.compatible = "amlogic,meson-gxl-nfc",
> +		.data = &meson_gxl_data,
> +	}, {
> +		.compatible = "amlogic,meson-axg-nfc",
> +		.data = &meson_axg_data,
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
> +
> +static int meson_nfc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct meson_nfc *nfc;
> +	struct resource *res;
> +	int ret, irq;
> +
> +	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
> +	if (!nfc)
> +		return -ENOMEM;
> +
> +	nfc->data = of_device_get_match_data(&pdev->dev);
> +	if (!nfc->data)
> +		return -ENODEV;
> +
> +	nand_controller_init(&nfc->controller);
> +	INIT_LIST_HEAD(&nfc->chips);
> +
> +	nfc->dev = dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	nfc->reg_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(nfc->reg_base))
> +		return PTR_ERR(nfc->reg_base);
> +
> +	nfc->reg_clk =
> +		syscon_regmap_lookup_by_phandle(dev->of_node,
> +						"amlogic,mmc-syscon");
> +	if (IS_ERR(nfc->reg_clk)) {
> +		dev_err(dev, "Failed to lookup clock base\n");
> +		return PTR_ERR(nfc->reg_clk);
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "no nfi irq resource\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = meson_nfc_clk_init(nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to initialize nand clk\n");
> +		goto err;

Useless goto, a return would be enough.

> +	}
> +
> +	writel(0, nfc->reg_base + NFC_REG_CFG);
> +	ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to request nfi irq\n");
> +		ret = -EINVAL;
> +		goto err_clk;
> +	}
> +
> +	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
> +	if (ret) {
> +		dev_err(dev, "failed to set dma mask\n");

Nit: I prefer when acronyms are upper case in plain English (DMA, NAND,
IRQ, etc).

> +		goto err_clk;
> +	}
> +
> +	platform_set_drvdata(pdev, nfc);
> +
> +	ret = meson_nfc_nand_chips_init(dev, nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to init nand chips\n");
> +		goto err_clk;
> +	}
> +
> +	return 0;
> +
> +err_clk:
> +	meson_nfc_disable_clk(nfc);
> +err:

This goto can be removed.

> +	return ret;
> +}
> +
> +static int meson_nfc_remove(struct platform_device *pdev)
> +{
> +	struct meson_nfc *nfc = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = meson_nfc_nand_chip_cleanup(nfc);
> +	if (ret)
> +		return ret;
> +
> +	meson_nfc_disable_clk(nfc);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver meson_nfc_driver = {
> +	.probe  = meson_nfc_probe,
> +	.remove = meson_nfc_remove,
> +	.driver = {
> +		.name  = "meson-nand",
> +		.of_match_table = meson_nfc_id_table,
> +	},
> +};
> +module_platform_driver(meson_nfc_driver);
> +
> +MODULE_LICENSE("Dual MIT/GPL");
> +MODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
> +MODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");




Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-07  9:24   ` Miquel Raynal
@ 2018-12-07  9:42     ` Miquel Raynal
  2018-12-07 10:41       ` Liang Yang
  2018-12-10 11:23     ` Liang Yang
  1 sibling, 1 reply; 19+ messages in thread
From: Miquel Raynal @ 2018-12-07  9:42 UTC (permalink / raw)
  To: Jianxin Pan
  Cc: Boris Brezillon, linux-mtd, Liang Yang, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Jianxin,

Miquel Raynal <miquel.raynal@bootlin.com> wrote on Fri, 7 Dec 2018
10:24:56 +0100:

> Hi Jianxin,
> 
> Looks good to me overall, a few comments inline.
> 
> Jianxin Pan <jianxin.pan@amlogic.com> wrote on Sat, 17 Nov 2018
> 00:40:38 +0800:
> 
> > From: Liang Yang <liang.yang@amlogic.com>
> > 
> > Add initial support for the Amlogic NAND flash controller which found
> > in the Meson-GXBB/GXL/AXG SoCs.
> > 
> > Signed-off-by: Liang Yang <liang.yang@amlogic.com>
> > Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
> > Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
> > ---
> >  drivers/mtd/nand/raw/Kconfig      |   10 +
> >  drivers/mtd/nand/raw/Makefile     |    1 +
> >  drivers/mtd/nand/raw/meson_nand.c | 1417 +++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1428 insertions(+)
> >  create mode 100644 drivers/mtd/nand/raw/meson_nand.c
> > 

I forgot to mention, Boris has done more cleanup which breaks your
patches, please have a look at the following commits in the nand/next
branch, they will force you to do some light rework to get the driver
building (especially, you should not export the ->select_chip hook anymore):

7a08dbaedd36 mtd: rawnand: Move ->setup_data_interface() to nand_controller_ops
f2abfeb2078b mtd: rawnand: Move the ->exec_op() method to nand_controller_ops
7d6c37e90cf9 mtd: rawnand: Deprecate the ->select_chip() hook
1770022ffa85 mtd: rawnand: ams-delta: Stop implementing ->select_chip()
653c57c7da08 mtd: rawnand: vf610: Stop implementing ->select_chip()
2ace451cae22 mtd: rawnand: tegra: Stop implementing ->select_chip()
b25251414f6e mtd: rawnand: marvell: Stop implementing ->select_chip()
550b9fc4e3af mtd: rawnand: fsmc: Stop implementing ->select_chip()
02b4a52604a4 mtd: rawnand: Make ->select_chip() optional when ->exec_op() is implemented
ae2294b10b0f mtd: rawnand: Pass the CS line to be selected in struct nand_operation
1d0178593d14 mtd: rawnand: Add nand_[de]select_target() helpers

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-07  9:42     ` Miquel Raynal
@ 2018-12-07 10:41       ` Liang Yang
  2018-12-07 10:48         ` Miquel Raynal
  0 siblings, 1 reply; 19+ messages in thread
From: Liang Yang @ 2018-12-07 10:41 UTC (permalink / raw)
  To: Miquel Raynal, Jianxin Pan
  Cc: Boris Brezillon, linux-mtd, Yixun Lan, David Woodhouse,
	Brian Norris, Marek Vasut, Richard Weinberger, Jerome Brunet,
	Neil Armstrong, Martin Blumenstingl, Carlo Caione, Kevin Hilman,
	Rob Herring, Jian Hu, Hanjie Lin, Victor Wan, linux-amlogic,
	linux-arm-kernel, linux-kernel

Hi Miquel,

Appreciate your time.
I will follow the nand/next and rework some next week as soon as possible.

On 2018/12/7 17:42, Miquel Raynal wrote:
> Hi Jianxin,
> 
> Miquel Raynal <miquel.raynal@bootlin.com> wrote on Fri, 7 Dec 2018
> 10:24:56 +0100:
> 
>> Hi Jianxin,
>>
>> Looks good to me overall, a few comments inline.
>>
>> Jianxin Pan <jianxin.pan@amlogic.com> wrote on Sat, 17 Nov 2018
>> 00:40:38 +0800:
>>
>>> From: Liang Yang <liang.yang@amlogic.com>
>>>
>>> Add initial support for the Amlogic NAND flash controller which found
>>> in the Meson-GXBB/GXL/AXG SoCs.
>>>
>>> Signed-off-by: Liang Yang <liang.yang@amlogic.com>
>>> Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
>>> Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
>>> ---
>>>   drivers/mtd/nand/raw/Kconfig      |   10 +
>>>   drivers/mtd/nand/raw/Makefile     |    1 +
>>>   drivers/mtd/nand/raw/meson_nand.c | 1417 +++++++++++++++++++++++++++++++++++++
>>>   3 files changed, 1428 insertions(+)
>>>   create mode 100644 drivers/mtd/nand/raw/meson_nand.c
>>>
> 
> I forgot to mention, Boris has done more cleanup which breaks your
> patches, please have a look at the following commits in the nand/next
> branch, they will force you to do some light rework to get the driver
> building (especially, you should not export the ->select_chip hook anymore):
> 
> 7a08dbaedd36 mtd: rawnand: Move ->setup_data_interface() to nand_controller_ops
> f2abfeb2078b mtd: rawnand: Move the ->exec_op() method to nand_controller_ops
> 7d6c37e90cf9 mtd: rawnand: Deprecate the ->select_chip() hook
> 1770022ffa85 mtd: rawnand: ams-delta: Stop implementing ->select_chip()
> 653c57c7da08 mtd: rawnand: vf610: Stop implementing ->select_chip()
> 2ace451cae22 mtd: rawnand: tegra: Stop implementing ->select_chip()
> b25251414f6e mtd: rawnand: marvell: Stop implementing ->select_chip()
> 550b9fc4e3af mtd: rawnand: fsmc: Stop implementing ->select_chip()
> 02b4a52604a4 mtd: rawnand: Make ->select_chip() optional when ->exec_op() is implemented
> ae2294b10b0f mtd: rawnand: Pass the CS line to be selected in struct nand_operation
> 1d0178593d14 mtd: rawnand: Add nand_[de]select_target() helpers
> 
> Thanks,
> Miquèl
> 
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-07 10:41       ` Liang Yang
@ 2018-12-07 10:48         ` Miquel Raynal
  0 siblings, 0 replies; 19+ messages in thread
From: Miquel Raynal @ 2018-12-07 10:48 UTC (permalink / raw)
  To: Liang Yang
  Cc: Jianxin Pan, Boris Brezillon, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Liang,

Liang Yang <liang.yang@amlogic.com> wrote on Fri, 7 Dec 2018 18:41:39
+0800:

> Hi Miquel,
> 
> Appreciate your time.

Sorry for being late to do this review.

> I will follow the nand/next and rework some next week as soon as possible.

There is no hurry, I don't think the driver will enter 4.21 (or
whatever it is called) as we are already late in the release cycle. It
should enter in the next release though.

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-07  9:24   ` Miquel Raynal
  2018-12-07  9:42     ` Miquel Raynal
@ 2018-12-10 11:23     ` Liang Yang
  2018-12-10 11:38       ` Boris Brezillon
  1 sibling, 1 reply; 19+ messages in thread
From: Liang Yang @ 2018-12-10 11:23 UTC (permalink / raw)
  To: Miquel Raynal, Jianxin Pan
  Cc: Boris Brezillon, linux-mtd, Yixun Lan, David Woodhouse,
	Brian Norris, Marek Vasut, Richard Weinberger, Jerome Brunet,
	Neil Armstrong, Martin Blumenstingl, Carlo Caione, Kevin Hilman,
	Rob Herring, Jian Hu, Hanjie Lin, Victor Wan, linux-amlogic,
	linux-arm-kernel, linux-kernel


On 2018/12/7 17:24, Miquel Raynal wrote:
> Hi Jianxin,
> 
> Looks good to me overall, a few comments inline.
> 
> Jianxin Pan <jianxin.pan@amlogic.com> wrote on Sat, 17 Nov 2018
> 00:40:38 +0800:
> 
>> From: Liang Yang <liang.yang@amlogic.com>
>>
>> Add initial support for the Amlogic NAND flash controller which found
>> in the Meson-GXBB/GXL/AXG SoCs.
>>
>> Signed-off-by: Liang Yang <liang.yang@amlogic.com>
>> Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
>> Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com>
>> ---
>>   drivers/mtd/nand/raw/Kconfig      |   10 +
>>   drivers/mtd/nand/raw/Makefile     |    1 +
>>   drivers/mtd/nand/raw/meson_nand.c | 1417 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 1428 insertions(+)
>>   create mode 100644 drivers/mtd/nand/raw/meson_nand.c
>>
>> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
>> index c7efc31..223b041 100644
>> --- a/drivers/mtd/nand/raw/Kconfig
>> +++ b/drivers/mtd/nand/raw/Kconfig
>> @@ -541,4 +541,14 @@ config MTD_NAND_TEGRA
>>   	  is supported. Extra OOB bytes when using HW ECC are currently
>>   	  not supported.
>>   
>> +config MTD_NAND_MESON
>> +	tristate "Support for NAND controller on Amlogic's Meson SoCs"
>> +	depends on ARCH_MESON || COMPILE_TEST
>> +	depends on COMMON_CLK_AMLOGIC
>> +	select COMMON_CLK_REGMAP_MESON
>> +	select MFD_SYSCON
>> +	help
>> +	  Enables support for NAND controller on Amlogic's Meson SoCs.
>> +	  This controller is found on Meson GXBB, GXL, AXG SoCs.
>> +
>>   endif # MTD_NAND
>> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
>> index 57159b3..a2cc2fe 100644
>> --- a/drivers/mtd/nand/raw/Makefile
>> +++ b/drivers/mtd/nand/raw/Makefile
>> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
>>   obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
>>   obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
>>   obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
>> +obj-$(CONFIG_MTD_NAND_MESON)		+= meson_nand.o
>>   
>>   nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
>>   nand-objs += nand_onfi.o
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> new file mode 100644
>> index 0000000..c566636
>> --- /dev/null
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -0,0 +1,1417 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Amlogic Meson Nand Flash Controller Driver
>> + *
>> + * Copyright (c) 2018 Amlogic, inc.
>> + * Author: Liang Yang <liang.yang@amlogic.com>
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/clk.h>
>> +#include <linux/mtd/rawnand.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/module.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +
>> +#define NFC_REG_CMD		0x00
>> +#define NFC_CMD_DRD		(0x8 << 14)
>> +#define NFC_CMD_IDLE		(0xc << 14)
>> +#define NFC_CMD_DWR		(0x4 << 14)
>> +#define NFC_CMD_CLE		(0x5 << 14)
>> +#define NFC_CMD_ALE		(0x6 << 14)
>> +#define NFC_CMD_ADL		((0 << 16) | (3 << 20))
>> +#define NFC_CMD_ADH		((1 << 16) | (3 << 20))
>> +#define NFC_CMD_AIL		((2 << 16) | (3 << 20))
>> +#define NFC_CMD_AIH		((3 << 16) | (3 << 20))
>> +#define NFC_CMD_SEED		((8 << 16) | (3 << 20))
>> +#define NFC_CMD_M2N		((0 << 17) | (2 << 20))
>> +#define NFC_CMD_N2M		((1 << 17) | (2 << 20))
>> +#define NFC_CMD_RB		BIT(20)
>> +#define NFC_CMD_IO6		((0xb << 10) | (1 << 18))
>> +#define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>> +#define NFC_CMD_RB_INT		BIT(14)
>> +
>> +#define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>> +
>> +#define NFC_REG_CFG		0x04
>> +#define NFC_REG_DADR		0x08
>> +#define NFC_REG_IADR		0x0c
>> +#define NFC_REG_BUF		0x10
>> +#define NFC_REG_INFO		0x14
>> +#define NFC_REG_DC		0x18
>> +#define NFC_REG_ADR		0x1c
>> +#define NFC_REG_DL		0x20
>> +#define NFC_REG_DH		0x24
>> +#define NFC_REG_CADR		0x28
>> +#define NFC_REG_SADR		0x2c
>> +#define NFC_REG_PINS		0x30
>> +#define NFC_REG_VER		0x38
>> +
>> +#define NFC_RB_IRQ_EN		BIT(21)
>> +#define NFC_INT_MASK		(3 << 20)
>> +
>> +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
>> +	(								\
>> +		(cmd_dir)			|			\
>> +		((ran) << 19)			|			\
>> +		((bch) << 14)			|			\
>> +		((short_mode) << 13)		|			\
>> +		(((page_size) & 0x7f) << 6)	|			\
>> +		((pages) & 0x3f)					\
>> +	)
>> +
>> +#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
>> +#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
>> +#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
>> +#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
>> +
>> +#define RB_STA(x)		(1 << (26 + (x)))
>> +#define DMA_DIR(dir)		((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
>> +
>> +#define ECC_CHECK_RETURN_FF	(-1)
>> +
>> +#define NAND_CE0		(0xe << 10)
>> +#define NAND_CE1		(0xd << 10)
>> +
>> +#define DMA_BUSY_TIMEOUT	0x100000
>> +#define CMD_FIFO_EMPTY_TIMEOUT	1000
>> +
>> +#define MAX_CE_NUM		2
>> +
>> +/* eMMC clock register, misc control */
>> +#define SD_EMMC_CLOCK		0x00
>> +#define CLK_ALWAYS_ON		BIT(28)
>> +#define CLK_SELECT_NAND		BIT(31)
>> +#define CLK_DIV_MASK		GENMASK(5, 0)
>> +
>> +#define NFC_CLK_CYCLE		6
>> +
>> +/* nand flash controller delay 3 ns */
>> +#define NFC_DEFAULT_DELAY	3000
>> +
>> +#define MAX_ECC_INDEX		10
>> +
>> +#define MUX_CLK_NUM_PARENTS	2
>> +
>> +#define ROW_ADDER(page, index)	(((page) >> (8 * (index))) & 0xff)
>> +#define MAX_CYCLE_ADDRS		5
>> +#define DIRREAD			1
>> +#define DIRWRITE		0
>> +
>> +#define ECC_PARITY_BCH8_512B	14
>> +
>> +#define PER_INFO_BYTE		8
>> +
>> +#define ECC_COMPLETE            BIT(31)
>> +#define ECC_ERR_CNT(x)		(((x) >> 24) & GENMASK(5, 0))
>> +#define ECC_ZERO_CNT(x)		(((x) >> 16) & GENMASK(5, 0))
>> +
>> +struct meson_nfc_nand_chip {
>> +	struct list_head node;
>> +	struct nand_chip nand;
>> +	unsigned long clk_rate;
>> +	unsigned long level1_divider;
>> +	u32 bus_timing;
>> +	u32 twb;
>> +	u32 tadl;
>> +	u32 tbers_max;
>> +
>> +	u32 bch_mode;
>> +	u8 *data_buf;
>> +	__le64 *info_buf;
>> +	u32 nsels;
>> +	u8 sels[0];
>> +};
>> +
>> +struct meson_nand_ecc {
>> +	u32 bch;
>> +	u32 strength;
>> +};
>> +
>> +struct meson_nfc_data {
>> +	const struct nand_ecc_caps *ecc_caps;
>> +};
>> +
>> +struct meson_nfc_param {
>> +	u32 chip_select;
>> +	u32 rb_select;
>> +};
>> +
>> +struct nand_rw_cmd {
>> +	u32 cmd0;
>> +	u32 addrs[MAX_CYCLE_ADDRS];
>> +	u32 cmd1;
>> +};
>> +
>> +struct nand_timing {
>> +	u32 twb;
>> +	u32 tadl;
>> +	u32 tbers_max;
>> +};
>> +
>> +struct meson_nfc {
>> +	struct nand_controller controller;
>> +	struct clk *core_clk;
>> +	struct clk *device_clk;
>> +	struct clk *phase_tx;
>> +	struct clk *phase_rx;
>> +
>> +	unsigned long clk_rate;
>> +	u32 bus_timing;
>> +
>> +	struct device *dev;
>> +	void __iomem *reg_base;
>> +	struct regmap *reg_clk;
>> +	struct completion completion;
>> +	struct list_head chips;
>> +	const struct meson_nfc_data *data;
>> +	struct meson_nfc_param param;
>> +	struct nand_timing timing;
>> +	union {
>> +		int cmd[32];
>> +		struct nand_rw_cmd rw;
>> +	} cmdfifo;
>> +
>> +	dma_addr_t daddr;
>> +	dma_addr_t iaddr;
>> +
>> +	unsigned long assigned_cs;
>> +};
>> +
>> +enum {
>> +	NFC_ECC_BCH8_1K		= 2,
>> +	NFC_ECC_BCH24_1K,
>> +	NFC_ECC_BCH30_1K,
>> +	NFC_ECC_BCH40_1K,
>> +	NFC_ECC_BCH50_1K,
>> +	NFC_ECC_BCH60_1K,
>> +};
>> +
>> +#define MESON_ECC_DATA(b, s)	{ .bch = (b),	.strength = (s)}
>> +
>> +static int meson_nand_calc_ecc_bytes(int step_size, int strength)
>> +{
>> +	int ecc_bytes;
>> +
>> +	if (step_size == 512 && strength == 8)
>> +		return ECC_PARITY_BCH8_512B;
>> +
>> +	ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
>> +	ecc_bytes = ALIGN(ecc_bytes, 2);
>> +
>> +	return ecc_bytes;
>> +}
>> +
>> +NAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps,
>> +		     meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60);
>> +NAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps,
>> +		     meson_nand_calc_ecc_bytes, 1024, 8);
>> +
>> +static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
>> +{
>> +	return container_of(nand, struct meson_nfc_nand_chip, nand);
>> +}
>> +
>> +static void meson_nfc_select_chip(struct nand_chip *nand, int chip)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	int ret, value;
>> +
>> +	if (chip < 0 || WARN_ON_ONCE(chip > MAX_CE_NUM))
>> +		return;
>> +
>> +	nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
>> +	nfc->param.rb_select = nfc->param.chip_select;
>> +	nfc->timing.twb = meson_chip->twb;
>> +	nfc->timing.tadl = meson_chip->tadl;
>> +	nfc->timing.tbers_max = meson_chip->tbers_max;
>> +
>> +	if (chip >= 0) {
>> +		if (nfc->clk_rate != meson_chip->clk_rate) {
>> +			ret = clk_set_rate(nfc->device_clk,
>> +					   meson_chip->clk_rate);
>> +			if (ret) {
>> +				dev_err(nfc->dev, "failed to set clock rate\n");
>> +				return;
>> +			}
>> +			nfc->clk_rate = meson_chip->clk_rate;
>> +		}
>> +		if (nfc->bus_timing != meson_chip->bus_timing) {
>> +			value = (NFC_CLK_CYCLE - 1)
>> +				| (meson_chip->bus_timing << 5);
>> +			writel(value, nfc->reg_base + NFC_REG_CFG);
>> +			writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
>> +			nfc->bus_timing =  meson_chip->bus_timing;
>> +		}
>> +	}
> 
> Don't you have timing registers to set?
> 
  there is no other timing setting except meson_chip->bus_timing.

>> +}
>> +
>> +static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>> +{
>> +	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
>> +	       nfc->reg_base + NFC_REG_CMD);
>> +}
>> +
>> +static void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
>> +{
>> +	writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
>> +	       nfc->reg_base + NFC_REG_CMD);
>> +}
>> +
>> +static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	u32 bch = meson_chip->bch_mode, cmd;
>> +	int len = mtd->writesize, pagesize, pages;
>> +	int scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;
> 
> There are quite a few places where you use hardcoded values, I would
> have preferred preprocessor defines for that. In this case, something
> link:
> >
>          // naming is just as a reference, use whatever you want
>          +#define CMD_SCRAMBLE BIT(19)
>          [...]
>          +int scramble = nand->options & NAND_NEED_SCRAMBLING) ? CMD_SCRAMBLE : 0;
> 
> would be better (you can extend to other places as well).
> 
ok, i will fix it.

>> +
>> +	pagesize = nand->ecc.size;
>> +
>> +	if (raw) {
>> +		len = mtd->writesize + mtd->oobsize;
>> +		cmd = (len & 0x3fff) | (scramble << 19) | DMA_DIR(dir);
>> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +		return;
>> +	}
>> +
>> +	pages = len / nand->ecc.size;
>> +
>> +	cmd = CMDRWGEN(DMA_DIR(dir), scramble, bch, 0, pagesize, pages);
>> +
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +}
>> +
>> +static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>> +{
>> +	/*
>> +	 * Insert two commands to make sure all valid commands are finished.
>> +	 *
>> +	 * The Nand flash controller is designed as two stages pipleline -
>> +	 *  a) fetch and b) excute.
>> +	 * There might be cases when the driver see command queue is empty,
>> +	 * but the Nand flash controller still has two commands buffered,
>> +	 * one is fetched into NFC request queue (ready to run), and another
>> +	 * is actively executing. So pushing 2 "IDLE" commands guarantees that
>> +	 * the pipeline is emptied.
>> +	 */
>> +	meson_nfc_cmd_idle(nfc, 0);
>> +	meson_nfc_cmd_idle(nfc, 0);
>> +}
>> +
>> +static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
>> +				     unsigned int timeout_ms)
>> +{
>> +	u32 cmd_size = 0;
>> +	int ret;
>> +
>> +	/* wait cmd fifo is empty */
>> +	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
>> +					 !NFC_CMD_GET_SIZE(cmd_size),
>> +					 10, timeout_ms * 1000);
>> +	if (ret)
>> +		dev_err(nfc->dev, "wait for empty cmd FIFO time out\n");
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>> +{
>> +	meson_nfc_drain_cmd(nfc);
>> +
>> +	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
>> +}
>> +
>> +static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	int len;
>> +
>> +	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>> +
>> +	return meson_chip->data_buf + len;
>> +}
>> +
>> +static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	int len, temp;
>> +
>> +	temp = nand->ecc.size + nand->ecc.bytes;
>> +	len = (temp + 2) * i;
>> +
>> +	return meson_chip->data_buf + len;
>> +}
>> +
>> +static void meson_nfc_get_data_oob(struct nand_chip *nand,
>> +				   u8 *buf, u8 *oobbuf)
>> +{
>> +	int i, oob_len = 0;
>> +	u8 *dsrc, *osrc;
>> +
>> +	oob_len = nand->ecc.bytes + 2;
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		if (buf) {
>> +			dsrc = meson_nfc_data_ptr(nand, i);
>> +			memcpy(buf, dsrc, nand->ecc.size);
>> +			buf += nand->ecc.size;
>> +		}
>> +		osrc = meson_nfc_oob_ptr(nand, i);
>> +		memcpy(oobbuf, osrc, oob_len);
>> +		oobbuf += oob_len;
>> +	}
>> +}
>> +
>> +static void meson_nfc_set_data_oob(struct nand_chip *nand,
>> +				   const u8 *buf, u8 *oobbuf)
>> +{
>> +	int i, oob_len = 0;
>> +	u8 *dsrc, *osrc;
>> +
>> +	oob_len = nand->ecc.bytes + 2;
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		if (buf) {
>> +			dsrc = meson_nfc_data_ptr(nand, i);
>> +			memcpy(dsrc, buf, nand->ecc.size);
>> +			buf += nand->ecc.size;
>> +		}
>> +		osrc = meson_nfc_oob_ptr(nand, i);
>> +		memcpy(osrc, oobbuf, oob_len);
>> +		oobbuf += oob_len;
>> +	}
>> +}
>> +
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +{
>> +	u32 cmd, cfg;
>> +	int ret = 0;
>> +
>> +	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +	cfg = readl(nfc->reg_base + NFC_REG_CFG);
>> +	cfg |= (1 << 21);
>> +	writel(cfg, nfc->reg_base + NFC_REG_CFG);
>> +
>> +	init_completion(&nfc->completion);
>> +
>> +	/* use the max erase time as the maximum clock for waiting R/B */
>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> +		| nfc->param.chip_select | nfc->timing.tbers_max;
> 
> Nit: I think the '|' should be on the previous line.
> 
ok
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +	ret = wait_for_completion_timeout(&nfc->completion,
>> +					  msecs_to_jiffies(timeout_ms));
>> +	if (ret == 0)
>> +		ret = -1;
>> +
>> +	return ret;
>> +}
>> +
>> +static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	__le64 *info;
>> +	int i, count;
>> +
>> +	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +		info = &meson_chip->info_buf[i];
>> +		*info |= oob_buf[count];
>> +		*info |= oob_buf[count + 1] << 8;
>> +	}
>> +}
>> +
>> +static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	__le64 *info;
>> +	int i, count;
>> +
>> +	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +		info = &meson_chip->info_buf[i];
>> +		oob_buf[count] = *info;
>> +		oob_buf[count + 1] = *info >> 8;
>> +	}
>> +}
>> +
>> +static int meson_nfc_ecc_correct(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	__le64 *info;
>> +	u32 bitflips = 0, i;
>> +	int scramble;
>> +	u8 zero_cnt;
>> +
>> +	scramble = (nand->options & NAND_NEED_SCRAMBLING) ? 1 : 0;
>> +
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		info = &meson_chip->info_buf[i];
>> +		if (ECC_ERR_CNT(*info) == 0x3f) {
>> +			zero_cnt = ECC_ZERO_CNT(*info);
>> +			if (scramble && zero_cnt < nand->ecc.strength)
>> +				return ECC_CHECK_RETURN_FF;
> 
> This and what you do later with ECC_CHECK_RETURN_FF is pretty unclear
> to me.
> 
it means a blank page here and later we will set data_buf and oob_buf 
all 0xff befor return back.
>> +			mtd->ecc_stats.failed++;
>> +			continue;
>> +		}
>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>> +	}
> 
> Are you sure you handle correctly empty pages with bf?
> 
if scramble is enable, i would say yes here.
when scramble is disabled, i am considering how to use the helper 
nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
bytes which is caculated by ecc engine.by the way, nfc dma doesn't send 
out the ecc parity bytes.
so i would suggest using scramble.

>> +
>> +	return bitflips;
>> +}
>> +
>> +static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, u8 *databuf,
>> +				      int datalen, u8 *infobuf, int infolen,
>> +				      enum dma_data_direction dir)
>> +{
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	u32 cmd;
>> +	int ret = 0;
>> +
>> +	nfc->daddr = dma_map_single(nfc->dev, (void *)databuf, datalen, dir);
>> +	ret = dma_mapping_error(nfc->dev, nfc->daddr);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "dma mapping error\n");
>> +		return ret;
>> +	}
>> +	cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +	cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +	if (infobuf) {
>> +		nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir);
>> +		ret = dma_mapping_error(nfc->dev, nfc->iaddr);
>> +		if (ret) {
>> +			dev_err(nfc->dev, "dma mapping error\n");
>> +			dma_unmap_single(nfc->dev,
>> +					 nfc->daddr, datalen, dir);
>> +			return ret;
>> +		}
>> +		cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
>> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +		cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
>> +		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
>> +					 int infolen, int datalen,
>> +					 enum dma_data_direction dir)
>> +{
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +
>> +	dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir);
>> +	if (infolen)
>> +		dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir);
>> +}
>> +
>> +static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>> +{
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	int ret = 0;
>> +	u32 cmd;
>> +	u8 *info;
>> +
>> +	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
>> +	ret = meson_nfc_dma_buffer_setup(nand, buf, len, info,
>> +					 PER_INFO_BYTE, DMA_FROM_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	cmd = NFC_CMD_N2M | (len & 0x3fff);
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, 1000);
>> +	meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE);
>> +	kfree(info);
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
>> +{
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	int ret = 0;
>> +	u32 cmd;
>> +
>> +	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
>> +					 0, DMA_TO_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	cmd = NFC_CMD_M2N | (len & 0x3fff);
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, 1000);
>> +	meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE);
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>> +						int page, bool in)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	const struct nand_sdr_timings *sdr =
>> +		nand_get_sdr_timings(&nand->data_interface);
>> +	u32 *addrs = nfc->cmdfifo.rw.addrs;
>> +	u32 cs = nfc->param.chip_select;
>> +	u32 cmd0, cmd_num, row_start;
>> +	int ret = 0, i;
>> +
>> +	cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int);
>> +
>> +	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
>> +	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
>> +
>> +	addrs[0] = cs | NFC_CMD_ALE | 0;
>> +	if (mtd->writesize <= 512) {
>> +		cmd_num--;
>> +		row_start = 1;
>> +	} else {
>> +		addrs[1] = cs | NFC_CMD_ALE | 0;
>> +		row_start = 2;
>> +	}
>> +
>> +	addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0);
>> +	addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1);
>> +
>> +	if (nand->options & NAND_ROW_ADDR_3)
>> +		addrs[row_start + 2] =
>> +			cs | NFC_CMD_ALE | ROW_ADDER(page, 2);
>> +	else
>> +		cmd_num--;
>> +
>> +	/* subtract cmd1 */
>> +	cmd_num--;
>> +
>> +	for (i = 0; i < cmd_num; i++)
>> +		writel_relaxed(nfc->cmdfifo.cmd[i],
>> +			       nfc->reg_base + NFC_REG_CMD);
>> +
>> +	if (in) {
>> +		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>> +		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +	} else {
>> +		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_write_page_sub(struct nand_chip *nand,
>> +				    int page, int raw)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	const struct nand_sdr_timings *sdr =
>> +		nand_get_sdr_timings(&nand->data_interface);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	int data_len, info_len;
>> +	u32 cmd;
>> +	int ret;
>> +
>> +	data_len =  mtd->writesize + mtd->oobsize;
>> +	info_len = nand->ecc.steps * PER_INFO_BYTE;
>> +
>> +	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>> +					 data_len, (u8 *)meson_chip->info_buf,
>> +					 info_len, DMA_TO_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	meson_nfc_cmd_seed(nfc, page);
>> +	meson_nfc_cmd_access(nand, raw, DIRWRITE);
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +
>> +	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>> +				    int oob_required, int page)
>> +{
>> +	u8 *oob_buf = nand->oob_poi;
>> +
>> +	meson_nfc_set_data_oob(nand, buf, oob_buf);
>> +
>> +	return meson_nfc_write_page_sub(nand, page, 1);
>> +}
>> +
>> +static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>> +				      const u8 *buf, int oob_required, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	u8 *oob_buf = nand->oob_poi;
>> +
>> +	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>> +	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>> +	meson_nfc_set_user_byte(nand, oob_buf);
>> +
>> +	return meson_nfc_write_page_sub(nand, page, 0);
>> +}
>> +
>> +static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>> +					    struct nand_chip *nand, int raw)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	__le64 *info;
>> +	u32 neccpages;
>> +	int ret;
>> +
>> +	neccpages = raw ? 1 : nand->ecc.steps;
>> +	info = &meson_chip->info_buf[neccpages - 1];
>> +	do {
>> +		usleep_range(10, 15);
>> +		/* info is updated by nfc dma engine*/
>> +		smp_rmb();
>> +		ret = *info & ECC_COMPLETE;
>> +	} while (!ret);
>> +}
>> +
>> +static int meson_nfc_read_page_sub(struct nand_chip *nand,
>> +				   int page, int raw)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	int data_len, info_len;
>> +	int ret;
>> +
>> +	data_len =  mtd->writesize + mtd->oobsize;
>> +	info_len = nand->ecc.steps * PER_INFO_BYTE;
>> +
>> +	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>> +					 data_len, (u8 *)meson_chip->info_buf,
>> +					 info_len, DMA_FROM_DEVICE);
>> +	if (ret)
>> +		return ret;
>> +
>> +	meson_nfc_cmd_seed(nfc, page);
>> +	meson_nfc_cmd_access(nand, raw, DIRREAD);
>> +	ret = meson_nfc_wait_dma_finish(nfc);
>> +	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>> +
>> +	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>> +				   int oob_required, int page)
>> +{
>> +	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>> +
>> +	ret = meson_nfc_read_page_sub(nand, page, 1);
>> +	if (ret)
>> +		return ret;
>> +
>> +	meson_nfc_get_data_oob(nand, buf, oob_buf);
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>> +				     int oob_required, int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>> +
>> +	ret = meson_nfc_read_page_sub(nand, page, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	meson_nfc_get_user_byte(nand, oob_buf);
>> +
>> +	ret = meson_nfc_ecc_correct(nand);
>> +	if (ret == ECC_CHECK_RETURN_FF) {
>> +		if (buf)
>> +			memset(buf, 0xff, mtd->writesize);
>> +
>> +		memset(oob_buf, 0xff, mtd->oobsize);
>> +		return 0;
>> +	}
>> +
>> +	if (buf && buf != meson_chip->data_buf)
>> +		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>> +
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>> +{
>> +	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>> +}
>> +
>> +static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>> +{
>> +	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>> +}
>> +
>> +void *
>> +meson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr)
>> +{
>> +	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR))
>> +		return NULL;
>> +	if (virt_addr_valid(instr->ctx.data.buf.in) &&
>> +	    !object_is_on_stack(instr->ctx.data.buf.in))
>> +		return instr->ctx.data.buf.in;
>> +
>> +	return kzalloc(instr->ctx.data.len, GFP_KERNEL);
> 
> I think allocating memory and using it without ever testing the
> allocation succeeded is wrong. You do that in many places. I would like
> to see allocations properly handled.
> 
ok, i will fix it.
>> +}
>> +
>> +void
>> +meson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr,
>> +				     void *buf)
>> +{
>> +	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) ||
>> +	    WARN_ON(!buf))
>> +		return;
>> +	if (buf == instr->ctx.data.buf.in)
>> +		return;
>> +
>> +	memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len);
>> +	kfree(buf);
>> +}
>> +
>> +const void *
>> +meson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
>> +{
>> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
>> +		return NULL;
>> +
>> +	if (virt_addr_valid(instr->ctx.data.buf.out) &&
>> +	    !object_is_on_stack(instr->ctx.data.buf.out))
> 
> Can you please create helpers for that? I guess it will help removing
> these checks once the core will have a DMA-safe approach.
> 
I will use below definition:
#define BUFFER_IS_DMA_SAFE(x)	\
	(virt_addr_valid((x)) && (!object_is_on_stack((x))))

Is it ok?

>> +		return instr->ctx.data.buf.out;
>> +
>> +	return kmemdup(instr->ctx.data.buf.out,
>> +		       instr->ctx.data.len, GFP_KERNEL);
>> +}
>> +
>> +void
>> +meson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr,
>> +				      const void *buf)
>> +{
>> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) ||
>> +	    WARN_ON(!buf))
>> +		return;
>> +
>> +	if (buf != instr->ctx.data.buf.out)
>> +		kfree(buf);
>> +}
>> +
>> +static int meson_nfc_exec_op(struct nand_chip *nand,
>> +			     const struct nand_operation *op, bool check_only)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	const struct nand_op_instr *instr = NULL;
>> +	void *buf;
>> +	u32 op_id, delay_idle, cmd;
>> +	int i;
>> +
>> +	for (op_id = 0; op_id < op->ninstrs; op_id++) {
>> +		instr = &op->instrs[op_id];
>> +		delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns),
>> +					  meson_chip->level1_divider *
>> +					  NFC_CLK_CYCLE);
>> +		switch (instr->type) {
>> +		case NAND_OP_CMD_INSTR:
>> +			cmd = nfc->param.chip_select | NFC_CMD_CLE;
>> +			cmd |= instr->ctx.cmd.opcode & 0xff;
>> +			writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +			meson_nfc_cmd_idle(nfc, delay_idle);
>> +			break;
>> +
>> +		case NAND_OP_ADDR_INSTR:
>> +			for (i = 0; i < instr->ctx.addr.naddrs; i++) {
>> +				cmd = nfc->param.chip_select | NFC_CMD_ALE;
>> +				cmd |= instr->ctx.addr.addrs[i] & 0xff;
>> +				writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +			}
>> +			meson_nfc_cmd_idle(nfc, delay_idle);
>> +			break;
>> +
>> +		case NAND_OP_DATA_IN_INSTR:
>> +			buf = meson_nand_op_get_dma_safe_input_buf(instr);
>> +			meson_nfc_read_buf(nand, buf,
>> +					   instr->ctx.data.len);
>> +			meson_nand_op_put_dma_safe_input_buf(instr, buf);
>> +			break;
>> +
>> +		case NAND_OP_DATA_OUT_INSTR:
>> +			buf =
>> +			(void *)meson_nand_op_get_dma_safe_output_buf(instr);
>> +			meson_nfc_write_buf(nand, buf,
>> +					    instr->ctx.data.len);
>> +			meson_nand_op_put_dma_safe_output_buf(instr, buf);
>> +			break;
>> +
>> +		case NAND_OP_WAITRDY_INSTR:
>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +			if (instr->delay_ns)
>> +				meson_nfc_cmd_idle(nfc, delay_idle);
>> +			break;
>> +		}
>> +	}
>> +	meson_nfc_wait_cmd_finish(nfc, 1000);
>> +	return 0;
>> +}
>> +
>> +static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>> +			       struct mtd_oob_region *oobregion)
>> +{
>> +	struct nand_chip *nand = mtd_to_nand(mtd);
>> +
>> +	if (section >= nand->ecc.steps)
>> +		return -ERANGE;
>> +
>> +	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>> +	oobregion->length = nand->ecc.bytes;
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>> +				struct mtd_oob_region *oobregion)
>> +{
>> +	struct nand_chip *nand = mtd_to_nand(mtd);
>> +
>> +	if (section >= nand->ecc.steps)
>> +		return -ERANGE;
>> +
>> +	oobregion->offset = section * (2 + nand->ecc.bytes);
>> +	oobregion->length = 2;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
>> +	.ecc = meson_ooblayout_ecc,
>> +	.free = meson_ooblayout_free,
>> +};
>> +
>> +static int meson_nfc_clk_init(struct meson_nfc *nfc)
>> +{
>> +	int ret;
>> +
>> +	/* request core clock */
>> +	nfc->core_clk = devm_clk_get(nfc->dev, "core");
>> +	if (IS_ERR(nfc->core_clk)) {
>> +		dev_err(nfc->dev, "failed to get core clk\n");
>> +		return PTR_ERR(nfc->core_clk);
>> +	}
>> +
>> +	nfc->device_clk = devm_clk_get(nfc->dev, "device");
>> +	if (IS_ERR(nfc->device_clk)) {
>> +		dev_err(nfc->dev, "failed to get device clk\n");
>> +		return PTR_ERR(nfc->device_clk);
>> +	}
>> +
>> +	nfc->phase_tx = devm_clk_get(nfc->dev, "tx");
>> +	if (IS_ERR(nfc->phase_tx)) {
>> +		dev_err(nfc->dev, "failed to get tx clk\n");
>> +		return PTR_ERR(nfc->phase_tx);
>> +	}
>> +
>> +	nfc->phase_rx = devm_clk_get(nfc->dev, "rx");
>> +	if (IS_ERR(nfc->phase_rx)) {
>> +		dev_err(nfc->dev, "failed to get rx clk\n");
>> +		return PTR_ERR(nfc->phase_rx);
>> +	}
>> +
>> +	/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
>> +	regmap_update_bits(nfc->reg_clk,
>> +			   0, CLK_SELECT_NAND, CLK_SELECT_NAND);
>> +
>> +	ret = clk_prepare_enable(nfc->core_clk);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "failed to enable core clk\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = clk_prepare_enable(nfc->device_clk);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "failed to enable device clk\n");
>> +		clk_disable_unprepare(nfc->core_clk);
>> +		return ret;
>> +	}
>> +
>> +	ret = clk_prepare_enable(nfc->phase_tx);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "failed to enable tx clk\n");
>> +		clk_disable_unprepare(nfc->core_clk);
>> +		clk_disable_unprepare(nfc->device_clk);
>> +		return ret;
>> +	}
>> +
>> +	ret = clk_prepare_enable(nfc->phase_rx);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "failed to enable rx clk\n");
>> +		clk_disable_unprepare(nfc->core_clk);
>> +		clk_disable_unprepare(nfc->device_clk);
>> +		clk_disable_unprepare(nfc->phase_tx);
> 
> This error case is a good candidate to a goto statement.
> 
ok
>> +		return ret;
>> +	}
>> +
>> +	ret = clk_set_rate(nfc->device_clk, 24000000);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static void meson_nfc_disable_clk(struct meson_nfc *nfc)
>> +{
>> +	clk_disable_unprepare(nfc->phase_rx);
>> +	clk_disable_unprepare(nfc->phase_tx);
>> +	clk_disable_unprepare(nfc->device_clk);
>> +	clk_disable_unprepare(nfc->core_clk);
>> +}
>> +
>> +static void meson_nfc_free_buffer(struct nand_chip *nand)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +
>> +	kfree(meson_chip->info_buf);
>> +	kfree(meson_chip->data_buf);
>> +}
>> +
>> +static int meson_chip_buffer_init(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	u32 page_bytes, info_bytes, nsectors;
>> +
>> +	nsectors = mtd->writesize / nand->ecc.size;
>> +
>> +	page_bytes =  mtd->writesize + mtd->oobsize;
>> +	info_bytes = nsectors * PER_INFO_BYTE;
>> +
>> +	meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL);
>> +	if (!meson_chip->data_buf)
>> +		return -ENOMEM;
>> +
>> +	meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL);
>> +	if (!meson_chip->info_buf) {
>> +		kfree(meson_chip->data_buf);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static
>> +int meson_nfc_setup_data_interface(struct nand_chip *nand, int csline,
>> +				   const struct nand_data_interface *conf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	const struct nand_sdr_timings *timings;
>> +	u32 div, bt_min, bt_max, tbers_clocks;
>> +
>> +	timings = nand_get_sdr_timings(conf);
>> +	if (IS_ERR(timings))
>> +		return -ENOTSUPP;
>> +
>> +	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
>> +		return 0;
>> +
>> +	div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE);
>> +	bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div;
>> +	bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min +
>> +		  timings->tRC_min / 2) / div;
>> +
>> +	meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max),
>> +				       div * NFC_CLK_CYCLE);
>> +	meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min),
>> +					div * NFC_CLK_CYCLE);
>> +	tbers_clocks = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tBERS_max),
>> +				    div * NFC_CLK_CYCLE);
>> +	meson_chip->tbers_max = ilog2(tbers_clocks);
>> +	if (!is_power_of_2(tbers_clocks))
>> +		meson_chip->tbers_max++;
>> +
>> +	bt_min = DIV_ROUND_UP(bt_min, 1000);
>> +	bt_max = DIV_ROUND_UP(bt_max, 1000);
>> +
>> +	if (bt_max < bt_min)
>> +		return -EINVAL;
>> +
>> +	meson_chip->level1_divider = div;
>> +	meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider;
>> +	meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1;
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_nand_bch_mode(struct nand_chip *nand)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct meson_nand_ecc meson_ecc[] = {
>> +		MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8),
>> +		MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24),
>> +		MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30),
>> +		MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40),
>> +		MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50),
>> +		MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60),
>> +	};
> 
> Maybe this array could be static?
> 
ok
>> +	int i;
>> +
>> +	if (nand->ecc.strength > 60 || nand->ecc.strength < 8)
>> +		return -EINVAL;
>> +
>> +	for (i = 0; i < sizeof(meson_ecc); i++) {
>> +		if (meson_ecc[i].strength == nand->ecc.strength) {
>> +			meson_chip->bch_mode = meson_ecc[i].bch;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static int meson_nand_attach_chip(struct nand_chip *nand)
>> +{
>> +	struct meson_nfc *nfc = nand_get_controller_data(nand);
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	int nsectors = mtd->writesize / 1024;
>> +	int ret;
>> +
>> +	if (!mtd->name) {
>> +		mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
>> +					   "%s:nand%d",
>> +					   dev_name(nfc->dev),
>> +					   meson_chip->sels[0]);
>> +		if (!mtd->name)
>> +			return -ENOMEM;
>> +	}
>> +
>> +	if (nand->bbt_options & NAND_BBT_USE_FLASH)
>> +		nand->bbt_options |= NAND_BBT_NO_OOB;
>> +
>> +	nand->options |= NAND_NO_SUBPAGE_WRITE;
>> +
>> +	ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps,
>> +				   mtd->oobsize - 2 * nsectors);
>> +	if (ret) {
>> +		dev_err(nfc->dev, "failed to ecc init\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = meson_nand_bch_mode(nand);
>> +	if (ret)
>> +		return -EINVAL;
>> +
>> +	nand->ecc.mode = NAND_ECC_HW;
>> +	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>> +	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>> +	nand->ecc.write_oob_raw = nand_write_oob_std;
>> +	nand->ecc.write_oob = nand_write_oob_std;
>> +
>> +	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>> +	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>> +	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>> +	nand->ecc.read_oob = meson_nfc_read_oob;
>> +
>> +	if (nand->options & NAND_BUSWIDTH_16) {
>> +		dev_err(nfc->dev, "16bits buswidth not supported");
>> +		return -EINVAL;
>> +	}
>> +	meson_chip_buffer_init(nand);
>> +	if (ret)
>> +		return -ENOMEM;
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct nand_controller_ops meson_nand_controller_ops = {
>> +	.attach_chip = meson_nand_attach_chip,
> 
> Don't you need a ->detach_chip hook to free data_buf/info_buf
> buffers?
> 
ok, i will add it.
>> +};
>> +
>> +static int
>> +meson_nfc_nand_chip_init(struct device *dev,
>> +			 struct meson_nfc *nfc, struct device_node *np)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip;
>> +	struct nand_chip *nand;
>> +	struct mtd_info *mtd;
>> +	int ret, i;
>> +	u32 tmp, nsels;
>> +
>> +	if (!of_get_property(np, "reg", &nsels))
>> +		return -EINVAL;
>> +
>> +	nsels /= sizeof(u32);
>> +	if (!nsels || nsels > MAX_CE_NUM) {
>> +		dev_err(dev, "invalid reg property size\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	meson_chip = devm_kzalloc(dev,
>> +				  sizeof(*meson_chip) + (nsels * sizeof(u8)),
>> +				  GFP_KERNEL);
>> +	if (!meson_chip)
>> +		return -ENOMEM;
>> +
>> +	meson_chip->nsels = nsels;
>> +
>> +	for (i = 0; i < nsels; i++) {
>> +		ret = of_property_read_u32_index(np, "reg", i, &tmp);
>> +		if (ret) {
>> +			dev_err(dev, "could not retrieve reg property: %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
>> +			dev_err(dev, "CS %d already assigned\n", tmp);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	nand = &meson_chip->nand;
>> +	nand->controller = &nfc->controller;
>> +	nand->controller->ops = &meson_nand_controller_ops;
>> +	nand_set_flash_node(nand, np);
>> +	nand_set_controller_data(nand, nfc);
>> +
>> +	nand->options |= NAND_USE_BOUNCE_BUFFER;
>> +	nand->select_chip = meson_nfc_select_chip;
>> +	nand->exec_op = meson_nfc_exec_op;
>> +	nand->setup_data_interface = meson_nfc_setup_data_interface;
>> +	mtd = nand_to_mtd(nand);
>> +	mtd->owner = THIS_MODULE;
>> +	mtd->dev.parent = dev;
>> +
>> +	ret = nand_scan(nand, nsels);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = mtd_device_register(mtd, NULL, 0);
>> +	if (ret) {
>> +		dev_err(dev, "failed to register mtd device: %d\n", ret);
>> +		nand_cleanup(nand);
>> +		return ret;
>> +	}
>> +
>> +	list_add_tail(&meson_chip->node, &nfc->chips);
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip;
>> +	struct mtd_info *mtd;
>> +	int ret;
>> +
>> +	while (!list_empty(&nfc->chips)) {
>> +		meson_chip = list_first_entry(&nfc->chips,
>> +					      struct meson_nfc_nand_chip, node);
>> +		mtd = nand_to_mtd(&meson_chip->nand);
>> +		ret = mtd_device_unregister(mtd);
>> +		if (ret)
>> +			return ret;
>> +
>> +		meson_nfc_free_buffer(&meson_chip->nand);
>> +		nand_cleanup(&meson_chip->nand);
>> +		list_del(&meson_chip->node);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_nfc_nand_chips_init(struct device *dev,
>> +				     struct meson_nfc *nfc)
>> +{
>> +	struct device_node *np = dev->of_node;
>> +	struct device_node *nand_np;
>> +	int ret;
>> +
>> +	for_each_child_of_node(np, nand_np) {
>> +		ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
>> +		if (ret) {
>> +			meson_nfc_nand_chip_cleanup(nfc);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static irqreturn_t meson_nfc_irq(int irq, void *id)
>> +{
>> +	struct meson_nfc *nfc = id;
>> +	u32 cfg;
>> +
>> +	cfg = readl(nfc->reg_base + NFC_REG_CFG);
>> +	if (!(cfg & NFC_RB_IRQ_EN))
>> +		return IRQ_NONE;
>> +
>> +	cfg &= ~(NFC_RB_IRQ_EN);
>> +	writel(cfg, nfc->reg_base + NFC_REG_CFG);
>> +
>> +	complete(&nfc->completion);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static const struct meson_nfc_data meson_gxl_data = {
>> +	.ecc_caps = &meson_gxl_ecc_caps,
>> +};
>> +
>> +static const struct meson_nfc_data meson_axg_data = {
>> +	.ecc_caps = &meson_axg_ecc_caps,
>> +};
>> +
>> +static const struct of_device_id meson_nfc_id_table[] = {
>> +	{
>> +		.compatible = "amlogic,meson-gxl-nfc",
>> +		.data = &meson_gxl_data,
>> +	}, {
>> +		.compatible = "amlogic,meson-axg-nfc",
>> +		.data = &meson_axg_data,
>> +	},
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
>> +
>> +static int meson_nfc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct meson_nfc *nfc;
>> +	struct resource *res;
>> +	int ret, irq;
>> +
>> +	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
>> +	if (!nfc)
>> +		return -ENOMEM;
>> +
>> +	nfc->data = of_device_get_match_data(&pdev->dev);
>> +	if (!nfc->data)
>> +		return -ENODEV;
>> +
>> +	nand_controller_init(&nfc->controller);
>> +	INIT_LIST_HEAD(&nfc->chips);
>> +
>> +	nfc->dev = dev;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	nfc->reg_base = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(nfc->reg_base))
>> +		return PTR_ERR(nfc->reg_base);
>> +
>> +	nfc->reg_clk =
>> +		syscon_regmap_lookup_by_phandle(dev->of_node,
>> +						"amlogic,mmc-syscon");
>> +	if (IS_ERR(nfc->reg_clk)) {
>> +		dev_err(dev, "Failed to lookup clock base\n");
>> +		return PTR_ERR(nfc->reg_clk);
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(dev, "no nfi irq resource\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = meson_nfc_clk_init(nfc);
>> +	if (ret) {
>> +		dev_err(dev, "failed to initialize nand clk\n");
>> +		goto err;
> 
> Useless goto, a return would be enough.
> 
ok
>> +	}
>> +
>> +	writel(0, nfc->reg_base + NFC_REG_CFG);
>> +	ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
>> +	if (ret) {
>> +		dev_err(dev, "failed to request nfi irq\n");
>> +		ret = -EINVAL;
>> +		goto err_clk;
>> +	}
>> +
>> +	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
>> +	if (ret) {
>> +		dev_err(dev, "failed to set dma mask\n");
> 
> Nit: I prefer when acronyms are upper case in plain English (DMA, NAND,
> IRQ, etc).
> 
ok, i will fix it.
>> +		goto err_clk;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, nfc);
>> +
>> +	ret = meson_nfc_nand_chips_init(dev, nfc);
>> +	if (ret) {
>> +		dev_err(dev, "failed to init nand chips\n");
>> +		goto err_clk;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_clk:
>> +	meson_nfc_disable_clk(nfc);
>> +err:
> 
> This goto can be removed.
> 
ok
>> +	return ret;
>> +}
>> +
>> +static int meson_nfc_remove(struct platform_device *pdev)
>> +{
>> +	struct meson_nfc *nfc = platform_get_drvdata(pdev);
>> +	int ret;
>> +
>> +	ret = meson_nfc_nand_chip_cleanup(nfc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	meson_nfc_disable_clk(nfc);
>> +
>> +	platform_set_drvdata(pdev, NULL);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver meson_nfc_driver = {
>> +	.probe  = meson_nfc_probe,
>> +	.remove = meson_nfc_remove,
>> +	.driver = {
>> +		.name  = "meson-nand",
>> +		.of_match_table = meson_nfc_id_table,
>> +	},
>> +};
>> +module_platform_driver(meson_nfc_driver);
>> +
>> +MODULE_LICENSE("Dual MIT/GPL");
>> +MODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
>> +MODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");
> 
> 
> 
> 
> Thanks,
> Miquèl
> 
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-10 11:23     ` Liang Yang
@ 2018-12-10 11:38       ` Boris Brezillon
  2018-12-10 12:12         ` Liang Yang
  0 siblings, 1 reply; 19+ messages in thread
From: Boris Brezillon @ 2018-12-10 11:38 UTC (permalink / raw)
  To: Liang Yang
  Cc: Miquel Raynal, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

On Mon, 10 Dec 2018 19:23:46 +0800
Liang Yang <liang.yang@amlogic.com> wrote:

> >> +			mtd->ecc_stats.failed++;
> >> +			continue;
> >> +		}
> >> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> >> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> >> +	}  
> > 
> > Are you sure you handle correctly empty pages with bf?
> >   
> if scramble is enable, i would say yes here.
> when scramble is disabled, i am considering how to use the helper 
> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send 
> out the ecc parity bytes.

Even if the ECC engine is disabled?

> so i would suggest using scramble.
> 

No, please don't force people to use the scrambler.

> >> +
> >> +const void *
> >> +meson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
> >> +{
> >> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
> >> +		return NULL;
> >> +
> >> +	if (virt_addr_valid(instr->ctx.data.buf.out) &&
> >> +	    !object_is_on_stack(instr->ctx.data.buf.out))  
> > 
> > Can you please create helpers for that? I guess it will help removing
> > these checks once the core will have a DMA-safe approach.
> >   
> I will use below definition:
> #define BUFFER_IS_DMA_SAFE(x)	\
> 	(virt_addr_valid((x)) && (!object_is_on_stack((x))))
> 
> Is it ok?

Please define a function, not a macro.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-10 11:38       ` Boris Brezillon
@ 2018-12-10 12:12         ` Liang Yang
  2018-12-10 14:50           ` Miquel Raynal
  0 siblings, 1 reply; 19+ messages in thread
From: Liang Yang @ 2018-12-10 12:12 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Miquel Raynal, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel



On 2018/12/10 19:38, Boris Brezillon wrote:
> On Mon, 10 Dec 2018 19:23:46 +0800
> Liang Yang <liang.yang@amlogic.com> wrote:
> 
>>>> +			mtd->ecc_stats.failed++;
>>>> +			continue;
>>>> +		}
>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>>>> +	}
>>>
>>> Are you sure you handle correctly empty pages with bf?
>>>    
>> if scramble is enable, i would say yes here.
>> when scramble is disabled, i am considering how to use the helper
>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
>> out the ecc parity bytes.
> 
> Even if the ECC engine is disabled?
> 
No.
When ECC engine is disabled, it can read the ecc parity bytes ; but 
there is another problem that i need to consider how code struct looks 
better when reading error with ecc opened and then try to raw read.
Is there a good idea?

>> so i would suggest using scramble.
>>
> 
> No, please don't force people to use the scrambler.
> 
>>>> +
>>>> +const void *
>>>> +meson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
>>>> +{
>>>> +	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
>>>> +		return NULL;
>>>> +
>>>> +	if (virt_addr_valid(instr->ctx.data.buf.out) &&
>>>> +	    !object_is_on_stack(instr->ctx.data.buf.out))
>>>
>>> Can you please create helpers for that? I guess it will help removing
>>> these checks once the core will have a DMA-safe approach.
>>>    
>> I will use below definition:
>> #define BUFFER_IS_DMA_SAFE(x)	\
>> 	(virt_addr_valid((x)) && (!object_is_on_stack((x))))
>>
>> Is it ok?
> 
> Please define a function, not a macro.
> ok
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-10 12:12         ` Liang Yang
@ 2018-12-10 14:50           ` Miquel Raynal
  2018-12-11  1:56             ` Liang Yang
  0 siblings, 1 reply; 19+ messages in thread
From: Miquel Raynal @ 2018-12-10 14:50 UTC (permalink / raw)
  To: Liang Yang
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Liang,

Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
+0800:

> On 2018/12/10 19:38, Boris Brezillon wrote:
> > On Mon, 10 Dec 2018 19:23:46 +0800
> > Liang Yang <liang.yang@amlogic.com> wrote:
> >   
> >>>> +			mtd->ecc_stats.failed++;
> >>>> +			continue;
> >>>> +		}
> >>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> >>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> >>>> +	}  
> >>>
> >>> Are you sure you handle correctly empty pages with bf?  
> >>>    >> if scramble is enable, i would say yes here.  
> >> when scramble is disabled, i am considering how to use the helper
> >> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
> >> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
> >> out the ecc parity bytes.  
> > 
> > Even if the ECC engine is disabled?
> >   
> No.
> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
> Is there a good idea?

When reading with ECC enabled, in case of uncorrectable error you
must re-read without ECC, then check if the page is empty or not with
the core helpers (nand_check_erased_*()).

Is this what you meant?

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-10 14:50           ` Miquel Raynal
@ 2018-12-11  1:56             ` Liang Yang
  2018-12-11  7:54               ` Miquel Raynal
  2018-12-11  8:39               ` Boris Brezillon
  0 siblings, 2 replies; 19+ messages in thread
From: Liang Yang @ 2018-12-11  1:56 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Miquel,

On 2018/12/10 22:50, Miquel Raynal wrote:
> Hi Liang,
> 
> Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
> +0800:
> 
>> On 2018/12/10 19:38, Boris Brezillon wrote:
>>> On Mon, 10 Dec 2018 19:23:46 +0800
>>> Liang Yang <liang.yang@amlogic.com> wrote:
>>>    
>>>>>> +			mtd->ecc_stats.failed++;
>>>>>> +			continue;
>>>>>> +		}
>>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>>>>>> +	}
>>>>>
>>>>> Are you sure you handle correctly empty pages with bf?
>>>>>     >> if scramble is enable, i would say yes here.
>>>> when scramble is disabled, i am considering how to use the helper
>>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
>>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
>>>> out the ecc parity bytes.
>>>
>>> Even if the ECC engine is disabled?
>>>    
>> No.
>> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
>> Is there a good idea?
> 
> When reading with ECC enabled, in case of uncorrectable error you
> must re-read without ECC, then check if the page is empty or not with
> the core helpers (nand_check_erased_*()).
> 
> Is this what you meant?
> 
yes. when uncorrectable ECC error, i need firstly read out the ECC bytes 
without ECC engine and then use the helper nand_check_erased_ecc_chunk 
to check if blank page.
Of course, the precondition is without scrambler, or the bland page can 
be detected by meson NFC.

> Thanks,
> Miquèl
> 
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  1:56             ` Liang Yang
@ 2018-12-11  7:54               ` Miquel Raynal
  2018-12-11  8:36                 ` Liang Yang
  2018-12-11  8:39               ` Boris Brezillon
  1 sibling, 1 reply; 19+ messages in thread
From: Miquel Raynal @ 2018-12-11  7:54 UTC (permalink / raw)
  To: Liang Yang
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Liang,

Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 09:56:25
+0800:

> Hi Miquel,
> 
> On 2018/12/10 22:50, Miquel Raynal wrote:
> > Hi Liang,
> > 
> > Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
> > +0800:
> >   
> >> On 2018/12/10 19:38, Boris Brezillon wrote:  
> >>> On Mon, 10 Dec 2018 19:23:46 +0800
> >>> Liang Yang <liang.yang@amlogic.com> wrote:  
> >>>    >>>>>> +			mtd->ecc_stats.failed++;  
> >>>>>> +			continue;
> >>>>>> +		}
> >>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> >>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> >>>>>> +	}  
> >>>>>
> >>>>> Are you sure you handle correctly empty pages with bf?  
> >>>>>     >> if scramble is enable, i would say yes here.  
> >>>> when scramble is disabled, i am considering how to use the helper
> >>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
> >>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
> >>>> out the ecc parity bytes.  
> >>>
> >>> Even if the ECC engine is disabled?  
> >>>    >> No.  
> >> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
> >> Is there a good idea?  
> > 
> > When reading with ECC enabled, in case of uncorrectable error you
> > must re-read without ECC, then check if the page is empty or not with
> > the core helpers (nand_check_erased_*()).
> > 
> > Is this what you meant?
> >   
> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes without ECC engine and then use the helper nand_check_erased_ecc_chunk to check if blank page.
> Of course, the precondition is without scrambler, or the bland page can be detected by meson NFC.

A suppose you meant "blank page"? If yes, then you don't need the
helper to check for only-0xFF pages. If the controller tells you if the
page was blank, then just check for that bit.


Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  7:54               ` Miquel Raynal
@ 2018-12-11  8:36                 ` Liang Yang
  2018-12-11  9:07                   ` Miquel Raynal
  0 siblings, 1 reply; 19+ messages in thread
From: Liang Yang @ 2018-12-11  8:36 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Miquel,

Thanks for your quickly reply.

On 2018/12/11 15:54, Miquel Raynal wrote:
> Hi Liang,
> 
> Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 09:56:25
> +0800:
> 
>> Hi Miquel,
>>
>> On 2018/12/10 22:50, Miquel Raynal wrote:
>>> Hi Liang,
>>>
>>> Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
>>> +0800:
>>>    
>>>> On 2018/12/10 19:38, Boris Brezillon wrote:
>>>>> On Mon, 10 Dec 2018 19:23:46 +0800
>>>>> Liang Yang <liang.yang@amlogic.com> wrote:
>>>>>     >>>>>> +			mtd->ecc_stats.failed++;
>>>>>>>> +			continue;
>>>>>>>> +		}
>>>>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>>>>>>>> +	}
>>>>>>>
>>>>>>> Are you sure you handle correctly empty pages with bf?
>>>>>>>      >> if scramble is enable, i would say yes here.
>>>>>> when scramble is disabled, i am considering how to use the helper
>>>>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
>>>>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
>>>>>> out the ecc parity bytes.
>>>>>
>>>>> Even if the ECC engine is disabled?
>>>>>     >> No.
>>>> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
>>>> Is there a good idea?
>>>
>>> When reading with ECC enabled, in case of uncorrectable error you
>>> must re-read without ECC, then check if the page is empty or not with
>>> the core helpers (nand_check_erased_*()).
>>>
>>> Is this what you meant?
>>>    
>> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes without ECC engine and then use the helper nand_check_erased_ecc_chunk to check if blank page.
>> Of course, the precondition is without scrambler, or the bland page can be detected by meson NFC.
> 
> A suppose you meant "blank page"? If yes, then you don't need the
> helper to check for only-0xFF pages. If the controller tells you if the
> page was blank, then just check for that bit.
> 

i think not. we need to return back the previous problem that how i can 
get the bitflips of one blank page. i think i need the helper.

> 
> Thanks,
> Miquèl
> 
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  1:56             ` Liang Yang
  2018-12-11  7:54               ` Miquel Raynal
@ 2018-12-11  8:39               ` Boris Brezillon
  2018-12-11  9:39                 ` Liang Yang
  1 sibling, 1 reply; 19+ messages in thread
From: Boris Brezillon @ 2018-12-11  8:39 UTC (permalink / raw)
  To: Liang Yang
  Cc: Miquel Raynal, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

On Tue, 11 Dec 2018 09:56:25 +0800
Liang Yang <liang.yang@amlogic.com> wrote:

> Hi Miquel,
> 
> On 2018/12/10 22:50, Miquel Raynal wrote:
> > Hi Liang,
> > 
> > Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
> > +0800:
> >   
> >> On 2018/12/10 19:38, Boris Brezillon wrote:  
> >>> On Mon, 10 Dec 2018 19:23:46 +0800
> >>> Liang Yang <liang.yang@amlogic.com> wrote:
> >>>      
> >>>>>> +			mtd->ecc_stats.failed++;
> >>>>>> +			continue;
> >>>>>> +		}
> >>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> >>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> >>>>>> +	}  
> >>>>>
> >>>>> Are you sure you handle correctly empty pages with bf?  
> >>>>>     >> if scramble is enable, i would say yes here.  
> >>>> when scramble is disabled, i am considering how to use the helper
> >>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
> >>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
> >>>> out the ecc parity bytes.  
> >>>
> >>> Even if the ECC engine is disabled?
> >>>      
> >> No.
> >> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
> >> Is there a good idea?  
> > 
> > When reading with ECC enabled, in case of uncorrectable error you
> > must re-read without ECC, then check if the page is empty or not with
> > the core helpers (nand_check_erased_*()).
> > 
> > Is this what you meant?
> >   
> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes 
> without ECC engine and then use the helper nand_check_erased_ecc_chunk 
> to check if blank page.
> Of course, the precondition is without scrambler, or the bland page can 
> be detected by meson NFC.

Yep, raw accessors should disable both the scrambler and the ECC
engine (see what's done in sunxi_nand.c).

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  8:36                 ` Liang Yang
@ 2018-12-11  9:07                   ` Miquel Raynal
  2018-12-11  9:43                     ` Liang Yang
  0 siblings, 1 reply; 19+ messages in thread
From: Miquel Raynal @ 2018-12-11  9:07 UTC (permalink / raw)
  To: Liang Yang
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Liang,

Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 16:36:47
+0800:

> Hi Miquel,
> 
> Thanks for your quickly reply.
> 
> On 2018/12/11 15:54, Miquel Raynal wrote:
> > Hi Liang,
> > 
> > Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 09:56:25
> > +0800:
> >   
> >> Hi Miquel,
> >>
> >> On 2018/12/10 22:50, Miquel Raynal wrote:  
> >>> Hi Liang,
> >>>
> >>> Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
> >>> +0800:  
> >>>    >>>> On 2018/12/10 19:38, Boris Brezillon wrote:  
> >>>>> On Mon, 10 Dec 2018 19:23:46 +0800
> >>>>> Liang Yang <liang.yang@amlogic.com> wrote:  
> >>>>>     >>>>>> +			mtd->ecc_stats.failed++;  
> >>>>>>>> +			continue;
> >>>>>>>> +		}
> >>>>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
> >>>>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
> >>>>>>>> +	}  
> >>>>>>>
> >>>>>>> Are you sure you handle correctly empty pages with bf?  
> >>>>>>>      >> if scramble is enable, i would say yes here.  
> >>>>>> when scramble is disabled, i am considering how to use the helper
> >>>>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
> >>>>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
> >>>>>> out the ecc parity bytes.  
> >>>>>
> >>>>> Even if the ECC engine is disabled?  
> >>>>>     >> No.  
> >>>> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
> >>>> Is there a good idea?  
> >>>
> >>> When reading with ECC enabled, in case of uncorrectable error you
> >>> must re-read without ECC, then check if the page is empty or not with
> >>> the core helpers (nand_check_erased_*()).
> >>>
> >>> Is this what you meant?  
> >>>    >> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes without ECC engine and then use the helper nand_check_erased_ecc_chunk to check if blank page.  
> >> Of course, the precondition is without scrambler, or the bland page can be detected by meson NFC.  
> > 
> > A suppose you meant "blank page"? If yes, then you don't need the
> > helper to check for only-0xFF pages. If the controller tells you if the
> > page was blank, then just check for that bit.
> >   
> 
> i think not. we need to return back the previous problem that how i can get the bitflips of one blank page. i think i need the helper.

You are right, I suppose the "blank page" flag is only triggered if
there is no bitflip. In this case you can assume there are no
bitflips. Otherwise the controller will trigger an
uncorrectable error event and you will have to re-read the page
without ECC and check for bitflips with the helper.


Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  8:39               ` Boris Brezillon
@ 2018-12-11  9:39                 ` Liang Yang
  0 siblings, 0 replies; 19+ messages in thread
From: Liang Yang @ 2018-12-11  9:39 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Miquel Raynal, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Boris,

On 2018/12/11 16:39, Boris Brezillon wrote:
> On Tue, 11 Dec 2018 09:56:25 +0800
> Liang Yang <liang.yang@amlogic.com> wrote:
> 
>> Hi Miquel,
>>
>> On 2018/12/10 22:50, Miquel Raynal wrote:
>>> Hi Liang,
>>>
>>> Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
>>> +0800:
>>>    
>>>> On 2018/12/10 19:38, Boris Brezillon wrote:
>>>>> On Mon, 10 Dec 2018 19:23:46 +0800
>>>>> Liang Yang <liang.yang@amlogic.com> wrote:
>>>>>       
>>>>>>>> +			mtd->ecc_stats.failed++;
>>>>>>>> +			continue;
>>>>>>>> +		}
>>>>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>>>>>>>> +	}
>>>>>>>
>>>>>>> Are you sure you handle correctly empty pages with bf?
>>>>>>>      >> if scramble is enable, i would say yes here.
>>>>>> when scramble is disabled, i am considering how to use the helper
>>>>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
>>>>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
>>>>>> out the ecc parity bytes.
>>>>>
>>>>> Even if the ECC engine is disabled?
>>>>>       
>>>> No.
>>>> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
>>>> Is there a good idea?
>>>
>>> When reading with ECC enabled, in case of uncorrectable error you
>>> must re-read without ECC, then check if the page is empty or not with
>>> the core helpers (nand_check_erased_*()).
>>>
>>> Is this what you meant?
>>>    
>> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes
>> without ECC engine and then use the helper nand_check_erased_ecc_chunk
>> to check if blank page.
>> Of course, the precondition is without scrambler, or the bland page can
>> be detected by meson NFC.
> 
> Yep, raw accessors should disable both the scrambler and the ECC
> engine (see what's done in sunxi_nand.c).
> 

i see sunxi_nfc_hw_ecc_read_chunk and it will re-read the data for 
bitflips with scrambler off when ECC failed.
also we can do the same implementation and it seems to be the only answer.

> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
  2018-12-11  9:07                   ` Miquel Raynal
@ 2018-12-11  9:43                     ` Liang Yang
  0 siblings, 0 replies; 19+ messages in thread
From: Liang Yang @ 2018-12-11  9:43 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Boris Brezillon, Jianxin Pan, linux-mtd, Yixun Lan,
	David Woodhouse, Brian Norris, Marek Vasut, Richard Weinberger,
	Jerome Brunet, Neil Armstrong, Martin Blumenstingl, Carlo Caione,
	Kevin Hilman, Rob Herring, Jian Hu, Hanjie Lin, Victor Wan,
	linux-amlogic, linux-arm-kernel, linux-kernel

Hi Miquel,

On 2018/12/11 17:07, Miquel Raynal wrote:
> Hi Liang,
> 
> Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 16:36:47
> +0800:
> 
>> Hi Miquel,
>>
>> Thanks for your quickly reply.
>>
>> On 2018/12/11 15:54, Miquel Raynal wrote:
>>> Hi Liang,
>>>
>>> Liang Yang <liang.yang@amlogic.com> wrote on Tue, 11 Dec 2018 09:56:25
>>> +0800:
>>>    
>>>> Hi Miquel,
>>>>
>>>> On 2018/12/10 22:50, Miquel Raynal wrote:
>>>>> Hi Liang,
>>>>>
>>>>> Liang Yang <liang.yang@amlogic.com> wrote on Mon, 10 Dec 2018 20:12:39
>>>>> +0800:
>>>>>     >>>> On 2018/12/10 19:38, Boris Brezillon wrote:
>>>>>>> On Mon, 10 Dec 2018 19:23:46 +0800
>>>>>>> Liang Yang <liang.yang@amlogic.com> wrote:
>>>>>>>      >>>>>> +			mtd->ecc_stats.failed++;
>>>>>>>>>> +			continue;
>>>>>>>>>> +		}
>>>>>>>>>> +		mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>>>>>>>> +		bitflips = max_t(u32, bitflips, ECC_ERR_CNT(*info));
>>>>>>>>>> +	}
>>>>>>>>>
>>>>>>>>> Are you sure you handle correctly empty pages with bf?
>>>>>>>>>       >> if scramble is enable, i would say yes here.
>>>>>>>> when scramble is disabled, i am considering how to use the helper
>>>>>>>> nand_check_erased_ecc_chunk, but it seems that i can't get the ecc
>>>>>>>> bytes which is caculated by ecc engine.by the way, nfc dma doesn't send
>>>>>>>> out the ecc parity bytes.
>>>>>>>
>>>>>>> Even if the ECC engine is disabled?
>>>>>>>      >> No.
>>>>>> When ECC engine is disabled, it can read the ecc parity bytes ; but there is another problem that i need to consider how code struct looks better when reading error with ecc opened and then try to raw read.
>>>>>> Is there a good idea?
>>>>>
>>>>> When reading with ECC enabled, in case of uncorrectable error you
>>>>> must re-read without ECC, then check if the page is empty or not with
>>>>> the core helpers (nand_check_erased_*()).
>>>>>
>>>>> Is this what you meant?
>>>>>     >> yes. when uncorrectable ECC error, i need firstly read out the ECC bytes without ECC engine and then use the helper nand_check_erased_ecc_chunk to check if blank page.
>>>> Of course, the precondition is without scrambler, or the bland page can be detected by meson NFC.
>>>
>>> A suppose you meant "blank page"? If yes, then you don't need the
>>> helper to check for only-0xFF pages. If the controller tells you if the
>>> page was blank, then just check for that bit.
>>>    
>>
>> i think not. we need to return back the previous problem that how i can get the bitflips of one blank page. i think i need the helper.
> 
> You are right, I suppose the "blank page" flag is only triggered if
> there is no bitflip. In this case you can assume there are no
> bitflips. Otherwise the controller will trigger an
> uncorrectable error event and you will have to re-read the page
> without ECC and check for bitflips with the helper.
> 
yes, that is right. i will do the "re-read" process.
> 
> Thanks,
> Miquèl
> 
> .
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2018-12-11  9:42 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-16 16:40 [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Jianxin Pan
2018-11-16 16:40 ` [PATCH v7 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver Jianxin Pan
2018-11-16 16:40 ` [PATCH v7 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller Jianxin Pan
2018-12-07  9:24   ` Miquel Raynal
2018-12-07  9:42     ` Miquel Raynal
2018-12-07 10:41       ` Liang Yang
2018-12-07 10:48         ` Miquel Raynal
2018-12-10 11:23     ` Liang Yang
2018-12-10 11:38       ` Boris Brezillon
2018-12-10 12:12         ` Liang Yang
2018-12-10 14:50           ` Miquel Raynal
2018-12-11  1:56             ` Liang Yang
2018-12-11  7:54               ` Miquel Raynal
2018-12-11  8:36                 ` Liang Yang
2018-12-11  9:07                   ` Miquel Raynal
2018-12-11  9:43                     ` Liang Yang
2018-12-11  8:39               ` Boris Brezillon
2018-12-11  9:39                 ` Liang Yang
2018-11-26  6:39 ` [PATCH v7 0/2] mtd: rawnand: meson: add Amlogic NAND driver support Liang Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).