diff mbox series

[v3,3/3] mtd: rawnand: denali: optimize timing parameters for data interface

Message ID 1529025532-22087-4-git-send-email-yamada.masahiro@socionext.com
State Superseded
Headers show
Series None | expand

Commit Message

Masahiro Yamada June 15, 2018, 1:18 a.m. UTC
This commit improves the ->setup_data_interface() hook.

The denali_setup_data_interface() needs the frequency of clk_x
and the ratio of clk_x / clk.

The latter is currently hardcoded in the driver, like this:

  #define DENALI_CLK_X_MULT       6

The IP datasheet requires that clk_x / clk be 4, 5, or 6.  I just
chose 6 because it is the most defensive value, but it is not optimal.
By getting the clock rate of both "clk" and "clk_x", the driver can
compute the timing values more precisely.

To not break the existing platforms, the fallback value, 50 MHz is
provided.  It is true for all upstreamed platforms.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>

---

Changes in v3: None
Changes in v2:
  - Split patches into sensible chunks

 drivers/mtd/nand/raw/denali.c     | 49 +++++++++++++++++++--------------------
 drivers/mtd/nand/raw/denali.h     |  1 +
 drivers/mtd/nand/raw/denali_dt.c  |  2 ++
 drivers/mtd/nand/raw/denali_pci.c |  1 +
 4 files changed, 28 insertions(+), 25 deletions(-)

-- 
2.7.4

Comments

Richard Weinberger June 18, 2018, 7:22 a.m. UTC | #1
Am Freitag, 15. Juni 2018, 03:18:52 CEST schrieb Masahiro Yamada:
> This commit improves the ->setup_data_interface() hook.

> 

> The denali_setup_data_interface() needs the frequency of clk_x

> and the ratio of clk_x / clk.

> 

> The latter is currently hardcoded in the driver, like this:

> 

>   #define DENALI_CLK_X_MULT       6

> 

> The IP datasheet requires that clk_x / clk be 4, 5, or 6.  I just

> chose 6 because it is the most defensive value, but it is not optimal.

> By getting the clock rate of both "clk" and "clk_x", the driver can

> compute the timing values more precisely.


What datasheet do you have, is it public?
Mine clearly states that the factor is 4.
"The frequency of nand_x_clk is four times the frequency of nand_clk."

> To not break the existing platforms, the fallback value, 50 MHz is

> provided.  It is true for all upstreamed platforms.

> 

> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>


Reviewed-by: Richard Weinberger <richard@nod.at>


Thanks,
//richard
Masahiro Yamada June 18, 2018, 1:53 p.m. UTC | #2
Hi Richard,


2018-06-18 16:22 GMT+09:00 Richard Weinberger <richard@nod.at>:
> Am Freitag, 15. Juni 2018, 03:18:52 CEST schrieb Masahiro Yamada:

>> This commit improves the ->setup_data_interface() hook.

>>

>> The denali_setup_data_interface() needs the frequency of clk_x

>> and the ratio of clk_x / clk.

>>

>> The latter is currently hardcoded in the driver, like this:

>>

>>   #define DENALI_CLK_X_MULT       6

>>

>> The IP datasheet requires that clk_x / clk be 4, 5, or 6.  I just

>> chose 6 because it is the most defensive value, but it is not optimal.

>> By getting the clock rate of both "clk" and "clk_x", the driver can

>> compute the timing values more precisely.

>

> What datasheet do you have, is it public?


No.  Not available in public.

I got the datasheet
because Socionext (formerly, Panasonic) bought this IP.


> Mine clearly states that the factor is 4.

> "The frequency of nand_x_clk is four times the frequency of nand_clk."


I checked
"Denali NAND Flash Memory Controller User's Guide"
released by Denali.

I also get access to a newer version
"Cadence Design IP - NAND Flash Memory Controller User's Guide"
because Cadence acquired Denali.


My datasheet says:
"clk_x - Cadence NAND Flash Memory Controller bus
interface clock. This runs at a configured
multiple of clk as is phase aligned. Configured
multiple can be between 4-6 times."



Various parts of this IP are configurable.
Is yours talking about this IP,
or about a particular configuration for SOCFPGA?



>> To not break the existing platforms, the fallback value, 50 MHz is

>> provided.  It is true for all upstreamed platforms.

>>

>> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>

>

> Reviewed-by: Richard Weinberger <richard@nod.at>

>

> Thanks,

> //richard

> --

> To unsubscribe from this list: send the line "unsubscribe linux-kbuild" in

> the body of a message to majordomo@vger.kernel.org

> More majordomo info at  http://vger.kernel.org/majordomo-info.html




