mmc: rk_sdmmc: new tuning policy
authorShawn Lin <shawn.lin@rock-chips.com>
Mon, 8 Jun 2015 08:04:20 +0000 (16:04 +0800)
committerShawn Lin <shawn.lin@rock-chips.com>
Tue, 23 Jun 2015 01:36:42 +0000 (09:36 +0800)
  Totally new tuning policy for rockchip mhsc
merging from upstream and sustainable experiments
conducted by massive test using tier one customers's board.
This fine ajustment might be more durable and readable.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Signed-off-by: Xiao Yao <xiaoyao@rock-chips.com>
Test-by: Xiao Yao <xiaoyao@rock-chips.com>
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/rk_sdmmc.c
drivers/mmc/host/rk_sdmmc.h
include/linux/mmc/rk_mmc.h

index 214b8fc05de6cf8d15fb51b17445ee864076144c..ad998fbab5459ee86620b1028d4e089069dd2850 100755 (executable)
 
 #include "rk_sdmmc_dbg.h"
 
-/*CRU SDMMC TUNING*/
-/*
-*   sdmmc,sdio0,sdio1,emmc id=0~3
-*   cclk_in_drv, cclk_in_sample  i=0,1
-*/
-
-static  u32 cru_tuning_base = 0;
-
-#define CRU_SDMMC_CON(id, tuning_type) (cru_tuning_base + ((id) * 8) + ((tuning_type) * 4))
-
-#define MAX_DELAY_LINE  (0xff)
-#define FREQ_REF_150MHZ (150000000)
-#define PRECISE_ADJUST  (0)
-
-#define SDMMC_TUNING_SEL(tuning_type)           ( tuning_type? 10:11 )
-#define SDMMC_TUNING_DELAYNUM(tuning_type)      ( tuning_type? 2:3 )
-#define SDMMC_TUNING_DEGREE(tuning_type)        ( tuning_type? 0:1 )
-#define SDMMC_TUNING_INIT_STATE                 (0)
-
-enum{
-       SDMMC_SHIFT_DEGREE_0 = 0,
-       SDMMC_SHIFT_DEGREE_90,
-       SDMMC_SHIFT_DEGREE_180,
-       SDMMC_SHIFT_DEGREE_270,
-       SDMMC_SHIFT_DEGREE_INVALID,
-};
-
-const char *phase_desc[SDMMC_SHIFT_DEGREE_INVALID + 1] = {
-        "SDMMC_SHIFT_DEGREE_0",
-        "SDMMC_SHIFT_DEGREE_90",
-        "SDMMC_SHIFT_DEGREE_180",
-        "SDMMC_SHIFT_DEGREE_270",
-        "SDMMC_SHIFT_DEGREE_INVALID",
-};
-
-enum{
-        USE_CLK_AFTER_PHASE = 0,
-        USE_CLK_AFTER_PHASE_AND_DELAY_LINE = 1,
-};
-
-enum{
-        IO_DRV_2MA  = 0x0,
-        IO_DRV_4MA  = 0x1,
-        IO_DRV_8MA  = 0x2,
-        IO_DRV_12MA = 0x3,
-};
-
-enum{
-        SLEW_RATE_SLOW = 0,
-        SLEW_RATE_FAST = 1,
-};
-
-
 /* Rockchip implementation specific driver private data */
 struct dw_mci_rockchip_priv_data {
        enum dw_mci_rockchip_type               ctrl_type;
@@ -98,39 +45,46 @@ static struct dw_mci_rockchip_compatible {
        {
                .compatible     = "rockchip,rk31xx-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK3188,
-       },{
+       },
+       {
                .compatible     = "rockchip,rk32xx-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK3288,
-       },{
+       },
+       {
                .compatible     = "rockchip,rk3036-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK3036,
-       },{
+       },
+       {
                .compatible     = "rockchip,rk312x-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK312X,
-       },{
+       },
+       {
                .compatible     = "rockchip,rk3368-sdmmc",
                .ctrl_type      = DW_MCI_TYPE_RK3368,
        },
 };
 
+#define syscon_find(np, property) \
+syscon_regmap_lookup_by_phandle(np, property)
+
 static int dw_mci_rockchip_priv_init(struct dw_mci *host)
 {
        struct dw_mci_rockchip_priv_data *priv;
        int idx;
 
        priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
-       if(!priv){
+       if (!priv) {
                dev_err(host->dev, "mem alloc failed for private data\n");
                return -ENOMEM;
        }
 
-       for(idx = 0; idx < ARRAY_SIZE(rockchip_compat); idx++){
-                if(of_device_is_compatible(host->dev->of_node,
-                                rockchip_compat[idx].compatible)) {
+       for (idx = 0; idx < ARRAY_SIZE(rockchip_compat); idx++) {
+               if (of_device_is_compatible(host->dev->of_node,
+                                           rockchip_compat[idx].compatible)) {
                        priv->ctrl_type = rockchip_compat[idx].ctrl_type;
                        host->cid = priv->ctrl_type;
-                       host->grf = syscon_regmap_lookup_by_phandle(host->dev->of_node,
-                                                               "rockchip,grf");
+                       host->grf = syscon_find(host->dev->of_node,
+                                               "rockchip,grf");
                }
        }
 
@@ -143,9 +97,9 @@ static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
        struct dw_mci_rockchip_priv_data *priv = host->priv;
 
        if ((priv->ctrl_type == DW_MCI_TYPE_RK3288) ||
-                (priv->ctrl_type == DW_MCI_TYPE_RK3036) ||
-                (priv->ctrl_type == DW_MCI_TYPE_RK312X) ||
-               (priv->ctrl_type == DW_MCI_TYPE_RK3368))
+           (priv->ctrl_type == DW_MCI_TYPE_RK3036) ||
+           (priv->ctrl_type == DW_MCI_TYPE_RK312X) ||
+           (priv->ctrl_type == DW_MCI_TYPE_RK3368))
                host->bus_hz /= (priv->ciu_div + 1);
 
        return 0;
@@ -153,175 +107,88 @@ static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
 
 static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
-
 }
 
 static void dw_mci_rockchip_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
-
 }
 
 static int dw_mci_rockchip_parse_dt(struct dw_mci *host)
 {
-        return 0;
-}
-static inline u8 dw_mci_rockchip_get_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type)
-{
-        u32 regs;
-        u8 delaynum;
-
-        regs =  cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
-        delaynum = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
-
-        return delaynum;
+       return 0;
 }
 
-static inline void dw_mci_rockchip_set_delaynum(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 delaynum)
-{
-        u32 regs;
-        regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
-        regs &= ~( 0xff << SDMMC_TUNING_DELAYNUM(tuning_type));
-        regs |= (delaynum  << SDMMC_TUNING_DELAYNUM(tuning_type));
-        regs |= (0xff  << (SDMMC_TUNING_DELAYNUM(tuning_type)+16));
-
-        MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[delayline]: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
-                con_id, tuning_type, regs, mmc_hostname(host->mmc));
+#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK 0x3
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
+#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+#define PSECS_PER_SEC 1000000000000LL
 
-        cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
+/*
+ * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
+ */
+#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
 
-       MMC_DBG_INFO_FUNC(host->mmc,"CRU_SDMMC_CON(con_id, tuning_type) = 0x%x, regs = 0x%x [%s]",
-                       CRU_SDMMC_CON(con_id, tuning_type), cru_readl(CRU_SDMMC_CON(con_id, tuning_type)), mmc_hostname(host->mmc));
-}
+#define NUM_PHASES                     32
+#define TUNING_ITERATION_TO_PHASE(i)   (DIV_ROUND_UP((i) * 360, NUM_PHASES))
 
