mmc: host: rk_sdmmc:
authorlintao <lintao@rock-chips.com>
Thu, 5 Jun 2014 05:28:15 +0000 (13:28 +0800)
committerlintao <lintao@rock-chips.com>
Thu, 5 Jun 2014 05:34:28 +0000 (13:34 +0800)
[IMPORTANT COMMIT]
        (1) Complete HS200 tuning algorithm for better performance & stability
        (2) Auto workaround for error emmc clk settingin dts by Linux BSP engineer.
        (3) Fixme: BUG on HS-DDR50 eMMC with div = 0

drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/rk_sdmmc.c

index a42c908edf9cb077187e7774d6a1320d229448c1..709d41ed2090175ba2f8504f49deec398fee98db 100755 (executable)
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/slab.h>
+#include <linux/rockchip/cpu.h>
 
 #include "rk_sdmmc.h"
 #include "dw_mmc-pltfm.h"
 */
 #define CRU_SDMMC_CON(id, tuning_type) (0x200 + ((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)
 
-#define SDMMC_SHIFT_DEGREE_0                 (0)
-#define SDMMC_SHIFT_DEGREE_90                (1)
-#define SDMMC_SHIFT_DEGREE_180               (2)
-#define SDMMC_SHIFT_DEGREE_270               (3)
+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,
+};
 
 /* Variations in Rockchip specific dw-mshc controller */
 enum dw_mci_rockchip_type {
@@ -115,39 +135,60 @@ 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;
+        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;
+        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 delaynum;
 }
 
 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: con_id=%d, tuning_type=%d,SDMMC_CON=0x%x. [%s]",
-        con_id,tuning_type,regs, mmc_hostname(host->mmc));     
-    cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
+        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));
+
+        cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
 }
 
 static inline void dw_mci_rockchip_set_degree(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 phase)
 {
-    u32 regs;
-       regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
+        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));
-    cru_writel(regs, CRU_SDMMC_CON(con_id, tuning_type));
+
+        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));
+}
+
+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));       
 }
 
 
