[PATCH v3 2/2] mtd: nand: gpmi: Support ->setup_data_interface()
Han Xu
han.xu at nxp.com
Mon Feb 12 14:48:17 PST 2018
On 01/19/2018 07:58 AM, Miquel Raynal wrote:
> Until now the GPMI driver had its own timings logic while the core
> already handles that and request the NAND controller drivers to support
> the ->setup_data_interface() hook. Implement that hook by reusing the
> already existing function. No real glue is necessary between core timing
> delays and GPMI registers because the driver already translates the
> ONFI timing modes into register values.
>
> Make use of the core's tREA, tRLOH and tRHOH values that allow computing
> more precise timings for mode [0-3] and get significantly better values
> (+20% with an i.MX6 Sabre Auto board). Otherwise use the existing logic.
>
> Signed-off-by: Miquel Raynal <miquel.raynal at free-electrons.com>
> ---
> drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 277 ++++++++++-----------------------
> drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 42 +++--
> drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 82 +++++-----
> drivers/mtd/nand/nand_base.c | 3 +-
> 4 files changed, 152 insertions(+), 252 deletions(-)
>
> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> index 97787246af41..9a58661eb3b1 100644
> --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> @@ -151,8 +151,15 @@ static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
> return ret;
> }
>
> -#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
> -#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
> +int gpmi_enable_clk(struct gpmi_nand_data *this)
> +{
> + return __gpmi_enable_clk(this, true);
> +}
> +
> +int gpmi_disable_clk(struct gpmi_nand_data *this)
> +{
> + return __gpmi_enable_clk(this, false);
> +}
>
> int gpmi_init(struct gpmi_nand_data *this)
> {
> @@ -326,14 +333,13 @@ static unsigned int ns_to_cycles(unsigned int time,
> #define DEF_MIN_PROP_DELAY 5
> #define DEF_MAX_PROP_DELAY 9
> /* Apply timing to current hardware conditions. */
> -static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> - struct gpmi_nfc_hardware_timing *hw)
> +static void
> +gpmi_nfc_compute_hardware_timings(struct gpmi_nand_data *this)
> {
> + struct gpmi_nfc_hardware_timing *hw = &this->hw;
> struct timing_threshold *nfc = &timing_default_threshold;
> - struct resources *r = &this->resources;
> struct nand_chip *nand = &this->nand;
> struct nand_timing target = this->timing;
> - bool improved_timing_is_available;
> unsigned long clock_frequency_in_hz;
> unsigned int clock_period_in_ns;
> bool dll_use_half_periods;
> @@ -349,6 +355,9 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY;
> unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY;
>
> + /* Clock rate for non-EDO modes */
> + hw->clk_rate = 22000000;
> +
> /*
> * If there are multiple chips, we need to relax the timings to allow
> * for signal distortion due to higher capacitance.
> @@ -363,14 +372,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> target.address_setup_in_ns += 5;
> }
>
> - /* Check if improved timing information is available. */
> - improved_timing_is_available =
> - (target.tREA_in_ns >= 0) &&
> - (target.tRLOH_in_ns >= 0) &&
> - (target.tRHOH_in_ns >= 0);
> -
> /* Inspect the clock. */
> - nfc->clock_frequency_in_hz = clk_get_rate(r->clock[0]);
> + nfc->clock_frequency_in_hz = hw->clk_rate;
> clock_frequency_in_hz = nfc->clock_frequency_in_hz;
> clock_period_in_ns = NSEC_PER_SEC / clock_frequency_in_hz;
>
> @@ -482,65 +485,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> }
>
> /*
> - * Check if improved timing information is available. If not, we have to
> - * use a less-sophisticated algorithm.
> - */
> - if (!improved_timing_is_available) {
> - /*
> - * Fold the read setup time required by the NFC into the ideal
> - * sample delay.
> - */
> - ideal_sample_delay_in_ns = target.gpmi_sample_delay_in_ns +
> - nfc->internal_data_setup_in_ns;
> -
> - /*
> - * The ideal sample delay may be greater than the maximum
> - * allowed by the NFC. If so, we can trade off sample delay time
> - * for more data setup time.
> - *
> - * In each iteration of the following loop, we add a cycle to
> - * the data setup time and subtract a corresponding amount from
> - * the sample delay until we've satisified the constraints or
> - * can't do any better.
> - */
> - while ((ideal_sample_delay_in_ns > max_sample_delay_in_ns) &&
> - (data_setup_in_cycles < nfc->max_data_setup_cycles)) {
> -
> - data_setup_in_cycles++;
> - ideal_sample_delay_in_ns -= clock_period_in_ns;
> -
> - if (ideal_sample_delay_in_ns < 0)
> - ideal_sample_delay_in_ns = 0;
> -
> - }
> -
> - /*
> - * Compute the sample delay factor that corresponds most closely
> - * to the ideal sample delay. If the result is too large for the
> - * NFC, use the maximum value.
> - *
> - * Notice that we use the ns_to_cycles function to compute the
> - * sample delay factor. We do this because the form of the
> - * computation is the same as that for calculating cycles.
> - */
> - sample_delay_factor =
> - ns_to_cycles(
> - ideal_sample_delay_in_ns << dll_delay_shift,
> - clock_period_in_ns, 0);
> -
> - if (sample_delay_factor > nfc->max_sample_delay_factor)
> - sample_delay_factor = nfc->max_sample_delay_factor;
> -
> - /* Skip to the part where we return our results. */
> - goto return_results;
> - }
> -
> - /*
> - * If control arrives here, we have more detailed timing information,
> - * so we can use a better algorithm.
> - */
> -
> - /*
> * Fold the read setup time required by the NFC into the maximum
> * propagation delay.
> */
> @@ -760,8 +704,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> sample_delay_factor = nfc->max_sample_delay_factor;
> }
>
> - /* Control arrives here when we're ready to return our results. */
> -return_results:
> hw->data_setup_in_cycles = data_setup_in_cycles;
> hw->data_hold_in_cycles = data_hold_in_cycles;
> hw->address_setup_in_cycles = address_setup_in_cycles;
> @@ -769,9 +711,6 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> hw->sample_delay_factor = sample_delay_factor;
> hw->device_busy_timeout = GPMI_DEFAULT_BUSY_TIMEOUT;
> hw->wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
> -
> - /* Return success. */
> - return 0;
> }
>
> /*
> @@ -857,19 +796,17 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
> * So we only support the mode 4 and mode 5. It is no need to
> * support other modes.
> */
> -static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
> - struct gpmi_nfc_hardware_timing *hw)
> +static void gpmi_nfc_compute_edo_timings(struct gpmi_nand_data *this,
> + int mode)
> {
> - struct resources *r = &this->resources;
> - unsigned long rate = clk_get_rate(r->clock[0]);
> - int mode = this->timing_mode;
> + struct gpmi_nfc_hardware_timing *hw = &this->hw;
> int dll_threshold = this->devdata->max_chain_delay;
> unsigned long delay;
> unsigned long clk_period;
> - int t_rea;
> - int c = 4;
> - int t_rp;
> - int rp;
> + int t_rp, t_rea, rp;
> +
> + /* Set the main clock to: 100MHz (mode 5) or 80MHz (mode 4) */
> + hw->clk_rate = (mode == 5) ? 100000000 : 80000000;
>
> /*
> * [1] for GPMI_HW_GPMI_TIMING0:
> @@ -880,7 +817,7 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
> */
> hw->data_setup_in_cycles = 1;
> hw->data_hold_in_cycles = 1;
> - hw->address_setup_in_cycles = ((mode == 5) ? 1 : 0);
> + hw->address_setup_in_cycles = (mode == 5) ? 1 : 0;
>
> /* [2] for GPMI_HW_GPMI_TIMING1 */
> hw->device_busy_timeout = 0x9000;
> @@ -892,11 +829,9 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
> * Enlarge 10 times for the numerator and denominator in {3}.
> * This make us to get more accurate result.
> */
> - clk_period = NSEC_PER_SEC / (rate / 10);
> + clk_period = NSEC_PER_SEC / (hw->clk_rate / 10);
> dll_threshold *= 10;
> - t_rea = ((mode == 5) ? 16 : 20) * 10;
> - c *= 10;
> -
> + t_rea = this->timing.tREA_in_ns * 10;
> t_rp = clk_period * 1; /* DATA_SETUP is 1 */
>
> if (clk_period > dll_threshold) {
> @@ -911,129 +846,42 @@ static void gpmi_compute_edo_timing(struct gpmi_nand_data *this,
> * Multiply the numerator with 10, we could do a round off:
> * 7.8 round up to 8; 7.4 round down to 7.
> */
> - delay = (((t_rea + c - t_rp) * 8) * 10) / rp;
> + delay = (((t_rea + 40 - t_rp) * 8) * 10) / rp;
> delay = (delay + 5) / 10;
>
> hw->sample_delay_factor = delay;
> }
>
> -static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
> -{
> - struct resources *r = &this->resources;
> - struct nand_chip *nand = &this->nand;
> - struct mtd_info *mtd = nand_to_mtd(nand);
> - uint8_t *feature;
> - unsigned long rate;
> - int ret;
> -
> - feature = kzalloc(ONFI_SUBFEATURE_PARAM_LEN, GFP_KERNEL);
> - if (!feature)
> - return -ENOMEM;
> -
> - nand->select_chip(mtd, 0);
> -
> - /* [1] send SET FEATURE command to NAND */
> - feature[0] = mode;
> - ret = nand->onfi_set_features(mtd, nand,
> - ONFI_FEATURE_ADDR_TIMING_MODE, feature);
> - if (ret)
> - goto err_out;
> -
> - /* [2] send GET FEATURE command to double-check the timing mode */
> - memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN);
> - ret = nand->onfi_get_features(mtd, nand,
> - ONFI_FEATURE_ADDR_TIMING_MODE, feature);
> - if (ret || feature[0] != mode)
> - goto err_out;
> -
> - nand->select_chip(mtd, -1);
> -
> - /* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
> - rate = (mode == 5) ? 100000000 : 80000000;
> - clk_set_rate(r->clock[0], rate);
> -
> - /* Let the gpmi_begin() re-compute the timing again. */
> - this->flags &= ~GPMI_TIMING_INIT_OK;
> -
> - this->flags |= GPMI_ASYNC_EDO_ENABLED;
> - this->timing_mode = mode;
> - kfree(feature);
> - dev_info(this->dev, "enable the asynchronous EDO mode %d\n", mode);
> - return 0;
> -
> -err_out:
> - nand->select_chip(mtd, -1);
> - kfree(feature);
> - dev_err(this->dev, "mode:%d ,failed in set feature.\n", mode);
> - return -EINVAL;
> -}
> -
> -int gpmi_extra_init(struct gpmi_nand_data *this)
> -{
> - struct nand_chip *chip = &this->nand;
> -
> - /* Enable the asynchronous EDO feature. */
> - if (GPMI_IS_MX6(this) && chip->onfi_version) {
> - int mode = onfi_get_async_timing_mode(chip);
> -
> - /* We only support the timing mode 4 and mode 5. */
> - if (mode & ONFI_TIMING_MODE_5)
> - mode = 5;
> - else if (mode & ONFI_TIMING_MODE_4)
> - mode = 4;
> - else
> - return 0;
> -
> - return enable_edo_mode(this, mode);
> - }
> - return 0;
> -}
> -
> /* Begin the I/O */
> -void gpmi_begin(struct gpmi_nand_data *this)
> +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
> {
> + struct gpmi_nfc_hardware_timing *hw = &this->hw;
> struct resources *r = &this->resources;
> void __iomem *gpmi_regs = r->gpmi_regs;
> unsigned int clock_period_in_ns;
> uint32_t reg;
> unsigned int dll_wait_time_in_us;
> - struct gpmi_nfc_hardware_timing hw;
> - int ret;
> -
> - /* Enable the clock. */
> - ret = gpmi_enable_clk(this);
> - if (ret) {
> - dev_err(this->dev, "We failed in enable the clk\n");
> - goto err_out;
> - }
>
> - /* Only initialize the timing once */
> - if (this->flags & GPMI_TIMING_INIT_OK)
> - return;
> - this->flags |= GPMI_TIMING_INIT_OK;
> -
> - if (this->flags & GPMI_ASYNC_EDO_ENABLED)
> - gpmi_compute_edo_timing(this, &hw);
> - else
> - gpmi_nfc_compute_hardware_timing(this, &hw);
> + /* [0] Set the main clock rate */
> + clk_set_rate(r->clock[0], hw->clk_rate);
>
> /* [1] Set HW_GPMI_TIMING0 */
> - reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) |
> - BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) |
> - BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles);
> + reg = BF_GPMI_TIMING0_ADDRESS_SETUP(hw->address_setup_in_cycles) |
> + BF_GPMI_TIMING0_DATA_HOLD(hw->data_hold_in_cycles) |
> + BF_GPMI_TIMING0_DATA_SETUP(hw->data_setup_in_cycles);
>
> writel(reg, gpmi_regs + HW_GPMI_TIMING0);
>
> /* [2] Set HW_GPMI_TIMING1 */
> - writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw.device_busy_timeout),
> - gpmi_regs + HW_GPMI_TIMING1);
> + writel(BF_GPMI_TIMING1_BUSY_TIMEOUT(hw->device_busy_timeout),
> + gpmi_regs + HW_GPMI_TIMING1);
>
> /* [3] The following code is to set the HW_GPMI_CTRL1. */
>
> /* Set the WRN_DLY_SEL */
> writel(BM_GPMI_CTRL1_WRN_DLY_SEL, gpmi_regs + HW_GPMI_CTRL1_CLR);
> - writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw.wrn_dly_sel),
> - gpmi_regs + HW_GPMI_CTRL1_SET);
> + writel(BF_GPMI_CTRL1_WRN_DLY_SEL(hw->wrn_dly_sel),
> + gpmi_regs + HW_GPMI_CTRL1_SET);
>
> /* DLL_ENABLE must be set to 0 when setting RDN_DELAY or HALF_PERIOD. */
> writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR);
> @@ -1043,12 +891,12 @@ void gpmi_begin(struct gpmi_nand_data *this)
> writel(reg, gpmi_regs + HW_GPMI_CTRL1_CLR);
>
> /* If no sample delay is called for, return immediately. */
> - if (!hw.sample_delay_factor)
> + if (!hw->sample_delay_factor)
> return;
>
> /* Set RDN_DELAY or HALF_PERIOD. */
> - reg = ((hw.use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0)
> - | BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor);
> + reg = ((hw->use_half_periods) ? BM_GPMI_CTRL1_HALF_PERIOD : 0)
> + | BF_GPMI_CTRL1_RDN_DELAY(hw->sample_delay_factor);
>
> writel(reg, gpmi_regs + HW_GPMI_CTRL1_SET);
>
> @@ -1060,7 +908,7 @@ void gpmi_begin(struct gpmi_nand_data *this)
> * we can use the GPMI. Calculate the amount of time we need to wait,
> * in microseconds.
> */
> - clock_period_in_ns = NSEC_PER_SEC / clk_get_rate(r->clock[0]);
> + clock_period_in_ns = NSEC_PER_SEC / hw->clk_rate;
> dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000;
>
> if (!dll_wait_time_in_us)
> @@ -1068,14 +916,45 @@ void gpmi_begin(struct gpmi_nand_data *this)
>
> /* Wait for the DLL to settle. */
> udelay(dll_wait_time_in_us);
> -
> -err_out:
> - return;
> }
>
> -void gpmi_end(struct gpmi_nand_data *this)
> +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
> + const struct nand_data_interface *conf)
> {
> - gpmi_disable_clk(this);
> + struct nand_chip *chip = mtd_to_nand(mtd);
> + struct gpmi_nand_data *this = nand_get_controller_data(chip);
> + const struct nand_sdr_timings *sdr;
> + bool edo_mode = false;
> +
> + /* Retrieve required NAND timings */
> + sdr = nand_get_sdr_timings(conf);
> + if (IS_ERR(sdr))
> + return PTR_ERR(sdr);
> +
> + if (sdr->tRC_min <= 25000)
> + edo_mode = true;
> +
> + /* Only MX6 GPMI controller can reach EDO timings */
> + if (edo_mode && !GPMI_IS_MX6(this))
> + return -ENOTSUPP;
> +
> + if (chipnr < 0)
> + return 0;
> +
> + this->timing.tREA_in_ns = sdr->tREA_max / 1000;
> + this->timing.tRLOH_in_ns = sdr->tRLOH_min / 1000;
> + this->timing.tRHOH_in_ns = sdr->tRHOH_min / 1000;
> +
> + /* Compute GPMI parameters depending on the mode */
> + if (edo_mode)
> + gpmi_nfc_compute_edo_timings(this,
> + sdr->tRC_min > 20000 ? 4 : 5);
> + else
> + gpmi_nfc_compute_hardware_timings(this);
> +
> + this->hw.must_apply_timings = true;
> +
> + return 0;
> }
>
> /* Clears a BCH interrupt. */
> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> index b51db8c85405..459b7fb5e15b 100644
> --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> @@ -938,11 +938,32 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
> {
> struct nand_chip *chip = mtd_to_nand(mtd);
> struct gpmi_nand_data *this = nand_get_controller_data(chip);
> + int ret;
>
> - if ((this->current_chip < 0) && (chipnr >= 0))
> - gpmi_begin(this);
> - else if ((this->current_chip >= 0) && (chipnr < 0))
> - gpmi_end(this);
> + /*
> + * For power consumption matters, disable/enable the clock each time a
> + * die is selected/unselected.
> + */
> + if (this->current_chip < 0 && chipnr >= 0) {
> + ret = gpmi_enable_clk(this);
> + if (ret)
> + dev_err(this->dev, "Failed to enable the clock\n");
> + } else if (this->current_chip >= 0 && chipnr < 0) {
> + ret = gpmi_disable_clk(this);
> + if (ret)
> + dev_err(this->dev, "Failed to disable the clock\n");
> + }
> +
> + /*
> + * This driver currently supports only one NAND chip. Plus, dies share
> + * the same configuration. So once timings have been applied on the
> + * controller side, they will not change anymore. When the time will
> + * come, the check on must_apply_timings will have to be dropped.
> + */
> + if (chipnr >= 0 && this->hw.must_apply_timings) {
> + this->hw.must_apply_timings = false;
> + gpmi_nfc_apply_timings(this);
> + }
>
> this->current_chip = chipnr;
> }
> @@ -1947,14 +1968,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
> chip->options |= NAND_SUBPAGE_READ;
> }
>
> - /*
> - * Can we enable the extra features? such as EDO or Sync mode.
> - *
> - * We do not check the return value now. That's means if we fail in
> - * enable the extra features, we still can run in the normal way.
> - */
> - gpmi_extra_init(this);
> -
> return 0;
> }
>
> @@ -1975,6 +1988,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
> nand_set_controller_data(chip, this);
> nand_set_flash_node(chip, this->pdev->dev.of_node);
> chip->select_chip = gpmi_select_chip;
> + chip->setup_data_interface = gpmi_setup_data_interface;
> chip->cmd_ctrl = gpmi_cmd_ctrl;
> chip->dev_ready = gpmi_dev_ready;
> chip->read_byte = gpmi_read_byte;
> @@ -2133,7 +2147,6 @@ static int gpmi_pm_resume(struct device *dev)
> return ret;
>
> /* re-init the GPMI registers */
> - this->flags &= ~GPMI_TIMING_INIT_OK;
> ret = gpmi_init(this);
> if (ret) {
> dev_err(this->dev, "Error setting GPMI : %d\n", ret);
> @@ -2147,9 +2160,6 @@ static int gpmi_pm_resume(struct device *dev)
> return ret;
> }
>
> - /* re-init others */
> - gpmi_extra_init(this);
> -
> return 0;
> }
> #endif /* CONFIG_PM_SLEEP */
> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
> index 06c1f993912c..23f9030cf6d2 100644
> --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
> @@ -135,11 +135,47 @@ struct gpmi_devdata {
> const int clks_count;
> };
>
> +/**
> + * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
> + * @timing_mode: The timing mode to comply with.
> + * @must_apply_timings: Whether controller timings have already been
> + * applied or not (useful only while there is
> + * support for only one chip select)
> + * @clk_rate: The clock rate that must be used to derive the
> + * following parameters.
> + * @data_setup_in_cycles: The data setup time, in cycles.
> + * @data_hold_in_cycles: The data hold time, in cycles.
> + * @address_setup_in_cycles: The address setup time, in cycles.
> + * @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
> + * this value is the number of cycles multiplied
> + * by 4096.
> + * @use_half_periods: Indicates the clock is running slowly, so the
> + * NFC DLL should use half-periods.
> + * @sample_delay_factor: The sample delay factor.
> + * @wrn_dly_sel: The delay on the GPMI write strobe.
> + */
> +struct gpmi_nfc_hardware_timing {
> + unsigned int timing_mode;
> + bool must_apply_timings;
> + unsigned long int clk_rate;
> +
> + /* for HW_GPMI_TIMING0 */
> + u8 data_setup_in_cycles;
> + u8 data_hold_in_cycles;
> + u8 address_setup_in_cycles;
> +
> + /* for HW_GPMI_TIMING1 */
> + u16 device_busy_timeout;
> +#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
> +
> + /* for HW_GPMI_CTRL1 */
> + bool use_half_periods;
> + u8 sample_delay_factor;
> + u8 wrn_dly_sel;
> +};
> +
> struct gpmi_nand_data {
> - /* flags */
> -#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
> -#define GPMI_TIMING_INIT_OK (1 << 1)
> - int flags;
> + /* Devdata */
> const struct gpmi_devdata *devdata;
>
> /* System Interface */
> @@ -152,6 +188,7 @@ struct gpmi_nand_data {
> /* Flash Hardware */
> struct nand_timing timing;
> int timing_mode;
> + struct gpmi_nfc_hardware_timing hw;
>
> /* BCH */
> struct bch_geometry bch_geometry;
> @@ -205,35 +242,6 @@ struct gpmi_nand_data {
> };
>
> /**
> - * struct gpmi_nfc_hardware_timing - GPMI hardware timing parameters.
> - * @data_setup_in_cycles: The data setup time, in cycles.
> - * @data_hold_in_cycles: The data hold time, in cycles.
> - * @address_setup_in_cycles: The address setup time, in cycles.
> - * @device_busy_timeout: The timeout waiting for NAND Ready/Busy,
> - * this value is the number of cycles multiplied
> - * by 4096.
> - * @use_half_periods: Indicates the clock is running slowly, so the
> - * NFC DLL should use half-periods.
> - * @sample_delay_factor: The sample delay factor.
> - * @wrn_dly_sel: The delay on the GPMI write strobe.
> - */
> -struct gpmi_nfc_hardware_timing {
> - /* for HW_GPMI_TIMING0 */
> - uint8_t data_setup_in_cycles;
> - uint8_t data_hold_in_cycles;
> - uint8_t address_setup_in_cycles;
> -
> - /* for HW_GPMI_TIMING1 */
> - uint16_t device_busy_timeout;
> -#define GPMI_DEFAULT_BUSY_TIMEOUT 0x500 /* default busy timeout value.*/
> -
> - /* for HW_GPMI_CTRL1 */
> - bool use_half_periods;
> - uint8_t sample_delay_factor;
> - uint8_t wrn_dly_sel;
> -};
> -
> -/**
> * struct timing_threshold - Timing threshold
> * @max_data_setup_cycles: The maximum number of data setup cycles that
> * can be expressed in the hardware.
> @@ -279,14 +287,16 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *,
>
> /* GPMI-NAND helper function library */
> int gpmi_init(struct gpmi_nand_data *);
> -int gpmi_extra_init(struct gpmi_nand_data *);
> void gpmi_clear_bch(struct gpmi_nand_data *);
> void gpmi_dump_info(struct gpmi_nand_data *);
> int bch_set_geometry(struct gpmi_nand_data *);
> int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
> int gpmi_send_command(struct gpmi_nand_data *);
> -void gpmi_begin(struct gpmi_nand_data *);
> -void gpmi_end(struct gpmi_nand_data *);
> +int gpmi_enable_clk(struct gpmi_nand_data *this);
> +int gpmi_disable_clk(struct gpmi_nand_data *this);
> +int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr,
> + const struct nand_data_interface *conf);
> +void gpmi_nfc_apply_timings(struct gpmi_nand_data *this);
> int gpmi_read_data(struct gpmi_nand_data *);
> int gpmi_send_data(struct gpmi_nand_data *);
> int gpmi_send_page(struct gpmi_nand_data *,
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index e209a65a17e0..9602d17845e2 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -1267,7 +1267,8 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
> return 0;
>
> err_reset_chip:
> - onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
> + /* Fallback to timing mode 0 */
> + nand_reset_data_interface(chip, chipnr);
> chip->select_chip(mtd, chipnr);
> nand_reset_op(chip);
> chip->select_chip(mtd, -1);
Tested-by: Han Xu <han.xu at nxp.com>
More information about the linux-mtd
mailing list