SDMMC:
authorxbw <xbw@rock-chips.com>
Tue, 1 Apr 2014 05:54:20 +0000 (13:54 +0800)
committerxbw <xbw@rock-chips.com>
Tue, 1 Apr 2014 05:54:20 +0000 (13:54 +0800)
1. emmc to support High Speed DDR MMC.
2. emmc to support HS200 mode ,and add the tuning timing for HS200 mode.
3. Improve mmc bus frequency to 400Mhz, and Imporve cclk_in to 200Mhz.
4. modify the p977.dts for the above performance.

arch/arm/boot/dts/rk3288-p977.dts
arch/arm/boot/dts/rk3288.dtsi
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 71024c3bb82eb65e7349092dfc1ad09e35f1a8ac..99e0df954234f8bb133130d49869fb45220319de 100755 (executable)
         supports-highspeed;
        supports-emmc;
         bootpart-no-access;
+
+       supports-DDR_MODE;
+       caps2-mmc-hs200;
+
         ignore-pm-notify;
                keep-power-in-suspend;
        status = "okay";
                ignore-pm-notify;
                keep-power-in-suspend;
                //cap-sdio-irq;
-               status = "okay";
+               status = "disabled";
 };
 
 &spi0 {
index da659d040669cbfe4727f5dd4b745f4f4770565d..749b814e18f3f24dd2027cb788c8930f45afec0f 100755 (executable)
                clocks = <&clk_emmc>, <&clk_gates8 6>;
                clock-names = "clk_mmc", "hclk_mmc";
                num-slots = <1>;                
-               fifo-depth = <0x80>;
-               bus-width = <4>;
+               fifo-depth = <0x100>;
+               bus-width = <8>;
        };
 
        sdmmc: rksdmmc@ff0c0000 {
index 98a9683c6686de04ed989ccc3a061ff2ea913f15..41955d5991a09fe50c19ccaf0dccc74bb792b9ae 100755 (executable)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
-
-#include <linux/slab.h>
-
 #include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
 #include <linux/mmc/rk_mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "rk_sdmmc.h"
 #include "dw_mmc-pltfm.h"
+#include "../../clk/rockchip/clk-ops.h"
 
 #include "rk_sdmmc_of.h"
 
-#define NUM_PINS(x)                    (x + 2)
-
-/* SDMMC_CLKSEL is not used in Rockchip
-#define SDMMC_CLKSEL                   0x09C
-#define SDMMC_CLKSEL_CCLK_SAMPLE(x)    (((x) & 7) << 0)
-#define SDMMC_CLKSEL_CCLK_DRIVE(x)     (((x) & 7) << 16)
-#define SDMMC_CLKSEL_CCLK_DIVIDER(x)   (((x) & 7) << 24)
-#define SDMMC_CLKSEL_GET_DRV_WD3(x)    (((x) >> 16) & 0x7)
-#define SDMMC_CLKSEL_TIMING(x, y, z)   (SDMMC_CLKSEL_CCLK_SAMPLE(x) |  \
-                                       SDMMC_CLKSEL_CCLK_DRIVE(y) |    \
-                                       SDMMC_CLKSEL_CCLK_DIVIDER(z))
+/*CRU SDMMC TUNING*/
+/*
+*   sdmmc,sdio0,sdio1,emmc id=0~3
+*   cclk_in_drv, cclk_in_sample  i=0,1
 */
-#define SDMMC_CMD_USE_HOLD_REG         BIT(29)
+#define CRU_SDMMC_CON(id, tuning_type) (0x200 + ((id) * 8) + ((tuning_type) * 4))
 
-//#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
-//#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
+#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_CMD_USE_HOLD_REG         BIT(29)
 
 /* Variations in Rockchip specific dw-mshc controller */
 enum dw_mci_rockchip_type {
@@ -54,6 +51,7 @@ struct dw_mci_rockchip_priv_data {
        u8                              ciu_div;
        u32                             sdr_timing;
        u32                             ddr_timing;
+       u32                             cur_speed;
 };
 
 static struct dw_mci_rockchip_compatible {
@@ -96,71 +94,182 @@ static int dw_mci_rockchip_setup_clock(struct dw_mci *host)
 
        if (priv->ctrl_type == DW_MCI_TYPE_RK3288)
                host->bus_hz /= (priv->ciu_div + 1);
-       /*else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
-               host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
-       else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
-               host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
-    */
+
        return 0;
 }
 
 static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
 {
-       /*
-        * Exynos4412 and Exynos5250 extends the use of CMD register with the
-        * use of bit 29 (which is reserved on standard MSHC controllers) for
-        * optionally bypassing the HOLD register for command and data. The
-        * HOLD register should be bypassed in case there is no phase shift
-        * applied on CMD/DATA that is sent to the card.
-        */
 //     if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
 //             *cmdr |= SDMMC_CMD_USE_HOLD_REG;
 }
 
 static void dw_mci_rockchip_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
-       //struct dw_mci_rockchip_priv_data *priv = host->priv;
-/*
-       if (ios->timing == MMC_TIMING_UHS_DDR50)
-               mci_writel(host, CLKSEL, priv->ddr_timing);
-       else
-               mci_writel(host, CLKSEL, priv->sdr_timing);
-*/
+
 }
 
 static int dw_mci_rockchip_parse_dt(struct dw_mci *host)
 {
-/*
-       struct dw_mci_rockchip_priv_data *priv = host->priv;
-       struct device_node *np = host->dev->of_node;
-       u32 timing[2];
-       u32 div = 0;
-       int ret;
-        //rk set the timing in CRU
-       of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
-       priv->ciu_div = div;
-
-       ret = of_property_read_u32_array(np,
-                       "samsung,dw-mshc-sdr-timing", timing, 2);
-       if (ret)
-               return ret;
-
-       priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
-
-       ret = of_property_read_u32_array(np,
-                       "samsung,dw-mshc-ddr-timing", timing, 2);
-       if (ret)
-               return ret;
-
-       priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
-       */
+
+       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;
+}
+
+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));
+}
+
+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)) ;
+       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));
+}
+
+
+static inline u8 dw_mci_rockchip_get_phase(struct dw_mci *host, u8 con_id, u8 tuning_type)
+{
        return 0;
 }
 