@@ -158,12 +199,12 @@ static inline u8 dw_mci_rockchip_get_phase(struct dw_mci *host, u8 con_id, u8 tu
 
 static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_id, u8 tuning_type, u8 val)
 {
-    u32 regs;
-    //u8 delaynum;
-       regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
+        u32 regs;
+        
+        regs = cru_readl(CRU_SDMMC_CON(con_id, tuning_type)) ;
 
        if(tuning_type) {
-           val = (regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff;
+           val = ((regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff);
        }
 
        return val;
@@ -172,106 +213,317 @@ static inline u8 dw_mci_rockchip_move_next_clksmpl(struct dw_mci *host, u8 con_i
 
 static u8 dw_mci_rockchip_get_best_clksmpl(u8 *candiates)
 {
-    u8 pos, i;
-    u8 bestval =0;
+        u8 pos, i;
+        u8 bestval =0;
     
-    for(pos=31; pos>0;pos--)
-        if(candiates[pos] != 0) {
-            for(i=7; i>0; i--)
-            {
-                if(candiates[pos]& (1<<i))
-                   bestval = pos*8+i; 
-            }          
+        for(pos = 31; pos > 0; pos--){
+                if(candiates[pos] != 0) {
+                        for(i = 7; i > 0; i--){
+                                if(candiates[pos]& (1<<i))
+                                bestval = pos*8+i; 
+                        }          
+                }
         }
+        
+        return bestval;
+}
 
-    return bestval;
+static int inline __dw_mci_rockchip_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                                       u8 *blk_test, unsigned int blksz)
+{
+        struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;       
+       struct mmc_request mrq = {NULL};
+       struct mmc_command cmd = {0};
+       struct mmc_command stop = {0};
+       struct mmc_data data = {0};
+       struct scatterlist sg;
+
+       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;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       sg_init_one(&sg, blk_test, blksz);
+       mrq.cmd = &cmd;
+       mrq.stop = &stop;
+       mrq.data = &data;
+       host->mrq = &mrq;
+       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;
+       }
+       
 }
 
+
 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;
-       struct mmc_host *mmc = slot->mmc;
-       const u8 *blk_pattern = tuning_data->blk_pattern;
-       u8 *blk_test;
-       unsigned int blksz = tuning_data->blksz;
-       u8 start_smpl, smpl, pos,index;
-       u8 candiates[32];
-       u8 found = 0;
-       int ret = 0;
-
+       u8 step;
+       u8 candidates_delayline[MAX_DELAY_LINE] = {0};
+       u8 candidates_degree[SDMMC_SHIFT_DEGREE_INVALID] = {4,4,4,4};
+       u8 index = 0;
+       u8 start_degree = 0;
+       u32 start_delayline = 0;
+       u8 *blk_pattern = tuning_data->blk_pattern;
+        u8 *blk_test;
+        int ret = -1;
+        int ref = 0;
+        unsigned int blksz = tuning_data->blksz;
+
+        MMC_DBG_INFO_FUNC(host->mmc,"execute tuning:  [%s]", mmc_hostname(host->mmc));
+       
        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;
-               
-    //be fixed to 90 degrees
-       //dw_mci_rockchip_set_degree(host, tuning_data->con_id, tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
+        }
+        
+        /* 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()){
+                 ref = ((FREQ_REF_150MHZ + host->bus_hz - 1) / 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 is UNKNOWN, step = %d[%s]",
+                                step, mmc_hostname(host->mmc));
+        }
+          
+        /* 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++;
+                         }
+               }              
+                
+        }
+        
+        MMC_DBG_INFO_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));
 
-    //start_smpl = dw_mci_rockchip_get_delaynum(host, tuning_data->con_id, tuning_data->tuning_type);
-    start_smpl = 0;
-    smpl = 0xff;
-    
-    for(pos=0; pos<32; pos++)
-        candiates[pos] = 0;
         
-       do {
-               struct mmc_request mrq = {NULL};
-               struct mmc_command cmd = {0};
-               struct mmc_command stop = {0};
-               struct mmc_data data = {0};
-               struct scatterlist sg;
-
-               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;
-               data.sg = &sg;
-               data.sg_len = 1;
-
-               sg_init_one(&sg, blk_test, blksz);
-               mrq.cmd = &cmd;
-               mrq.stop = &stop;
-               mrq.data = &data;
-               host->mrq = &mrq;
-
-               mci_writel(host, TMOUT, ~0);
-               //smpl = smpl >> 1;//smpl = dw_mci_rockchip_move_next_clksmpl(host);
-
-               mmc_wait_for_req(mmc, &mrq);
-
-               if (!cmd.error && !data.error) {
-                       if (!memcmp(blk_pattern, blk_test, blksz)) {
-                           pos = smpl/8;
-                           index = smpl%8;
-                           candiates[pos] |= (1 << index);
-
-                           //temporary settings,!!!!!!!!!!!!!!!
-                           if(smpl<64)
-                               break;
-                       }                               
-               } else {
-                       dev_dbg(host->dev,
-                               "Tuning error: cmd.error:%d, data.error:%d\n",
-                               cmd.error, data.error);
-               }
-               smpl = smpl >> 1;
-       } while (start_smpl != smpl);
-
-       found = dw_mci_rockchip_get_best_clksmpl((u8 *)&candiates[0]);
-       if (found >= 0)
-               dw_mci_rockchip_set_delaynum(host, tuning_data->con_id, tuning_data->tuning_type, found);//dw_mci_rockchip_set_clksmpl(host, found);
-       else
-               ret = -EIO;
-
-       kfree(blk_test);
-       return ret;
+        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));
+                                
+                dw_mci_rockchip_set_degree(host, tuning_data->con_id, 
+                        tuning_data->tuning_type, SDMMC_SHIFT_DEGREE_90);
+                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)){
+
+                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
+                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
+                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
+                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)); 
+              BUG();
+        }
+
+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)) {
+                        MMC_DBG_INFO_FUNC(host->mmc,
+                                "execute tuning: candidates_delayline failed for only one element [%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;
+        
 }
 
 /* Common capabilities of RK32XX SoC */
index cdd63807a1f0ff488505e74ca8c47febdca906c5..a1fd5d377521f7ad401473497ee30faab2f115f0 100755 (executable)
 #include "rk_sdmmc.h"
 #include "rk_sdmmc_of.h"
 #include <linux/regulator/rockchip_io_vol_domain.h>
+#include "../../clk/rockchip/clk-ops.h"
 
-#define RK_SDMMC_DRIVER_VERSION "Ver 1.00. The last modify date is 2014-05-05" 
+#define grf_writel(v, offset)   do { writel_relaxed(v, RK_GRF_VIRT + offset); dsb(); } while (0)
+
+#define RK_SDMMC_DRIVER_VERSION "Ver 1.10 2014-06-05" 
 
 /* Common flag combinations */
 #define DW_MCI_DATA_ERROR_FLAGS        (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
@@ -870,8 +873,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
        u32 clk_en_a;
        u32 sdio_int;
        
-    MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n", 
-        __LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc));
+        MMC_DBG_INFO_FUNC(host->mmc,"%d..%s: clock=%d, current_speed=%d, bus_hz=%d,forc=%d[%s]\n", 
+                __LINE__, __FUNCTION__, clock, host->current_speed,host->bus_hz,force_clkinit,mmc_hostname(host->mmc));
 
        if (!clock) {
                mci_writel(host, CLKENA, 0);
@@ -896,7 +899,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                                 tempck, div);
 
                        host->set_speed = tempck;
-                   host->set_div = div;
+                       host->set_div = div;
                }
 
                /* disable clock */
@@ -904,25 +907,63 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                mci_writel(host, CLKSRC, 0);
 
                /* inform CIU */
