
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