[PATCH 0/7] Support NAND ONFI EDO mode for imx8mn architecture

Debugging and testing the series made it necessary to add some patches to fix correct clock access and compilation issues raised by buildman.
Dario Binacchi (3): dm: clk: add missing stub when CONFIG_CLK is deactivated mtd: mxs_nand: don't get the gpmi_apbh_dma clock mtd: mxs_nand: get the clock with the right name
Michael Trimarchi (4): clk: imx: gate2 support shared counter and relative clock functions clk: imx: clk-imx8mn add gpmi nand clocks imx: gpmi: Add register needed to control nand bus timing mtd: mxs_nand: Support EDO mode for imx8mn architecture
arch/arm/include/asm/mach-imx/regs-gpmi.h | 9 + drivers/clk/imx/clk-gate2.c | 15 +- drivers/clk/imx/clk-imx8mn.c | 14 ++ drivers/clk/imx/clk.h | 27 ++- drivers/mtd/nand/raw/mxs_nand.c | 200 ++++++++++++++++++++++ drivers/mtd/nand/raw/mxs_nand_dt.c | 79 +++++---- include/clk.h | 32 +++- include/mxs_nand.h | 3 + 8 files changed, 333 insertions(+), 46 deletions(-)

Add missing stub for functions [devm_]clk_...() when CONFIG_CLK is deactivated.
Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
include/clk.h | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/include/clk.h b/include/clk.h index 76bb64bb5ee0..407513e0fa29 100644 --- a/include/clk.h +++ b/include/clk.h @@ -88,8 +88,9 @@ struct clk_bulk { unsigned int count; };
-#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) struct phandle_1_arg; + +#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) /** * clk_get_by_phandle() - Get a clock by its phandle information (of-platadata) * @dev: Device containing the phandle @@ -258,12 +259,26 @@ int clk_release_all(struct clk *clk, int count); void devm_clk_put(struct udevice *dev, struct clk *clk);
#else + +static inline int clk_get_by_phandle(struct udevice *dev, const + struct phandle_1_arg *cells, + struct clk *clk) +{ + return -ENOSYS; +} + static inline int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { return -ENOSYS; }
+static inline int clk_get_by_index_nodev(ofnode node, int index, + struct clk *clk) +{ + return -ENOSYS; +} + static inline int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) { return -ENOSYS; @@ -275,6 +290,17 @@ static inline int clk_get_by_name(struct udevice *dev, const char *name, return -ENOSYS; }
+static inline struct clk *devm_clk_get(struct udevice *dev, const char *id) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct clk *devm_clk_get_optional(struct udevice *dev, + const char *id) +{ + return ERR_PTR(-ENOSYS); +} + static inline int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk) { @@ -285,6 +311,10 @@ static inline int clk_release_all(struct clk *clk, int count) { return -ENOSYS; } + +static inline void devm_clk_put(struct udevice *dev, struct clk *clk) +{ +} #endif
/**

On 9/28/22 04:45, Dario Binacchi wrote:
Add missing stub for functions [devm_]clk_...() when CONFIG_CLK is deactivated.
Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
include/clk.h | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/include/clk.h b/include/clk.h index 76bb64bb5ee0..407513e0fa29 100644 --- a/include/clk.h +++ b/include/clk.h @@ -88,8 +88,9 @@ struct clk_bulk { unsigned int count; };
-#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) struct phandle_1_arg;
+#if CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(CLK) /**
- clk_get_by_phandle() - Get a clock by its phandle information (of-platadata)
- @dev: Device containing the phandle
@@ -258,12 +259,26 @@ int clk_release_all(struct clk *clk, int count); void devm_clk_put(struct udevice *dev, struct clk *clk);
#else
+static inline int clk_get_by_phandle(struct udevice *dev, const
struct phandle_1_arg *cells,
struct clk *clk)
+{
- return -ENOSYS;
+}
- static inline int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { return -ENOSYS; }
+static inline int clk_get_by_index_nodev(ofnode node, int index,
struct clk *clk)
+{
- return -ENOSYS;
+}
- static inline int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk) { return -ENOSYS;
@@ -275,6 +290,17 @@ static inline int clk_get_by_name(struct udevice *dev, const char *name, return -ENOSYS; }
+static inline struct clk *devm_clk_get(struct udevice *dev, const char *id) +{
- return ERR_PTR(-ENOSYS);
+}
+static inline struct clk *devm_clk_get_optional(struct udevice *dev,
const char *id)
+{
- return ERR_PTR(-ENOSYS);
+}
- static inline int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk) {
@@ -285,6 +311,10 @@ static inline int clk_release_all(struct clk *clk, int count) { return -ENOSYS; }
+static inline void devm_clk_put(struct udevice *dev, struct clk *clk) +{ +} #endif
/**
Reviewed-by: Sean Anderson seanga2@gmail.com

From: Michael Trimarchi michael@amarulasolutions.com
Add shared counter in order to avoid to swich off clock that are already used.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-gate2.c | 15 ++++++++++++++- drivers/clk/imx/clk.h | 27 +++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 40b2d4caab49..da2723023778 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -20,6 +20,7 @@ #include <clk-uclass.h> #include <dm/device.h> #include <dm/devres.h> +#include <linux/bug.h> #include <linux/clk-provider.h> #include <clk.h> #include "clk.h" @@ -33,6 +34,7 @@ struct clk_gate2 { u8 bit_idx; u8 cgr_val; u8 flags; + unsigned int *share_count; };
#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) @@ -42,6 +44,9 @@ static int clk_gate2_enable(struct clk *clk) struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg;
+ if (gate->share_count && (*gate->share_count)++ > 0) + return 0; + reg = readl(gate->reg); reg &= ~(3 << gate->bit_idx); reg |= gate->cgr_val << gate->bit_idx; @@ -55,6 +60,13 @@ static int clk_gate2_disable(struct clk *clk) struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg;
+ if (gate->share_count) { + if (WARN_ON(*gate->share_count == 0)) + return 0; + else if (--(*gate->share_count) > 0) + return 0; + } + reg = readl(gate->reg); reg &= ~(3 << gate->bit_idx); writel(reg, gate->reg); @@ -82,7 +94,7 @@ static const struct clk_ops clk_gate2_ops = { struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, - u8 clk_gate2_flags) + u8 clk_gate2_flags, unsigned int *share_count) { struct clk_gate2 *gate; struct clk *clk; @@ -96,6 +108,7 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, gate->bit_idx = bit_idx; gate->cgr_val = cgr_val; gate->flags = clk_gate2_flags; + gate->share_count = share_count;
clk = &gate->clk;
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 46dee35a6735..11f5dca1175b 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -53,7 +53,7 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, - u8 clk_gate_flags); + u8 clk_gate_flags, unsigned int *share_count);
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, const char *parent_name, void __iomem *base, @@ -63,7 +63,26 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, - shift, 0x3, 0); + shift, 0x3, 0, NULL); +} + +static inline struct clk *imx_clk_gate2_shared(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + unsigned int *share_count) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0x3, 0, share_count); +} + +static inline struct clk *imx_clk_gate2_shared2(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + unsigned int *share_count) +{ + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | + CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, + share_count); }
static inline struct clk *imx_clk_gate4(const char *name, const char *parent, @@ -71,7 +90,7 @@ static inline struct clk *imx_clk_gate4(const char *name, const char *parent, { return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0x3, 0); + reg, shift, 0x3, 0, NULL); }
static inline struct clk *imx_clk_gate4_flags(const char *name, @@ -80,7 +99,7 @@ static inline struct clk *imx_clk_gate4_flags(const char *name, { return clk_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, - reg, shift, 0x3, 0); + reg, shift, 0x3, 0, NULL); }
static inline struct clk *imx_clk_fixed_factor(const char *name,

From: Michael Trimarchi michael@amarulasolutions.com
Add gpmi nand clock. Those clock can be used in mxs nand driver to run nand to EDO mode 5, 4, ...
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/clk/imx/clk-imx8mn.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 15d7599cfb7d..35e0d935d390 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -15,6 +15,8 @@
#include "clk.h"
+static u32 share_count_nand; + static const char *pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; @@ -90,6 +92,10 @@ static const char *imx8mn_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sy static const char *imx8mn_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", };
+static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", + "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", + "sys_pll2_250m", "video_pll1_out", }; + static const char * const imx8mn_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; @@ -268,6 +274,8 @@ static int imx8mn_clk_probe(struct udevice *dev) clk_dm(IMX8MN_CLK_USDHC3, imx8m_clk_composite("usdhc3", imx8mn_usdhc3_sels, base + 0xbc80)); + clk_dm(IMX8MN_CLK_NAND, + imx8m_clk_composite("nand", imx8mn_nand_sels, base + 0xab00)); clk_dm(IMX8MN_CLK_QSPI, imx8m_clk_composite("qspi", imx8mn_qspi_sels, base + 0xab80)); clk_dm(IMX8MN_CLK_USB_CORE_REF, @@ -299,6 +307,12 @@ static int imx8mn_clk_probe(struct udevice *dev) imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MN_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + clk_dm(IMX8MN_CLK_NAND_ROOT, + imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand)); + clk_dm(IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK, + imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", + "nand_usdhc_bus", base + 0x4300, 0, + &share_count_nand)); clk_dm(IMX8MN_CLK_USB1_CTRL_ROOT, imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0));

From: Michael Trimarchi michael@amarulasolutions.com
It is used as delay for gpmi write strobe.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
arch/arm/include/asm/mach-imx/regs-gpmi.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/include/asm/mach-imx/regs-gpmi.h b/arch/arm/include/asm/mach-imx/regs-gpmi.h index 33daa53c45df..7a1577863195 100644 --- a/arch/arm/include/asm/mach-imx/regs-gpmi.h +++ b/arch/arm/include/asm/mach-imx/regs-gpmi.h @@ -93,6 +93,11 @@ struct mxs_gpmi_regs { #define GPMI_CTRL1_DECOUPLE_CS (1 << 24) #define GPMI_CTRL1_WRN_DLY_SEL_MASK (0x3 << 22) #define GPMI_CTRL1_WRN_DLY_SEL_OFFSET 22 +#define GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 +#define GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 +#define GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 +#define GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 + #define GPMI_CTRL1_TIMEOUT_IRQ_EN (1 << 20) #define GPMI_CTRL1_GANGED_RDYBUSY (1 << 19) #define GPMI_CTRL1_BCH_MODE (1 << 18) @@ -111,6 +116,10 @@ struct mxs_gpmi_regs { #define GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) #define GPMI_CTRL1_CAMERA_MODE (1 << 1) #define GPMI_CTRL1_GPMI_MODE (1 << 0) +#define GPMI_CTRL1_CLEAR_MASK (GPMI_CTRL1_WRN_DLY_SEL_MASK | \ + GPMI_CTRL1_DLL_ENABLE | \ + GPMI_CTRL1_RDN_DELAY_MASK | \ + GPMI_CTRL1_HALF_PERIOD)
#define GPMI_TIMING0_ADDRESS_SETUP_MASK (0xff << 16) #define GPMI_TIMING0_ADDRESS_SETUP_OFFSET 16

This clock name is not present in any U-boot and Linux kernel device tree.
Fixes: commit a59691280daca ("MXS_NAND: Add clock support for iMX8") Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/mtd/nand/raw/mxs_nand_dt.c | 13 ------------- 1 file changed, 13 deletions(-)
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index b9833a646f01..c1e784832f33 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -143,19 +143,6 @@ static int mxs_nand_dt_probe(struct udevice *dev) debug("Can't enable gpmi_apb_bch clk: %d\n", ret); return ret; } - - /* this clock is used for apbh_dma, since the apbh dma does not support DM, - * we optionally enable it here - */ - ret = clk_get_by_name(dev, "gpmi_apbh_dma", &gpmi_clk); - if (ret < 0) { - debug("Can't get gpmi_apbh_dma clk: %d\n", ret); - } else { - ret = clk_enable(&gpmi_clk); - if (ret < 0) { - debug("Can't enable gpmi_apbh_dma clk: %d\n", ret); - } - } }
return mxs_nand_init_ctrl(info);

