mtd: rawnand: denali: optimize timing parameters for data interface

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>
Reviewed-by: Richard Weinberger <richard@nod.at>
Tested-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
This commit is contained in:
Masahiro Yamada 2018-06-23 01:06:38 +09:00 committed by Miquel Raynal
parent 6f1fe97bec
commit 1dfac31a5a
4 changed files with 28 additions and 25 deletions

View File

@ -51,14 +51,6 @@ MODULE_LICENSE("GPL");
#define DENALI_INVALID_BANK -1 #define DENALI_INVALID_BANK -1
#define DENALI_NR_BANKS 4 #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) 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); 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); struct denali_nand_info *denali = mtd_to_denali(mtd);
const struct nand_sdr_timings *timings; 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 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 rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
int addr_2_data_mask; 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); return PTR_ERR(timings);
/* clk_x period in picoseconds */ /* clk_x period in picoseconds */
t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
if (!t_clk) 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; return -EINVAL;
if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
return 0; return 0;
/* tREA -> ACC_CLKS */ /* 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); acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
tmp = ioread32(denali->reg + ACC_CLKS); 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); iowrite32(tmp, denali->reg + ACC_CLKS);
/* tRWH -> RE_2_WE */ /* 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); re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
tmp = ioread32(denali->reg + RE_2_WE); 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); iowrite32(tmp, denali->reg + RE_2_WE);
/* tRHZ -> RE_2_RE */ /* 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); re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
tmp = ioread32(denali->reg + RE_2_RE); 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 * With WE_2_RE properly set, the Denali controller automatically takes
* care of the delay; the driver need not set NAND_WAIT_TCCS. * 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), we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x);
t_clk);
we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); 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); 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) if (denali->revision < 0x0501)
addr_2_data_mask >>= 1; 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); addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); 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 */ /* tREH, tWH -> RDWR_EN_HI_CNT */
rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), 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); rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); 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); iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
/* tRP, tWP -> RDWR_EN_LO_CNT */ /* tRP, tWP -> RDWR_EN_LO_CNT */
rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x);
t_clk);
rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
t_clk); t_x);
rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); 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 = 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); 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); iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
/* tCS, tCEA -> CS_SETUP_CNT */ /* tCS, tCEA -> CS_SETUP_CNT */
cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo,
(int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, (int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks,
0); 0);
cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
@ -1255,7 +1254,7 @@ int denali_init(struct denali_nand_info *denali)
} }
/* clk rate info is needed for setup_data_interface */ /* 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; chip->setup_data_interface = denali_setup_data_interface;
ret = nand_scan_ident(mtd, denali->max_banks, NULL); ret = nand_scan_ident(mtd, denali->max_banks, NULL);

View File

@ -300,6 +300,7 @@
struct denali_nand_info { struct denali_nand_info {
struct nand_chip nand; struct nand_chip nand;
unsigned long clk_rate; /* core clock rate */
unsigned long clk_x_rate; /* bus interface clock rate */ unsigned long clk_x_rate; /* bus interface clock rate */
int active_bank; /* currently selected bank */ int active_bank; /* currently selected bank */
struct device *dev; struct device *dev;

View File

@ -150,6 +150,7 @@ static int denali_dt_probe(struct platform_device *pdev)
goto out_disable_clk_x; goto out_disable_clk_x;
if (dt->clk_x) { if (dt->clk_x) {
denali->clk_rate = clk_get_rate(dt->clk);
denali->clk_x_rate = clk_get_rate(dt->clk_x); denali->clk_x_rate = clk_get_rate(dt->clk_x);
} else { } else {
/* /*
@ -158,6 +159,7 @@ static int denali_dt_probe(struct platform_device *pdev)
*/ */
dev_notice(dev, dev_notice(dev,
"necessary clock is missing. default clock rates are used.\n"); "necessary clock is missing. default clock rates are used.\n");
denali->clk_rate = 50000000;
denali->clk_x_rate = 200000000; denali->clk_x_rate = 200000000;
} }

View File

@ -73,6 +73,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
denali->irq = dev->irq; denali->irq = dev->irq;
denali->ecc_caps = &denali_pci_ecc_caps; denali->ecc_caps = &denali_pci_ecc_caps;
denali->nand.ecc.options |= NAND_ECC_MAXIMIZE; denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
denali->clk_rate = 50000000; /* 50 MHz */
denali->clk_x_rate = 200000000; /* 200 MHz */ denali->clk_x_rate = 200000000; /* 200 MHz */
ret = pci_request_regions(dev, DENALI_NAND_NAME); ret = pci_request_regions(dev, DENALI_NAND_NAME);