-- 
Best Regards
Masahiro Yamada
Richard Weinberger June 19, 2018, 11:17 a.m. UTC | #3
Am Freitag, 15. Juni 2018, 03:18:52 CEST schrieb Masahiro Yamada:
> This commit improves the ->setup_data_interface() hook.

> 

> The denali_setup_data_interface() needs the frequency of clk_x

> and the ratio of clk_x / clk.

> 

> The latter is currently hardcoded in the driver, like this:

> 

>   #define DENALI_CLK_X_MULT       6

> 

> The IP datasheet requires that clk_x / clk be 4, 5, or 6.  I just

> chose 6 because it is the most defensive value, but it is not optimal.

> By getting the clock rate of both "clk" and "clk_x", the driver can

> compute the timing values more precisely.

> 

> To not break the existing platforms, the fallback value, 50 MHz is

> provided.  It is true for all upstreamed platforms.

> 

> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>

> ---

> 

> Changes in v3: None

> Changes in v2:

>   - Split patches into sensible chunks

> 

>  drivers/mtd/nand/raw/denali.c     | 49 +++++++++++++++++++--------------------

>  drivers/mtd/nand/raw/denali.h     |  1 +

>  drivers/mtd/nand/raw/denali_dt.c  |  2 ++

>  drivers/mtd/nand/raw/denali_pci.c |  1 +

>  4 files changed, 28 insertions(+), 25 deletions(-)

> 

> diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c

> index 2a302a1..2de46d4 100644

> --- a/drivers/mtd/nand/raw/denali.c

> +++ b/drivers/mtd/nand/raw/denali.c

> @@ -51,14 +51,6 @@ MODULE_LICENSE("GPL");

>  #define DENALI_INVALID_BANK	-1

>  #define DENALI_NR_BANKS		4

>  

> -/*

> - * The bus interface clock, clk_x, is phase aligned with the core clock.  The

> - * clk_x is an integral multiple N of the core clk.  The value N is configured

> - * at IP delivery time, and its available value is 4, 5, or 6.  We need to align

> - * to the largest value to make it work with any possible configuration.

> - */

> -#define DENALI_CLK_X_MULT	6

> -

>  static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)

>  {

>  	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);

> @@ -954,7 +946,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  {

>  	struct denali_nand_info *denali = mtd_to_denali(mtd);

>  	const struct nand_sdr_timings *timings;

> -	unsigned long t_clk;

> +	unsigned long t_x, mult_x;

>  	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;

>  	int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;

>  	int addr_2_data_mask;

> @@ -965,15 +957,24 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  		return PTR_ERR(timings);

>  

>  	/* clk_x period in picoseconds */

> -	t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);

> -	if (!t_clk)

> +	t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);

> +	if (!t_x)

> +		return -EINVAL;

> +

> +	/*

> +	 * The bus interface clock, clk_x, is phase aligned with the core clock.

> +	 * The clk_x is an integral multiple N of the core clk.  The value N is

> +	 * configured at IP delivery time, and its available value is 4, 5, 6.

> +	 */

> +	mult_x = DIV_ROUND_CLOSEST_ULL(denali->clk_x_rate, denali->clk_rate);

> +	if (mult_x < 4 || mult_x > 6)

>  		return -EINVAL;

>  

>  	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)

>  		return 0;

>  

>  	/* tREA -> ACC_CLKS */

> -	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);

> +	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x);

>  	acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);

>  

>  	tmp = ioread32(denali->reg + ACC_CLKS);

> @@ -982,7 +983,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	iowrite32(tmp, denali->reg + ACC_CLKS);

>  

>  	/* tRWH -> RE_2_WE */

> -	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);

> +	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x);

>  	re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);

>  

>  	tmp = ioread32(denali->reg + RE_2_WE);

> @@ -991,7 +992,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	iowrite32(tmp, denali->reg + RE_2_WE);

>  

>  	/* tRHZ -> RE_2_RE */

> -	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);

> +	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x);

>  	re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);

>  

>  	tmp = ioread32(denali->reg + RE_2_RE);

> @@ -1005,8 +1006,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	 * With WE_2_RE properly set, the Denali controller automatically takes

>  	 * care of the delay; the driver need not set NAND_WAIT_TCCS.

>  	 */

> -	we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),

> -			       t_clk);

> +	we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x);

>  	we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);

>  

>  	tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);

> @@ -1021,7 +1021,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	if (denali->revision < 0x0501)

>  		addr_2_data_mask >>= 1;

>  

> -	addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);

> +	addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_x);

>  	addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);

>  

>  	tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);

> @@ -1031,7 +1031,7 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  

