All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] mtd: mxs_nand: Add support for edo mode for imx6ul(lz) architecture
@ 2021-07-04 19:56 Michael Trimarchi
  0 siblings, 0 replies; only message in thread
From: Michael Trimarchi @ 2021-07-04 19:56 UTC (permalink / raw)
  To: Stefano Babic, Fabio Estevam; +Cc: uboot-imx, u-boot

Add a proof of concept to support nand speed mode. This reduce load
time about 4 times for large binary

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
---
 arch/arm/include/asm/mach-imx/regs-gpmi.h |   9 +
 drivers/mtd/nand/raw/mxs_nand.c           | 238 ++++++++++++++++++++--
 drivers/mtd/nand/raw/mxs_nand_dt.c        |   9 +-
 include/mxs_nand.h                        |   1 +
 4 files changed, 242 insertions(+), 15 deletions(-)

diff --git a/arch/arm/include/asm/mach-imx/regs-gpmi.h b/arch/arm/include/asm/mach-imx/regs-gpmi.h
index 33daa53c45..7a15778631 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
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
index e56ebcfddc..ba1ce9cf4c 100644
--- a/drivers/mtd/nand/raw/mxs_nand.c
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -26,8 +26,11 @@
 #include <asm/arch/imx-regs.h>
 #include <asm/mach-imx/regs-bch.h>
 #include <asm/mach-imx/regs-gpmi.h>
+#include <asm/arch/crm_regs.h>
 #include <asm/arch/sys_proto.h>
 #include <mxs_nand.h>
+#include <linux/math64.h>
+#include <clk.h>
 
 #define	MXS_NAND_DMA_DESCRIPTOR_COUNT		4
 
@@ -46,6 +49,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;
 
@@ -1310,6 +1317,206 @@ err1:
 	return ret;
 }
 
+#if (defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
+/*
+ * <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 int 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;
+	u32 clkcfg;
+
+	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;
+		clkcfg = (3 << MXC_CCM_CSCDR1_BCH_PODF_OFFSET) | \
+			 (3 << MXC_CCM_CSCDR1_GPMI_PODF_OFFSET);
+	} else if (sdr->tRC_min >= 25000) {
+		/* ONFI EDO mode 4 */
+		clk_rate = 80000000;
+		wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+		clkcfg = (1 << MXC_CCM_CSCDR1_BCH_PODF_OFFSET) | \
+			 (1 << MXC_CCM_CSCDR1_GPMI_PODF_OFFSET);
+	} else {
+		/* ONFI EDO mode 5 */
+		clk_rate = 100000000;
+		wrn_dly_sel = GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
+		clkcfg = (1 << MXC_CCM_CSCDR1_BCH_PODF_OFFSET) | \
+			 (1 << MXC_CCM_CSCDR1_GPMI_PODF_OFFSET);
+	}
+
+	/* 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);
+
+	setup_gpmi_io_clk(clkcfg);
+
+	/* 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;
+}
+
+#endif
+
 int mxs_nand_init_spl(struct nand_chip *nand)
 {
 	struct mxs_nand_info *nand_info;
@@ -1342,16 +1549,16 @@ int mxs_nand_init_spl(struct nand_chip *nand)
 
 	nand->options |= NAND_NO_SUBPAGE_WRITE;
 
-	nand->cmd_ctrl		= mxs_nand_cmd_ctrl;
-	nand->dev_ready		= mxs_nand_device_ready;
-	nand->select_chip	= mxs_nand_select_chip;
+	nand->cmd_ctrl			= mxs_nand_cmd_ctrl;
+	nand->dev_ready			= mxs_nand_device_ready;
+	nand->select_chip		= mxs_nand_select_chip;
 
-	nand->read_byte		= mxs_nand_read_byte;
-	nand->read_buf		= mxs_nand_read_buf;
+	nand->read_byte			= mxs_nand_read_byte;
+	nand->read_buf			= mxs_nand_read_buf;
 
-	nand->ecc.read_page	= mxs_nand_ecc_read_page;
+	nand->ecc.read_page		= mxs_nand_ecc_read_page;
 
-	nand->ecc.mode		= NAND_ECC_HW;
+	nand->ecc.mode			= NAND_ECC_HW;
 
 	return 0;
 }
@@ -1384,16 +1591,19 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info)
 	if (nand_info->dev)
 		nand->flash_node = dev_of_offset(nand_info->dev);
 
-	nand->cmd_ctrl		= mxs_nand_cmd_ctrl;
+	nand->cmd_ctrl			= mxs_nand_cmd_ctrl;
 
-	nand->dev_ready		= mxs_nand_device_ready;
-	nand->select_chip	= mxs_nand_select_chip;
-	nand->block_bad		= mxs_nand_block_bad;
+	nand->dev_ready			= mxs_nand_device_ready;
+	nand->select_chip		= mxs_nand_select_chip;
+	nand->block_bad			= mxs_nand_block_bad;
 
-	nand->read_byte		= mxs_nand_read_byte;
+#if (defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
+	nand->setup_data_interface	= mxs_nand_setup_interface;
+#endif
+	nand->read_byte			= mxs_nand_read_byte;
 
-	nand->read_buf		= mxs_nand_read_buf;
-	nand->write_buf		= mxs_nand_write_buf;
+	nand->read_buf			= mxs_nand_read_buf;
+	nand->write_buf			= mxs_nand_write_buf;
 
 	/* first scan to find the device and get the page size */
 	if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL))
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c
index 18c042c7bb..872e5fe684 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;
 
diff --git a/include/mxs_nand.h b/include/mxs_nand.h
index c0cefaca90..c7c6870ed9 100644
--- a/include/mxs_nand.h
+++ b/include/mxs_nand.h
@@ -42,6 +42,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;
 	/* legacy bch geometry flag */
 	bool		legacy_bch_geometry;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2021-07-04 19:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-04 19:56 [RFC PATCH] mtd: mxs_nand: Add support for edo mode for imx6ul(lz) architecture Michael Trimarchi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.