-               mci_send_cmd(slot,
-                            SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
-
-                if(div > 1){
-                        if((host->mmc->restrict_caps & RESTRICT_CARD_TYPE_EMMC)
-                          && host->bus_hz > 100000000){
-                                printk("rk_sdmmc: emmc : div larger than 1, illegal clk in dts ![%s]\n ", 
-                                        mmc_hostname(host->mmc));
-                                printk("eMMC ERROR, emergancy halt!!!!!!!!!\n");        
-                                printk("Please refer to your eMMC datasheet to determine speed mode!\n");
-                                printk("================================rk3288====================================");
-                                printk("DDR 8-bits mode: clk in dts should be 100MHz!\n");
-                                printk("DDR 4-bits mode: clk in dts should be <=100MHz(recommand 50 or 100Mhz)!\n");
-                                printk("SDR mode: clk in dts should <= 100MHz(recommand 50 or 100Mhz)!\n");
-                                printk("HS200 mode: clk in dts should <= 150MHz!\n");
-                                printk("==========================================================================");
-                                BUG();
+               mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
+                 
+                if(clock == 400*1000){
+                       MMC_DBG_BOOT_FUNC(host->mmc,
+                                "dw_mci_setup_bus: argue clk_mmc workaround out 800K for init[%s]",
+                                mmc_hostname(host->mmc)); 
+                        /* RK3288 clk_mmc will change parents to 24MHz xtal*/
+                       clk_set_rate(host->clk_mmc,800*1000);                  
+
+                       div = 0;
+                       host->set_div = div;
+               }
+               else
+               {
+                       MMC_DBG_BOOT_FUNC(host->mmc,
+                                "dw_mci_setup_bus: argue clk_mmc workaround out normal clock [%s]",
+                                mmc_hostname(host->mmc)); 
+                        if(div > 1)
+                        {
+                                MMC_DBG_ERR_FUNC(host->mmc,
+                                        "dw_mci_setup_bus: div SHOULD NOT LARGER THAN ONE! [%s]",
+                                        mmc_hostname(host->mmc)); 
+                                 div = 1;
+                                 host->set_div = div;
+                                 host->bus_hz = host->set_speed * 2;
+                                 MMC_DBG_BOOT_FUNC(host->mmc,
+                                        "dw_mci_setup_bus: workaround div = %d, host->bus_hz = %d [%s]",
+                                        div, host->bus_hz, mmc_hostname(host->mmc));                                 
                         }
-                }
+                        /* BUG may be here, come on,  Linux BSP engineer looks!
+                           FIXME:  HS-DDR eMMC, div SHOULD be ONE, but we here cannot fetch eMMC bus mode!!!!!!!! 
+                           WRONG dts set clk = 50M, and calc div be zero. Controller denied this setting!
+                           some oops happened like that:
+                           mmc_host mmc0: Bus speed (slot 0) = 50000000Hz (slot req 50000000Hz, actual 50000000HZ div = 0)
+                           rk_sdmmc: BOOT dw_mci_setup_bus: argue clk_mmc workaround out normal clock [mmc0]
+                           rk_sdmmc: BOOT Bus speed=50000000Hz,Bus width=8bits.[mmc0]
+                           mmc0: new high speed DDR MMC card at address 0001
+                           mmcblk0: mmc0:0001 M8G1GC 7.28 GiB 
+                           ....
+                           mmcblk0: error -84 transferring data, sector 606208, nr 32, cmd response 0x900, card status 0xb00
+                           mmcblk0: retrying using single block read
+                           mmcblk0: error -110 sending status command, retrying
+
+                           How to: If eMMC HW version < 4.51, or > 4.51 but no caps2-mmc-hs200 support in dts
+                                   Please set dts emmc clk to 100M or 150M, I will workaround it!
+                         */
+
+                       if (host->verid < DW_MMC_240A)
+                               clk_set_rate(host->clk_mmc,(host->bus_hz));
+                       else
+                               clk_set_rate(host->clk_mmc,(host->bus_hz) * 2);
+
+
+                               
+               }
+               
+                
                /* set clock to desired speed */
                mci_writel(host, CLKDIV, div);
 
@@ -935,7 +976,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
 
                if (host->verid < DW_MMC_240A)
                    sdio_int = SDMMC_INT_SDIO(slot->id);
-           else
+               else
                    sdio_int = SDMMC_INT_SDIO((slot->id) + 8);
 
                if (!(mci_readl(host, INTMASK) & sdio_int))
@@ -2834,7 +2875,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
                mmc->hold_reg_flag |= drv_data->hold_reg_flag[ctrl_id];         
 
        //set the compatibility of driver.      
-    mmc->caps |= MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE;
+    mmc->caps |= MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE ;
 
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
@@ -3248,10 +3289,10 @@ int dw_mci_probe(struct dw_mci *host)
         }
     }
 
-       host->quirks = host->pdata->quirks;
-    host->irq_state = true;
-    host->set_speed = 0;
-    host->set_div = 0;
+        host->quirks = host->pdata->quirks;
+        host->irq_state = true;
+        host->set_speed = 0;
+        host->set_div = 0;
 
        spin_lock_init(&host->lock);
        INIT_LIST_HEAD(&host->queue);