-static inline void dw_mci_rockchip_set_degree(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 phase)
+static int rockchip_mmc_set_phase(int degrees, struct dw_mci *host)
 {
-        u32 regs;
-        
-       regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type));
-       regs &= ~( 0x3 << SDMMC_TUNING_DEGREE(tuning_type));
-       regs |= (phase  << SDMMC_TUNING_DEGREE(tuning_type));
-       regs |= (0x3  << (SDMMC_TUNING_DEGREE(tuning_type)+16));
-
-        MMC_DBG_INFO_FUNC(host->mmc,"tuning_result[phase]: con_id = %d, tuning_type= %d, CRU_CON = 0x%x. [%s]",
-                con_id, tuning_type, regs, mmc_hostname(host->mmc));
-       
-       cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
-       MMC_DBG_INFO_FUNC(host->mmc,"CRU_SDMMC_CON(con_id, tuning_type) = 0x%x, regs = 0x%x [%s]",
-               CRU_SDMMC_CON(con_id, tuning_type), cru_readl(CRU_SDMMC_CON(con_id, tuning_type)), mmc_hostname(host->mmc));
-}
+       unsigned long rate = clk_get_rate(host->clk_mmc)/2; /* 150M */
+       u8 nineties, remainder;
+       u8 delay_num;
+       u32 raw_value;
+       u64 delay;
 
-static inline void dw_mci_rockchip_turning_sel(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 mode)
-{
-        u32 regs;
-       regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
-       regs &= ~( 0x1 << SDMMC_TUNING_SEL(tuning_type));
-       regs |= (mode  << SDMMC_TUNING_SEL(tuning_type));
-       regs |= (0x1  << (SDMMC_TUNING_SEL(tuning_type)+16));
-
-       MMC_DBG_INFO_FUNC(host->mmc,"tuning_sel: con_id = %d, tuning_type = %d, CRU_CON = 0x%x. [%s]",
-                con_id, tuning_type, regs, mmc_hostname(host->mmc));
-                
-       cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));       
-}
+       degrees++;
+       degrees -= ((degrees) * 10 % (360/NUM_PHASES*10)) / 10;
+       nineties = degrees / 90;
+       remainder = (degrees % 90) / (360/NUM_PHASES);
 
+       delay = PSECS_PER_SEC;
+       do_div(delay, rate);
+       do_div(delay, NUM_PHASES);
+       do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC);
 
-static inline u8 dw_mci_rockchip_get_phase(struct dw_mci *host, u8 con_id, u8 tuning_type)
-{
-       return 0;
-}
+       delay *= remainder;
+       delay_num = (u8) min(delay, 255ULL);
 
-static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 val)
-{
-        u32 regs;
-        
-        regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
+       raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
+       raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
+       raw_value |= nineties;
 
-       if(tuning_type) {
-           val = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
-       }
+       cru_writel(0x00010001, host->tune_regsbase);
+       cru_writel(((0x07ff << 16)|(raw_value)), host->tune_regsbase + 4);
+       cru_writel(0x00010000, host->tune_regsbase);
 
-       return val;
-}
-
-static void dw_mci_rockchip_load_signal_integrity(struct dw_mci *host, u32 sr, u32 drv)
-{
-        if (unlikely((drv > IO_DRV_12MA) || (sr > SLEW_RATE_FAST))) {
-                MMC_DBG_ERR_FUNC(host->mmc,"wrong signal integrity setting: drv = %d, sr = %d ![%s]",
-                        drv, sr, mmc_hostname(host->mmc));
-                return;
-        }
-
-        if(cpu_is_rk3288()){
-                /*Note 00: 2ma 01:4ma 10:8ma 11:12ma
-                For consider line loading and IP's slew rate,
-                we should match these by every board depends for signal integrity.
-                slew rate >= 2*pi*f*Vpeak = max(|d'(Vpeak)/dt|)
-                */
-                if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) {
-                        grf_writel(0xff005500 | (drv << 14) | (drv << 12) |
-                                                 (drv << 10) | (drv << 8), 0x01f8); /* GPIO4C4-C7 */
-                        grf_writel(0x000f0000 | (drv << 0) | (drv << 2), 0x01fc); /* GPIO4D0-D1 */
-                        grf_writel(0x03f00000 | (sr << 4) | (sr << 5) | (sr << 6) |
-                                                (sr << 7) | (sr << 8) | (sr << 9) , 0x011c); /* slew rate*/
-                }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
-                        grf_writel(0x3fff0000 | (drv << 0) | (drv << 2) | (drv << 4) |
-                                                 (drv << 6) | (drv << 8) | (drv << 10) |
-                                                 (drv << 12), 0x0218); /* GPIO6C0-C6 */
-                        grf_writel(0x003f0000 | (sr << 0) | (sr << 1) | (sr << 2) |
-                                                (sr << 3) | (sr << 4) | (sr << 5), 0x012c); /* slew rate */
-                }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) {
-                        /* emmc hardware relative addr match requirement, assume 4ma not slow slew rate */
-                        grf_writel(0xffff5555, 0x01e0); /* GPIO3A0-A7 */
-                        grf_writel(0x000c0006, 0x01e4); /* GPIO3B1 */
-                        grf_writel(0x003f0015, 0x01e8); /* GPIO3C2-C0 */
-                }
-        } else if (host->cid == DW_MCI_TYPE_RK3368) {
-                if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO) {
-                       regmap_write(host->grf, 0x21c, 0xff000000 | (drv << 14) | (drv << 12) |
-                                               (drv << 10) | (drv << 8)); /* GPIO2D4-D7 */
-                       regmap_write(host->grf, 0x220, 0x000f0000 | (drv << 0) | (drv << 2)); /* GPIO3A0-A1 */
-               }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD) {
-                       regmap_write(host->grf, 0x210, 0xfc000000 | (drv << 10) | (drv << 12) | (drv << 14)); /* GPIO2A5-A7 */
-                       regmap_write(host->grf, 0x214, 0x003f0000 | (drv << 0) | (drv << 2) | (drv << 4)); /* GPIO2B0-2 */
-               }else if (host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC) {
-                       regmap_write(host->grf, 0x208, 0xfff00000 | (drv << 4) | (drv << 6) | (drv << 8) | (drv << 10) 
-                                                               | (drv << 12) | (drv << 14)); /* GPIO1C2-C7 */
-                       regmap_write(host->grf, 0x20c, 0x003f0000 | (drv << 0) | (drv << 2) | (drv << 4)); /* GPIO1D0-D2 */
-                       regmap_write(host->grf, 0x210, 0x03000000 | (drv << 8)); /* GPIO2A4 */
-               }
-       }
-
-}
-static void dw_mci_rockchip_load_tuning_base(struct dw_mci *host)
-{
-        /* load tuning base */
-        if (cpu_is_rk3288()) {
-                cru_tuning_base =  RK3288_CRU_SDMMC_CON0;
-       } else if (host->cid == DW_MCI_TYPE_RK3368) {
-               if (IS_ERR(host->grf))
-                       pr_err("rk_sdmmc: dts couldn't find grf regmap\n");
-
-               cru_tuning_base =  0x400;
-       }
+       return 0;
 }
 
