mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs
@ 2023-08-03 10:32 Alexander Shiyan
  2023-08-03 10:32 ` [PATCH 2/2] mtd: nand: Add driver for NAND controller " Alexander Shiyan
  2023-08-07  6:59 ` [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface " Sascha Hauer
  0 siblings, 2 replies; 5+ messages in thread
From: Alexander Shiyan @ 2023-08-03 10:32 UTC (permalink / raw)
  To: barebox; +Cc: Alexander Shiyan

This adds support for FMC2 External Bus Interface on STM32MP SoCs.
The original source is taken from the STMicroelectronics/u-boot repository [1].

[1] https://github.com/STMicroelectronics/u-boot/blob/v2022.10-stm32mp/drivers/memory/stm32-fmc2-ebi.c

Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
---
 drivers/memory/Kconfig          |    9 +
 drivers/memory/Makefile         |    1 +
 drivers/memory/stm32-fmc2-ebi.c | 1063 +++++++++++++++++++++++++++++++
 3 files changed, 1073 insertions(+)
 create mode 100644 drivers/memory/stm32-fmc2-ebi.c

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index e18b452009..b0d99c3a54 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -23,4 +23,13 @@ config MC_TEGRA124
 	  the Tegra124 SoC. This driver performs the necessary initialization
 	  to provide a function GPU when the OS is running.
 
+config STM32_FMC2_EBI
+	bool "Support for FMC2 External Bus Interface on STM32MP SoCs"
+	depends on ARCH_STM32MP || COMPILE_TEST
+	help
+	  Select this option to enable the STM32 FMC2 External Bus Interface
+	  controller. This driver configures the transactions with external
+	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
+	  SOCs containing the FMC2 External Bus Interface.
+
 endmenu
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index bdf8db66e8..67d3c47621 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_ATMEL_EBI)		+= atmel-ebi.o
 obj-$(CONFIG_MC_TEGRA124)	+= mc-tegra124.o
+obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c
new file mode 100644
index 0000000000..ac2ea1b0e0
--- /dev/null
+++ b/drivers/memory/stm32-fmc2-ebi.c
@@ -0,0 +1,1063 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) STMicroelectronics 2020
+ */
+
+#define pr_fmt(fmt) "stm32-fmc2-ebi: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <of_address.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1			0x0
+#define FMC2_BTR1			0x4
+#define FMC2_BCR(x)			((x) * 0x8 + FMC2_BCR1)
+#define FMC2_BTR(x)			((x) * 0x8 + FMC2_BTR1)
+#define FMC2_PCSCNTR			0x20
+#define FMC2_BWTR1			0x104
+#define FMC2_BWTR(x)			((x) * 0x8 + FMC2_BWTR1)
+
+/* Register: FMC2_BCR1 */
+#define FMC2_BCR1_CCLKEN		BIT(20)
+#define FMC2_BCR1_FMC2EN		BIT(31)
+
+/* Register: FMC2_BCRx */
+#define FMC2_BCR_MBKEN			BIT(0)
+#define FMC2_BCR_MUXEN			BIT(1)
+#define FMC2_BCR_MTYP			GENMASK(3, 2)
+#define FMC2_BCR_MWID			GENMASK(5, 4)
+#define FMC2_BCR_FACCEN			BIT(6)
+#define FMC2_BCR_BURSTEN		BIT(8)
+#define FMC2_BCR_WAITPOL		BIT(9)
+#define FMC2_BCR_WAITCFG		BIT(11)
+#define FMC2_BCR_WREN			BIT(12)
+#define FMC2_BCR_WAITEN			BIT(13)
+#define FMC2_BCR_EXTMOD			BIT(14)
+#define FMC2_BCR_ASYNCWAIT		BIT(15)
+#define FMC2_BCR_CPSIZE			GENMASK(18, 16)
+#define FMC2_BCR_CBURSTRW		BIT(19)
+#define FMC2_BCR_NBLSET			GENMASK(23, 22)
+
+/* Register: FMC2_BTRx/FMC2_BWTRx */
+#define FMC2_BXTR_ADDSET		GENMASK(3, 0)
+#define FMC2_BXTR_ADDHLD		GENMASK(7, 4)
+#define FMC2_BXTR_DATAST		GENMASK(15, 8)
+#define FMC2_BXTR_BUSTURN		GENMASK(19, 16)
+#define FMC2_BTR_CLKDIV			GENMASK(23, 20)
+#define FMC2_BTR_DATLAT			GENMASK(27, 24)
+#define FMC2_BXTR_ACCMOD		GENMASK(29, 28)
+#define FMC2_BXTR_DATAHLD		GENMASK(31, 30)
+
+/* Register: FMC2_PCSCNTR */
+#define FMC2_PCSCNTR_CSCOUNT		GENMASK(15, 0)
+#define FMC2_PCSCNTR_CNTBEN(x)		BIT((x) + 16)
+
+#define FMC2_MAX_EBI_CE			4
+#define FMC2_MAX_BANKS			5
+
+#define FMC2_BCR_CPSIZE_0		0x0
+#define FMC2_BCR_CPSIZE_128		0x1
+#define FMC2_BCR_CPSIZE_256		0x2
+#define FMC2_BCR_CPSIZE_512		0x3
+#define FMC2_BCR_CPSIZE_1024		0x4
+
+#define FMC2_BCR_MWID_8			0x0
+#define FMC2_BCR_MWID_16		0x1
+
+#define FMC2_BCR_MTYP_SRAM		0x0
+#define FMC2_BCR_MTYP_PSRAM		0x1
+#define FMC2_BCR_MTYP_NOR		0x2
+
+#define FMC2_BXTR_EXTMOD_A		0x0
+#define FMC2_BXTR_EXTMOD_B		0x1
+#define FMC2_BXTR_EXTMOD_C		0x2
+#define FMC2_BXTR_EXTMOD_D		0x3
+
+#define FMC2_BCR_NBLSET_MAX		0x3
+#define FMC2_BXTR_ADDSET_MAX		0xf
+#define FMC2_BXTR_ADDHLD_MAX		0xf
+#define FMC2_BXTR_DATAST_MAX		0xff
+#define FMC2_BXTR_BUSTURN_MAX		0xf
+#define FMC2_BXTR_DATAHLD_MAX		0x3
+#define FMC2_BTR_CLKDIV_MAX		0xf
+#define FMC2_BTR_DATLAT_MAX		0xf
+#define FMC2_PCSCNTR_CSCOUNT_MAX	0xff
+
+enum stm32_fmc2_ebi_bank {
+	FMC2_EBI1 = 0,
+	FMC2_EBI2,
+	FMC2_EBI3,
+	FMC2_EBI4,
+	FMC2_NAND
+};
+
+enum stm32_fmc2_ebi_register_type {
+	FMC2_REG_BCR = 1,
+	FMC2_REG_BTR,
+	FMC2_REG_BWTR,
+	FMC2_REG_PCSCNTR
+};
+
+enum stm32_fmc2_ebi_transaction_type {
+	FMC2_ASYNC_MODE_1_SRAM = 0,
+	FMC2_ASYNC_MODE_1_PSRAM,
+	FMC2_ASYNC_MODE_A_SRAM,
+	FMC2_ASYNC_MODE_A_PSRAM,
+	FMC2_ASYNC_MODE_2_NOR,
+	FMC2_ASYNC_MODE_B_NOR,
+	FMC2_ASYNC_MODE_C_NOR,
+	FMC2_ASYNC_MODE_D_NOR,
+	FMC2_SYNC_READ_SYNC_WRITE_PSRAM,
+	FMC2_SYNC_READ_ASYNC_WRITE_PSRAM,
+	FMC2_SYNC_READ_SYNC_WRITE_NOR,
+	FMC2_SYNC_READ_ASYNC_WRITE_NOR
+};
+
+enum stm32_fmc2_ebi_buswidth {
+	FMC2_BUSWIDTH_8 = 8,
+	FMC2_BUSWIDTH_16 = 16
+};
+
+enum stm32_fmc2_ebi_cpsize {
+	FMC2_CPSIZE_0 = 0,
+	FMC2_CPSIZE_128 = 128,
+	FMC2_CPSIZE_256 = 256,
+	FMC2_CPSIZE_512 = 512,
+	FMC2_CPSIZE_1024 = 1024
+};
+
+struct stm32_fmc2_ebi {
+	struct clk *clk;
+	void __iomem *io_base;
+	u8 bank_assigned;
+};
+
+/*
+ * struct stm32_fmc2_prop - STM32 FMC2 EBI property
+ * @name: the device tree binding name of the property
+ * @bprop: indicate that it is a boolean property
+ * @mprop: indicate that it is a mandatory property
+ * @reg_type: the register that have to be modified
+ * @reg_mask: the bit that have to be modified in the selected register
+ *            in case of it is a boolean property
+ * @reset_val: the default value that have to be set in case the property
+ *             has not been defined in the device tree
+ * @check: this callback ckecks that the property is compliant with the
+ *         transaction type selected
+ * @calculate: this callback is called to calculate for exemple a timing
+ *             set in nanoseconds in the device tree in clock cycles or in
+ *             clock period
+ * @set: this callback applies the values in the registers
+ */
+struct stm32_fmc2_prop {
+	const char *name;
+	bool bprop;
+	bool mprop;
+	int reg_type;
+	u32 reg_mask;
+	u32 reset_val;
+	int (*check)(struct stm32_fmc2_ebi *ebi,
+		     const struct stm32_fmc2_prop *prop, int cs);
+	u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup);
+	int (*set)(struct stm32_fmc2_ebi *ebi,
+		   const struct stm32_fmc2_prop *prop,
+		   int cs, u32 setup);
+};
+
+static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi,
+				    const struct stm32_fmc2_prop *prop,
+				    int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+
+	if (bcr & FMC2_BCR_MTYP)
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi,
+					const struct stm32_fmc2_prop *prop,
+					int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+	u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+
+	if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi,
+					   const struct stm32_fmc2_prop *prop,
+					   int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+
+	if (bcr & FMC2_BCR_BURSTEN)
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi,
+					    const struct stm32_fmc2_prop *prop,
+					    int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+
+	if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW))
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi,
+				       const struct stm32_fmc2_prop *prop,
+				       int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+	u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+
+	if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi,
+					     const struct stm32_fmc2_prop *prop,
+					     int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+	u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
+		   readl(ebi->io_base + FMC2_BWTR(cs)) :
+		   readl(ebi->io_base + FMC2_BTR(cs));
+	u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+
+	if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) &&
+	    ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN))
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi,
+					   const struct stm32_fmc2_prop *prop,
+					   int cs)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+	u32 bcr1 = cs ? readl(ebi->io_base + FMC2_BCR1) : bcr;
+
+	if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN)))
+		return 0;
+
+	return -EINVAL;
+}
+
+static int stm32_fmc2_ebi_check_cclk(struct stm32_fmc2_ebi *ebi,
+				     const struct stm32_fmc2_prop *prop,
+				     int cs)
+{
+	if (cs)
+		return -EINVAL;
+
+	return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
+}
+
+static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi,
+					     int cs, u32 setup)
+{
+	unsigned long hclk = clk_get_rate(ebi->clk);
+	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
+
+	return DIV_ROUND_UP(setup * 1000, hclkp);
+}
+
+static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
+					   int cs, u32 setup)
+{
+	u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup);
+	u32 bcr = readl(ebi->io_base + FMC2_BCR1);
+	u32 btr = bcr & FMC2_BCR1_CCLKEN || !cs ?
+		  readl(ebi->io_base + FMC2_BTR1) :
+		  readl(ebi->io_base + FMC2_BTR(cs));
+	u32 clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1;
+
+	return DIV_ROUND_UP(nb_clk_cycles, clk_period);
+}
+
+static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
+{
+	switch (reg_type) {
+	case FMC2_REG_BCR:
+		*reg = FMC2_BCR(cs);
+		break;
+	case FMC2_REG_BTR:
+		*reg = FMC2_BTR(cs);
+		break;
+	case FMC2_REG_BWTR:
+		*reg = FMC2_BWTR(cs);
+		break;
+	case FMC2_REG_PCSCNTR:
+		*reg = FMC2_PCSCNTR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_bit_field(struct stm32_fmc2_ebi *ebi,
+					const struct stm32_fmc2_prop *prop,
+					int cs, u32 setup)
+{
+	u32 reg;
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	clrsetbits_le32(ebi->io_base + reg, prop->reg_mask,
+			setup ? prop->reg_mask : 0);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_trans_type(struct stm32_fmc2_ebi *ebi,
+					 const struct stm32_fmc2_prop *prop,
+					 int cs, u32 setup)
+{
+	u32 bcr_mask, bcr = FMC2_BCR_WREN;
+	u32 btr_mask, btr = 0;
+	u32 bwtr_mask, bwtr = 0;
+
+	bwtr_mask = FMC2_BXTR_ACCMOD;
+	btr_mask = FMC2_BXTR_ACCMOD;
+	bcr_mask = FMC2_BCR_MUXEN | FMC2_BCR_MTYP | FMC2_BCR_FACCEN |
+		   FMC2_BCR_WREN | FMC2_BCR_WAITEN | FMC2_BCR_BURSTEN |
+		   FMC2_BCR_EXTMOD | FMC2_BCR_CBURSTRW;
+
+	switch (setup) {
+	case FMC2_ASYNC_MODE_1_SRAM:
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
+		/*
+		 * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+		 */
+		break;
+	case FMC2_ASYNC_MODE_1_PSRAM:
+		/*
+		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+		break;
+	case FMC2_ASYNC_MODE_A_SRAM:
+		/*
+		 * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
+		bcr |= FMC2_BCR_EXTMOD;
+		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+		break;
+	case FMC2_ASYNC_MODE_A_PSRAM:
+		/*
+		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+		bcr |= FMC2_BCR_EXTMOD;
+		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
+		break;
+	case FMC2_ASYNC_MODE_2_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN;
+		break;
+	case FMC2_ASYNC_MODE_B_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 1
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
+		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
+		break;
+	case FMC2_ASYNC_MODE_C_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 2
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
+		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
+		break;
+	case FMC2_ASYNC_MODE_D_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 3
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
+		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+		break;
+	case FMC2_SYNC_READ_SYNC_WRITE_PSRAM:
+		/*
+		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+		bcr |= FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
+		break;
+	case FMC2_SYNC_READ_ASYNC_WRITE_PSRAM:
+		/*
+		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
+		bcr |= FMC2_BCR_BURSTEN;
+		break;
+	case FMC2_SYNC_READ_SYNC_WRITE_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
+		break;
+	case FMC2_SYNC_READ_ASYNC_WRITE_NOR:
+		/*
+		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
+		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
+		 */
+		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
+		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN;
+		break;
+	default:
+		/* Type of transaction not supported */
+		return -EINVAL;
+	}
+
+	if (bcr & FMC2_BCR_EXTMOD)
+		clrsetbits_le32(ebi->io_base + FMC2_BWTR(cs),
+				bwtr_mask, bwtr);
+	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), btr_mask, btr);
+	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), bcr_mask, bcr);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_buswidth(struct stm32_fmc2_ebi *ebi,
+				       const struct stm32_fmc2_prop *prop,
+				       int cs, u32 setup)
+{
+	u32 val;
+
+	switch (setup) {
+	case FMC2_BUSWIDTH_8:
+		val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_8);
+		break;
+	case FMC2_BUSWIDTH_16:
+		val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_16);
+		break;
+	default:
+		/* Buswidth not supported */
+		return -EINVAL;
+	}
+
+	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MWID, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_cpsize(struct stm32_fmc2_ebi *ebi,
+				     const struct stm32_fmc2_prop *prop,
+				     int cs, u32 setup)
+{
+	u32 val;
+
+	switch (setup) {
+	case FMC2_CPSIZE_0:
+		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0);
+		break;
+	case FMC2_CPSIZE_128:
+		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128);
+		break;
+	case FMC2_CPSIZE_256:
+		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256);
+		break;
+	case FMC2_CPSIZE_512:
+		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512);
+		break;
+	case FMC2_CPSIZE_1024:
+		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024);
+		break;
+	default:
+		/* Cpsize not supported */
+		return -EINVAL;
+	}
+
+	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_CPSIZE, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_bl_setup(struct stm32_fmc2_ebi *ebi,
+				       const struct stm32_fmc2_prop *prop,
+				       int cs, u32 setup)
+{
+	u32 val;
+
+	val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX);
+	val = FIELD_PREP(FMC2_BCR_NBLSET, val);
+	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_NBLSET, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi,
+					    const struct stm32_fmc2_prop *prop,
+					    int cs, u32 setup)
+{
+	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
+	u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
+		   readl(ebi->io_base + FMC2_BWTR(cs)) :
+		   readl(ebi->io_base + FMC2_BTR(cs));
+	u32 reg, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)
+		val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX);
+	else
+		val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX);
+	val = FIELD_PREP(FMC2_BXTR_ADDSET, val);
+	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDSET, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi,
+					   const struct stm32_fmc2_prop *prop,
+					   int cs, u32 setup)
+{
+	u32 val, reg;
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX);
+	val = FIELD_PREP(FMC2_BXTR_ADDHLD, val);
+	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDHLD, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi,
+					 const struct stm32_fmc2_prop *prop,
+					 int cs, u32 setup)
+{
+	u32 val, reg;
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX);
+	val = FIELD_PREP(FMC2_BXTR_DATAST, val);
+	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAST, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi,
+					     const struct stm32_fmc2_prop *prop,
+					     int cs, u32 setup)
+{
+	u32 val, reg;
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0;
+	val = FIELD_PREP(FMC2_BXTR_BUSTURN, val);
+	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_BUSTURN, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi,
+					const struct stm32_fmc2_prop *prop,
+					int cs, u32 setup)
+{
+	u32 val, reg;
+	int ret;
+
+	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
+	if (ret)
+		return ret;
+
+	if (prop->reg_type == FMC2_REG_BWTR)
+		val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0;
+	else
+		val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX);
+	val = FIELD_PREP(FMC2_BXTR_DATAHLD, val);
+	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAHLD, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi,
+					 const struct stm32_fmc2_prop *prop,
+					 int cs, u32 setup)
+{
+	u32 val;
+
+	val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1;
+	val = FIELD_PREP(FMC2_BTR_CLKDIV, val);
+	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_CLKDIV, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi,
+					   const struct stm32_fmc2_prop *prop,
+					   int cs, u32 setup)
+{
+	u32 val;
+
+	val = setup > 1 ? min_t(u32, setup - 2, FMC2_BTR_DATLAT_MAX) : 0;
+	val = FIELD_PREP(FMC2_BTR_DATLAT, val);
+	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_DATLAT, val);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
+					    const struct stm32_fmc2_prop *prop,
+					    int cs, u32 setup)
+{
+	u32 old_val, new_val, pcscntr;
+
+	if (setup < 1)
+		return 0;
+
+	pcscntr = readl(ebi->io_base + FMC2_PCSCNTR);
+
+	/* Enable counter for the bank */
+	setbits_le32(ebi->io_base + FMC2_PCSCNTR, FMC2_PCSCNTR_CNTBEN(cs));
+
+	new_val = min_t(u32, setup - 1, FMC2_PCSCNTR_CSCOUNT_MAX);
+	old_val = FIELD_GET(FMC2_PCSCNTR_CSCOUNT, pcscntr);
+	if (old_val && new_val > old_val)
+		/* Keep current counter value */
+		return 0;
+
+	new_val = FIELD_PREP(FMC2_PCSCNTR_CSCOUNT, new_val);
+	clrsetbits_le32(ebi->io_base + FMC2_PCSCNTR,
+			FMC2_PCSCNTR_CSCOUNT, new_val);
+
+	return 0;
+}
+
+static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
+	/* st,fmc2-ebi-cs-trans-type must be the first property */
+	{
+		.name = "st,fmc2-ebi-cs-transaction-type",
+		.mprop = true,
+		.set = stm32_fmc2_ebi_set_trans_type,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-cclk-enable",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR1_CCLKEN,
+		.check = stm32_fmc2_ebi_check_cclk,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-mux-enable",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR_MUXEN,
+		.check = stm32_fmc2_ebi_check_mux,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-buswidth",
+		.reset_val = FMC2_BUSWIDTH_16,
+		.set = stm32_fmc2_ebi_set_buswidth,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-waitpol-high",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR_WAITPOL,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-waitcfg-enable",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR_WAITCFG,
+		.check = stm32_fmc2_ebi_check_waitcfg,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-wait-enable",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR_WAITEN,
+		.check = stm32_fmc2_ebi_check_sync_trans,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-asyncwait-enable",
+		.bprop = true,
+		.reg_type = FMC2_REG_BCR,
+		.reg_mask = FMC2_BCR_ASYNCWAIT,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.set = stm32_fmc2_ebi_set_bit_field,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-cpsize",
+		.check = stm32_fmc2_ebi_check_cpsize,
+		.set = stm32_fmc2_ebi_set_cpsize,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-byte-lane-setup-ns",
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_bl_setup,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-address-setup-ns",
+		.reg_type = FMC2_REG_BTR,
+		.reset_val = FMC2_BXTR_ADDSET_MAX,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_address_setup,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-address-hold-ns",
+		.reg_type = FMC2_REG_BTR,
+		.reset_val = FMC2_BXTR_ADDHLD_MAX,
+		.check = stm32_fmc2_ebi_check_address_hold,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_address_hold,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-data-setup-ns",
+		.reg_type = FMC2_REG_BTR,
+		.reset_val = FMC2_BXTR_DATAST_MAX,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_data_setup,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-bus-turnaround-ns",
+		.reg_type = FMC2_REG_BTR,
+		.reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_bus_turnaround,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-data-hold-ns",
+		.reg_type = FMC2_REG_BTR,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_data_hold,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-clk-period-ns",
+		.reset_val = FMC2_BTR_CLKDIV_MAX + 1,
+		.check = stm32_fmc2_ebi_check_clk_period,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_clk_period,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-data-latency-ns",
+		.check = stm32_fmc2_ebi_check_sync_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clk_period,
+		.set = stm32_fmc2_ebi_set_data_latency,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-write-address-setup-ns",
+		.reg_type = FMC2_REG_BWTR,
+		.reset_val = FMC2_BXTR_ADDSET_MAX,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_address_setup,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-write-address-hold-ns",
+		.reg_type = FMC2_REG_BWTR,
+		.reset_val = FMC2_BXTR_ADDHLD_MAX,
+		.check = stm32_fmc2_ebi_check_address_hold,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_address_hold,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-write-data-setup-ns",
+		.reg_type = FMC2_REG_BWTR,
+		.reset_val = FMC2_BXTR_DATAST_MAX,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_data_setup,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-write-bus-turnaround-ns",
+		.reg_type = FMC2_REG_BWTR,
+		.reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_bus_turnaround,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-write-data-hold-ns",
+		.reg_type = FMC2_REG_BWTR,
+		.check = stm32_fmc2_ebi_check_async_trans,
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_data_hold,
+	},
+	{
+		.name = "st,fmc2-ebi-cs-max-low-pulse-ns",
+		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
+		.set = stm32_fmc2_ebi_set_max_low_pulse,
+	},
+};
+
+static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi,
+				     struct device_node *node,
+				     const struct stm32_fmc2_prop *prop,
+				     int cs)
+{
+	u32 setup = 0;
+
+	if (!prop->set) {
+		pr_err("property %s is not well defined\n", prop->name);
+		return -EINVAL;
+	}
+
+	if (prop->check && prop->check(ebi, prop, cs))
+		/* Skip this property */
+		return 0;
+
+	if (prop->bprop) {
+		bool bprop;
+
+		bprop = of_property_read_bool(node, prop->name);
+		if (prop->mprop && !bprop) {
+			pr_err("mandatory property %s not defined in the device tree\n",
+			       prop->name);
+			return -EINVAL;
+		}
+
+		if (bprop)
+			setup = 1;
+	} else {
+		u32 val;
+		int ret;
+
+		ret = of_property_read_u32(node, prop->name, &val);
+		if (prop->mprop && ret) {
+			pr_err("mandatory property %s not defined in the device tree\n",
+			       prop->name);
+			return ret;
+		}
+
+		if (ret)
+			setup = prop->reset_val;
+		else if (prop->calculate)
+			setup = prop->calculate(ebi, cs, val);
+		else
+			setup = val;
+	}
+
+	return prop->set(ebi, prop, cs, setup);
+}
+
+static void stm32_fmc2_ebi_enable_bank(struct stm32_fmc2_ebi *ebi, int cs)
+{
+	setbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
+}
+
+static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs)
+{
+	clrbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
+}
+
+/* NWAIT signal can not be connected to EBI controller and NAND controller */
+static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
+{
+	unsigned int cs;
+	u32 bcr;
+
+	for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
+		if (!(ebi->bank_assigned & BIT(cs)))
+			continue;
+
+		bcr = readl(ebi->io_base + FMC2_BCR(cs));
+		if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
+		    ebi->bank_assigned & BIT(FMC2_NAND))
+			return true;
+	}
+
+	return false;
+}
+
+static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
+{
+	setbits_le32(ebi->io_base + FMC2_BCR1, FMC2_BCR1_FMC2EN);
+}
+
+static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
+				   struct device_node *node, u32 cs)
+{
+	unsigned int i;
+	int ret;
+
+	stm32_fmc2_ebi_disable_bank(ebi, cs);
+
+	for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) {
+		const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i];
+
+		ret = stm32_fmc2_ebi_parse_prop(ebi, node, p, cs);
+		if (ret) {
+			pr_err("property %s could not be set: %d\n", p->name, ret);
+			return ret;
+		}
+	}
+
+	stm32_fmc2_ebi_enable_bank(ebi, cs);
+
+	return 0;
+}
+
+static int stm32_fmc2_ebi_parse_dt(struct device *dev,
+				   struct stm32_fmc2_ebi *ebi)
+{
+	struct device_node *child;
+	bool child_found = false;
+	u32 bank;
+	int ret;
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		ret = of_property_read_u32(child, "reg", &bank);
+		if (ret) {
+			dev_err(dev, "could not retrieve reg property: %d\n", ret);
+			return ret;
+		}
+
+		if (bank >= FMC2_MAX_BANKS) {
+			dev_err(dev, "invalid reg value: %d\n", bank);
+			return -EINVAL;
+		}
+
+		if (ebi->bank_assigned & BIT(bank)) {
+			dev_err(dev, "bank already assigned: %d\n", bank);
+			return -EINVAL;
+		}
+
+		if (bank < FMC2_MAX_EBI_CE) {
+			ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
+			if (ret) {
+				dev_err(dev, "setup chip select %d failed: %d\n", bank, ret);
+				return ret;
+			}
+		}
+
+		ebi->bank_assigned |= BIT(bank);
+		child_found = true;
+	}
+
+	if (!child_found) {
+		dev_warn(dev, "no subnodes found.\n");
+		return -ENODEV;
+	}
+
+	if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) {
+		dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n");
+		return -EINVAL;
+	}
+
+	stm32_fmc2_ebi_enable(ebi);
+
+	return 0;
+}
+
+static int __init stm32_fmc2_ebi_probe(struct device *dev)
+{
+	struct stm32_fmc2_ebi *ebi;
+	int ret;
+
+	ebi = xzalloc(sizeof(*ebi));
+
+	ebi->clk = clk_get(dev, NULL);
+	if (IS_ERR(ebi->clk)) {
+		ret = PTR_ERR(ebi->clk);
+		goto out_kfree;
+	}
+
+	clk_enable(ebi->clk);
+
+	ebi->io_base = of_iomap(dev->of_node, 0);
+	if (!ebi->io_base) {
+		ret = -ENOMEM;
+		goto out_clk;
+	}
+
+	ret = device_reset_us(dev, 2);
+	if (ret)
+		goto out_clk;
+
+	ret = stm32_fmc2_ebi_parse_dt(dev, ebi);
+	if (ret)
+		goto out_clk;
+	
+	return of_platform_populate(dev->of_node, NULL, dev);
+
+out_clk:
+	clk_disable(ebi->clk);
+
+out_kfree:
+	kfree(ebi);
+
+	return ret;
+}
+
+static __maybe_unused struct of_device_id stm32_fmc2_ebi_dt_ids[] = {
+	{ .compatible = "st,stm32mp1-fmc2-ebi", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_dt_ids);
+
+static struct driver stm32_fmc2_ebi_driver = {
+	.name		= "stm32_fmc2_ebi",
+	.probe		= stm32_fmc2_ebi_probe,
+	.of_compatible	= DRV_OF_COMPAT(stm32_fmc2_ebi_dt_ids),
+};
+coredevice_platform_driver(stm32_fmc2_ebi_driver);
-- 
2.39.1




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

* [PATCH 2/2] mtd: nand: Add driver for NAND controller on STM32MP SoCs
  2023-08-03 10:32 [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs Alexander Shiyan
@ 2023-08-03 10:32 ` Alexander Shiyan
  2023-08-07  6:59 ` [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface " Sascha Hauer
  1 sibling, 0 replies; 5+ messages in thread
From: Alexander Shiyan @ 2023-08-03 10:32 UTC (permalink / raw)
  To: barebox; +Cc: Alexander Shiyan

This adds support for NAND controller on STM32MP SoCs.
The original source is taken from the STMicroelectronics/u-boot repository [1].

[1] https://github.com/STMicroelectronics/u-boot/blob/v2022.10-stm32mp/drivers/mtd/nand/raw/stm32_fmc2_nand.c

Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
---
 drivers/mtd/nand/Kconfig      |   10 +
 drivers/mtd/nand/Makefile     |    1 +
 drivers/mtd/nand/nand_stm32.c | 1036 +++++++++++++++++++++++++++++++++
 3 files changed, 1047 insertions(+)
 create mode 100644 drivers/mtd/nand/nand_stm32.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index d4b941d20c..3237b4233f 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -95,6 +95,16 @@ config NAND_MRVL_NFC
 	  Support for the PXA3xx NAND controller, present in Armada 370/XP and
 	  PXA3xx SoCs.
 
+config NAND_STM32
+	bool "Support for NAND controller on STM32MP SoCs"
+	depends on ARCH_STM32MP || COMPILE_TEST
+	select STM32_FMC2_EBI if ARCH_STM32MP
+	help
+	  Enables support for NAND Flash chips on SoCs containing the FMC2
+	  NAND controller. This controller is found on STM32MP SoCs.
+	  The controller supports a maximum 8k page size and supports
+	  a maximum 8-bit correction error per sector of 512 bytes.
+
 config NAND_ATMEL
 	bool
 	prompt "Atmel (AT91SAM9xxx) NAND driver"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 6258eb2177..8142d67116 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_NAND_IMX)			+= nand_imx.o
 obj-$(CONFIG_NAND_OMAP_GPMC)		+= nand_omap_gpmc.o nand_omap_bch_decoder.o
 obj-$(CONFIG_MTD_NAND_OMAP_ELM)		+= omap_elm.o
 obj-$(CONFIG_NAND_ORION)		+= nand_orion.o
+obj-$(CONFIG_NAND_STM32)		+= nand_stm32.o
 obj-$(CONFIG_NAND_MRVL_NFC)		+= nand_mrvl_nfc.o
 obj-$(CONFIG_NAND_ATMEL)		+= atmel/
 obj-$(CONFIG_NAND_MXS)			+= nand_mxs.o
diff --git a/drivers/mtd/nand/nand_stm32.c b/drivers/mtd/nand/nand_stm32.c
new file mode 100644
index 0000000000..be13543a97
--- /dev/null
+++ b/drivers/mtd/nand/nand_stm32.c
@@ -0,0 +1,1036 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) STMicroelectronics 2019
+ * Author: Christophe Kerello <christophe.kerello@st.com>
+ */
+
+#define pr_fmt(fmt) "nand-stm32: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <of_address.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "internals.h"
+
+/* Bad block marker length */
+#define FMC2_BBM_LEN			2
+
+/* ECC step size */
+#define FMC2_ECC_STEP_SIZE		512
+
+/* Command delay */
+#define FMC2_RB_DELAY_US		30
+
+/* Max chip enable */
+#define FMC2_MAX_CE			2
+
+/* Timings */
+#define FMC2_THIZ			1
+#define FMC2_TIO			8000
+#define FMC2_TSYNC			3000
+#define FMC2_PCR_TIMING_MASK		0xf
+#define FMC2_PMEM_PATT_TIMING_MASK	0xff
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1			0x0
+#define FMC2_PCR			0x80
+#define FMC2_SR				0x84
+#define FMC2_PMEM			0x88
+#define FMC2_PATT			0x8c
+#define FMC2_HECCR			0x94
+#define FMC2_BCHISR			0x254
+#define FMC2_BCHICR			0x258
+#define FMC2_BCHPBR1			0x260
+#define FMC2_BCHPBR2			0x264
+#define FMC2_BCHPBR3			0x268
+#define FMC2_BCHPBR4			0x26c
+#define FMC2_BCHDSR0			0x27c
+#define FMC2_BCHDSR1			0x280
+#define FMC2_BCHDSR2			0x284
+#define FMC2_BCHDSR3			0x288
+#define FMC2_BCHDSR4			0x28c
+
+/* Register: FMC2_BCR1 */
+#define FMC2_BCR1_FMC2EN		BIT(31)
+
+/* Register: FMC2_PCR */
+#define FMC2_PCR_PWAITEN		BIT(1)
+#define FMC2_PCR_PBKEN			BIT(2)
+#define FMC2_PCR_PWID			GENMASK(5, 4)
+#define FMC2_PCR_PWID_BUSWIDTH_8	0
+#define FMC2_PCR_PWID_BUSWIDTH_16	1
+#define FMC2_PCR_ECCEN			BIT(6)
+#define FMC2_PCR_ECCALG			BIT(8)
+#define FMC2_PCR_TCLR			GENMASK(12, 9)
+#define FMC2_PCR_TCLR_DEFAULT		0xf
+#define FMC2_PCR_TAR			GENMASK(16, 13)
+#define FMC2_PCR_TAR_DEFAULT		0xf
+#define FMC2_PCR_ECCSS			GENMASK(19, 17)
+#define FMC2_PCR_ECCSS_512		1
+#define FMC2_PCR_ECCSS_2048		3
+#define FMC2_PCR_BCHECC			BIT(24)
+#define FMC2_PCR_WEN			BIT(25)
+
+/* Register: FMC2_SR */
+#define FMC2_SR_NWRF			BIT(6)
+
+/* Register: FMC2_PMEM */
+#define FMC2_PMEM_MEMSET		GENMASK(7, 0)
+#define FMC2_PMEM_MEMWAIT		GENMASK(15, 8)
+#define FMC2_PMEM_MEMHOLD		GENMASK(23, 16)
+#define FMC2_PMEM_MEMHIZ		GENMASK(31, 24)
+#define FMC2_PMEM_DEFAULT		0x0a0a0a0a
+
+/* Register: FMC2_PATT */
+#define FMC2_PATT_ATTSET		GENMASK(7, 0)
+#define FMC2_PATT_ATTWAIT		GENMASK(15, 8)
+#define FMC2_PATT_ATTHOLD		GENMASK(23, 16)
+#define FMC2_PATT_ATTHIZ		GENMASK(31, 24)
+#define FMC2_PATT_DEFAULT		0x0a0a0a0a
+
+/* Register: FMC2_BCHISR */
+#define FMC2_BCHISR_DERF		BIT(1)
+#define FMC2_BCHISR_EPBRF		BIT(4)
+
+/* Register: FMC2_BCHICR */
+#define FMC2_BCHICR_CLEAR_IRQ		GENMASK(4, 0)
+
+/* Register: FMC2_BCHDSR0 */
+#define FMC2_BCHDSR0_DUE		BIT(0)
+#define FMC2_BCHDSR0_DEF		BIT(1)
+#define FMC2_BCHDSR0_DEN		GENMASK(7, 4)
+
+/* Register: FMC2_BCHDSR1 */
+#define FMC2_BCHDSR1_EBP1		GENMASK(12, 0)
+#define FMC2_BCHDSR1_EBP2		GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR2 */
+#define FMC2_BCHDSR2_EBP3		GENMASK(12, 0)
+#define FMC2_BCHDSR2_EBP4		GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR3 */
+#define FMC2_BCHDSR3_EBP5		GENMASK(12, 0)
+#define FMC2_BCHDSR3_EBP6		GENMASK(28, 16)
+
+/* Register: FMC2_BCHDSR4 */
+#define FMC2_BCHDSR4_EBP7		GENMASK(12, 0)
+#define FMC2_BCHDSR4_EBP8		GENMASK(28, 16)
+
+#define FMC2_TIMEOUT_5S			5000000
+
+enum stm32_fmc2_ecc {
+	FMC2_ECC_HAM = 1,
+	FMC2_ECC_BCH4 = 4,
+	FMC2_ECC_BCH8 = 8
+};
+
+struct stm32_fmc2_timings {
+	u8 tclr;
+	u8 tar;
+	u8 thiz;
+	u8 twait;
+	u8 thold_mem;
+	u8 tset_mem;
+	u8 thold_att;
+	u8 tset_att;
+};
+
+struct stm32_fmc2_nand {
+	struct nand_chip chip;
+	struct stm32_fmc2_timings timings;
+	int wp_gpio;
+	int ncs;
+	int cs_used[FMC2_MAX_CE];
+};
+
+static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip)
+{
+	return container_of(chip, struct stm32_fmc2_nand, chip);
+}
+
+struct stm32_fmc2_nfc {
+	struct stm32_fmc2_nand nand;
+	struct nand_ecclayout ecclayout;
+	void __iomem *io_base;
+	void __iomem *data_base[FMC2_MAX_CE];
+	void __iomem *cmd_base[FMC2_MAX_CE];
+	void __iomem *addr_base[FMC2_MAX_CE];
+	struct clk *clk;
+
+	u8 cs_assigned;
+	int cs_sel;
+};
+
+static void stm32_fmc2_nfc_timings_init(struct nand_chip *chip)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+	struct stm32_fmc2_timings *timings = &nand->timings;
+	u32 pmem, patt;
+
+	/* Set tclr/tar timings */
+	clrsetbits_le32(nfc->io_base + FMC2_PCR,
+			FMC2_PCR_TCLR | FMC2_PCR_TAR,
+			FIELD_PREP(FMC2_PCR_TCLR, timings->tclr) |
+			FIELD_PREP(FMC2_PCR_TAR, timings->tar));
+
+	/* Set tset/twait/thold/thiz timings in common bank */
+	pmem = FIELD_PREP(FMC2_PMEM_MEMSET, timings->tset_mem);
+	pmem |= FIELD_PREP(FMC2_PMEM_MEMWAIT, timings->twait);
+	pmem |= FIELD_PREP(FMC2_PMEM_MEMHOLD, timings->thold_mem);
+	pmem |= FIELD_PREP(FMC2_PMEM_MEMHIZ, timings->thiz);
+	writel(pmem, nfc->io_base + FMC2_PMEM);
+
+	/* Set tset/twait/thold/thiz timings in attribut bank */
+	patt = FIELD_PREP(FMC2_PATT_ATTSET, timings->tset_att);
+	patt |= FIELD_PREP(FMC2_PATT_ATTWAIT, timings->twait);
+	patt |= FIELD_PREP(FMC2_PATT_ATTHOLD, timings->thold_att);
+	patt |= FIELD_PREP(FMC2_PATT_ATTHIZ, timings->thiz);
+	writel(patt, nfc->io_base + FMC2_PATT);
+}
+
+static void stm32_fmc2_nfc_setup(struct nand_chip *chip)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	u32 pcr = 0, pcr_mask;
+
+	/* Configure ECC algorithm (default configuration is Hamming) */
+	pcr_mask = FMC2_PCR_ECCALG;
+	pcr_mask |= FMC2_PCR_BCHECC;
+	if (chip->ecc.strength == FMC2_ECC_BCH8) {
+		pcr |= FMC2_PCR_ECCALG;
+		pcr |= FMC2_PCR_BCHECC;
+	} else if (chip->ecc.strength == FMC2_ECC_BCH4) {
+		pcr |= FMC2_PCR_ECCALG;
+	}
+
+	/* Set buswidth */
+	pcr_mask |= FMC2_PCR_PWID;
+	if (chip->options & NAND_BUSWIDTH_16)
+		pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16);
+
+	/* Set ECC sector size */
+	pcr_mask |= FMC2_PCR_ECCSS;
+	pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_512);
+
+	clrsetbits_le32(nfc->io_base + FMC2_PCR, pcr_mask, pcr);
+}
+
+static void stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+
+	if (chipnr < 0 || chipnr >= nand->ncs)
+		return;
+
+	if (nand->cs_used[chipnr] == nfc->cs_sel)
+		return;
+
+	nfc->cs_sel = nand->cs_used[chipnr];
+	chip->legacy.IO_ADDR_R = nfc->data_base[nfc->cs_sel];
+	chip->legacy.IO_ADDR_W = nfc->data_base[nfc->cs_sel];
+
+	stm32_fmc2_nfc_setup(chip);
+	stm32_fmc2_nfc_timings_init(chip);
+}
+
+static void stm32_fmc2_nfc_set_buswidth_16(struct stm32_fmc2_nfc *nfc,
+					   bool set)
+{
+	u32 pcr;
+
+	pcr = set ? FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16) :
+		    FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_8);
+
+	clrsetbits_le32(nfc->io_base + FMC2_PCR, FMC2_PCR_PWID, pcr);
+}
+
+static void stm32_fmc2_nfc_set_ecc(struct stm32_fmc2_nfc *nfc, bool enable)
+{
+	clrsetbits_le32(nfc->io_base + FMC2_PCR, FMC2_PCR_ECCEN,
+			enable ? FMC2_PCR_ECCEN : 0);
+}
+
+static void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc)
+{
+	writel(FMC2_BCHICR_CLEAR_IRQ, nfc->io_base + FMC2_BCHICR);
+}
+
+static void stm32_fmc2_nfc_cmd_ctrl(struct nand_chip *chip, int cmd,
+				    unsigned int ctrl)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+
+	if (cmd == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE) {
+		writeb(cmd, nfc->cmd_base[nfc->cs_sel]);
+		return;
+	}
+
+	writeb(cmd, nfc->addr_base[nfc->cs_sel]);
+}
+
+/*
+ * Enable ECC logic and reset syndrome/parity bits previously calculated
+ * Syndrome/parity bits is cleared by setting the ECCEN bit to 0
+ */
+static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+
+	stm32_fmc2_nfc_set_ecc(nfc, false);
+
+	if (chip->ecc.strength != FMC2_ECC_HAM) {
+		clrsetbits_le32(nfc->io_base + FMC2_PCR, FMC2_PCR_WEN,
+				mode == NAND_ECC_WRITE ? FMC2_PCR_WEN : 0);
+
+		stm32_fmc2_nfc_clear_bch_irq(nfc);
+	}
+
+	stm32_fmc2_nfc_set_ecc(nfc, true);
+}
+
+/*
+ * ECC Hamming calculation
+ * ECC is 3 bytes for 512 bytes of data (supports error correction up to
+ * max of 1-bit)
+ */
+static int stm32_fmc2_nfc_ham_calculate(struct nand_chip *chip, const u8 *data,
+					u8 *ecc)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	u32 heccr, sr;
+	int ret;
+
+	ret = readl_poll_timeout(nfc->io_base + FMC2_SR, sr,
+				 sr & FMC2_SR_NWRF, FMC2_TIMEOUT_5S);
+	if (ret < 0) {
+		pr_err("Ham timeout\n");
+		return ret;
+	}
+
+	heccr = readl(nfc->io_base + FMC2_HECCR);
+
+	ecc[0] = heccr;
+	ecc[1] = heccr >> 8;
+	ecc[2] = heccr >> 16;
+
+	stm32_fmc2_nfc_set_ecc(nfc, false);
+
+	return 0;
+}
+
+static int stm32_fmc2_nfc_ham_correct(struct nand_chip *chip, u8 *dat,
+				      u8 *read_ecc, u8 *calc_ecc)
+{
+	u8 bit_position = 0, b0, b1, b2;
+	u32 byte_addr = 0, b;
+	u32 i, shifting = 1;
+
+	/* Indicate which bit and byte is faulty (if any) */
+	b0 = read_ecc[0] ^ calc_ecc[0];
+	b1 = read_ecc[1] ^ calc_ecc[1];
+	b2 = read_ecc[2] ^ calc_ecc[2];
+	b = b0 | (b1 << 8) | (b2 << 16);
+
+	/* No errors */
+	if (likely(!b))
+		return 0;
+
+	/* Calculate bit position */
+	for (i = 0; i < 3; i++) {
+		switch (b % 4) {
+		case 2:
+			bit_position += shifting;
+		case 1:
+			break;
+		default:
+			return -EBADMSG;
+		}
+		shifting <<= 1;
+		b >>= 2;
+	}
+
+	/* Calculate byte position */
+	shifting = 1;
+	for (i = 0; i < 9; i++) {
+		switch (b % 4) {
+		case 2:
+			byte_addr += shifting;
+		case 1:
+			break;
+		default:
+			return -EBADMSG;
+		}
+		shifting <<= 1;
+		b >>= 2;
+	}
+
+	/* Flip the bit */
+	dat[byte_addr] ^= (1 << bit_position);
+
+	return 1;
+}
+
+/*
+ * ECC BCH calculation and correction
+ * ECC is 7/13 bytes for 512 bytes of data (supports error correction up to
+ * max of 4-bit/8-bit)
+ */
+static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data,
+					u8 *ecc)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	u32 bchpbr, bchisr;
+	int ret;
+
+	/* Wait until the BCH code is ready */
+	ret = readl_poll_timeout(nfc->io_base + FMC2_BCHISR, bchisr,
+				 bchisr & FMC2_BCHISR_EPBRF, FMC2_TIMEOUT_5S);
+	if (ret < 0) {
+		pr_err("Bch timeout\n");
+		return ret;
+	}
+
+	/* Read parity bits */
+	bchpbr = readl(nfc->io_base + FMC2_BCHPBR1);
+	ecc[0] = bchpbr;
+	ecc[1] = bchpbr >> 8;
+	ecc[2] = bchpbr >> 16;
+	ecc[3] = bchpbr >> 24;
+
+	bchpbr = readl(nfc->io_base + FMC2_BCHPBR2);
+	ecc[4] = bchpbr;
+	ecc[5] = bchpbr >> 8;
+	ecc[6] = bchpbr >> 16;
+
+	if (chip->ecc.strength == FMC2_ECC_BCH8) {
+		ecc[7] = bchpbr >> 24;
+
+		bchpbr = readl(nfc->io_base + FMC2_BCHPBR3);
+		ecc[8] = bchpbr;
+		ecc[9] = bchpbr >> 8;
+		ecc[10] = bchpbr >> 16;
+		ecc[11] = bchpbr >> 24;
+
+		bchpbr = readl(nfc->io_base + FMC2_BCHPBR4);
+		ecc[12] = bchpbr;
+	}
+
+	stm32_fmc2_nfc_set_ecc(nfc, false);
+
+	return 0;
+}
+
+static int stm32_fmc2_nfc_bch_correct(struct nand_chip *chip, u8 *dat,
+				      u8 *read_ecc, u8 *calc_ecc)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	u32 bchdsr0, bchdsr1, bchdsr2, bchdsr3, bchdsr4, bchisr;
+	u16 pos[8];
+	int i, ret, den, eccsize = chip->ecc.size;
+	unsigned int nb_errs = 0;
+
+	/* Wait until the decoding error is ready */
+	ret = readl_poll_timeout(nfc->io_base + FMC2_BCHISR, bchisr,
+				 bchisr & FMC2_BCHISR_DERF, FMC2_TIMEOUT_5S);
+	if (ret < 0) {
+		pr_err("Bch timeout\n");
+		return ret;
+	}
+
+	bchdsr0 = readl(nfc->io_base + FMC2_BCHDSR0);
+	bchdsr1 = readl(nfc->io_base + FMC2_BCHDSR1);
+	bchdsr2 = readl(nfc->io_base + FMC2_BCHDSR2);
+	bchdsr3 = readl(nfc->io_base + FMC2_BCHDSR3);
+	bchdsr4 = readl(nfc->io_base + FMC2_BCHDSR4);
+
+	stm32_fmc2_nfc_set_ecc(nfc, false);
+
+	/* No errors found */
+	if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF)))
+		return 0;
+
+	/* Too many errors detected */
+	if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE))
+		return -EBADMSG;
+
+	pos[0] = FIELD_GET(FMC2_BCHDSR1_EBP1, bchdsr1);
+	pos[1] = FIELD_GET(FMC2_BCHDSR1_EBP2, bchdsr1);
+	pos[2] = FIELD_GET(FMC2_BCHDSR2_EBP3, bchdsr2);
+	pos[3] = FIELD_GET(FMC2_BCHDSR2_EBP4, bchdsr2);
+	pos[4] = FIELD_GET(FMC2_BCHDSR3_EBP5, bchdsr3);
+	pos[5] = FIELD_GET(FMC2_BCHDSR3_EBP6, bchdsr3);
+	pos[6] = FIELD_GET(FMC2_BCHDSR4_EBP7, bchdsr4);
+	pos[7] = FIELD_GET(FMC2_BCHDSR4_EBP8, bchdsr4);
+
+	den = FIELD_GET(FMC2_BCHDSR0_DEN, bchdsr0);
+	for (i = 0; i < den; i++) {
+		if (pos[i] < eccsize * 8) {
+			__change_bit(pos[i], (unsigned long *)dat);
+			nb_errs++;
+		}
+	}
+
+	return nb_errs;
+}
+
+static int stm32_fmc2_nfc_read_page(struct nand_chip *chip, u8 *buf,
+				    int oob_required, int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	int i, s, stat, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	int eccstrength = chip->ecc.strength;
+	u8 *p = buf;
+	u8 *ecc_calc = chip->ecc.calc_buf;
+	u8 *ecc_code = chip->ecc.code_buf;
+	unsigned int max_bitflips = 0;
+
+	for (i = mtd->writesize + FMC2_BBM_LEN, s = 0; s < eccsteps;
+	     s++, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+		/* Read the nand page sector (512 bytes) */
+		chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, s * eccsize, -1);
+		chip->legacy.read_buf(chip, p, eccsize);
+
+		/* Read the corresponding ECC bytes */
+		chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, i, -1);
+		chip->legacy.read_buf(chip, ecc_code, eccbytes);
+
+		/* Correct the data */
+		stat = chip->ecc.correct(chip, p, ecc_code, ecc_calc);
+		if (stat == -EBADMSG)
+			/* Check for empty pages with bitflips */
+			stat = nand_check_erased_ecc_chunk(p, eccsize,
+							   ecc_code, eccbytes,
+							   NULL, 0,
+							   eccstrength);
+
+		if (stat < 0) {
+			mtd->ecc_stats.failed++;
+		} else {
+			mtd->ecc_stats.corrected += stat;
+			max_bitflips = max_t(unsigned int, max_bitflips, stat);
+		}
+	}
+
+	/* Read oob */
+	if (oob_required) {
+		chip->legacy.cmdfunc(chip, NAND_CMD_RNDOUT, mtd->writesize, -1);
+		chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
+	}
+
+	return max_bitflips;
+}
+
+static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc, bool has_parent)
+{
+	u32 pcr = readl(nfc->io_base + FMC2_PCR);
+
+	/* Set CS used to undefined */
+	nfc->cs_sel = -1;
+
+	/* Enable wait feature and nand flash memory bank */
+	pcr |= FMC2_PCR_PWAITEN;
+	pcr |= FMC2_PCR_PBKEN;
+
+	/* Set buswidth to 8 bits mode for identification */
+	pcr &= ~FMC2_PCR_PWID;
+
+	/* ECC logic is disabled */
+	pcr &= ~FMC2_PCR_ECCEN;
+
+	/* Default mode */
+	pcr &= ~FMC2_PCR_ECCALG;
+	pcr &= ~FMC2_PCR_BCHECC;
+	pcr &= ~FMC2_PCR_WEN;
+
+	/* Set default ECC sector size */
+	pcr &= ~FMC2_PCR_ECCSS;
+	pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_2048);
+
+	/* Set default tclr/tar timings */
+	pcr &= ~FMC2_PCR_TCLR;
+	pcr |= FIELD_PREP(FMC2_PCR_TCLR, FMC2_PCR_TCLR_DEFAULT);
+	pcr &= ~FMC2_PCR_TAR;
+	pcr |= FIELD_PREP(FMC2_PCR_TAR, FMC2_PCR_TAR_DEFAULT);
+
+	/* Enable FMC2 controller */
+	if (!has_parent)
+		setbits_le32(nfc->io_base + FMC2_BCR1, FMC2_BCR1_FMC2EN);
+
+	writel(pcr, nfc->io_base + FMC2_PCR);
+	writel(FMC2_PMEM_DEFAULT, nfc->io_base + FMC2_PMEM);
+	writel(FMC2_PATT_DEFAULT, nfc->io_base + FMC2_PATT);
+}
+
+static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip,
+					const struct nand_sdr_timings *sdrt)
+{
+	struct stm32_fmc2_nfc *nfc = (struct stm32_fmc2_nfc *)chip->priv;
+	struct stm32_fmc2_nand *nand = to_fmc2_nand(chip);
+	struct stm32_fmc2_timings *tims = &nand->timings;
+	unsigned long hclk = clk_get_rate(nfc->clk);
+	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
+	unsigned long timing, tar, tclr, thiz, twait;
+	unsigned long tset_mem, tset_att, thold_mem, thold_att;
+
+	tar = max_t(unsigned long, hclkp, sdrt->tAR_min);
+	timing = DIV_ROUND_UP(tar, hclkp) - 1;
+	tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
+
+	tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min);
+	timing = DIV_ROUND_UP(tclr, hclkp) - 1;
+	tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
+
+	tims->thiz = FMC2_THIZ;
+	thiz = (tims->thiz + 1) * hclkp;
+
+	/*
+	 * tWAIT > tRP
+	 * tWAIT > tWP
+	 * tWAIT > tREA + tIO
+	 */
+	twait = max_t(unsigned long, hclkp, sdrt->tRP_min);
+	twait = max_t(unsigned long, twait, sdrt->tWP_min);
+	twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO);
+	timing = DIV_ROUND_UP(twait, hclkp);
+	tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tSETUP_MEM > tCS - tWAIT
+	 * tSETUP_MEM > tALS - tWAIT
+	 * tSETUP_MEM > tDS - (tWAIT - tHIZ)
+	 */
+	tset_mem = hclkp;
+	if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait))
+		tset_mem = sdrt->tCS_min - twait;
+	if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait))
+		tset_mem = sdrt->tALS_min - twait;
+	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+	    (tset_mem < sdrt->tDS_min - (twait - thiz)))
+		tset_mem = sdrt->tDS_min - (twait - thiz);
+	timing = DIV_ROUND_UP(tset_mem, hclkp);
+	tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tHOLD_MEM > tCH
+	 * tHOLD_MEM > tREH - tSETUP_MEM
+	 * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
+	 */
+	thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min);
+	if (sdrt->tREH_min > tset_mem &&
+	    (thold_mem < sdrt->tREH_min - tset_mem))
+		thold_mem = sdrt->tREH_min - tset_mem;
+	if ((sdrt->tRC_min > tset_mem + twait) &&
+	    (thold_mem < sdrt->tRC_min - (tset_mem + twait)))
+		thold_mem = sdrt->tRC_min - (tset_mem + twait);
+	if ((sdrt->tWC_min > tset_mem + twait) &&
+	    (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
+		thold_mem = sdrt->tWC_min - (tset_mem + twait);
+	timing = DIV_ROUND_UP(thold_mem, hclkp);
+	tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tSETUP_ATT > tCS - tWAIT
+	 * tSETUP_ATT > tCLS - tWAIT
+	 * tSETUP_ATT > tALS - tWAIT
+	 * tSETUP_ATT > tRHW - tHOLD_MEM
+	 * tSETUP_ATT > tDS - (tWAIT - tHIZ)
+	 */
+	tset_att = hclkp;
+	if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait))
+		tset_att = sdrt->tCS_min - twait;
+	if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait))
+		tset_att = sdrt->tCLS_min - twait;
+	if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait))
+		tset_att = sdrt->tALS_min - twait;
+	if (sdrt->tRHW_min > thold_mem &&
+	    (tset_att < sdrt->tRHW_min - thold_mem))
+		tset_att = sdrt->tRHW_min - thold_mem;
+	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+	    (tset_att < sdrt->tDS_min - (twait - thiz)))
+		tset_att = sdrt->tDS_min - (twait - thiz);
+	timing = DIV_ROUND_UP(tset_att, hclkp);
+	tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+
+	/*
+	 * tHOLD_ATT > tALH
+	 * tHOLD_ATT > tCH
+	 * tHOLD_ATT > tCLH
+	 * tHOLD_ATT > tCOH
+	 * tHOLD_ATT > tDH
+	 * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
+	 * tHOLD_ATT > tADL - tSETUP_MEM
+	 * tHOLD_ATT > tWH - tSETUP_MEM
+	 * tHOLD_ATT > tWHR - tSETUP_MEM
+	 * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
+	 * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
+	 */
+	thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min);
+	thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min);
+	thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min);
+	thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min);
+	thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min);
+	if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
+	    (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
+		thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
+	if (sdrt->tADL_min > tset_mem &&
+	    (thold_att < sdrt->tADL_min - tset_mem))
+		thold_att = sdrt->tADL_min - tset_mem;
+	if (sdrt->tWH_min > tset_mem &&
+	    (thold_att < sdrt->tWH_min - tset_mem))
+		thold_att = sdrt->tWH_min - tset_mem;
+	if (sdrt->tWHR_min > tset_mem &&
+	    (thold_att < sdrt->tWHR_min - tset_mem))
+		thold_att = sdrt->tWHR_min - tset_mem;
+	if ((sdrt->tRC_min > tset_att + twait) &&
+	    (thold_att < sdrt->tRC_min - (tset_att + twait)))
+		thold_att = sdrt->tRC_min - (tset_att + twait);
+	if ((sdrt->tWC_min > tset_att + twait) &&
+	    (thold_att < sdrt->tWC_min - (tset_att + twait)))
+		thold_att = sdrt->tWC_min - (tset_att + twait);
+	timing = DIV_ROUND_UP(thold_att, hclkp);
+	tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
+}
+
+static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
+					  const struct nand_interface_config *cf)
+{
+	const struct nand_sdr_timings *sdrt;
+
+	sdrt = nand_get_sdr_timings(cf);
+	if (IS_ERR(sdrt))
+		return PTR_ERR(sdrt);
+
+	if (sdrt->tRC_min < 30000)
+		return -EOPNOTSUPP;
+
+	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
+		return 0;
+
+	stm32_fmc2_nfc_calc_timings(chip, sdrt);
+	stm32_fmc2_nfc_timings_init(chip);
+
+	return 0;
+}
+
+static void stm32_fmc2_nfc_nand_callbacks_setup(struct nand_chip *chip)
+{
+	chip->ecc.hwctl = stm32_fmc2_nfc_hwctl;
+
+	/*
+	 * Specific callbacks to read/write a page depending on
+	 * the algo used (Hamming, BCH).
+	 */
+	if (chip->ecc.strength == FMC2_ECC_HAM) {
+		/* Hamming is used */
+		chip->ecc.calculate = stm32_fmc2_nfc_ham_calculate;
+		chip->ecc.correct = stm32_fmc2_nfc_ham_correct;
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3;
+		chip->ecc.options |= NAND_ECC_GENERIC_ERASED_CHECK;
+		return;
+	}
+
+	/* BCH is used */
+	chip->ecc.read_page = stm32_fmc2_nfc_read_page;
+	chip->ecc.calculate = stm32_fmc2_nfc_bch_calculate;
+	chip->ecc.correct = stm32_fmc2_nfc_bch_correct;
+
+	if (chip->ecc.strength == FMC2_ECC_BCH8)
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13;
+	else
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7;
+}
+
+static int stm32_fmc2_nfc_calc_ecc_bytes(int step_size, int strength)
+{
+	/* Hamming */
+	if (strength == FMC2_ECC_HAM)
+		return 4;
+
+	/* BCH8 */
+	if (strength == FMC2_ECC_BCH8)
+		return 14;
+
+	/* BCH4 */
+	return 8;
+}
+
+NAND_ECC_CAPS_SINGLE(stm32_fmc2_nfc_ecc_caps, stm32_fmc2_nfc_calc_ecc_bytes,
+		     FMC2_ECC_STEP_SIZE,
+		     FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8);
+
+static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
+				      struct device_node *node)
+{
+	struct stm32_fmc2_nand *nand = &nfc->nand;
+	u32 cs[FMC2_MAX_CE];
+	enum of_gpio_flags flags;
+	int ret, i;
+
+	if (!of_get_property(node, "reg", &nand->ncs))
+		return -EINVAL;
+
+	nand->ncs /= sizeof(u32);
+	if (!nand->ncs) {
+		pr_err("Invalid reg property size\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(node, "reg", cs, nand->ncs);
+	if (ret < 0) {
+		pr_err("Could not retrieve reg property\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nand->ncs; i++) {
+		if (cs[i] >= FMC2_MAX_CE) {
+			pr_err("Invalid reg value: %d\n", nand->cs_used[i]);
+			return -EINVAL;
+		}
+
+		if (nfc->cs_assigned & BIT(cs[i])) {
+			pr_err("Cs already assigned: %d\n", nand->cs_used[i]);
+			return -EINVAL;
+		}
+
+		nfc->cs_assigned |= BIT(cs[i]);
+		nand->cs_used[i] = cs[i];
+	}
+
+	nand->wp_gpio = of_get_named_gpio_flags(node, "wp-gpios", 0, &flags);
+	if (gpio_is_valid(nand->wp_gpio)) {
+		gpio_request(nand->wp_gpio, "nand-wp");
+		gpio_direction_output(nand->wp_gpio, !(flags & OF_GPIO_ACTIVE_LOW));
+	}
+
+	return 0;
+}
+
+static int stm32_fmc2_nfc_parse_dt(struct device *dev,
+				   struct stm32_fmc2_nfc *nfc)
+{
+	struct device_node *child;
+	int ret, nchips = 0;
+
+	for_each_available_child_of_node(dev->of_node, child)
+		nchips++;
+
+	if (!nchips) {
+		dev_err(dev, "NAND chip not defined\n");
+		return -EINVAL;
+	}
+
+	if (nchips > 1) {
+		dev_err(dev, "Too many NAND chips defined\n");
+		return -EINVAL;
+	}
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		ret = stm32_fmc2_nfc_parse_child(nfc, child);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct device *stm32_fmc2_nfc_get_cdev(struct device *dev)
+{
+	struct device *pdev = dev->parent;
+	bool ebi_found = false;
+
+	if (pdev && of_device_is_compatible(pdev->of_node,
+					    "st,stm32mp1-fmc2-ebi"))
+		ebi_found = true;
+
+	if (of_device_is_compatible(dev->of_node, "st,stm32mp1-fmc2-nfc")) {
+		if (ebi_found)
+			return pdev;
+
+		return NULL;
+	}
+
+	if (!ebi_found)
+		return dev;
+
+	return NULL;
+}
+
+static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = {
+	.setup_interface = stm32_fmc2_nfc_setup_interface,
+};
+
+static int __init stm32_fmc2_nfc_probe(struct device *dev)
+{
+	struct stm32_fmc2_nfc *nfc;
+	struct nand_ecclayout *ecclayout;
+	struct device *cdev;
+	int oob_index, chip_cs, mem_region, ret;
+	unsigned int i;
+	int start_region = 0;
+	struct mtd_info *mtd;
+	struct nand_chip *nand;
+
+	cdev = stm32_fmc2_nfc_get_cdev(dev);
+	if (!cdev)
+		return -EINVAL;
+
+	nfc = kzalloc(sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return -ENOMEM;
+
+	ret = stm32_fmc2_nfc_parse_dt(dev, nfc);
+	if (ret)
+		goto out_kfree;
+
+	nfc->clk = clk_get(cdev, NULL);
+	if (IS_ERR(nfc->clk)) {
+		ret = PTR_ERR(nfc->clk);
+		goto out_kfree;
+	}
+
+	clk_enable(nfc->clk);
+
+	nfc->io_base = of_iomap(cdev->of_node, 0);
+	if (!nfc->io_base) {
+		ret = -ENOMEM;
+		goto out_clk;
+	}
+
+	ret = device_reset_us(dev, 2);
+	if (ret)
+		goto out_clk;
+
+	if (dev == cdev)
+		start_region = 1;
+
+	for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
+	     chip_cs++, mem_region += 3) {
+		if (!(nfc->cs_assigned & BIT(chip_cs)))
+			continue;
+
+		nfc->data_base[chip_cs] = of_iomap(dev->of_node, mem_region);
+		if (!nfc->data_base[chip_cs]) {
+			dev_err(dev, "Resource data_base not found for cs%d", chip_cs);
+			return ret;
+		}
+
+		nfc->cmd_base[chip_cs] = of_iomap(dev->of_node, mem_region + 1);
+		if (!nfc->cmd_base[chip_cs]) {
+			dev_err(dev, "Resource cmd_base not found for cs%d", chip_cs);
+			return ret;
+		}
+
+		nfc->addr_base[chip_cs] = of_iomap(dev->of_node, mem_region + 2);
+		if (!nfc->addr_base[chip_cs]) {
+			dev_err(dev, "Resource addr_base not found for cs%d", chip_cs);
+			return ret;
+		}
+	}
+
+	stm32_fmc2_nfc_init(nfc, dev != cdev);
+
+	nand = &nfc->nand.chip;
+	mtd = nand_to_mtd(nand);
+	nand->priv = nfc;
+	mtd->dev.parent = dev;
+
+	nand_controller_init(&nand->legacy.dummy_controller);
+	nand->legacy.dummy_controller.ops = &stm32_fmc2_nfc_controller_ops;
+
+	nand->legacy.select_chip = stm32_fmc2_nfc_select_chip;
+	nand->legacy.cmd_ctrl = stm32_fmc2_nfc_cmd_ctrl;
+	nand->legacy.chip_delay = FMC2_RB_DELAY_US;
+	nand->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
+
+	/* Default ECC settings */
+	nand->ecc.mode = NAND_ECC_HW;
+	nand->ecc.size = FMC2_ECC_STEP_SIZE;
+	nand->ecc.strength = FMC2_ECC_BCH8;
+
+	/* Disable Write Protect */
+	if (gpio_is_valid(nfc->nand.wp_gpio))
+		gpio_set_value(nfc->nand.wp_gpio, 0);
+
+	ret = nand_scan_ident(nand, nfc->nand.ncs, NULL);
+	if (ret)
+		return ret;
+
+	/*
+	 * Only NAND_ECC_HW mode is actually supported
+	 * Hamming => ecc.strength = 1
+	 * BCH4 => ecc.strength = 4
+	 * BCH8 => ecc.strength = 8
+	 * ECC sector size = 512
+	 */
+	if (nand->ecc.mode != NAND_ECC_HW) {
+		dev_err(dev, "Nand_ecc_mode is not well defined in the DT\n");
+		return -EINVAL;
+	}
+
+	ret = nand_ecc_choose_conf(nand, &stm32_fmc2_nfc_ecc_caps,
+				   mtd->oobsize - FMC2_BBM_LEN);
+	if (ret) {
+		dev_err(dev, "No valid ECC settings set\n");
+		return ret;
+	}
+
+	if (nand->bbt_options & NAND_BBT_USE_FLASH)
+		nand->bbt_options |= NAND_BBT_NO_OOB;
+
+	stm32_fmc2_nfc_nand_callbacks_setup(nand);
+
+	/* Define ECC layout */
+	ecclayout = &nfc->ecclayout;
+	ecclayout->eccbytes = nand->ecc.bytes *
+			      (mtd->writesize / nand->ecc.size);
+	oob_index = FMC2_BBM_LEN;
+	for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
+		ecclayout->eccpos[i] = oob_index;
+	ecclayout->oobfree->offset = oob_index;
+	ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
+	mtd_set_ecclayout(mtd, ecclayout);
+
+	stm32_fmc2_nfc_set_buswidth_16(nfc, nand->options & NAND_BUSWIDTH_16);
+
+	ret = nand_scan_tail(nand);
+	if (ret)
+		return ret;
+
+	return add_mtd_nand_device(mtd, "nand");
+
+out_clk:
+	clk_disable(nfc->clk);
+
+out_kfree:
+	kfree(nfc);
+
+	return ret;
+}
+
+static __maybe_unused struct of_device_id stm32_nand_dt_ids[] = {
+	{ .compatible = "st,stm32mp15-fmc2", },
+	{ .compatible = "st,stm32mp1-fmc2-nfc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_nand_dt_ids);
+
+static struct driver stm32_nand_driver = {
+	.name		= "stm32_fmc2_nfc",
+	.probe		= stm32_fmc2_nfc_probe,
+	.of_compatible	= DRV_OF_COMPAT(stm32_nand_dt_ids),
+};
+coredevice_platform_driver(stm32_nand_driver);
-- 
2.39.1




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

* Re: [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs
  2023-08-03 10:32 [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs Alexander Shiyan
  2023-08-03 10:32 ` [PATCH 2/2] mtd: nand: Add driver for NAND controller " Alexander Shiyan
@ 2023-08-07  6:59 ` Sascha Hauer
  2023-08-07  8:30   ` Alexander Shiyan
  1 sibling, 1 reply; 5+ messages in thread
From: Sascha Hauer @ 2023-08-07  6:59 UTC (permalink / raw)
  To: Alexander Shiyan; +Cc: barebox

On Thu, Aug 03, 2023 at 01:32:37PM +0300, Alexander Shiyan wrote:
> This adds support for FMC2 External Bus Interface on STM32MP SoCs.
> The original source is taken from the STMicroelectronics/u-boot repository [1].
> 
> [1] https://github.com/STMicroelectronics/u-boot/blob/v2022.10-stm32mp/drivers/memory/stm32-fmc2-ebi.c
> 
> Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
> ---
>  drivers/memory/Kconfig          |    9 +
>  drivers/memory/Makefile         |    1 +
>  drivers/memory/stm32-fmc2-ebi.c | 1063 +++++++++++++++++++++++++++++++
>  3 files changed, 1073 insertions(+)
>  create mode 100644 drivers/memory/stm32-fmc2-ebi.c

Applied, thanks

Sascha

> 
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index e18b452009..b0d99c3a54 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -23,4 +23,13 @@ config MC_TEGRA124
>  	  the Tegra124 SoC. This driver performs the necessary initialization
>  	  to provide a function GPU when the OS is running.
>  
> +config STM32_FMC2_EBI
> +	bool "Support for FMC2 External Bus Interface on STM32MP SoCs"
> +	depends on ARCH_STM32MP || COMPILE_TEST
> +	help
> +	  Select this option to enable the STM32 FMC2 External Bus Interface
> +	  controller. This driver configures the transactions with external
> +	  devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
> +	  SOCs containing the FMC2 External Bus Interface.
> +
>  endmenu
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index bdf8db66e8..67d3c47621 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -1,3 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  obj-$(CONFIG_ATMEL_EBI)		+= atmel-ebi.o
>  obj-$(CONFIG_MC_TEGRA124)	+= mc-tegra124.o
> +obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
> diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c
> new file mode 100644
> index 0000000000..ac2ea1b0e0
> --- /dev/null
> +++ b/drivers/memory/stm32-fmc2-ebi.c
> @@ -0,0 +1,1063 @@
> +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
> +/*
> + * Copyright (C) STMicroelectronics 2020
> + */
> +
> +#define pr_fmt(fmt) "stm32-fmc2-ebi: " fmt
> +
> +#include <common.h>
> +#include <init.h>
> +#include <of_address.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/reset.h>
> +
> +/* FMC2 Controller Registers */
> +#define FMC2_BCR1			0x0
> +#define FMC2_BTR1			0x4
> +#define FMC2_BCR(x)			((x) * 0x8 + FMC2_BCR1)
> +#define FMC2_BTR(x)			((x) * 0x8 + FMC2_BTR1)
> +#define FMC2_PCSCNTR			0x20
> +#define FMC2_BWTR1			0x104
> +#define FMC2_BWTR(x)			((x) * 0x8 + FMC2_BWTR1)
> +
> +/* Register: FMC2_BCR1 */
> +#define FMC2_BCR1_CCLKEN		BIT(20)
> +#define FMC2_BCR1_FMC2EN		BIT(31)
> +
> +/* Register: FMC2_BCRx */
> +#define FMC2_BCR_MBKEN			BIT(0)
> +#define FMC2_BCR_MUXEN			BIT(1)
> +#define FMC2_BCR_MTYP			GENMASK(3, 2)
> +#define FMC2_BCR_MWID			GENMASK(5, 4)
> +#define FMC2_BCR_FACCEN			BIT(6)
> +#define FMC2_BCR_BURSTEN		BIT(8)
> +#define FMC2_BCR_WAITPOL		BIT(9)
> +#define FMC2_BCR_WAITCFG		BIT(11)
> +#define FMC2_BCR_WREN			BIT(12)
> +#define FMC2_BCR_WAITEN			BIT(13)
> +#define FMC2_BCR_EXTMOD			BIT(14)
> +#define FMC2_BCR_ASYNCWAIT		BIT(15)
> +#define FMC2_BCR_CPSIZE			GENMASK(18, 16)
> +#define FMC2_BCR_CBURSTRW		BIT(19)
> +#define FMC2_BCR_NBLSET			GENMASK(23, 22)
> +
> +/* Register: FMC2_BTRx/FMC2_BWTRx */
> +#define FMC2_BXTR_ADDSET		GENMASK(3, 0)
> +#define FMC2_BXTR_ADDHLD		GENMASK(7, 4)
> +#define FMC2_BXTR_DATAST		GENMASK(15, 8)
> +#define FMC2_BXTR_BUSTURN		GENMASK(19, 16)
> +#define FMC2_BTR_CLKDIV			GENMASK(23, 20)
> +#define FMC2_BTR_DATLAT			GENMASK(27, 24)
> +#define FMC2_BXTR_ACCMOD		GENMASK(29, 28)
> +#define FMC2_BXTR_DATAHLD		GENMASK(31, 30)
> +
> +/* Register: FMC2_PCSCNTR */
> +#define FMC2_PCSCNTR_CSCOUNT		GENMASK(15, 0)
> +#define FMC2_PCSCNTR_CNTBEN(x)		BIT((x) + 16)
> +
> +#define FMC2_MAX_EBI_CE			4
> +#define FMC2_MAX_BANKS			5
> +
> +#define FMC2_BCR_CPSIZE_0		0x0
> +#define FMC2_BCR_CPSIZE_128		0x1
> +#define FMC2_BCR_CPSIZE_256		0x2
> +#define FMC2_BCR_CPSIZE_512		0x3
> +#define FMC2_BCR_CPSIZE_1024		0x4
> +
> +#define FMC2_BCR_MWID_8			0x0
> +#define FMC2_BCR_MWID_16		0x1
> +
> +#define FMC2_BCR_MTYP_SRAM		0x0
> +#define FMC2_BCR_MTYP_PSRAM		0x1
> +#define FMC2_BCR_MTYP_NOR		0x2
> +
> +#define FMC2_BXTR_EXTMOD_A		0x0
> +#define FMC2_BXTR_EXTMOD_B		0x1
> +#define FMC2_BXTR_EXTMOD_C		0x2
> +#define FMC2_BXTR_EXTMOD_D		0x3
> +
> +#define FMC2_BCR_NBLSET_MAX		0x3
> +#define FMC2_BXTR_ADDSET_MAX		0xf
> +#define FMC2_BXTR_ADDHLD_MAX		0xf
> +#define FMC2_BXTR_DATAST_MAX		0xff
> +#define FMC2_BXTR_BUSTURN_MAX		0xf
> +#define FMC2_BXTR_DATAHLD_MAX		0x3
> +#define FMC2_BTR_CLKDIV_MAX		0xf
> +#define FMC2_BTR_DATLAT_MAX		0xf
> +#define FMC2_PCSCNTR_CSCOUNT_MAX	0xff
> +
> +enum stm32_fmc2_ebi_bank {
> +	FMC2_EBI1 = 0,
> +	FMC2_EBI2,
> +	FMC2_EBI3,
> +	FMC2_EBI4,
> +	FMC2_NAND
> +};
> +
> +enum stm32_fmc2_ebi_register_type {
> +	FMC2_REG_BCR = 1,
> +	FMC2_REG_BTR,
> +	FMC2_REG_BWTR,
> +	FMC2_REG_PCSCNTR
> +};
> +
> +enum stm32_fmc2_ebi_transaction_type {
> +	FMC2_ASYNC_MODE_1_SRAM = 0,
> +	FMC2_ASYNC_MODE_1_PSRAM,
> +	FMC2_ASYNC_MODE_A_SRAM,
> +	FMC2_ASYNC_MODE_A_PSRAM,
> +	FMC2_ASYNC_MODE_2_NOR,
> +	FMC2_ASYNC_MODE_B_NOR,
> +	FMC2_ASYNC_MODE_C_NOR,
> +	FMC2_ASYNC_MODE_D_NOR,
> +	FMC2_SYNC_READ_SYNC_WRITE_PSRAM,
> +	FMC2_SYNC_READ_ASYNC_WRITE_PSRAM,
> +	FMC2_SYNC_READ_SYNC_WRITE_NOR,
> +	FMC2_SYNC_READ_ASYNC_WRITE_NOR
> +};
> +
> +enum stm32_fmc2_ebi_buswidth {
> +	FMC2_BUSWIDTH_8 = 8,
> +	FMC2_BUSWIDTH_16 = 16
> +};
> +
> +enum stm32_fmc2_ebi_cpsize {
> +	FMC2_CPSIZE_0 = 0,
> +	FMC2_CPSIZE_128 = 128,
> +	FMC2_CPSIZE_256 = 256,
> +	FMC2_CPSIZE_512 = 512,
> +	FMC2_CPSIZE_1024 = 1024
> +};
> +
> +struct stm32_fmc2_ebi {
> +	struct clk *clk;
> +	void __iomem *io_base;
> +	u8 bank_assigned;
> +};
> +
> +/*
> + * struct stm32_fmc2_prop - STM32 FMC2 EBI property
> + * @name: the device tree binding name of the property
> + * @bprop: indicate that it is a boolean property
> + * @mprop: indicate that it is a mandatory property
> + * @reg_type: the register that have to be modified
> + * @reg_mask: the bit that have to be modified in the selected register
> + *            in case of it is a boolean property
> + * @reset_val: the default value that have to be set in case the property
> + *             has not been defined in the device tree
> + * @check: this callback ckecks that the property is compliant with the
> + *         transaction type selected
> + * @calculate: this callback is called to calculate for exemple a timing
> + *             set in nanoseconds in the device tree in clock cycles or in
> + *             clock period
> + * @set: this callback applies the values in the registers
> + */
> +struct stm32_fmc2_prop {
> +	const char *name;
> +	bool bprop;
> +	bool mprop;
> +	int reg_type;
> +	u32 reg_mask;
> +	u32 reset_val;
> +	int (*check)(struct stm32_fmc2_ebi *ebi,
> +		     const struct stm32_fmc2_prop *prop, int cs);
> +	u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup);
> +	int (*set)(struct stm32_fmc2_ebi *ebi,
> +		   const struct stm32_fmc2_prop *prop,
> +		   int cs, u32 setup);
> +};
> +
> +static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi,
> +				    const struct stm32_fmc2_prop *prop,
> +				    int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +
> +	if (bcr & FMC2_BCR_MTYP)
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi,
> +					const struct stm32_fmc2_prop *prop,
> +					int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +	u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +
> +	if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi,
> +					   const struct stm32_fmc2_prop *prop,
> +					   int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +
> +	if (bcr & FMC2_BCR_BURSTEN)
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi,
> +					    const struct stm32_fmc2_prop *prop,
> +					    int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +
> +	if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi,
> +				       const struct stm32_fmc2_prop *prop,
> +				       int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +	u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> +
> +	if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi,
> +					     const struct stm32_fmc2_prop *prop,
> +					     int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +	u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
> +		   readl(ebi->io_base + FMC2_BWTR(cs)) :
> +		   readl(ebi->io_base + FMC2_BTR(cs));
> +	u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> +
> +	if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) &&
> +	    ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi,
> +					   const struct stm32_fmc2_prop *prop,
> +					   int cs)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +	u32 bcr1 = cs ? readl(ebi->io_base + FMC2_BCR1) : bcr;
> +
> +	if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN)))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static int stm32_fmc2_ebi_check_cclk(struct stm32_fmc2_ebi *ebi,
> +				     const struct stm32_fmc2_prop *prop,
> +				     int cs)
> +{
> +	if (cs)
> +		return -EINVAL;
> +
> +	return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
> +}
> +
> +static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi,
> +					     int cs, u32 setup)
> +{
> +	unsigned long hclk = clk_get_rate(ebi->clk);
> +	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
> +
> +	return DIV_ROUND_UP(setup * 1000, hclkp);
> +}
> +
> +static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
> +					   int cs, u32 setup)
> +{
> +	u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup);
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR1);
> +	u32 btr = bcr & FMC2_BCR1_CCLKEN || !cs ?
> +		  readl(ebi->io_base + FMC2_BTR1) :
> +		  readl(ebi->io_base + FMC2_BTR(cs));
> +	u32 clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1;
> +
> +	return DIV_ROUND_UP(nb_clk_cycles, clk_period);
> +}
> +
> +static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
> +{
> +	switch (reg_type) {
> +	case FMC2_REG_BCR:
> +		*reg = FMC2_BCR(cs);
> +		break;
> +	case FMC2_REG_BTR:
> +		*reg = FMC2_BTR(cs);
> +		break;
> +	case FMC2_REG_BWTR:
> +		*reg = FMC2_BWTR(cs);
> +		break;
> +	case FMC2_REG_PCSCNTR:
> +		*reg = FMC2_PCSCNTR;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_bit_field(struct stm32_fmc2_ebi *ebi,
> +					const struct stm32_fmc2_prop *prop,
> +					int cs, u32 setup)
> +{
> +	u32 reg;
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	clrsetbits_le32(ebi->io_base + reg, prop->reg_mask,
> +			setup ? prop->reg_mask : 0);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_trans_type(struct stm32_fmc2_ebi *ebi,
> +					 const struct stm32_fmc2_prop *prop,
> +					 int cs, u32 setup)
> +{
> +	u32 bcr_mask, bcr = FMC2_BCR_WREN;
> +	u32 btr_mask, btr = 0;
> +	u32 bwtr_mask, bwtr = 0;
> +
> +	bwtr_mask = FMC2_BXTR_ACCMOD;
> +	btr_mask = FMC2_BXTR_ACCMOD;
> +	bcr_mask = FMC2_BCR_MUXEN | FMC2_BCR_MTYP | FMC2_BCR_FACCEN |
> +		   FMC2_BCR_WREN | FMC2_BCR_WAITEN | FMC2_BCR_BURSTEN |
> +		   FMC2_BCR_EXTMOD | FMC2_BCR_CBURSTRW;
> +
> +	switch (setup) {
> +	case FMC2_ASYNC_MODE_1_SRAM:
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
> +		/*
> +		 * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		break;
> +	case FMC2_ASYNC_MODE_1_PSRAM:
> +		/*
> +		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> +		break;
> +	case FMC2_ASYNC_MODE_A_SRAM:
> +		/*
> +		 * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
> +		bcr |= FMC2_BCR_EXTMOD;
> +		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> +		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> +		break;
> +	case FMC2_ASYNC_MODE_A_PSRAM:
> +		/*
> +		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> +		bcr |= FMC2_BCR_EXTMOD;
> +		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> +		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> +		break;
> +	case FMC2_ASYNC_MODE_2_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN;
> +		break;
> +	case FMC2_ASYNC_MODE_B_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 1
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> +		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
> +		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
> +		break;
> +	case FMC2_ASYNC_MODE_C_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 2
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> +		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
> +		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
> +		break;
> +	case FMC2_ASYNC_MODE_D_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 3
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> +		btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> +		bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> +		break;
> +	case FMC2_SYNC_READ_SYNC_WRITE_PSRAM:
> +		/*
> +		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> +		bcr |= FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
> +		break;
> +	case FMC2_SYNC_READ_ASYNC_WRITE_PSRAM:
> +		/*
> +		 * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> +		bcr |= FMC2_BCR_BURSTEN;
> +		break;
> +	case FMC2_SYNC_READ_SYNC_WRITE_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
> +		break;
> +	case FMC2_SYNC_READ_ASYNC_WRITE_NOR:
> +		/*
> +		 * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
> +		 * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> +		 */
> +		bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> +		bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN;
> +		break;
> +	default:
> +		/* Type of transaction not supported */
> +		return -EINVAL;
> +	}
> +
> +	if (bcr & FMC2_BCR_EXTMOD)
> +		clrsetbits_le32(ebi->io_base + FMC2_BWTR(cs),
> +				bwtr_mask, bwtr);
> +	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), btr_mask, btr);
> +	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), bcr_mask, bcr);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_buswidth(struct stm32_fmc2_ebi *ebi,
> +				       const struct stm32_fmc2_prop *prop,
> +				       int cs, u32 setup)
> +{
> +	u32 val;
> +
> +	switch (setup) {
> +	case FMC2_BUSWIDTH_8:
> +		val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_8);
> +		break;
> +	case FMC2_BUSWIDTH_16:
> +		val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_16);
> +		break;
> +	default:
> +		/* Buswidth not supported */
> +		return -EINVAL;
> +	}
> +
> +	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MWID, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_cpsize(struct stm32_fmc2_ebi *ebi,
> +				     const struct stm32_fmc2_prop *prop,
> +				     int cs, u32 setup)
> +{
> +	u32 val;
> +
> +	switch (setup) {
> +	case FMC2_CPSIZE_0:
> +		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0);
> +		break;
> +	case FMC2_CPSIZE_128:
> +		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128);
> +		break;
> +	case FMC2_CPSIZE_256:
> +		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256);
> +		break;
> +	case FMC2_CPSIZE_512:
> +		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512);
> +		break;
> +	case FMC2_CPSIZE_1024:
> +		val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024);
> +		break;
> +	default:
> +		/* Cpsize not supported */
> +		return -EINVAL;
> +	}
> +
> +	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_CPSIZE, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_bl_setup(struct stm32_fmc2_ebi *ebi,
> +				       const struct stm32_fmc2_prop *prop,
> +				       int cs, u32 setup)
> +{
> +	u32 val;
> +
> +	val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX);
> +	val = FIELD_PREP(FMC2_BCR_NBLSET, val);
> +	clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_NBLSET, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi,
> +					    const struct stm32_fmc2_prop *prop,
> +					    int cs, u32 setup)
> +{
> +	u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +	u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
> +		   readl(ebi->io_base + FMC2_BWTR(cs)) :
> +		   readl(ebi->io_base + FMC2_BTR(cs));
> +	u32 reg, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)
> +		val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX);
> +	else
> +		val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX);
> +	val = FIELD_PREP(FMC2_BXTR_ADDSET, val);
> +	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDSET, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi,
> +					   const struct stm32_fmc2_prop *prop,
> +					   int cs, u32 setup)
> +{
> +	u32 val, reg;
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX);
> +	val = FIELD_PREP(FMC2_BXTR_ADDHLD, val);
> +	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDHLD, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi,
> +					 const struct stm32_fmc2_prop *prop,
> +					 int cs, u32 setup)
> +{
> +	u32 val, reg;
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX);
> +	val = FIELD_PREP(FMC2_BXTR_DATAST, val);
> +	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAST, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi,
> +					     const struct stm32_fmc2_prop *prop,
> +					     int cs, u32 setup)
> +{
> +	u32 val, reg;
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0;
> +	val = FIELD_PREP(FMC2_BXTR_BUSTURN, val);
> +	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_BUSTURN, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi,
> +					const struct stm32_fmc2_prop *prop,
> +					int cs, u32 setup)
> +{
> +	u32 val, reg;
> +	int ret;
> +
> +	ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> +	if (ret)
> +		return ret;
> +
> +	if (prop->reg_type == FMC2_REG_BWTR)
> +		val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0;
> +	else
> +		val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX);
> +	val = FIELD_PREP(FMC2_BXTR_DATAHLD, val);
> +	clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAHLD, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi,
> +					 const struct stm32_fmc2_prop *prop,
> +					 int cs, u32 setup)
> +{
> +	u32 val;
> +
> +	val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1;
> +	val = FIELD_PREP(FMC2_BTR_CLKDIV, val);
> +	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_CLKDIV, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi,
> +					   const struct stm32_fmc2_prop *prop,
> +					   int cs, u32 setup)
> +{
> +	u32 val;
> +
> +	val = setup > 1 ? min_t(u32, setup - 2, FMC2_BTR_DATLAT_MAX) : 0;
> +	val = FIELD_PREP(FMC2_BTR_DATLAT, val);
> +	clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_DATLAT, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
> +					    const struct stm32_fmc2_prop *prop,
> +					    int cs, u32 setup)
> +{
> +	u32 old_val, new_val, pcscntr;
> +
> +	if (setup < 1)
> +		return 0;
> +
> +	pcscntr = readl(ebi->io_base + FMC2_PCSCNTR);
> +
> +	/* Enable counter for the bank */
> +	setbits_le32(ebi->io_base + FMC2_PCSCNTR, FMC2_PCSCNTR_CNTBEN(cs));
> +
> +	new_val = min_t(u32, setup - 1, FMC2_PCSCNTR_CSCOUNT_MAX);
> +	old_val = FIELD_GET(FMC2_PCSCNTR_CSCOUNT, pcscntr);
> +	if (old_val && new_val > old_val)
> +		/* Keep current counter value */
> +		return 0;
> +
> +	new_val = FIELD_PREP(FMC2_PCSCNTR_CSCOUNT, new_val);
> +	clrsetbits_le32(ebi->io_base + FMC2_PCSCNTR,
> +			FMC2_PCSCNTR_CSCOUNT, new_val);
> +
> +	return 0;
> +}
> +
> +static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
> +	/* st,fmc2-ebi-cs-trans-type must be the first property */
> +	{
> +		.name = "st,fmc2-ebi-cs-transaction-type",
> +		.mprop = true,
> +		.set = stm32_fmc2_ebi_set_trans_type,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-cclk-enable",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR1_CCLKEN,
> +		.check = stm32_fmc2_ebi_check_cclk,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-mux-enable",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR_MUXEN,
> +		.check = stm32_fmc2_ebi_check_mux,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-buswidth",
> +		.reset_val = FMC2_BUSWIDTH_16,
> +		.set = stm32_fmc2_ebi_set_buswidth,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-waitpol-high",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR_WAITPOL,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-waitcfg-enable",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR_WAITCFG,
> +		.check = stm32_fmc2_ebi_check_waitcfg,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-wait-enable",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR_WAITEN,
> +		.check = stm32_fmc2_ebi_check_sync_trans,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-asyncwait-enable",
> +		.bprop = true,
> +		.reg_type = FMC2_REG_BCR,
> +		.reg_mask = FMC2_BCR_ASYNCWAIT,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.set = stm32_fmc2_ebi_set_bit_field,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-cpsize",
> +		.check = stm32_fmc2_ebi_check_cpsize,
> +		.set = stm32_fmc2_ebi_set_cpsize,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-byte-lane-setup-ns",
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_bl_setup,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-address-setup-ns",
> +		.reg_type = FMC2_REG_BTR,
> +		.reset_val = FMC2_BXTR_ADDSET_MAX,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_address_setup,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-address-hold-ns",
> +		.reg_type = FMC2_REG_BTR,
> +		.reset_val = FMC2_BXTR_ADDHLD_MAX,
> +		.check = stm32_fmc2_ebi_check_address_hold,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_address_hold,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-data-setup-ns",
> +		.reg_type = FMC2_REG_BTR,
> +		.reset_val = FMC2_BXTR_DATAST_MAX,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_data_setup,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-bus-turnaround-ns",
> +		.reg_type = FMC2_REG_BTR,
> +		.reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_bus_turnaround,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-data-hold-ns",
> +		.reg_type = FMC2_REG_BTR,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_data_hold,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-clk-period-ns",
> +		.reset_val = FMC2_BTR_CLKDIV_MAX + 1,
> +		.check = stm32_fmc2_ebi_check_clk_period,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_clk_period,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-data-latency-ns",
> +		.check = stm32_fmc2_ebi_check_sync_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clk_period,
> +		.set = stm32_fmc2_ebi_set_data_latency,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-write-address-setup-ns",
> +		.reg_type = FMC2_REG_BWTR,
> +		.reset_val = FMC2_BXTR_ADDSET_MAX,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_address_setup,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-write-address-hold-ns",
> +		.reg_type = FMC2_REG_BWTR,
> +		.reset_val = FMC2_BXTR_ADDHLD_MAX,
> +		.check = stm32_fmc2_ebi_check_address_hold,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_address_hold,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-write-data-setup-ns",
> +		.reg_type = FMC2_REG_BWTR,
> +		.reset_val = FMC2_BXTR_DATAST_MAX,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_data_setup,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-write-bus-turnaround-ns",
> +		.reg_type = FMC2_REG_BWTR,
> +		.reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_bus_turnaround,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-write-data-hold-ns",
> +		.reg_type = FMC2_REG_BWTR,
> +		.check = stm32_fmc2_ebi_check_async_trans,
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_data_hold,
> +	},
> +	{
> +		.name = "st,fmc2-ebi-cs-max-low-pulse-ns",
> +		.calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> +		.set = stm32_fmc2_ebi_set_max_low_pulse,
> +	},
> +};
> +
> +static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi,
> +				     struct device_node *node,
> +				     const struct stm32_fmc2_prop *prop,
> +				     int cs)
> +{
> +	u32 setup = 0;
> +
> +	if (!prop->set) {
> +		pr_err("property %s is not well defined\n", prop->name);
> +		return -EINVAL;
> +	}
> +
> +	if (prop->check && prop->check(ebi, prop, cs))
> +		/* Skip this property */
> +		return 0;
> +
> +	if (prop->bprop) {
> +		bool bprop;
> +
> +		bprop = of_property_read_bool(node, prop->name);
> +		if (prop->mprop && !bprop) {
> +			pr_err("mandatory property %s not defined in the device tree\n",
> +			       prop->name);
> +			return -EINVAL;
> +		}
> +
> +		if (bprop)
> +			setup = 1;
> +	} else {
> +		u32 val;
> +		int ret;
> +
> +		ret = of_property_read_u32(node, prop->name, &val);
> +		if (prop->mprop && ret) {
> +			pr_err("mandatory property %s not defined in the device tree\n",
> +			       prop->name);
> +			return ret;
> +		}
> +
> +		if (ret)
> +			setup = prop->reset_val;
> +		else if (prop->calculate)
> +			setup = prop->calculate(ebi, cs, val);
> +		else
> +			setup = val;
> +	}
> +
> +	return prop->set(ebi, prop, cs, setup);
> +}
> +
> +static void stm32_fmc2_ebi_enable_bank(struct stm32_fmc2_ebi *ebi, int cs)
> +{
> +	setbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
> +}
> +
> +static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs)
> +{
> +	clrbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
> +}
> +
> +/* NWAIT signal can not be connected to EBI controller and NAND controller */
> +static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
> +{
> +	unsigned int cs;
> +	u32 bcr;
> +
> +	for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
> +		if (!(ebi->bank_assigned & BIT(cs)))
> +			continue;
> +
> +		bcr = readl(ebi->io_base + FMC2_BCR(cs));
> +		if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
> +		    ebi->bank_assigned & BIT(FMC2_NAND))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
> +{
> +	setbits_le32(ebi->io_base + FMC2_BCR1, FMC2_BCR1_FMC2EN);
> +}
> +
> +static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
> +				   struct device_node *node, u32 cs)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	stm32_fmc2_ebi_disable_bank(ebi, cs);
> +
> +	for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) {
> +		const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i];
> +
> +		ret = stm32_fmc2_ebi_parse_prop(ebi, node, p, cs);
> +		if (ret) {
> +			pr_err("property %s could not be set: %d\n", p->name, ret);
> +			return ret;
> +		}
> +	}
> +
> +	stm32_fmc2_ebi_enable_bank(ebi, cs);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_ebi_parse_dt(struct device *dev,
> +				   struct stm32_fmc2_ebi *ebi)
> +{
> +	struct device_node *child;
> +	bool child_found = false;
> +	u32 bank;
> +	int ret;
> +
> +	for_each_available_child_of_node(dev->of_node, child) {
> +		ret = of_property_read_u32(child, "reg", &bank);
> +		if (ret) {
> +			dev_err(dev, "could not retrieve reg property: %d\n", ret);
> +			return ret;
> +		}
> +
> +		if (bank >= FMC2_MAX_BANKS) {
> +			dev_err(dev, "invalid reg value: %d\n", bank);
> +			return -EINVAL;
> +		}
> +
> +		if (ebi->bank_assigned & BIT(bank)) {
> +			dev_err(dev, "bank already assigned: %d\n", bank);
> +			return -EINVAL;
> +		}
> +
> +		if (bank < FMC2_MAX_EBI_CE) {
> +			ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
> +			if (ret) {
> +				dev_err(dev, "setup chip select %d failed: %d\n", bank, ret);
> +				return ret;
> +			}
> +		}
> +
> +		ebi->bank_assigned |= BIT(bank);
> +		child_found = true;
> +	}
> +
> +	if (!child_found) {
> +		dev_warn(dev, "no subnodes found.\n");
> +		return -ENODEV;
> +	}
> +
> +	if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) {
> +		dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n");
> +		return -EINVAL;
> +	}
> +
> +	stm32_fmc2_ebi_enable(ebi);
> +
> +	return 0;
> +}
> +
> +static int __init stm32_fmc2_ebi_probe(struct device *dev)
> +{
> +	struct stm32_fmc2_ebi *ebi;
> +	int ret;
> +
> +	ebi = xzalloc(sizeof(*ebi));
> +
> +	ebi->clk = clk_get(dev, NULL);
> +	if (IS_ERR(ebi->clk)) {
> +		ret = PTR_ERR(ebi->clk);
> +		goto out_kfree;
> +	}
> +
> +	clk_enable(ebi->clk);
> +
> +	ebi->io_base = of_iomap(dev->of_node, 0);
> +	if (!ebi->io_base) {
> +		ret = -ENOMEM;
> +		goto out_clk;
> +	}
> +
> +	ret = device_reset_us(dev, 2);
> +	if (ret)
> +		goto out_clk;
> +
> +	ret = stm32_fmc2_ebi_parse_dt(dev, ebi);
> +	if (ret)
> +		goto out_clk;
> +	
> +	return of_platform_populate(dev->of_node, NULL, dev);
> +
> +out_clk:
> +	clk_disable(ebi->clk);
> +
> +out_kfree:
> +	kfree(ebi);
> +
> +	return ret;
> +}
> +
> +static __maybe_unused struct of_device_id stm32_fmc2_ebi_dt_ids[] = {
> +	{ .compatible = "st,stm32mp1-fmc2-ebi", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_dt_ids);
> +
> +static struct driver stm32_fmc2_ebi_driver = {
> +	.name		= "stm32_fmc2_ebi",
> +	.probe		= stm32_fmc2_ebi_probe,
> +	.of_compatible	= DRV_OF_COMPAT(stm32_fmc2_ebi_dt_ids),
> +};
> +coredevice_platform_driver(stm32_fmc2_ebi_driver);
> -- 
> 2.39.1
> 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



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

* Re: [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs
  2023-08-07  6:59 ` [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface " Sascha Hauer
@ 2023-08-07  8:30   ` Alexander Shiyan
  2023-08-07  9:24     ` Sascha Hauer
  0 siblings, 1 reply; 5+ messages in thread
From: Alexander Shiyan @ 2023-08-07  8:30 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox

Hello.

Please do not apply the second part of the patch to git. NAND chip
detected, but read/write operations are not performed correctly. I'll
deal with it.

пн, 7 авг. 2023 г. в 09:59, Sascha Hauer <sha@pengutronix.de>:
>
> On Thu, Aug 03, 2023 at 01:32:37PM +0300, Alexander Shiyan wrote:
> > This adds support for FMC2 External Bus Interface on STM32MP SoCs.
> > The original source is taken from the STMicroelectronics/u-boot repository [1].
> >
> > [1] https://github.com/STMicroelectronics/u-boot/blob/v2022.10-stm32mp/drivers/memory/stm32-fmc2-ebi.c
> >
> > Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
> > ---
> >  drivers/memory/Kconfig          |    9 +
> >  drivers/memory/Makefile         |    1 +
> >  drivers/memory/stm32-fmc2-ebi.c | 1063 +++++++++++++++++++++++++++++++
> >  3 files changed, 1073 insertions(+)
> >  create mode 100644 drivers/memory/stm32-fmc2-ebi.c
>
> Applied, thanks
>
> Sascha
>
> >
> > diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> > index e18b452009..b0d99c3a54 100644
> > --- a/drivers/memory/Kconfig
> > +++ b/drivers/memory/Kconfig
> > @@ -23,4 +23,13 @@ config MC_TEGRA124
> >         the Tegra124 SoC. This driver performs the necessary initialization
> >         to provide a function GPU when the OS is running.
> >
> > +config STM32_FMC2_EBI
> > +     bool "Support for FMC2 External Bus Interface on STM32MP SoCs"
> > +     depends on ARCH_STM32MP || COMPILE_TEST
> > +     help
> > +       Select this option to enable the STM32 FMC2 External Bus Interface
> > +       controller. This driver configures the transactions with external
> > +       devices (like SRAM, ethernet adapters, FPGAs, LCD displays, ...) on
> > +       SOCs containing the FMC2 External Bus Interface.
> > +
> >  endmenu
> > diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> > index bdf8db66e8..67d3c47621 100644
> > --- a/drivers/memory/Makefile
> > +++ b/drivers/memory/Makefile
> > @@ -1,3 +1,4 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  obj-$(CONFIG_ATMEL_EBI)              += atmel-ebi.o
> >  obj-$(CONFIG_MC_TEGRA124)    += mc-tegra124.o
> > +obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
> > diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c
> > new file mode 100644
> > index 0000000000..ac2ea1b0e0
> > --- /dev/null
> > +++ b/drivers/memory/stm32-fmc2-ebi.c
> > @@ -0,0 +1,1063 @@
> > +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
> > +/*
> > + * Copyright (C) STMicroelectronics 2020
> > + */
> > +
> > +#define pr_fmt(fmt) "stm32-fmc2-ebi: " fmt
> > +
> > +#include <common.h>
> > +#include <init.h>
> > +#include <of_address.h>
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/reset.h>
> > +
> > +/* FMC2 Controller Registers */
> > +#define FMC2_BCR1                    0x0
> > +#define FMC2_BTR1                    0x4
> > +#define FMC2_BCR(x)                  ((x) * 0x8 + FMC2_BCR1)
> > +#define FMC2_BTR(x)                  ((x) * 0x8 + FMC2_BTR1)
> > +#define FMC2_PCSCNTR                 0x20
> > +#define FMC2_BWTR1                   0x104
> > +#define FMC2_BWTR(x)                 ((x) * 0x8 + FMC2_BWTR1)
> > +
> > +/* Register: FMC2_BCR1 */
> > +#define FMC2_BCR1_CCLKEN             BIT(20)
> > +#define FMC2_BCR1_FMC2EN             BIT(31)
> > +
> > +/* Register: FMC2_BCRx */
> > +#define FMC2_BCR_MBKEN                       BIT(0)
> > +#define FMC2_BCR_MUXEN                       BIT(1)
> > +#define FMC2_BCR_MTYP                        GENMASK(3, 2)
> > +#define FMC2_BCR_MWID                        GENMASK(5, 4)
> > +#define FMC2_BCR_FACCEN                      BIT(6)
> > +#define FMC2_BCR_BURSTEN             BIT(8)
> > +#define FMC2_BCR_WAITPOL             BIT(9)
> > +#define FMC2_BCR_WAITCFG             BIT(11)
> > +#define FMC2_BCR_WREN                        BIT(12)
> > +#define FMC2_BCR_WAITEN                      BIT(13)
> > +#define FMC2_BCR_EXTMOD                      BIT(14)
> > +#define FMC2_BCR_ASYNCWAIT           BIT(15)
> > +#define FMC2_BCR_CPSIZE                      GENMASK(18, 16)
> > +#define FMC2_BCR_CBURSTRW            BIT(19)
> > +#define FMC2_BCR_NBLSET                      GENMASK(23, 22)
> > +
> > +/* Register: FMC2_BTRx/FMC2_BWTRx */
> > +#define FMC2_BXTR_ADDSET             GENMASK(3, 0)
> > +#define FMC2_BXTR_ADDHLD             GENMASK(7, 4)
> > +#define FMC2_BXTR_DATAST             GENMASK(15, 8)
> > +#define FMC2_BXTR_BUSTURN            GENMASK(19, 16)
> > +#define FMC2_BTR_CLKDIV                      GENMASK(23, 20)
> > +#define FMC2_BTR_DATLAT                      GENMASK(27, 24)
> > +#define FMC2_BXTR_ACCMOD             GENMASK(29, 28)
> > +#define FMC2_BXTR_DATAHLD            GENMASK(31, 30)
> > +
> > +/* Register: FMC2_PCSCNTR */
> > +#define FMC2_PCSCNTR_CSCOUNT         GENMASK(15, 0)
> > +#define FMC2_PCSCNTR_CNTBEN(x)               BIT((x) + 16)
> > +
> > +#define FMC2_MAX_EBI_CE                      4
> > +#define FMC2_MAX_BANKS                       5
> > +
> > +#define FMC2_BCR_CPSIZE_0            0x0
> > +#define FMC2_BCR_CPSIZE_128          0x1
> > +#define FMC2_BCR_CPSIZE_256          0x2
> > +#define FMC2_BCR_CPSIZE_512          0x3
> > +#define FMC2_BCR_CPSIZE_1024         0x4
> > +
> > +#define FMC2_BCR_MWID_8                      0x0
> > +#define FMC2_BCR_MWID_16             0x1
> > +
> > +#define FMC2_BCR_MTYP_SRAM           0x0
> > +#define FMC2_BCR_MTYP_PSRAM          0x1
> > +#define FMC2_BCR_MTYP_NOR            0x2
> > +
> > +#define FMC2_BXTR_EXTMOD_A           0x0
> > +#define FMC2_BXTR_EXTMOD_B           0x1
> > +#define FMC2_BXTR_EXTMOD_C           0x2
> > +#define FMC2_BXTR_EXTMOD_D           0x3
> > +
> > +#define FMC2_BCR_NBLSET_MAX          0x3
> > +#define FMC2_BXTR_ADDSET_MAX         0xf
> > +#define FMC2_BXTR_ADDHLD_MAX         0xf
> > +#define FMC2_BXTR_DATAST_MAX         0xff
> > +#define FMC2_BXTR_BUSTURN_MAX                0xf
> > +#define FMC2_BXTR_DATAHLD_MAX                0x3
> > +#define FMC2_BTR_CLKDIV_MAX          0xf
> > +#define FMC2_BTR_DATLAT_MAX          0xf
> > +#define FMC2_PCSCNTR_CSCOUNT_MAX     0xff
> > +
> > +enum stm32_fmc2_ebi_bank {
> > +     FMC2_EBI1 = 0,
> > +     FMC2_EBI2,
> > +     FMC2_EBI3,
> > +     FMC2_EBI4,
> > +     FMC2_NAND
> > +};
> > +
> > +enum stm32_fmc2_ebi_register_type {
> > +     FMC2_REG_BCR = 1,
> > +     FMC2_REG_BTR,
> > +     FMC2_REG_BWTR,
> > +     FMC2_REG_PCSCNTR
> > +};
> > +
> > +enum stm32_fmc2_ebi_transaction_type {
> > +     FMC2_ASYNC_MODE_1_SRAM = 0,
> > +     FMC2_ASYNC_MODE_1_PSRAM,
> > +     FMC2_ASYNC_MODE_A_SRAM,
> > +     FMC2_ASYNC_MODE_A_PSRAM,
> > +     FMC2_ASYNC_MODE_2_NOR,
> > +     FMC2_ASYNC_MODE_B_NOR,
> > +     FMC2_ASYNC_MODE_C_NOR,
> > +     FMC2_ASYNC_MODE_D_NOR,
> > +     FMC2_SYNC_READ_SYNC_WRITE_PSRAM,
> > +     FMC2_SYNC_READ_ASYNC_WRITE_PSRAM,
> > +     FMC2_SYNC_READ_SYNC_WRITE_NOR,
> > +     FMC2_SYNC_READ_ASYNC_WRITE_NOR
> > +};
> > +
> > +enum stm32_fmc2_ebi_buswidth {
> > +     FMC2_BUSWIDTH_8 = 8,
> > +     FMC2_BUSWIDTH_16 = 16
> > +};
> > +
> > +enum stm32_fmc2_ebi_cpsize {
> > +     FMC2_CPSIZE_0 = 0,
> > +     FMC2_CPSIZE_128 = 128,
> > +     FMC2_CPSIZE_256 = 256,
> > +     FMC2_CPSIZE_512 = 512,
> > +     FMC2_CPSIZE_1024 = 1024
> > +};
> > +
> > +struct stm32_fmc2_ebi {
> > +     struct clk *clk;
> > +     void __iomem *io_base;
> > +     u8 bank_assigned;
> > +};
> > +
> > +/*
> > + * struct stm32_fmc2_prop - STM32 FMC2 EBI property
> > + * @name: the device tree binding name of the property
> > + * @bprop: indicate that it is a boolean property
> > + * @mprop: indicate that it is a mandatory property
> > + * @reg_type: the register that have to be modified
> > + * @reg_mask: the bit that have to be modified in the selected register
> > + *            in case of it is a boolean property
> > + * @reset_val: the default value that have to be set in case the property
> > + *             has not been defined in the device tree
> > + * @check: this callback ckecks that the property is compliant with the
> > + *         transaction type selected
> > + * @calculate: this callback is called to calculate for exemple a timing
> > + *             set in nanoseconds in the device tree in clock cycles or in
> > + *             clock period
> > + * @set: this callback applies the values in the registers
> > + */
> > +struct stm32_fmc2_prop {
> > +     const char *name;
> > +     bool bprop;
> > +     bool mprop;
> > +     int reg_type;
> > +     u32 reg_mask;
> > +     u32 reset_val;
> > +     int (*check)(struct stm32_fmc2_ebi *ebi,
> > +                  const struct stm32_fmc2_prop *prop, int cs);
> > +     u32 (*calculate)(struct stm32_fmc2_ebi *ebi, int cs, u32 setup);
> > +     int (*set)(struct stm32_fmc2_ebi *ebi,
> > +                const struct stm32_fmc2_prop *prop,
> > +                int cs, u32 setup);
> > +};
> > +
> > +static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi,
> > +                                 const struct stm32_fmc2_prop *prop,
> > +                                 int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +
> > +     if (bcr & FMC2_BCR_MTYP)
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi,
> > +                                     const struct stm32_fmc2_prop *prop,
> > +                                     int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +     u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +
> > +     if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi,
> > +                                        const struct stm32_fmc2_prop *prop,
> > +                                        int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +
> > +     if (bcr & FMC2_BCR_BURSTEN)
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi,
> > +                                         const struct stm32_fmc2_prop *prop,
> > +                                         int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +
> > +     if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW))
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi,
> > +                                    const struct stm32_fmc2_prop *prop,
> > +                                    int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +     u32 val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> > +
> > +     if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN)
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi,
> > +                                          const struct stm32_fmc2_prop *prop,
> > +                                          int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +     u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
> > +                readl(ebi->io_base + FMC2_BWTR(cs)) :
> > +                readl(ebi->io_base + FMC2_BTR(cs));
> > +     u32 val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> > +
> > +     if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) &&
> > +         ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN))
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi,
> > +                                        const struct stm32_fmc2_prop *prop,
> > +                                        int cs)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +     u32 bcr1 = cs ? readl(ebi->io_base + FMC2_BCR1) : bcr;
> > +
> > +     if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN)))
> > +             return 0;
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int stm32_fmc2_ebi_check_cclk(struct stm32_fmc2_ebi *ebi,
> > +                                  const struct stm32_fmc2_prop *prop,
> > +                                  int cs)
> > +{
> > +     if (cs)
> > +             return -EINVAL;
> > +
> > +     return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs);
> > +}
> > +
> > +static u32 stm32_fmc2_ebi_ns_to_clock_cycles(struct stm32_fmc2_ebi *ebi,
> > +                                          int cs, u32 setup)
> > +{
> > +     unsigned long hclk = clk_get_rate(ebi->clk);
> > +     unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
> > +
> > +     return DIV_ROUND_UP(setup * 1000, hclkp);
> > +}
> > +
> > +static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi,
> > +                                        int cs, u32 setup)
> > +{
> > +     u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup);
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR1);
> > +     u32 btr = bcr & FMC2_BCR1_CCLKEN || !cs ?
> > +               readl(ebi->io_base + FMC2_BTR1) :
> > +               readl(ebi->io_base + FMC2_BTR(cs));
> > +     u32 clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1;
> > +
> > +     return DIV_ROUND_UP(nb_clk_cycles, clk_period);
> > +}
> > +
> > +static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg)
> > +{
> > +     switch (reg_type) {
> > +     case FMC2_REG_BCR:
> > +             *reg = FMC2_BCR(cs);
> > +             break;
> > +     case FMC2_REG_BTR:
> > +             *reg = FMC2_BTR(cs);
> > +             break;
> > +     case FMC2_REG_BWTR:
> > +             *reg = FMC2_BWTR(cs);
> > +             break;
> > +     case FMC2_REG_PCSCNTR:
> > +             *reg = FMC2_PCSCNTR;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_bit_field(struct stm32_fmc2_ebi *ebi,
> > +                                     const struct stm32_fmc2_prop *prop,
> > +                                     int cs, u32 setup)
> > +{
> > +     u32 reg;
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     clrsetbits_le32(ebi->io_base + reg, prop->reg_mask,
> > +                     setup ? prop->reg_mask : 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_trans_type(struct stm32_fmc2_ebi *ebi,
> > +                                      const struct stm32_fmc2_prop *prop,
> > +                                      int cs, u32 setup)
> > +{
> > +     u32 bcr_mask, bcr = FMC2_BCR_WREN;
> > +     u32 btr_mask, btr = 0;
> > +     u32 bwtr_mask, bwtr = 0;
> > +
> > +     bwtr_mask = FMC2_BXTR_ACCMOD;
> > +     btr_mask = FMC2_BXTR_ACCMOD;
> > +     bcr_mask = FMC2_BCR_MUXEN | FMC2_BCR_MTYP | FMC2_BCR_FACCEN |
> > +                FMC2_BCR_WREN | FMC2_BCR_WAITEN | FMC2_BCR_BURSTEN |
> > +                FMC2_BCR_EXTMOD | FMC2_BCR_CBURSTRW;
> > +
> > +     switch (setup) {
> > +     case FMC2_ASYNC_MODE_1_SRAM:
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
> > +             /*
> > +              * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             break;
> > +     case FMC2_ASYNC_MODE_1_PSRAM:
> > +             /*
> > +              * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> > +             break;
> > +     case FMC2_ASYNC_MODE_A_SRAM:
> > +             /*
> > +              * MUXEN = 0, MTYP = 0, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_SRAM);
> > +             bcr |= FMC2_BCR_EXTMOD;
> > +             btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> > +             bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> > +             break;
> > +     case FMC2_ASYNC_MODE_A_PSRAM:
> > +             /*
> > +              * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> > +             bcr |= FMC2_BCR_EXTMOD;
> > +             btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> > +             bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_A);
> > +             break;
> > +     case FMC2_ASYNC_MODE_2_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN;
> > +             break;
> > +     case FMC2_ASYNC_MODE_B_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 1
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> > +             btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
> > +             bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_B);
> > +             break;
> > +     case FMC2_ASYNC_MODE_C_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 2
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> > +             btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
> > +             bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_C);
> > +             break;
> > +     case FMC2_ASYNC_MODE_D_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 0, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 1, CBURSTRW = 0, ACCMOD = 3
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN | FMC2_BCR_EXTMOD;
> > +             btr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> > +             bwtr |= FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> > +             break;
> > +     case FMC2_SYNC_READ_SYNC_WRITE_PSRAM:
> > +             /*
> > +              * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> > +             bcr |= FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
> > +             break;
> > +     case FMC2_SYNC_READ_ASYNC_WRITE_PSRAM:
> > +             /*
> > +              * MUXEN = 0, MTYP = 1, FACCEN = 0, BURSTEN = 1, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM);
> > +             bcr |= FMC2_BCR_BURSTEN;
> > +             break;
> > +     case FMC2_SYNC_READ_SYNC_WRITE_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 1, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN | FMC2_BCR_CBURSTRW;
> > +             break;
> > +     case FMC2_SYNC_READ_ASYNC_WRITE_NOR:
> > +             /*
> > +              * MUXEN = 0, MTYP = 2, FACCEN = 1, BURSTEN = 1, WAITEN = 0,
> > +              * WREN = 1, EXTMOD = 0, CBURSTRW = 0, ACCMOD = 0
> > +              */
> > +             bcr |= FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR);
> > +             bcr |= FMC2_BCR_FACCEN | FMC2_BCR_BURSTEN;
> > +             break;
> > +     default:
> > +             /* Type of transaction not supported */
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (bcr & FMC2_BCR_EXTMOD)
> > +             clrsetbits_le32(ebi->io_base + FMC2_BWTR(cs),
> > +                             bwtr_mask, bwtr);
> > +     clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), btr_mask, btr);
> > +     clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), bcr_mask, bcr);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_buswidth(struct stm32_fmc2_ebi *ebi,
> > +                                    const struct stm32_fmc2_prop *prop,
> > +                                    int cs, u32 setup)
> > +{
> > +     u32 val;
> > +
> > +     switch (setup) {
> > +     case FMC2_BUSWIDTH_8:
> > +             val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_8);
> > +             break;
> > +     case FMC2_BUSWIDTH_16:
> > +             val = FIELD_PREP(FMC2_BCR_MWID, FMC2_BCR_MWID_16);
> > +             break;
> > +     default:
> > +             /* Buswidth not supported */
> > +             return -EINVAL;
> > +     }
> > +
> > +     clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MWID, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_cpsize(struct stm32_fmc2_ebi *ebi,
> > +                                  const struct stm32_fmc2_prop *prop,
> > +                                  int cs, u32 setup)
> > +{
> > +     u32 val;
> > +
> > +     switch (setup) {
> > +     case FMC2_CPSIZE_0:
> > +             val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_0);
> > +             break;
> > +     case FMC2_CPSIZE_128:
> > +             val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_128);
> > +             break;
> > +     case FMC2_CPSIZE_256:
> > +             val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_256);
> > +             break;
> > +     case FMC2_CPSIZE_512:
> > +             val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_512);
> > +             break;
> > +     case FMC2_CPSIZE_1024:
> > +             val = FIELD_PREP(FMC2_BCR_CPSIZE, FMC2_BCR_CPSIZE_1024);
> > +             break;
> > +     default:
> > +             /* Cpsize not supported */
> > +             return -EINVAL;
> > +     }
> > +
> > +     clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_CPSIZE, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_bl_setup(struct stm32_fmc2_ebi *ebi,
> > +                                    const struct stm32_fmc2_prop *prop,
> > +                                    int cs, u32 setup)
> > +{
> > +     u32 val;
> > +
> > +     val = min_t(u32, setup, FMC2_BCR_NBLSET_MAX);
> > +     val = FIELD_PREP(FMC2_BCR_NBLSET, val);
> > +     clrsetbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_NBLSET, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi,
> > +                                         const struct stm32_fmc2_prop *prop,
> > +                                         int cs, u32 setup)
> > +{
> > +     u32 bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +     u32 bxtr = prop->reg_type == FMC2_REG_BWTR ?
> > +                readl(ebi->io_base + FMC2_BWTR(cs)) :
> > +                readl(ebi->io_base + FMC2_BTR(cs));
> > +     u32 reg, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D);
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)
> > +             val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX);
> > +     else
> > +             val = min_t(u32, setup, FMC2_BXTR_ADDSET_MAX);
> > +     val = FIELD_PREP(FMC2_BXTR_ADDSET, val);
> > +     clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDSET, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_address_hold(struct stm32_fmc2_ebi *ebi,
> > +                                        const struct stm32_fmc2_prop *prop,
> > +                                        int cs, u32 setup)
> > +{
> > +     u32 val, reg;
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     val = clamp_val(setup, 1, FMC2_BXTR_ADDHLD_MAX);
> > +     val = FIELD_PREP(FMC2_BXTR_ADDHLD, val);
> > +     clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_ADDHLD, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_data_setup(struct stm32_fmc2_ebi *ebi,
> > +                                      const struct stm32_fmc2_prop *prop,
> > +                                      int cs, u32 setup)
> > +{
> > +     u32 val, reg;
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     val = clamp_val(setup, 1, FMC2_BXTR_DATAST_MAX);
> > +     val = FIELD_PREP(FMC2_BXTR_DATAST, val);
> > +     clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAST, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_bus_turnaround(struct stm32_fmc2_ebi *ebi,
> > +                                          const struct stm32_fmc2_prop *prop,
> > +                                          int cs, u32 setup)
> > +{
> > +     u32 val, reg;
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     val = setup ? min_t(u32, setup - 1, FMC2_BXTR_BUSTURN_MAX) : 0;
> > +     val = FIELD_PREP(FMC2_BXTR_BUSTURN, val);
> > +     clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_BUSTURN, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_data_hold(struct stm32_fmc2_ebi *ebi,
> > +                                     const struct stm32_fmc2_prop *prop,
> > +                                     int cs, u32 setup)
> > +{
> > +     u32 val, reg;
> > +     int ret;
> > +
> > +     ret = stm32_fmc2_ebi_get_reg(prop->reg_type, cs, &reg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (prop->reg_type == FMC2_REG_BWTR)
> > +             val = setup ? min_t(u32, setup - 1, FMC2_BXTR_DATAHLD_MAX) : 0;
> > +     else
> > +             val = min_t(u32, setup, FMC2_BXTR_DATAHLD_MAX);
> > +     val = FIELD_PREP(FMC2_BXTR_DATAHLD, val);
> > +     clrsetbits_le32(ebi->io_base + reg, FMC2_BXTR_DATAHLD, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi,
> > +                                      const struct stm32_fmc2_prop *prop,
> > +                                      int cs, u32 setup)
> > +{
> > +     u32 val;
> > +
> > +     val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1;
> > +     val = FIELD_PREP(FMC2_BTR_CLKDIV, val);
> > +     clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_CLKDIV, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi,
> > +                                        const struct stm32_fmc2_prop *prop,
> > +                                        int cs, u32 setup)
> > +{
> > +     u32 val;
> > +
> > +     val = setup > 1 ? min_t(u32, setup - 2, FMC2_BTR_DATLAT_MAX) : 0;
> > +     val = FIELD_PREP(FMC2_BTR_DATLAT, val);
> > +     clrsetbits_le32(ebi->io_base + FMC2_BTR(cs), FMC2_BTR_DATLAT, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi,
> > +                                         const struct stm32_fmc2_prop *prop,
> > +                                         int cs, u32 setup)
> > +{
> > +     u32 old_val, new_val, pcscntr;
> > +
> > +     if (setup < 1)
> > +             return 0;
> > +
> > +     pcscntr = readl(ebi->io_base + FMC2_PCSCNTR);
> > +
> > +     /* Enable counter for the bank */
> > +     setbits_le32(ebi->io_base + FMC2_PCSCNTR, FMC2_PCSCNTR_CNTBEN(cs));
> > +
> > +     new_val = min_t(u32, setup - 1, FMC2_PCSCNTR_CSCOUNT_MAX);
> > +     old_val = FIELD_GET(FMC2_PCSCNTR_CSCOUNT, pcscntr);
> > +     if (old_val && new_val > old_val)
> > +             /* Keep current counter value */
> > +             return 0;
> > +
> > +     new_val = FIELD_PREP(FMC2_PCSCNTR_CSCOUNT, new_val);
> > +     clrsetbits_le32(ebi->io_base + FMC2_PCSCNTR,
> > +                     FMC2_PCSCNTR_CSCOUNT, new_val);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = {
> > +     /* st,fmc2-ebi-cs-trans-type must be the first property */
> > +     {
> > +             .name = "st,fmc2-ebi-cs-transaction-type",
> > +             .mprop = true,
> > +             .set = stm32_fmc2_ebi_set_trans_type,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-cclk-enable",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR1_CCLKEN,
> > +             .check = stm32_fmc2_ebi_check_cclk,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-mux-enable",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR_MUXEN,
> > +             .check = stm32_fmc2_ebi_check_mux,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-buswidth",
> > +             .reset_val = FMC2_BUSWIDTH_16,
> > +             .set = stm32_fmc2_ebi_set_buswidth,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-waitpol-high",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR_WAITPOL,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-waitcfg-enable",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR_WAITCFG,
> > +             .check = stm32_fmc2_ebi_check_waitcfg,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-wait-enable",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR_WAITEN,
> > +             .check = stm32_fmc2_ebi_check_sync_trans,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-asyncwait-enable",
> > +             .bprop = true,
> > +             .reg_type = FMC2_REG_BCR,
> > +             .reg_mask = FMC2_BCR_ASYNCWAIT,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .set = stm32_fmc2_ebi_set_bit_field,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-cpsize",
> > +             .check = stm32_fmc2_ebi_check_cpsize,
> > +             .set = stm32_fmc2_ebi_set_cpsize,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-byte-lane-setup-ns",
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_bl_setup,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-address-setup-ns",
> > +             .reg_type = FMC2_REG_BTR,
> > +             .reset_val = FMC2_BXTR_ADDSET_MAX,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_address_setup,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-address-hold-ns",
> > +             .reg_type = FMC2_REG_BTR,
> > +             .reset_val = FMC2_BXTR_ADDHLD_MAX,
> > +             .check = stm32_fmc2_ebi_check_address_hold,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_address_hold,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-data-setup-ns",
> > +             .reg_type = FMC2_REG_BTR,
> > +             .reset_val = FMC2_BXTR_DATAST_MAX,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_data_setup,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-bus-turnaround-ns",
> > +             .reg_type = FMC2_REG_BTR,
> > +             .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_bus_turnaround,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-data-hold-ns",
> > +             .reg_type = FMC2_REG_BTR,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_data_hold,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-clk-period-ns",
> > +             .reset_val = FMC2_BTR_CLKDIV_MAX + 1,
> > +             .check = stm32_fmc2_ebi_check_clk_period,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_clk_period,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-data-latency-ns",
> > +             .check = stm32_fmc2_ebi_check_sync_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clk_period,
> > +             .set = stm32_fmc2_ebi_set_data_latency,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-write-address-setup-ns",
> > +             .reg_type = FMC2_REG_BWTR,
> > +             .reset_val = FMC2_BXTR_ADDSET_MAX,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_address_setup,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-write-address-hold-ns",
> > +             .reg_type = FMC2_REG_BWTR,
> > +             .reset_val = FMC2_BXTR_ADDHLD_MAX,
> > +             .check = stm32_fmc2_ebi_check_address_hold,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_address_hold,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-write-data-setup-ns",
> > +             .reg_type = FMC2_REG_BWTR,
> > +             .reset_val = FMC2_BXTR_DATAST_MAX,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_data_setup,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-write-bus-turnaround-ns",
> > +             .reg_type = FMC2_REG_BWTR,
> > +             .reset_val = FMC2_BXTR_BUSTURN_MAX + 1,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_bus_turnaround,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-write-data-hold-ns",
> > +             .reg_type = FMC2_REG_BWTR,
> > +             .check = stm32_fmc2_ebi_check_async_trans,
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_data_hold,
> > +     },
> > +     {
> > +             .name = "st,fmc2-ebi-cs-max-low-pulse-ns",
> > +             .calculate = stm32_fmc2_ebi_ns_to_clock_cycles,
> > +             .set = stm32_fmc2_ebi_set_max_low_pulse,
> > +     },
> > +};
> > +
> > +static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi,
> > +                                  struct device_node *node,
> > +                                  const struct stm32_fmc2_prop *prop,
> > +                                  int cs)
> > +{
> > +     u32 setup = 0;
> > +
> > +     if (!prop->set) {
> > +             pr_err("property %s is not well defined\n", prop->name);
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (prop->check && prop->check(ebi, prop, cs))
> > +             /* Skip this property */
> > +             return 0;
> > +
> > +     if (prop->bprop) {
> > +             bool bprop;
> > +
> > +             bprop = of_property_read_bool(node, prop->name);
> > +             if (prop->mprop && !bprop) {
> > +                     pr_err("mandatory property %s not defined in the device tree\n",
> > +                            prop->name);
> > +                     return -EINVAL;
> > +             }
> > +
> > +             if (bprop)
> > +                     setup = 1;
> > +     } else {
> > +             u32 val;
> > +             int ret;
> > +
> > +             ret = of_property_read_u32(node, prop->name, &val);
> > +             if (prop->mprop && ret) {
> > +                     pr_err("mandatory property %s not defined in the device tree\n",
> > +                            prop->name);
> > +                     return ret;
> > +             }
> > +
> > +             if (ret)
> > +                     setup = prop->reset_val;
> > +             else if (prop->calculate)
> > +                     setup = prop->calculate(ebi, cs, val);
> > +             else
> > +                     setup = val;
> > +     }
> > +
> > +     return prop->set(ebi, prop, cs, setup);
> > +}
> > +
> > +static void stm32_fmc2_ebi_enable_bank(struct stm32_fmc2_ebi *ebi, int cs)
> > +{
> > +     setbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
> > +}
> > +
> > +static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs)
> > +{
> > +     clrbits_le32(ebi->io_base + FMC2_BCR(cs), FMC2_BCR_MBKEN);
> > +}
> > +
> > +/* NWAIT signal can not be connected to EBI controller and NAND controller */
> > +static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi)
> > +{
> > +     unsigned int cs;
> > +     u32 bcr;
> > +
> > +     for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) {
> > +             if (!(ebi->bank_assigned & BIT(cs)))
> > +                     continue;
> > +
> > +             bcr = readl(ebi->io_base + FMC2_BCR(cs));
> > +             if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) &&
> > +                 ebi->bank_assigned & BIT(FMC2_NAND))
> > +                     return true;
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi)
> > +{
> > +     setbits_le32(ebi->io_base + FMC2_BCR1, FMC2_BCR1_FMC2EN);
> > +}
> > +
> > +static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi,
> > +                                struct device_node *node, u32 cs)
> > +{
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     stm32_fmc2_ebi_disable_bank(ebi, cs);
> > +
> > +     for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) {
> > +             const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i];
> > +
> > +             ret = stm32_fmc2_ebi_parse_prop(ebi, node, p, cs);
> > +             if (ret) {
> > +                     pr_err("property %s could not be set: %d\n", p->name, ret);
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     stm32_fmc2_ebi_enable_bank(ebi, cs);
> > +
> > +     return 0;
> > +}
> > +
> > +static int stm32_fmc2_ebi_parse_dt(struct device *dev,
> > +                                struct stm32_fmc2_ebi *ebi)
> > +{
> > +     struct device_node *child;
> > +     bool child_found = false;
> > +     u32 bank;
> > +     int ret;
> > +
> > +     for_each_available_child_of_node(dev->of_node, child) {
> > +             ret = of_property_read_u32(child, "reg", &bank);
> > +             if (ret) {
> > +                     dev_err(dev, "could not retrieve reg property: %d\n", ret);
> > +                     return ret;
> > +             }
> > +
> > +             if (bank >= FMC2_MAX_BANKS) {
> > +                     dev_err(dev, "invalid reg value: %d\n", bank);
> > +                     return -EINVAL;
> > +             }
> > +
> > +             if (ebi->bank_assigned & BIT(bank)) {
> > +                     dev_err(dev, "bank already assigned: %d\n", bank);
> > +                     return -EINVAL;
> > +             }
> > +
> > +             if (bank < FMC2_MAX_EBI_CE) {
> > +                     ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank);
> > +                     if (ret) {
> > +                             dev_err(dev, "setup chip select %d failed: %d\n", bank, ret);
> > +                             return ret;
> > +                     }
> > +             }
> > +
> > +             ebi->bank_assigned |= BIT(bank);
> > +             child_found = true;
> > +     }
> > +
> > +     if (!child_found) {
> > +             dev_warn(dev, "no subnodes found.\n");
> > +             return -ENODEV;
> > +     }
> > +
> > +     if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) {
> > +             dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     stm32_fmc2_ebi_enable(ebi);
> > +
> > +     return 0;
> > +}
> > +
> > +static int __init stm32_fmc2_ebi_probe(struct device *dev)
> > +{
> > +     struct stm32_fmc2_ebi *ebi;
> > +     int ret;
> > +
> > +     ebi = xzalloc(sizeof(*ebi));
> > +
> > +     ebi->clk = clk_get(dev, NULL);
> > +     if (IS_ERR(ebi->clk)) {
> > +             ret = PTR_ERR(ebi->clk);
> > +             goto out_kfree;
> > +     }
> > +
> > +     clk_enable(ebi->clk);
> > +
> > +     ebi->io_base = of_iomap(dev->of_node, 0);
> > +     if (!ebi->io_base) {
> > +             ret = -ENOMEM;
> > +             goto out_clk;
> > +     }
> > +
> > +     ret = device_reset_us(dev, 2);
> > +     if (ret)
> > +             goto out_clk;
> > +
> > +     ret = stm32_fmc2_ebi_parse_dt(dev, ebi);
> > +     if (ret)
> > +             goto out_clk;
> > +
> > +     return of_platform_populate(dev->of_node, NULL, dev);
> > +
> > +out_clk:
> > +     clk_disable(ebi->clk);
> > +
> > +out_kfree:
> > +     kfree(ebi);
> > +
> > +     return ret;
> > +}
> > +
> > +static __maybe_unused struct of_device_id stm32_fmc2_ebi_dt_ids[] = {
> > +     { .compatible = "st,stm32mp1-fmc2-ebi", },
> > +     { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_dt_ids);
> > +
> > +static struct driver stm32_fmc2_ebi_driver = {
> > +     .name           = "stm32_fmc2_ebi",
> > +     .probe          = stm32_fmc2_ebi_probe,
> > +     .of_compatible  = DRV_OF_COMPAT(stm32_fmc2_ebi_dt_ids),
> > +};
> > +coredevice_platform_driver(stm32_fmc2_ebi_driver);
> > --
> > 2.39.1
> >
> >
> >
>
> --
> Pengutronix e.K.                           |                             |
> Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



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

* Re: [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs
  2023-08-07  8:30   ` Alexander Shiyan
@ 2023-08-07  9:24     ` Sascha Hauer
  0 siblings, 0 replies; 5+ messages in thread
From: Sascha Hauer @ 2023-08-07  9:24 UTC (permalink / raw)
  To: Alexander Shiyan; +Cc: barebox

On Mon, Aug 07, 2023 at 11:30:25AM +0300, Alexander Shiyan wrote:
> Hello.
> 
> Please do not apply the second part of the patch to git. NAND chip
> detected, but read/write operations are not performed correctly. I'll
> deal with it.

Ok, in that case I drop the whole series for now, just in case you find
a problem in the FMC2 driver as well. In the end we won't need the FMC2
driver without any client.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



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

end of thread, other threads:[~2023-08-07  9:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-03 10:32 [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface on STM32MP SoCs Alexander Shiyan
2023-08-03 10:32 ` [PATCH 2/2] mtd: nand: Add driver for NAND controller " Alexander Shiyan
2023-08-07  6:59 ` [PATCH 1/2] memory: Add driver for FMC2 External Bus Interface " Sascha Hauer
2023-08-07  8:30   ` Alexander Shiyan
2023-08-07  9:24     ` Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox