mtd: rawnand: denali: optimize timing parameters for data interface
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Wed, 19 Dec 2018 11:03:18 +0000 (20:03 +0900)
committerMasahiro Yamada <yamada.masahiro@socionext.com>
Sat, 29 Dec 2018 02:38:37 +0000 (11:38 +0900)
Based on Linux commit 1dfac31a5a63ac04a9b5fbc3f5105a586560f191

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>
drivers/mtd/nand/raw/denali.c
drivers/mtd/nand/raw/denali.h
drivers/mtd/nand/raw/denali_dt.c

index d1cac063f46f5a755ac624eec98ea477ce230e4e..bbfa3b6112e8ba003963870db4d266ced663a908 100644 (file)
@@ -69,14 +69,6 @@ static int dma_mapping_error(void *dev, dma_addr_t addr)
 #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);
@@ -946,7 +938,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;
@@ -957,15 +949,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);
@@ -974,7 +975,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);
@@ -983,7 +984,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);
@@ -997,8 +998,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);
@@ -1013,7 +1013,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);
@@ -1023,7 +1023,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);
@@ -1032,11 +1032,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);
 
@@ -1046,8 +1045,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);
 
index 9b797beffa7c007bb74c5fd10c0012af88b63daf..019deda094e5de952ad9725d4d4968aad9df764a 100644 (file)
@@ -292,6 +292,7 @@ struct udevice;
 
 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 udevice *dev;
index f9eadb4a310b2bcca75e3844ea34e1ee3d882570..d384b974df1c9c8cbc6498133f11a667ce0d4744 100644 (file)
@@ -118,6 +118,7 @@ static int denali_dt_probe(struct udevice *dev)
        }
 
        if (clk_x.dev) {
+               denali->clk_rate = clk_get_rate(&clk);
                denali->clk_x_rate = clk_get_rate(&clk_x);
        } else {
                /*
@@ -126,6 +127,7 @@ static int denali_dt_probe(struct udevice *dev)
                 */
                dev_notice(dev,
                           "necessary clock is missing. default clock rates are used.\n");
+               denali->clk_rate = 50000000;
                denali->clk_x_rate = 200000000;
        }