diff --git a/arch/arm/boot/dts/rk3288-p977.dts b/arch/arm/boot/dts/rk3288-p977.dts index 71024c3bb82e..99e0df954234 100755 --- a/arch/arm/boot/dts/rk3288-p977.dts +++ b/arch/arm/boot/dts/rk3288-p977.dts @@ -164,6 +164,10 @@ supports-highspeed; supports-emmc; bootpart-no-access; + + supports-DDR_MODE; + caps2-mmc-hs200; + ignore-pm-notify; keep-power-in-suspend; status = "okay"; @@ -192,7 +196,7 @@ ignore-pm-notify; keep-power-in-suspend; //cap-sdio-irq; - status = "okay"; + status = "disabled"; }; &spi0 { diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index da659d040669..749b814e18f3 100755 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -242,8 +242,8 @@ 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 { diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 98a9683c6686..41955d5991a0 100755 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -12,35 +12,32 @@ #include #include #include - -#include - #include +#include #include #include #include +#include #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<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[] = { diff --git a/drivers/mmc/host/rk_sdmmc.c b/drivers/mmc/host/rk_sdmmc.c index e7ed22c7c0db..939467eae6fc 100755 --- a/drivers/mmc/host/rk_sdmmc.c +++ b/drivers/mmc/host/rk_sdmmc.c @@ -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); diff --git a/drivers/mmc/host/rk_sdmmc.h b/drivers/mmc/host/rk_sdmmc.h index 706efe7261cd..f06dcb5c140b 100755 --- a/drivers/mmc/host/rk_sdmmc.h +++ b/drivers/mmc/host/rk_sdmmc.h @@ -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; }; /** diff --git a/include/linux/mmc/rk_mmc.h b/include/linux/mmc/rk_mmc.h index 62696369ebf4..f9dd8c7124d8 100755 --- a/include/linux/mmc/rk_mmc.h +++ b/include/linux/mmc/rk_mmc.h @@ -160,6 +160,7 @@ struct dw_mci { u32 bus_hz; u32 current_speed; + u32 current_div; u32 num_slots; u32 fifoth_val; u16 verid;