>  	/* tREH, tWH -> RDWR_EN_HI_CNT */

>  	rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),

> -				  t_clk);

> +				  t_x);

>  	rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);

>  

>  	tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);

> @@ -1040,11 +1040,10 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);

>  

>  	/* tRP, tWP -> RDWR_EN_LO_CNT */

> -	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),

> -				  t_clk);

> +	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);

>  	rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),

> -				     t_clk);

> -	rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);

> +				     t_x);

> +	rdwr_en_lo_hi = max_t(int, rdwr_en_lo_hi, mult_x);

>  	rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);

>  	rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);

>  

> @@ -1054,8 +1053,8 @@ static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,

>  	iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);

>  

>  	/* tCS, tCEA -> CS_SETUP_CNT */

> -	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,

> -			(int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,

> +	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,

> +			(int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks,

>  			0);

>  	cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);

>  

> @@ -1282,7 +1281,7 @@ int denali_init(struct denali_nand_info *denali)

>  	}

>  

>  	/* clk rate info is needed for setup_data_interface */

> -	if (denali->clk_x_rate)

> +	if (denali->clk_rate && denali->clk_x_rate)

>  		chip->setup_data_interface = denali_setup_data_interface;

>  

>  	ret = nand_scan_ident(mtd, denali->max_banks, NULL);

> diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h

> index 9ad33d2..1f8feaf 100644

> --- a/drivers/mtd/nand/raw/denali.h

> +++ b/drivers/mtd/nand/raw/denali.h

> @@ -300,6 +300,7 @@

>  

>  struct denali_nand_info {

>  	struct nand_chip nand;

> +	unsigned long clk_rate;		/* core clock rate */

>  	unsigned long clk_x_rate;	/* bus interface clock rate */

>  	int active_bank;		/* currently selected bank */

>  	struct device *dev;

> diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c

> index afaae37..0faaad0 100644

> --- a/drivers/mtd/nand/raw/denali_dt.c

> +++ b/drivers/mtd/nand/raw/denali_dt.c

> @@ -150,6 +150,7 @@ static int denali_dt_probe(struct platform_device *pdev)

>  		goto out_disable_clk_x;

>  

>  	if (dt->clk_x) {

> +		denali->clk_rate = clk_get_rate(dt->clk);

>  		denali->clk_x_rate = clk_get_rate(dt->clk_x);

>  	} else {

>  		/*

> @@ -158,6 +159,7 @@ static int denali_dt_probe(struct platform_device *pdev)

>  		 */

>  		dev_notice(dev,

>  			   "necessary clock is missing. default clock rates are used.\n");

> +		denali->clk_rate = 50000000;

>  		denali->clk_x_rate = 200000000;

>  	}

>  

> diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c

> index 49cb3e1..7c8efc4 100644

> --- a/drivers/mtd/nand/raw/denali_pci.c

> +++ b/drivers/mtd/nand/raw/denali_pci.c

> @@ -73,6 +73,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)

>  	denali->irq = dev->irq;

>  	denali->ecc_caps = &denali_pci_ecc_caps;

>  	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;

> +	denali->clk_rate = 50000000;		/* 50 MHz */

>  	denali->clk_x_rate = 200000000;		/* 200 MHz */

>  

>  	ret = pci_request_regions(dev, DENALI_NAND_NAME);

> 


Tested-by: Richard Weinberger <richard@nod.at>


Thanks,
//richard
diff mbox series

Patch

diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c
index 2a302a1..2de46d4 100644
--- a/drivers/mtd/nand/raw/denali.c
+++ b/drivers/mtd/nand/raw/denali.c
@@ -51,14 +51,6 @@  MODULE_LICENSE("GPL");
 #define DENALI_INVALID_BANK	-1
 #define DENALI_NR_BANKS		4
 
-/*
- * The bus interface clock, clk_x, is phase aligned with the core clock.  The
- * clk_x is an integral multiple N of the core clk.  The value N is configured
- * at IP delivery time, and its available value is 4, 5, or 6.  We need to align
- * to the largest value to make it work with any possible configuration.
- */
-#define DENALI_CLK_X_MULT	6
-
 static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 {
 	return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
@@ -954,7 +946,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	const struct nand_sdr_timings *timings;
-	unsigned long t_clk;
+	unsigned long t_x, mult_x;
 	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
 	int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
 	int addr_2_data_mask;
@@ -965,15 +957,24 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 		return PTR_ERR(timings);
 
 	/* clk_x period in picoseconds */
-	t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
-	if (!t_clk)
+	t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+	if (!t_x)
+		return -EINVAL;
+
+	/*
+	 * The bus interface clock, clk_x, is phase aligned with the core clock.
+	 * The clk_x is an integral multiple N of the core clk.  The value N is
+	 * configured at IP delivery time, and its available value is 4, 5, 6.
+	 */
+	mult_x = DIV_ROUND_CLOSEST_ULL(denali->clk_x_rate, denali->clk_rate);
+	if (mult_x < 4 || mult_x > 6)
 		return -EINVAL;
 
 	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
 		return 0;
 
 	/* tREA -> ACC_CLKS */
-	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_x);
 	acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
 
 	tmp = ioread32(denali->reg + ACC_CLKS);