Rename the gpmi_apb_bch clock name to gpmi_bch_apb, as you can find in the device tree.
Fixes: commit a59691280daca ("MXS_NAND: Add clock support for iMX8") Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com ---
drivers/mtd/nand/raw/mxs_nand_dt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index c1e784832f33..94ee7ed9ec83 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -132,15 +132,15 @@ static int mxs_nand_dt_probe(struct udevice *dev) return ret; }
- ret = clk_get_by_name(dev, "gpmi_apb_bch", &gpmi_clk); + ret = clk_get_by_name(dev, "gpmi_bch_apb", &gpmi_clk); if (ret < 0) { - debug("Can't get gpmi_apb_bch clk: %d\n", ret); + debug("Can't get gpmi_bch_apb clk: %d\n", ret); return ret; }
ret = clk_enable(&gpmi_clk); if (ret < 0) { - debug("Can't enable gpmi_apb_bch clk: %d\n", ret); + debug("Can't enable gpmi_bch_apb clk: %d\n", ret); return ret; } }

From: Michael Trimarchi michael@amarulasolutions.com
Add support for imx8mn architecture in order to run the NAND in fast edo mode.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
---
drivers/mtd/nand/raw/mxs_nand.c | 200 +++++++++++++++++++++++++++++ drivers/mtd/nand/raw/mxs_nand_dt.c | 66 ++++++---- include/mxs_nand.h | 3 + 3 files changed, 242 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index 7893e9d7e343..65eab4c8088a 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -14,6 +14,7 @@ */
#include <common.h> +#include <clk.h> #include <cpu_func.h> #include <dm.h> #include <dm/device_compat.h> @@ -26,10 +27,12 @@ #include <asm/io.h> #include <asm/mach-imx/regs-bch.h> #include <asm/mach-imx/regs-gpmi.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/mtd/rawnand.h> #include <linux/sizes.h> #include <linux/types.h> +#include <linux/math64.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -49,6 +52,10 @@ #endif
#define MXS_NAND_BCH_TIMEOUT 10000 +#define USEC_PER_SEC 1000000 +#define NSEC_PER_SEC 1000000000L + +#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
struct nand_ecclayout fake_ecc_layout;
@@ -1344,6 +1351,196 @@ err1: return ret; }
+/* + * <1> Firstly, we should know what's the GPMI-clock means. + * The GPMI-clock is the internal clock in the gpmi nand controller. + * If you set 100MHz to gpmi nand controller, the GPMI-clock's period + * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. + * + * <2> Secondly, we should know what's the frequency on the nand chip pins. + * The frequency on the nand chip pins is derived from the GPMI-clock. + * We can get it from the following equation: + * + * F = G / (DS + DH) + * + * F : the frequency on the nand chip pins. + * G : the GPMI clock, such as 100MHz. + * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP + * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD + * + * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, + * the nand EDO(extended Data Out) timing could be applied. + * The GPMI implements a feedback read strobe to sample the read data. + * The feedback read strobe can be delayed to support the nand EDO timing + * where the read strobe may deasserts before the read data is valid, and + * read data is valid for some time after read strobe. + * + * The following figure illustrates some aspects of a NAND Flash read: + * + * |<---tREA---->| + * | | + * | | | + * |<--tRP-->| | + * | | | + * __ ___|__________________________________ + * RDN ________/ | + * | + * /---------\ + * Read Data --------------< >--------- + * ---------/ + * | | + * |<-D->| + * FeedbackRDN ________ ____________ + * ___________/ + * + * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. + * + * + * <4> Now, we begin to describe how to compute the right RDN_DELAY. + * + * 4.1) From the aspect of the nand chip pins: + * Delay = (tREA + C - tRP) {1} + * + * tREA : the maximum read access time. + * C : a constant to adjust the delay. default is 4000ps. + * tRP : the read pulse width, which is exactly: + * tRP = (GPMI-clock-period) * DATA_SETUP + * + * 4.2) From the aspect of the GPMI nand controller: + * Delay = RDN_DELAY * 0.125 * RP {2} + * + * RP : the DLL reference period. + * if (GPMI-clock-period > DLL_THRETHOLD) + * RP = GPMI-clock-period / 2; + * else + * RP = GPMI-clock-period; + * + * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period + * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD + * is 16000ps, but in mx6q, we use 12000ps. + * + * 4.3) since {1} equals {2}, we get: + * + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- {3} + * RP + */ +static void mxs_compute_timings(struct nand_chip *chip, + const struct nand_sdr_timings *sdr) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + unsigned long clk_rate; + unsigned int dll_wait_time_us; + unsigned int dll_threshold_ps = nand_info->max_chain_delay; + unsigned int period_ps, reference_period_ps; + unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; + unsigned int tRP_ps; + bool use_half_period; + int sample_delay_ps, sample_delay_factor; + u16 busy_timeout_cycles; + u8 wrn_dly_sel; + u32 timing0; + u32 timing1; + u32 ctrl1n; + + if (sdr->tRC_min >= 30000) { + /* ONFI non-EDO modes [0-3] */ + clk_rate = 22000000; + wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; + } else if (sdr->tRC_min >= 25000) { + /* ONFI EDO mode 4 */ + clk_rate = 80000000; + wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + debug("%s, setting ONFI onfi edo 4\n", __func__); + } else { + /* ONFI EDO mode 5 */ + clk_rate = 100000000; + wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; + debug("%s, setting ONFI onfi edo 5\n", __func__); + } + + /* SDR core timings are given in picoseconds */ + period_ps = div_u64((u64)NSEC_PER_SEC * 1000, clk_rate); + + addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); + data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); + data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); + busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps); + + timing0 = (addr_setup_cycles << GPMI_TIMING0_ADDRESS_SETUP_OFFSET) | + (data_hold_cycles << GPMI_TIMING0_DATA_HOLD_OFFSET) | + (data_setup_cycles << GPMI_TIMING0_DATA_SETUP_OFFSET); + timing1 = (busy_timeout_cycles * 4096) << GPMI_TIMING1_DEVICE_BUSY_TIMEOUT_OFFSET; + + /* + * Derive NFC ideal delay from {3}: + * + * (tREA + 4000 - tRP) * 8 + * RDN_DELAY = ----------------------- + * RP + */ + if (period_ps > dll_threshold_ps) { + use_half_period = true; + reference_period_ps = period_ps / 2; + } else { + use_half_period = false; + reference_period_ps = period_ps; + } + + tRP_ps = data_setup_cycles * period_ps; + sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; + if (sample_delay_ps > 0) + sample_delay_factor = sample_delay_ps / reference_period_ps; + else + sample_delay_factor = 0; + + ctrl1n = (wrn_dly_sel << GPMI_CTRL1_WRN_DLY_SEL_OFFSET); + if (sample_delay_factor) + ctrl1n |= (sample_delay_factor << GPMI_CTRL1_RDN_DELAY_OFFSET) | + GPMI_CTRL1_DLL_ENABLE | + (use_half_period ? GPMI_CTRL1_HALF_PERIOD : 0); + + writel(timing0, &nand_info->gpmi_regs->hw_gpmi_timing0); + writel(timing1, &nand_info->gpmi_regs->hw_gpmi_timing1); + + /* + * Clear several CTRL1 fields, DLL must be disabled when setting + * RDN_DELAY or HALF_PERIOD. + */ + writel(GPMI_CTRL1_CLEAR_MASK, &nand_info->gpmi_regs->hw_gpmi_ctrl1_clr); + writel(ctrl1n, &nand_info->gpmi_regs->hw_gpmi_ctrl1_set); + + clk_set_rate(nand_info->gpmi_clk, clk_rate); + + /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ + dll_wait_time_us = USEC_PER_SEC / clk_rate * 64; + if (!dll_wait_time_us) + dll_wait_time_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_us); +} + +static int mxs_nand_setup_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const struct nand_sdr_timings *sdr; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + /* Stop here if this call was just a check */ + if (chipnr < 0) + return 0; + + /* Do the actual derivation of the controller timings */ + mxs_compute_timings(chip, sdr); + + return 0; +} + int mxs_nand_init_spl(struct nand_chip *nand) { struct mxs_nand_info *nand_info; @@ -1432,6 +1629,9 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) nand->read_buf = mxs_nand_read_buf; nand->write_buf = mxs_nand_write_buf;
+ if (nand_info->gpmi_clk) + nand->setup_data_interface = mxs_nand_setup_interface; + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) goto err_free_buffers; diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index 94ee7ed9ec83..a922a22b2730 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -22,22 +22,27 @@
struct mxs_nand_dt_data { unsigned int max_ecc_strength_supported; + int max_chain_delay; /* See the async EDO mode */ };
static const struct mxs_nand_dt_data mxs_nand_imx6q_data = { .max_ecc_strength_supported = 40, + .max_chain_delay = 12000, };
static const struct mxs_nand_dt_data mxs_nand_imx6sx_data = { .max_ecc_strength_supported = 62, + .max_chain_delay = 12000, };
static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { .max_ecc_strength_supported = 62, + .max_chain_delay = 12000, };
static const struct mxs_nand_dt_data mxs_nand_imx8qxp_data = { .max_ecc_strength_supported = 62, + .max_chain_delay = 12000, };
static const struct udevice_id mxs_nand_dt_ids[] = { @@ -72,8 +77,10 @@ static int mxs_nand_dt_probe(struct udevice *dev) int ret;
data = (void *)dev_get_driver_data(dev); - if (data) + if (data) { info->max_ecc_strength_supported = data->max_ecc_strength_supported; + info->max_chain_delay = data->max_chain_delay; + }
info->dev = dev;
@@ -92,44 +99,49 @@ static int mxs_nand_dt_probe(struct udevice *dev)
info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
- if (IS_ENABLED(CONFIG_CLK) && IS_ENABLED(CONFIG_IMX8)) { + if (IS_ENABLED(CONFIG_CLK) && + (IS_ENABLED(CONFIG_IMX8) || IS_ENABLED(CONFIG_IMX8M))) { /* Assigned clock already set clock */ struct clk gpmi_clk;
- ret = clk_get_by_name(dev, "gpmi_io", &gpmi_clk); - if (ret < 0) { + info->gpmi_clk = devm_clk_get(dev, "gpmi_io"); + + if (IS_ERR(info->gpmi_clk)) { + ret = PTR_ERR(info->gpmi_clk); debug("Can't get gpmi io clk: %d\n", ret); return ret; }
- ret = clk_enable(&gpmi_clk); + ret = clk_enable(info->gpmi_clk); if (ret < 0) { debug("Can't enable gpmi io clk: %d\n", ret); return ret; }
- ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk); - if (ret < 0) { - debug("Can't get gpmi_apb clk: %d\n", ret); - return ret; - } - - ret = clk_enable(&gpmi_clk); - if (ret < 0) { - debug("Can't enable gpmi_apb clk: %d\n", ret); - return ret; - } - - ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk); - if (ret < 0) { - debug("Can't get gpmi_bch clk: %d\n", ret); - return ret; - } - - ret = clk_enable(&gpmi_clk); - if (ret < 0) { - debug("Can't enable gpmi_bch clk: %d\n", ret); - return ret; + if (IS_ENABLED(CONFIG_IMX8)) { + ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_apb clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_apb clk: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_bch clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_bch clk: %d\n", ret); + return ret; + } }
ret = clk_get_by_name(dev, "gpmi_bch_apb", &gpmi_clk); diff --git a/include/mxs_nand.h b/include/mxs_nand.h index 741dc8734eae..bb5b84b8c26e 100644 --- a/include/mxs_nand.h +++ b/include/mxs_nand.h @@ -12,6 +12,7 @@ #include <asm/cache.h> #include <nand.h> #include <asm/mach-imx/dma.h> +#include <clk.h>
/** * @gf_len: The length of Galois Field. (e.g., 13 or 14) @@ -43,6 +44,7 @@ struct mxs_nand_info { struct nand_chip chip; struct udevice *dev; unsigned int max_ecc_strength_supported; + int max_chain_delay; bool use_minimum_ecc; int cur_chip;
@@ -59,6 +61,7 @@ struct mxs_nand_info {
struct mxs_gpmi_regs *gpmi_regs; struct mxs_bch_regs *bch_regs; + struct clk *gpmi_clk;
/* Functions with altered behaviour */ int (*hooked_read_oob)(struct mtd_info *mtd,

Hi Michael,
On Wed, Sep 28, 2022 at 10:45 AM Dario Binacchi < dario.binacchi@amarulasolutions.com> wrote:
From: Michael Trimarchi michael@amarulasolutions.com
Add support for imx8mn architecture in order to run the NAND in fast edo mode.
Signed-off-by: Michael Trimarchi michael@amarulasolutions.com Signed-off-by: Dario Binacchi dario.binacchi@amarulasolutions.com
drivers/mtd/nand/raw/mxs_nand.c | 200 +++++++++++++++++++++++++++++ drivers/mtd/nand/raw/mxs_nand_dt.c | 66 ++++++---- include/mxs_nand.h | 3 + 3 files changed, 242 insertions(+), 27 deletions(-)
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index 7893e9d7e343..65eab4c8088a 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -14,6 +14,7 @@ */
#include <common.h> +#include <clk.h> #include <cpu_func.h> #include <dm.h> #include <dm/device_compat.h> @@ -26,10 +27,12 @@ #include <asm/io.h> #include <asm/mach-imx/regs-bch.h> #include <asm/mach-imx/regs-gpmi.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/mtd/rawnand.h> #include <linux/sizes.h> #include <linux/types.h> +#include <linux/math64.h>
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -49,6 +52,10 @@ #endif
#define MXS_NAND_BCH_TIMEOUT 10000 +#define USEC_PER_SEC 1000000 +#define NSEC_PER_SEC 1000000000L
+#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
struct nand_ecclayout fake_ecc_layout;
@@ -1344,6 +1351,196 @@ err1: return ret; }
+/*
- <1> Firstly, we should know what's the GPMI-clock means.
The GPMI-clock is the internal clock in the gpmi nand controller.
If you set 100MHz to gpmi nand controller, the GPMI-clock's period
is 10ns. Mark the GPMI-clock's period as GPMI-clock-period.
- <2> Secondly, we should know what's the frequency on the nand chip
pins.
The frequency on the nand chip pins is derived from the GPMI-clock.
We can get it from the following equation:
F = G / (DS + DH)
F : the frequency on the nand chip pins.
G : the GPMI clock, such as 100MHz.
DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP
DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD
- <3> Thirdly, when the frequency on the nand chip pins is above 33MHz,
the nand EDO(extended Data Out) timing could be applied.
The GPMI implements a feedback read strobe to sample the read data.
The feedback read strobe can be delayed to support the nand EDO
timing
where the read strobe may deasserts before the read data is valid,
and
read data is valid for some time after read strobe.
The following figure illustrates some aspects of a NAND Flash read:
|<---tREA---->|
| |
| | |
|<--tRP-->| |
| | |
__ ___|__________________________________
RDN \________/ |
|
/---------\
Read Data --------------< >---------
\---------/
| |
|<-D->|
FeedbackRDN ________ ____________
\___________/
D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY.
- <4> Now, we begin to describe how to compute the right RDN_DELAY.
- 4.1) From the aspect of the nand chip pins:
Delay = (tREA + C - tRP) {1}
tREA : the maximum read access time.
C : a constant to adjust the delay. default is 4000ps.
tRP : the read pulse width, which is exactly:
tRP = (GPMI-clock-period) * DATA_SETUP
- 4.2) From the aspect of the GPMI nand controller:
Delay = RDN_DELAY * 0.125 * RP {2}
RP : the DLL reference period.
if (GPMI-clock-period > DLL_THRETHOLD)
RP = GPMI-clock-period / 2;
else
RP = GPMI-clock-period;
Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
is 16000ps, but in mx6q, we use 12000ps.
- 4.3) since {1} equals {2}, we get:
(tREA + 4000 - tRP) * 8
RDN_DELAY = ----------------------- {3}
RP
- */
+static void mxs_compute_timings(struct nand_chip *chip,
const struct nand_sdr_timings *sdr)
+{
struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
unsigned long clk_rate;
unsigned int dll_wait_time_us;
unsigned int dll_threshold_ps = nand_info->max_chain_delay;
unsigned int period_ps, reference_period_ps;
unsigned int data_setup_cycles, data_hold_cycles,
addr_setup_cycles;
unsigned int tRP_ps;
bool use_half_period;
int sample_delay_ps, sample_delay_factor;
u16 busy_timeout_cycles;
u8 wrn_dly_sel;
u32 timing0;
u32 timing1;
u32 ctrl1n;
if (sdr->tRC_min >= 30000) {
/* ONFI non-EDO modes [0-3] */
clk_rate = 22000000;
wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
} else if (sdr->tRC_min >= 25000) {
/* ONFI EDO mode 4 */
clk_rate = 80000000;
wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
debug("%s, setting ONFI onfi edo 4\n", __func__);
} else {
/* ONFI EDO mode 5 */
clk_rate = 100000000;
wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
debug("%s, setting ONFI onfi edo 5\n", __func__);
}
/* SDR core timings are given in picoseconds */
period_ps = div_u64((u64)NSEC_PER_SEC * 1000, clk_rate);
addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max,
period_ps);
timing0 = (addr_setup_cycles << GPMI_TIMING0_ADDRESS_SETUP_OFFSET)
|
(data_hold_cycles << GPMI_TIMING0_DATA_HOLD_OFFSET) |
(data_setup_cycles <<
GPMI_TIMING0_DATA_SETUP_OFFSET);
timing1 = (busy_timeout_cycles * 4096) <<
GPMI_TIMING1_DEVICE_BUSY_TIMEOUT_OFFSET;
/*
* Derive NFC ideal delay from {3}:
*
* (tREA + 4000 - tRP) * 8
* RDN_DELAY = -----------------------
* RP
*/
if (period_ps > dll_threshold_ps) {
use_half_period = true;
reference_period_ps = period_ps / 2;
} else {
use_half_period = false;
reference_period_ps = period_ps;
}
tRP_ps = data_setup_cycles * period_ps;
sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8;
if (sample_delay_ps > 0)
sample_delay_factor = sample_delay_ps /
reference_period_ps;
else
sample_delay_factor = 0;
ctrl1n = (wrn_dly_sel << GPMI_CTRL1_WRN_DLY_SEL_OFFSET);
if (sample_delay_factor)
ctrl1n |= (sample_delay_factor <<
GPMI_CTRL1_RDN_DELAY_OFFSET) |
GPMI_CTRL1_DLL_ENABLE |
(use_half_period ? GPMI_CTRL1_HALF_PERIOD : 0);
writel(timing0, &nand_info->gpmi_regs->hw_gpmi_timing0);
writel(timing1, &nand_info->gpmi_regs->hw_gpmi_timing1);
/*
* Clear several CTRL1 fields, DLL must be disabled when setting
* RDN_DELAY or HALF_PERIOD.
*/
writel(GPMI_CTRL1_CLEAR_MASK,
&nand_info->gpmi_regs->hw_gpmi_ctrl1_clr);
writel(ctrl1n, &nand_info->gpmi_regs->hw_gpmi_ctrl1_set);
clk_set_rate(nand_info->gpmi_clk, clk_rate);
/* Wait 64 clock cycles before using the GPMI after enabling the
DLL */
dll_wait_time_us = USEC_PER_SEC / clk_rate * 64;
if (!dll_wait_time_us)
dll_wait_time_us = 1;
/* Wait for the DLL to settle. */
udelay(dll_wait_time_us);
+}
+static int mxs_nand_setup_interface(struct mtd_info *mtd, int chipnr,
const struct nand_data_interface *conf)
+{
struct nand_chip *chip = mtd_to_nand(mtd);
const struct nand_sdr_timings *sdr;
sdr = nand_get_sdr_timings(conf);
if (IS_ERR(sdr))
return PTR_ERR(sdr);
/* Stop here if this call was just a check */
if (chipnr < 0)
return 0;
/* Do the actual derivation of the controller timings */
mxs_compute_timings(chip, sdr);
return 0;
+}
int mxs_nand_init_spl(struct nand_chip *nand) { struct mxs_nand_info *nand_info; @@ -1432,6 +1629,9 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) nand->read_buf = mxs_nand_read_buf; nand->write_buf = mxs_nand_write_buf;
if (nand_info->gpmi_clk)
nand->setup_data_interface = mxs_nand_setup_interface;
/* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) goto err_free_buffers;
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index 94ee7ed9ec83..a922a22b2730 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -22,22 +22,27 @@
struct mxs_nand_dt_data { unsigned int max_ecc_strength_supported;
int max_chain_delay; /* See the async EDO mode */
};
static const struct mxs_nand_dt_data mxs_nand_imx6q_data = { .max_ecc_strength_supported = 40,
.max_chain_delay = 12000,
};
static const struct mxs_nand_dt_data mxs_nand_imx6sx_data = { .max_ecc_strength_supported = 62,
.max_chain_delay = 12000,
};
static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { .max_ecc_strength_supported = 62,
.max_chain_delay = 12000,
};
static const struct mxs_nand_dt_data mxs_nand_imx8qxp_data = { .max_ecc_strength_supported = 62,
.max_chain_delay = 12000,
};
static const struct udevice_id mxs_nand_dt_ids[] = { @@ -72,8 +77,10 @@ static int mxs_nand_dt_probe(struct udevice *dev) int ret;
data = (void *)dev_get_driver_data(dev);
if (data)
if (data) { info->max_ecc_strength_supported =
data->max_ecc_strength_supported;
info->max_chain_delay = data->max_chain_delay;
} info->dev = dev;
@@ -92,44 +99,49 @@ static int mxs_nand_dt_probe(struct udevice *dev)
info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
if (IS_ENABLED(CONFIG_CLK) && IS_ENABLED(CONFIG_IMX8)) {
if (IS_ENABLED(CONFIG_CLK) &&
(IS_ENABLED(CONFIG_IMX8) || IS_ENABLED(CONFIG_IMX8M))) { /* Assigned clock already set clock */ struct clk gpmi_clk;
ret = clk_get_by_name(dev, "gpmi_io", &gpmi_clk);
if (ret < 0) {
info->gpmi_clk = devm_clk_get(dev, "gpmi_io");
if (IS_ERR(info->gpmi_clk)) {
ret = PTR_ERR(info->gpmi_clk); debug("Can't get gpmi io clk: %d\n", ret); return ret; }
ret = clk_enable(&gpmi_clk);
ret = clk_enable(info->gpmi_clk); if (ret < 0) { debug("Can't enable gpmi io clk: %d\n", ret); return ret; }
ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk);
if (ret < 0) {
debug("Can't get gpmi_apb clk: %d\n", ret);
return ret;
}
ret = clk_enable(&gpmi_clk);
if (ret < 0) {
debug("Can't enable gpmi_apb clk: %d\n", ret);
return ret;
}
ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk);
if (ret < 0) {
debug("Can't get gpmi_bch clk: %d\n", ret);
return ret;
}
ret = clk_enable(&gpmi_clk);
if (ret < 0) {
debug("Can't enable gpmi_bch clk: %d\n", ret);
return ret;
if (IS_ENABLED(CONFIG_IMX8)) {
ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk);
if (ret < 0) {
debug("Can't get gpmi_apb clk: %d\n", ret);
return ret;
}
ret = clk_enable(&gpmi_clk);
if (ret < 0) {
debug("Can't enable gpmi_apb clk: %d\n",
ret);
return ret;
}
ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk);
if (ret < 0) {
debug("Can't get gpmi_bch clk: %d\n", ret);
return ret;
}
ret = clk_enable(&gpmi_clk);
if (ret < 0) {
debug("Can't enable gpmi_bch clk: %d\n",
ret);
return ret;
} } ret = clk_get_by_name(dev, "gpmi_bch_apb", &gpmi_clk);
diff --git a/include/mxs_nand.h b/include/mxs_nand.h index 741dc8734eae..bb5b84b8c26e 100644 --- a/include/mxs_nand.h +++ b/include/mxs_nand.h @@ -12,6 +12,7 @@ #include <asm/cache.h> #include <nand.h> #include <asm/mach-imx/dma.h> +#include <clk.h>
/**
- @gf_len: The length of Galois Field. (e.g., 13 or 14)
@@ -43,6 +44,7 @@ struct mxs_nand_info { struct nand_chip chip; struct udevice *dev; unsigned int max_ecc_strength_supported;
int max_chain_delay; bool use_minimum_ecc; int cur_chip;
@@ -59,6 +61,7 @@ struct mxs_nand_info {
struct mxs_gpmi_regs *gpmi_regs; struct mxs_bch_regs *bch_regs;
struct clk *gpmi_clk; /* Functions with altered behaviour */ int (*hooked_read_oob)(struct mtd_info *mtd,
-- 2.32.0
All the series applied to nand-next, thanks!
Dario
participants (2)
-
Dario Binacchi
-
Sean Anderson