-static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
-                                       u8 *blk_test, unsigned int blksz)
+static int dw_mci_tuning_test(struct dw_mci_slot *slot, u32 opcode,
+                             struct dw_mci_tuning_data *tuning_data,
+                             u8 *blk_test)
 {
-        struct dw_mci *host = slot->host;
-       struct mmc_host *mmc = slot->mmc;       
-       struct mmc_request mrq = {NULL};
+       struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;
+       const u8 *blk_pattern = tuning_data->blk_pattern;
+       unsigned int blksz = tuning_data->blksz;
+       struct mmc_request mrq = { NULL };
        struct mmc_command cmd = {0};
        struct mmc_command stop = {0};
        struct mmc_data data = {0};
        struct scatterlist sg;
 
+       memset(blk_test, 0, blksz);
+
        cmd.opcode = opcode;
        cmd.arg = 0;
        cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
        stop.opcode = MMC_STOP_TRANSMISSION;
        stop.arg = 0;
        stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
        data.blksz = blksz;
        data.blocks = 1;
        data.flags = MMC_DATA_READ;
@@ -336,322 +203,139 @@ static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32
        mci_writel(host, TMOUT, ~0);
 
        mmc_wait_for_req(mmc, &mrq);
-       if(!cmd.error && !data.error){
-                return 0;
-        }else{
-                dev_dbg(host->dev,
-                       "Tuning error: cmd.error:%d, data.error:%d\n",cmd.error, data.error);
-               return -EIO;
+       if (!cmd.error && !data.error) {
+               if (!memcmp(blk_pattern, blk_test, blksz))
+                       return 0;
+
+               return -EIO;
+       } else {
+               dev_err(host->dev,
+                       "Tuning error: cmd.error:%d, data.error:%d\n",
+                       cmd.error, data.error);
+               if (cmd.error)
+                       return cmd.error;
+               else
+                       return data.error;
        }
-       
 }
 
-
-static int dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
-                                       struct dw_mci_tuning_data *tuning_data)
+static int
+dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                              struct dw_mci_tuning_data *tuning_data)
 {
-        
        struct dw_mci *host = slot->host;
-       u8 step;
-       u8 candidates_delayline[MAX_DELAY_LINE] = {0};
-       u8 candidates_degree[SDMMC_SHIFT_DEGREE_INVALID] = {4,4,4,4};
-       u8 default_drv;
-       u8 index = 0;
-       u8 start_degree = 0;
-       u32 start_delayline = 0;
-       const u8 *blk_pattern = tuning_data->blk_pattern;
-       u8 *blk_test;
-       int ret = -1;
-       int ref = 0;
+       struct device *dev = host->dev;
+       struct device_node *np = dev->of_node;
        unsigned int blksz = tuning_data->blksz;
-
-       MMC_DBG_INFO_FUNC(host->mmc,"execute tuning:  [%s]", mmc_hostname(host->mmc));
-
-       dw_mci_rockchip_load_tuning_base(host);
+       u8 *blk_test;
+       int ret = 0;
+       int i;
+       bool v, prev_v = 0, first_v;
+       struct range_t {
+               int start;
+               int end; /* inclusive */
+       };
+       struct range_t *ranges;
+       unsigned int range_count = 0;
+       int longest_range_len = -1;
+       int longest_range = -1;
+       int middle_phase;
+
+       if (!of_property_read_u32(np, "tune_regsbase", &host->tune_regsbase)) {
+               pr_info("[%s] tuning regsbase addr 0x%03x.\n",
+                       mmc_hostname(host->mmc), host->tune_regsbase);
+       } else {
+               pr_err("[%s] tuning regsbase addr is missing!\n",
+                      mmc_hostname(host->mmc));
+               return -1;
+       }
 
        blk_test = kmalloc(blksz, GFP_KERNEL);
        if (!blk_test)
-       {
-               MMC_DBG_ERR_FUNC(host->mmc,"execute tuning:  blk_test kmalloc failed[%s]",
-                       mmc_hostname(host->mmc));
                return -ENOMEM;
-        }
-        
-        /* Select use delay line*/
-        dw_mci_rockchip_turning_sel(host, tuning_data->con_id, tuning_data->tuning_type,
-                                    USE_CLK_AFTER_PHASE_AND_DELAY_LINE);
-                                    
-        /* For RK32XX signoff 150M clk, 1 cycle = 6.66ns , and 1/4 phase = 1.66ns. 
-           Netlist level sample LT:  10.531ns / 42.126ps   WC: 19.695ns / 76.936ps.
-           So we take average --- 60ps, (1.66ns/ 2) = 0.83(middle-value),TAKE 0.9
-           0.9 / 60ps = 15 delayline
-         */
-        if (cpu_is_rk3288() && !(rockchip_get_cpu_version() > 0)) {
-                /* RK3288, non-eco */
-                ref = DIV_ROUND_UP(FREQ_REF_150MHZ, host->bus_hz);
-                step = (15 * ref);
-
-                if (step > MAX_DELAY_LINE) {
-                        step = MAX_DELAY_LINE;       
-                        MMC_DBG_WARN_FUNC(host->mmc,
-                                        "execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
-                }
-                MMC_DBG_INFO_FUNC(host->mmc,
-                                "execute tuning: SOC is RK3288, ref = %d, step = %d[%s]",
-                                ref, step, mmc_hostname(host->mmc));
-                 
-        } else {
-                step = (15 * ((FREQ_REF_150MHZ / host->bus_hz) * 100)) / 100;
-
-                if (step > MAX_DELAY_LINE) {
-                        step = MAX_DELAY_LINE;
-                        MMC_DBG_WARN_FUNC(host->mmc,
-                                        "execute tuning: TOO LARGE STEP![%s]", mmc_hostname(host->mmc));
-                }
-                MMC_DBG_INFO_FUNC(host->mmc,
-                                "execute tuning: SOC id is %d, step = %d[%s]",
-                                host->cid, step, mmc_hostname(host->mmc));
-        }
-
-re_phase:
-        /* calcute slew rate & drv strength in timing tuning */
-        if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SD)
-                default_drv = IO_DRV_4MA;
-        else
-                default_drv = IO_DRV_8MA;
-
-        dw_mci_rockchip_load_signal_integrity(host, SLEW_RATE_SLOW, default_drv);
-        /* Loop degree from 0 ~ 270 */
-        for(start_degree = SDMMC_SHIFT_DEGREE_0; start_degree <= SDMMC_SHIFT_DEGREE_270; start_degree++){
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, start_degree);
-                if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
-                        if(!memcmp(blk_pattern, blk_test, blksz)){
-                                /* Successfully tuning in this condition*/                      
-                                candidates_degree[index] = start_degree;
-                                index++;
-                         }
-                }
-                /* eMMC spec does not require a delay between tuning cycles
-                 * but eMMC should be guaranteed to complete a sequence of 40 times CMD21
-                 * withnin 150ms, some eMMC may limit 4ms gap between any two sequential CMD21
-                 */
-                if (opcode == MMC_SEND_TUNING_BLOCK)
-                        mdelay(1);
-                else
-                        /* MMC_SEND_TUNING_BLOCK_HS200 */
-                        mdelay(5);
-        }
-        
-        MMC_DBG_BOOT_FUNC(host->mmc,"\n execute tuning: candidates_degree = %s \t%s \t%s \t%s[%s]",
-                phase_desc[candidates_degree[0]], phase_desc[candidates_degree[1]],
-                phase_desc[candidates_degree[2]], phase_desc[candidates_degree[3]],
-                mmc_hostname(host->mmc));
-
-        
-        if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0)
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90)
-                && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_180)){
-           
-                MMC_DBG_INFO_FUNC(host->mmc,
-                                "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 [%s]",
-                                mmc_hostname(host->mmc));
-                if (host->cid == DW_MCI_TYPE_RK3368 && (candidates_degree[3] == SDMMC_SHIFT_DEGREE_270)) {
-                       MMC_DBG_INFO_FUNC(host->mmc,
-                               "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 [%s]",
-                               mmc_hostname(host->mmc));
-                       dw_mci_rockchip_set_degree(host, tuning_data->con_id,
-                               tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
+
+
+       ranges = kmalloc_array(NUM_PHASES / 2 + 1, sizeof(*ranges), GFP_KERNEL);
+       if (!ranges) {
+               ret = -ENOMEM;
+               goto free_blk_test;
+       }
+
+       /* Try each phase and extract good ranges */
+       for (i = 0; i < NUM_PHASES - 6; i++) {
+               rockchip_mmc_set_phase(TUNING_ITERATION_TO_PHASE(i), host);
+               v = !dw_mci_tuning_test(slot, opcode, tuning_data, blk_test);
+
+               if ((!prev_v) && v) {
+                       range_count++;
+                       ranges[range_count-1].start = i;
                }
-               else {
-                       dw_mci_rockchip_set_degree(host, tuning_data->con_id,
-                               tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
+               if (v)
+                       ranges[range_count-1].end = i;
+
+               if (i == 0)
+                       first_v = v;
+
+               prev_v = v;
+       }
+
+       if (range_count == 0) {
+               dev_err(host->dev, "All phases bad!");
+               ret = -EIO;
+               goto free;
+       }
+
+       /* wrap around case, merge the end points
+       if ((range_count > 1) && first_v && v) {
+               ranges[0].start = ranges[range_count-1].start;
+               range_count--;
+       }
+       */
+
+       if ((ranges[0].start == 0) && (ranges[0].end == NUM_PHASES - 1)) {
+               rockchip_mmc_set_phase(0, host);
+               dev_err(host->dev,
+                       "All phases work, using default phase %d.", 0);
+               goto free;
+       }
+
+       /* Find the longest range */
+       for (i = 0; i < range_count; i++) {
+               int len = (ranges[i].end - ranges[i].start + 1);
+
+               if (len < 0)
+                       len += NUM_PHASES;
+
+               if (longest_range_len < len) {
+                       longest_range_len = len;
+                       longest_range = i;
                }
 
-                ret = 0;
-                goto done;
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180) 
-                && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_270)){
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 [%s]",
-                        mmc_hostname(host->mmc));
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, 
-                        tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
-                ret = 0;
-                goto done;
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_0) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_90) 
-                && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 ~  SDMMC_SHIFT_DEGREE_90[%s]",
-                        mmc_hostname(host->mmc));
-                
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
-                #if PRECISE_ADJUST
-                goto delayline; 
-                #else              
-                dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                ret = 0;
-               goto done;  
-                #endif       
-        }else if((candidates_degree[0]==SDMMC_SHIFT_DEGREE_0)
-               && (candidates_degree[1]==SDMMC_SHIFT_DEGREE_180)
-               && (candidates_degree[2]==SDMMC_SHIFT_DEGREE_270)){
-               MMC_DBG_INFO_FUNC(host->mmc,
-                       "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 AND SDMMC_SHIFT_DEGREE_180 AND SDMMC_SHIFT_DEGREE_270[%s]",
-                       mmc_hostname(host->mmc));
-               dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
-               dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-               ret = 0;
-               goto done;
-       }else if((candidates_degree[0]==SDMMC_SHIFT_DEGREE_0) 
-                && (candidates_degree[1]==SDMMC_SHIFT_DEGREE_180)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_0 AND SDMMC_SHIFT_DEGREE_180[%s]",
-                        mmc_hostname(host->mmc));
-
-                /* FixMe: NO sense any signal indicator make this case happen*/
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
-                goto delayline;
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_180) 
-                && (candidates_degree[2] == SDMMC_SHIFT_DEGREE_INVALID)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_90 ~  SDMMC_SHIFT_DEGREE_180[%s]",
-                        mmc_hostname(host->mmc));
-               
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
-                #if PRECISE_ADJUST
-                goto delayline; 
-                #else              
-                dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                ret = 0;
-               goto done;  
-                #endif                             
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_270)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_180 ~  SDMMC_SHIFT_DEGREE_270[%s]",
-                        mmc_hostname(host->mmc));
-                       
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
-                #if PRECISE_ADJUST
-                goto delayline; 
-                #else              
-                dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                ret = 0;
-               goto done;
-                #endif
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_180) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_90 + n ~  SDMMC_SHIFT_DEGREE_180][%s]",
-                        mmc_hostname(host->mmc));             
-                
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
-                #if PRECISE_ADJUST
-                goto delayline;
-                #else
-                default_drv++;
-                goto re_phase;
-                //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                //ret = 0;
-               //goto done;
-                #endif
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_90) 
-                && (candidates_degree[1] == SDMMC_SHIFT_DEGREE_INVALID)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = [SDMMC_SHIFT_DEGREE_0 + n ~  SDMMC_SHIFT_DEGREE_90][%s]",
-                        mmc_hostname(host->mmc));             
-                
-                dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_0);
-                #if PRECISE_ADJUST
-                goto delayline; 
-                #else
-                default_drv++;
-                goto re_phase;
-                //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                //ret = 0;
-               //goto done;
-                #endif
-        }else if((candidates_degree[0] == SDMMC_SHIFT_DEGREE_270)){
-
-                MMC_DBG_INFO_FUNC(host->mmc,
-                        "execute tuning: candidates_degree = SDMMC_SHIFT_DEGREE_270 [%s]",
-                        mmc_hostname(host->mmc));         
-
-                /*FixME: so urgly signal indicator, HW engineer help!*/
-
-                //dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_180);
-                #if PRECISE_ADJUST
-                goto delayline; 
-                #else
-                default_drv++;
-                goto re_phase;
-                //dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, step);
-                //ret = 0;
-               //goto done;
-                #endif            
-        }else{
-                MMC_DBG_ERR_FUNC(host->mmc,
-                                "execute tuning: candidates_degree beyong limited case! [%s]",
-                                mmc_hostname(host->mmc));
-                default_drv++;
-                goto re_phase;
-                if(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)
-                        BUG();
-                return -EAGAIN;
-        }
-
-delayline:
-                index = 0;
-                for(start_delayline = 0; start_delayline <= MAX_DELAY_LINE; start_delayline += step){
-                
-                        dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, 
-                                tuning_data->tuning_type, start_delayline);
-                        if(0 == __dw_mci_rockchip_execute_tuning(slot, opcode, blk_test, blksz)){
-                                if(!memcmp(blk_pattern, blk_test, blksz)){
-                                        /* Successfully tuning in this condition*/                                        
-                                        candidates_delayline[index] = start_delayline;
-                                        index++; 
-                                }
-                        }                    
-                }
-                if((index < 2) && (index != 0)) {
-                        /* setup 400ps, consider line loading, at least 600ps wc.
-                           for 150M, 15 steps =900ps ,too larger scale, should step smaller in principle
-                         */
-                        MMC_DBG_INFO_FUNC(host->mmc,
-                                "execute tuning: candidates_delayline failed for no enough elements [%s]",
-                                mmc_hostname(host->mmc));
-
-                        /* Make step smaller, and re-calculate */
-                        step = step >> 1;
-                        index = 0;
-                        goto delayline;
-                }else if(index >= 2){
-                        /* Find it! */
-                        MMC_DBG_INFO_FUNC(host->mmc,
-                                "execute tuning: candidates_delayline calculate successfully  [%s]",
-                                mmc_hostname(host->mmc));
-
-                        dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, 
-                                tuning_data->tuning_type, candidates_delayline[index/2]); 
-                        ret = 0; 
-                        goto done;
-                }
-        
-done:
-        kfree(blk_test);
-        blk_test = NULL;
-        return ret;
-        
+               dev_err(host->dev, "Good phase range %d-%d (%d len)\n",
+                       TUNING_ITERATION_TO_PHASE(ranges[i].start),
+                       TUNING_ITERATION_TO_PHASE(ranges[i].end),
+                       len
+               );
+       }
+
+       dev_err(host->dev, "Best phase range %d-%d (%d len)\n",
+               TUNING_ITERATION_TO_PHASE(ranges[longest_range].start),
+               TUNING_ITERATION_TO_PHASE(ranges[longest_range].end),
+               longest_range_len
+       );
+
+       middle_phase = ranges[longest_range].start + longest_range_len / 2;
+       middle_phase %= NUM_PHASES;
+       dev_err(host->dev, "Successfully tuned phase to %d\n",
+               TUNING_ITERATION_TO_PHASE(middle_phase));
+
+       rockchip_mmc_set_phase(TUNING_ITERATION_TO_PHASE(middle_phase), host);
+
+free:
+       kfree(ranges);
+free_blk_test:
+       kfree(blk_test);
+       return ret;
 }
 
 /* Common capabilities of RK32XX SoC */