@@ -982,7 +983,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	iowrite32(tmp, denali->reg + ACC_CLKS);
 
 	/* tRWH -> RE_2_WE */
-	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x);
 	re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
 
 	tmp = ioread32(denali->reg + RE_2_WE);
@@ -991,7 +992,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	iowrite32(tmp, denali->reg + RE_2_WE);
 
 	/* tRHZ -> RE_2_RE */
-	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x);
 	re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
 
 	tmp = ioread32(denali->reg + RE_2_RE);
@@ -1005,8 +1006,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	 * With WE_2_RE properly set, the Denali controller automatically takes
 	 * care of the delay; the driver need not set NAND_WAIT_TCCS.
 	 */
-	we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min),
-			       t_clk);
+	we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x);
 	we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
 
 	tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
@@ -1021,7 +1021,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	if (denali->revision < 0x0501)
 		addr_2_data_mask >>= 1;
 
-	addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+	addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_x);
 	addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
 
 	tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
@@ -1031,7 +1031,7 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 
 	/* tREH, tWH -> RDWR_EN_HI_CNT */
 	rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
-				  t_clk);
+				  t_x);
 	rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
 
 	tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
@@ -1040,11 +1040,10 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
 
 	/* tRP, tWP -> RDWR_EN_LO_CNT */
-	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
-				  t_clk);
+	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);
 	rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
-				     t_clk);
-	rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+				     t_x);
+	rdwr_en_lo_hi = max_t(int, rdwr_en_lo_hi, mult_x);
 	rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
 	rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
 
@@ -1054,8 +1053,8 @@  static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
 	iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
 
 	/* tCS, tCEA -> CS_SETUP_CNT */
-	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
-			(int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,
+			(int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks,
 			0);
 	cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
 
@@ -1282,7 +1281,7 @@  int denali_init(struct denali_nand_info *denali)
 	}
 
 	/* clk rate info is needed for setup_data_interface */
-	if (denali->clk_x_rate)
+	if (denali->clk_rate && denali->clk_x_rate)
 		chip->setup_data_interface = denali_setup_data_interface;
 
 	ret = nand_scan_ident(mtd, denali->max_banks, NULL);
diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h
index 9ad33d2..1f8feaf 100644
--- a/drivers/mtd/nand/raw/denali.h
+++ b/drivers/mtd/nand/raw/denali.h
@@ -300,6 +300,7 @@ 
 
 struct denali_nand_info {
 	struct nand_chip nand;
+	unsigned long clk_rate;		/* core clock rate */
 	unsigned long clk_x_rate;	/* bus interface clock rate */
 	int active_bank;		/* currently selected bank */
 	struct device *dev;
diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c
index afaae37..0faaad0 100644
--- a/drivers/mtd/nand/raw/denali_dt.c
+++ b/drivers/mtd/nand/raw/denali_dt.c
@@ -150,6 +150,7 @@  static int denali_dt_probe(struct platform_device *pdev)
 		goto out_disable_clk_x;
 
 	if (dt->clk_x) {
+		denali->clk_rate = clk_get_rate(dt->clk);
 		denali->clk_x_rate = clk_get_rate(dt->clk_x);
 	} else {
 		/*
@@ -158,6 +159,7 @@  static int denali_dt_probe(struct platform_device *pdev)
 		 */
 		dev_notice(dev,
 			   "necessary clock is missing. default clock rates are used.\n");
+		denali->clk_rate = 50000000;
 		denali->clk_x_rate = 200000000;
 	}
 
diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c
index 49cb3e1..7c8efc4 100644
--- a/drivers/mtd/nand/raw/denali_pci.c
+++ b/drivers/mtd/nand/raw/denali_pci.c
@@ -73,6 +73,7 @@  static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	denali->irq = dev->irq;
 	denali->ecc_caps = &denali_pci_ecc_caps;
 	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
+	denali->clk_rate = 50000000;		/* 50 MHz */
 	denali->clk_x_rate = 200000000;		/* 200 MHz */
 
 	ret = pci_request_regions(dev, DENALI_NAND_NAME);