+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)) ;
+
+       if(tuning_type) {
+           val = (regs>>SDMMC_TUNING_DELAYNUM(tuning_type)) & 0xff;
+       }
+
+       return val;
+}
+
+
+static u8 dw_mci_rockchip_get_best_clksmpl(u8 *candiates)
+{
+    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; 
+            }          
+        }
+
+    return bestval;
+}
+
+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;
+
+       blk_test = kmalloc(blksz, GFP_KERNEL);
+       if (!blk_test)
+               return -ENOMEM;
+
+    //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;
+}
+
 /* Common capabilities of RK32XX SoC */
 static unsigned long rockchip_dwmmc_caps[4] = {
-       /*MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | //Temporarily comment out!!!!!!, deleted by xbw, at 2014-03-12*/
-       MMC_CAP_8_BIT_DATA|MMC_CAP_4_BIT_DATA|MMC_CAP_CMD23|MMC_CAP_UHS_SDR12|MMC_CAP_UHS_SDR25|MMC_CAP_UHS_SDR50|MMC_CAP_UHS_SDR104|MMC_CAP_ERASE,
+       MMC_CAP_CMD23,
        MMC_CAP_CMD23,
        MMC_CAP_CMD23,
        MMC_CAP_CMD23,
@@ -176,6 +285,7 @@ static const struct dw_mci_drv_data rockchip_drv_data = {
        .prepare_command        = dw_mci_rockchip_prepare_command,
        .set_ios                = dw_mci_rockchip_set_ios,
        .parse_dt               = dw_mci_rockchip_parse_dt,
+       .execute_tuning         = dw_mci_rockchip_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_rockchip_match[] = {
index e7ed22c7c0dbd6711fb80c3cd43139ea8765db84..939467eae6fc5dc68bda376fe6619f8a7883db63 100755 (executable)
@@ -883,8 +883,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
                             SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
 
                /* set clock to desired speed */
-               mci_writel(host, CLKDIV, div);
-
+               mci_writel(host, CLKDIV, div>>1); // *2 due to fix divider 2 in controller
+               host->current_div = div;
                /* inform CIU */
                mci_send_cmd(slot,
                             SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
@@ -1331,6 +1331,17 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
                return -EINVAL;
        }
 
+    /////////////////////////////////////////////////
+       //temporary settings,!!!!!!!!!!!!!!!
+       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;         
+       tuning_data.tuning_type = 1; //0--drv, 1--sample
+    /////////////////////////////////////////////////
+
        if (drv_data && drv_data->execute_tuning)
                err = drv_data->execute_tuning(slot, opcode, &tuning_data);
        return err;
@@ -2301,12 +2312,7 @@ static void dw_mci_work_routine_card(struct work_struct *work)
 
                        /* Power down slot */
                        if (present == 0) {
-
-                               /*
-                                * Clear down the FIFO - doing so generates a
-                                * block interrupt, hence setting the
-                                * scatter-gather pointer to NULL.
-                                */
+                               /* Clear down the FIFO */
                                dw_mci_fifo_reset(host);
 #ifdef CONFIG_MMC_DW_IDMAC
                                dw_mci_idmac_reset(host);
@@ -2558,6 +2564,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        if (drv_data && drv_data->hold_reg_flag)
                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;
+
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
 
@@ -2849,6 +2858,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 
        if (of_find_property(np, "supports-highspeed", NULL))
                pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+               
+    if (of_find_property(np, "supports-UHS_SDR104", NULL))
+               pdata->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+
+    if (of_find_property(np, "supports-DDR_MODE", NULL))
+               pdata->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_1_2V_DDR;
+
+    if (of_find_property(np, "caps2-mmc-hs200", NULL))
+               pdata->caps2 |= MMC_CAP2_HS200;
 
        if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL))
                pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
@@ -2937,6 +2955,7 @@ int dw_mci_probe(struct dw_mci *host)
 
        host->quirks = host->pdata->quirks;
     host->irq_state = true;
+    host->current_div = 0;
 
        spin_lock_init(&host->lock);
        INIT_LIST_HEAD(&host->queue);
index 706efe7261cd605abe48ed9e3d3617d053284ff1..f06dcb5c140b98544ab9b4f9759ab87d8393243b 100755 (executable)
@@ -299,6 +299,9 @@ struct dw_mci_slot {
 struct dw_mci_tuning_data {
        const u8 *blk_pattern;
        unsigned int blksz;
+
+       u8 con_id;
+       u8 tuning_type;
 };
 
 /**
index 62696369ebf48b665fc40ddc0ecd9af5133e7670..f9dd8c7124d807b715e2c7734917d2c868129db5 100755 (executable)
@@ -160,6 +160,7 @@ struct dw_mci {
 
        u32                     bus_hz;
        u32                     current_speed;
+       u32         current_div;
        u32                     num_slots;
        u32                     fifoth_val;
        u16                     verid;