@@ -662,7 +346,7 @@ static unsigned long rockchip_dwmmc_caps[4] = {
        MMC_CAP_CMD23,
 };
 
-unsigned int  rockchip_dwmmc_hold_reg[4] = {1,0,0,0};
+unsigned int  rockchip_dwmmc_hold_reg[4] = {1, 0, 0, 0};
 
 static const struct dw_mci_drv_data rockchip_drv_data = {
        .caps                   = rockchip_dwmmc_caps,
@@ -686,7 +370,7 @@ static int dw_mci_rockchip_probe(struct platform_device *pdev)
 {
        const struct dw_mci_drv_data *drv_data;
        const struct of_device_id *match;
-       
+
        match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
        drv_data = match->data;
        return dw_mci_pltfm_register(pdev, drv_data);
index 6621ea9ec1b7ad88abc6f95a04b83b1cdb664429..4fdcf930232a8851b38129ce908f1fafd807e66d 100755 (executable)
@@ -1946,23 +1946,6 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                return -EINVAL;
        }
 
-    
-       /* Recommend sample phase and delayline
-           Fixme: Mix-use these three controllers will cause
-           con_id mismatch.
-         */
-       if (mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)
-           tuning_data.con_id = 3;
-       else if(mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO)
-           tuning_data.con_id = 1;
-       else
-           tuning_data.con_id = 0;     
-
-       /* 0: driver, from host->devices
-          1: sample, from devices->host
-        */    
-       tuning_data.tuning_type = 1; 
-   
        if (drv_data && drv_data->execute_tuning)
                err = drv_data->execute_tuning(slot, opcode, &tuning_data);
                
index 2e0012924822042a8513c4ace10421621df6749e..e0c4818e0d39d973832236073c0d439f72249ca8 100755 (executable)
@@ -342,9 +342,6 @@ int                 last_detect_state;
 struct dw_mci_tuning_data {
        const u8 *blk_pattern;
        unsigned int blksz;
-
-       u8 con_id;
-       u8 tuning_type;
 };
 
 /**
index 6492a4764dfa4d689a48c59a59cb9470851e9e19..a736e4afb859bfc85a259e0deac734618ed3790b 100755 (executable)
@@ -220,6 +220,7 @@ struct dw_mci {
        struct regmap   *grf;
        u32 *regs_buffer;
        const struct dw_mci_rst_ops *rst_ops;
+       u32     tune_regsbase;
 };
 
 /* DMA ops for Internal/External DMAC interface */