|
|
|
|
@@ -60,7 +60,7 @@ int meson_mmc_clk_init_v3(struct amlsd_host *host)
|
|
|
|
|
vclkc = 0;
|
|
|
|
|
pclkc->div = 60; /* 400KHz */
|
|
|
|
|
pclkc->src = 0; /* 0: Crystal 24MHz */
|
|
|
|
|
pclkc->core_phase = 2; /* 2: 180 phase */
|
|
|
|
|
pclkc->core_phase = 3; /* 2: 180 phase */
|
|
|
|
|
pclkc->rx_phase = 0;
|
|
|
|
|
pclkc->tx_phase = 0;
|
|
|
|
|
pclkc->always_on = 1; /* Keep clock always on */
|
|
|
|
|
@@ -143,10 +143,14 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
|
|
|
|
|
|
|
|
|
|
mmc->actual_clock = clk_rate / clk_div;
|
|
|
|
|
#else
|
|
|
|
|
if (clk_ios == mmc->actual_clock)
|
|
|
|
|
if (clk_ios == mmc->actual_clock) {
|
|
|
|
|
pr_info("[%s] clk_ios: %lu, return .............. clock: 0x%x\n",
|
|
|
|
|
__func__, clk_ios,
|
|
|
|
|
readl(host->base + SD_EMMC_CLOCK_V3));
|
|
|
|
|
return 0;
|
|
|
|
|
pr_info("[%s] before clock: 0x%x\n",
|
|
|
|
|
__func__, readl(host->base + SD_EMMC_CLOCK_V3));
|
|
|
|
|
}
|
|
|
|
|
pr_info("[%s] clk_ios: %lu,before clock: 0x%x\n",
|
|
|
|
|
__func__, clk_ios, readl(host->base + SD_EMMC_CLOCK_V3));
|
|
|
|
|
/* stop clock */
|
|
|
|
|
vcfg = readl(host->base + SD_EMMC_CFG);
|
|
|
|
|
if (!conf->stop_clk) {
|
|
|
|
|
@@ -167,10 +171,11 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
|
|
|
|
|
dev_dbg(host->dev, "change clock rate %u -> %lu\n",
|
|
|
|
|
mmc->actual_clock, clk_ios);
|
|
|
|
|
ret = clk_set_rate(host->cfg_div_clk, clk_ios);
|
|
|
|
|
if (clk_ios && clk_ios != clk_get_rate(host->cfg_div_clk))
|
|
|
|
|
if (clk_ios && clk_ios != clk_get_rate(host->cfg_div_clk)) {
|
|
|
|
|
dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
|
|
|
|
|
clk_ios, clk_get_rate(host->cfg_div_clk), ret);
|
|
|
|
|
else
|
|
|
|
|
mmc->actual_clock = clk_get_rate(host->cfg_div_clk);
|
|
|
|
|
} else
|
|
|
|
|
mmc->actual_clock = clk_ios;
|
|
|
|
|
|
|
|
|
|
/* (re)start clock, if non-zero */
|
|
|
|
|
@@ -183,12 +188,12 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host,
|
|
|
|
|
source_base =
|
|
|
|
|
ioremap_nocache(P_HHI_NAND_CLK_CNTL,
|
|
|
|
|
sizeof(u32));
|
|
|
|
|
/* pr_info("actual_clock :%u, HHI_nand: 0x%x\n",
|
|
|
|
|
* mmc->actual_clock, readl(source_base));
|
|
|
|
|
*/
|
|
|
|
|
/* pr_info("[%s] after clock: 0x%x\n",
|
|
|
|
|
* __func__, readl(host->base + SD_EMMC_CLOCK_V3));
|
|
|
|
|
*/
|
|
|
|
|
pr_info("actual_clock :%u, HHI_nand: 0x%x\n",
|
|
|
|
|
mmc->actual_clock, readl(source_base));
|
|
|
|
|
|
|
|
|
|
pr_info("[%s] after clock: 0x%x\n",
|
|
|
|
|
__func__, readl(host->base + SD_EMMC_CLOCK_V3));
|
|
|
|
|
|
|
|
|
|
iounmap(source_base);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
@@ -199,12 +204,11 @@ static void aml_sd_emmc_set_timing_v3(struct amlsd_host *host,
|
|
|
|
|
struct amlsd_platform *pdata = host->pdata;
|
|
|
|
|
u32 vctrl;
|
|
|
|
|
struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl;
|
|
|
|
|
u32 vclkc;
|
|
|
|
|
u32 vclkc = readl(host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
struct sd_emmc_clock_v3 *clkc = (struct sd_emmc_clock_v3 *)&vclkc;
|
|
|
|
|
u32 adjust;
|
|
|
|
|
struct sd_emmc_adjust_v3 *gadjust = (struct sd_emmc_adjust_v3 *)&adjust;
|
|
|
|
|
u8 clk_div = 0;
|
|
|
|
|
u32 clk_rate = 1000000000;
|
|
|
|
|
|
|
|
|
|
vctrl = readl(host->base + SD_EMMC_CFG);
|
|
|
|
|
if ((timing == MMC_TIMING_MMC_HS400) ||
|
|
|
|
|
@@ -216,21 +220,26 @@ static void aml_sd_emmc_set_timing_v3(struct amlsd_host *host,
|
|
|
|
|
adjust = readl(host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
gadjust->ds_enable = 1;
|
|
|
|
|
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
clk_rate = 400000000;
|
|
|
|
|
/*host->tuning_mode = AUTO_TUNING_MODE;*/
|
|
|
|
|
clkc->tx_delay = pdata->tx_delay;
|
|
|
|
|
}
|
|
|
|
|
pr_info("%s: try set sd/emmc to HS400 mode\n",
|
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
}
|
|
|
|
|
vclkc = readl(host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
ctrl->ddr = 1;
|
|
|
|
|
clk_div = clkc->div;
|
|
|
|
|
if (clk_div & 0x01)
|
|
|
|
|
clk_div++;
|
|
|
|
|
clkc->div = clk_div / 2;
|
|
|
|
|
if (aml_card_type_mmc(pdata))
|
|
|
|
|
clkc->core_phase = 2;
|
|
|
|
|
writel(vclkc, host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
pdata->clkc = vclkc;
|
|
|
|
|
host->mmc->actual_clock = clk_rate / clk_div;
|
|
|
|
|
pr_info("%s: try set sd/emmc to DDR mode\n",
|
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
} else if (timing == MMC_TIMING_MMC_HS) {
|
|
|
|
|
clkc->core_phase = 3;
|
|
|
|
|
writel(vclkc, host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
pdata->clkc = vclkc;
|
|
|
|
|
} else
|
|
|
|
|
ctrl->ddr = 0;
|
|
|
|
|
|
|
|
|
|
@@ -258,6 +267,7 @@ static void aml_sd_emmc_set_power_v3(struct amlsd_host *host,
|
|
|
|
|
writel(0, host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
writel(0, host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
writel(0, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
writel(0, host->base + SD_EMMC_INTF3);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (pdata->pwr_pre)
|
|
|
|
|
@@ -294,42 +304,6 @@ void meson_mmc_set_ios_v3(struct mmc_host *mmc,
|
|
|
|
|
aml_cs_dont_care(mmc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode)
|
|
|
|
|
{
|
|
|
|
|
struct amlsd_host *host = mmc_priv(mmc);
|
|
|
|
|
struct aml_tuning_data tuning_data;
|
|
|
|
|
int err = 0;
|
|
|
|
|
u32 intf3;
|
|
|
|
|
|
|
|
|
|
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
|
|
|
|
|
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_8bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
|
|
|
|
|
} else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else if (opcode == MMC_SEND_TUNING_BLOCK) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
|
|
|
|
} else {
|
|
|
|
|
sd_emmc_err("Undefined command(%d) for tuning\n", opcode);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
intf3 = readl(host->base + SD_EMMC_INTF3);
|
|
|
|
|
intf3 |= (1 << 22);
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
|
|
|
|
|
pr_info("%s: gclock=0x%x, gdelay1=0x%x, gdelay2=0x%x,intf3=0x%x\n",
|
|
|
|
|
mmc_hostname(mmc), readl(host->base + SD_EMMC_CLOCK_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id)
|
|
|
|
|
{
|
|
|
|
|
@@ -512,8 +486,6 @@ static int aml_sd_emmc_cali_v3(struct mmc_host *mmc,
|
|
|
|
|
return data.error | cmd.error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define EMMC_TIMMING_DBG 1
|
|
|
|
|
|
|
|
|
|
static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
{
|
|
|
|
|
struct amlsd_host *host = mmc_priv(mmc);
|
|
|
|
|
@@ -521,22 +493,13 @@ static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
u32 adjust = readl(host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
struct sd_emmc_adjust_v3 *gadjust =
|
|
|
|
|
(struct sd_emmc_adjust_v3 *)&adjust;
|
|
|
|
|
/*u32 delay1 = sd_emmc_regs->reg.v3.gdelay1;
|
|
|
|
|
*struct sd_emmc_delay1 *gdelay1 = (struct sd_emmc_delay1 *)&delay1;
|
|
|
|
|
*u32 delay2 = sd_emmc_regs->reg.v3.gdelay2;
|
|
|
|
|
*struct sd_emmc_delay2 *gdelay2 = (struct sd_emmc_delay2 *)&delay2;
|
|
|
|
|
*u32 clktest_log = 0;
|
|
|
|
|
*struct clktest_log gclktest_log =
|
|
|
|
|
* (struct clktest_log *)&(clktest_log);
|
|
|
|
|
*u32 clktest_out = 0;
|
|
|
|
|
*/
|
|
|
|
|
u32 eyetest_log = 0;
|
|
|
|
|
struct eyetest_log *geyetest_log = (struct eyetest_log *)&(eyetest_log);
|
|
|
|
|
u32 eyetest_out0 = 0, eyetest_out1 = 0;
|
|
|
|
|
u32 intf3 = readl(host->base + SD_EMMC_INTF3);
|
|
|
|
|
struct intf3 *gintf3 = (struct intf3 *)&(intf3);
|
|
|
|
|
u32 vcfg = readl(host->base + SD_EMMC_CFG);
|
|
|
|
|
int j = 0;
|
|
|
|
|
int retry = 3;
|
|
|
|
|
u64 tmp = 0;
|
|
|
|
|
u32 blksz = 512;
|
|
|
|
|
u8 *blk_test;
|
|
|
|
|
@@ -544,14 +507,9 @@ static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
if (!blk_test)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
host->is_tunning = 1;
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG,
|
|
|
|
|
"line_x: %d\n", line_x);
|
|
|
|
|
/*sd_emmc_regs->reg.v3.gdelay1 = delay1;
|
|
|
|
|
*sd_emmc_regs->reg.v3.gdelay2 = delay2;
|
|
|
|
|
*/
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x , delay2: 0x%x\n",
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "delay1: 0x%x , delay2: 0x%x, line_x: %d\n",
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3));
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3), line_x);
|
|
|
|
|
gadjust->cali_enable = 1;
|
|
|
|
|
gadjust->cali_sel = line_x;
|
|
|
|
|
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
@@ -559,13 +517,13 @@ static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
gintf3->eyetest_exp = 7;
|
|
|
|
|
else
|
|
|
|
|
gintf3->eyetest_exp = 3;
|
|
|
|
|
RETRY:
|
|
|
|
|
|
|
|
|
|
gintf3->eyetest_on = 1;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3),
|
|
|
|
|
readl(host->base + SD_EMMC_ADJUST_V3));
|
|
|
|
|
|
|
|
|
|
/*****test start*************/
|
|
|
|
|
udelay(5);
|
|
|
|
|
if (line_x < 9)
|
|
|
|
|
aml_sd_emmc_cali_v3(mmc,
|
|
|
|
|
MMC_READ_MULTIPLE_BLOCK,
|
|
|
|
|
@@ -574,30 +532,27 @@ static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
aml_sd_emmc_cali_v3(mmc,
|
|
|
|
|
MMC_READ_MULTIPLE_BLOCK,
|
|
|
|
|
blk_test, blksz, 80);
|
|
|
|
|
|
|
|
|
|
udelay(1);
|
|
|
|
|
eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG);
|
|
|
|
|
eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0);
|
|
|
|
|
eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1);
|
|
|
|
|
|
|
|
|
|
while ((!(geyetest_log->eyetest_done & 0x1))) {
|
|
|
|
|
eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0);
|
|
|
|
|
eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1);
|
|
|
|
|
eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "testing eyetest times: 0x%x, out: 0x%x, 0x%x\n",
|
|
|
|
|
if (!(geyetest_log->eyetest_done & 0x1)) {
|
|
|
|
|
pr_warn("testing eyetest times: 0x%x, out: 0x%x, 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_EYETEST_LOG),
|
|
|
|
|
eyetest_out0, eyetest_out1);
|
|
|
|
|
if (j == 10)
|
|
|
|
|
break;
|
|
|
|
|
j++;
|
|
|
|
|
gintf3->eyetest_on = 0;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
retry--;
|
|
|
|
|
if (retry == 0) {
|
|
|
|
|
pr_warn("[%s][%d] retry eyetest failed\n",
|
|
|
|
|
__func__, __LINE__);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
goto RETRY;
|
|
|
|
|
}
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "test done! eyetest times: 0x%x, out: 0x%x, 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_EYETEST_LOG),
|
|
|
|
|
eyetest_out0, eyetest_out1);
|
|
|
|
|
gintf3->eyetest_on = 0;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3),
|
|
|
|
|
readl(host->base + SD_EMMC_ADJUST_V3));
|
|
|
|
|
if (vcfg & 0x4) {
|
|
|
|
|
if (pdata->count > 32) {
|
|
|
|
|
eyetest_out1 <<= (32 - (pdata->count - 32));
|
|
|
|
|
@@ -606,7 +561,7 @@ static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x)
|
|
|
|
|
eyetest_out1 = 0x0;
|
|
|
|
|
}
|
|
|
|
|
pdata->align[line_x] = ((tmp | eyetest_out1) << 32) | eyetest_out0;
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "u64 eyetestout 0x%llx\n",
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "u64 eyetestout 0x%llx\n",
|
|
|
|
|
pdata->align[line_x]);
|
|
|
|
|
host->is_tunning = 0;
|
|
|
|
|
kfree(blk_test);
|
|
|
|
|
@@ -636,8 +591,8 @@ static int emmc_detect_base_line(u64 *arr)
|
|
|
|
|
max = first[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pr_warn("%s [%d] detect line:%d, max: %u\n",
|
|
|
|
|
__func__, __LINE__, l_max, max);
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "%s detect line:%d, max: %u\n",
|
|
|
|
|
__func__, l_max, max);
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -659,20 +614,20 @@ static int emmc_all_data_line_alignment(struct mmc_host *mmc)
|
|
|
|
|
if (temp <= 4)
|
|
|
|
|
continue;
|
|
|
|
|
result = base_line - temp;
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "line_x: %d, result: %d\n",
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "line_x: %d, result: %d\n",
|
|
|
|
|
line_x, result);
|
|
|
|
|
if (line_x < 5)
|
|
|
|
|
delay1 |= result << (6 * line_x);
|
|
|
|
|
else
|
|
|
|
|
delay2 |= result << (6 * (line_x - 5));
|
|
|
|
|
}
|
|
|
|
|
delay1 |= readl(host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
delay2 |= readl(host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "delay1: 0x%x, delay2: 0x%x\n",
|
|
|
|
|
delay1, delay2);
|
|
|
|
|
delay1 += readl(host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
delay2 += readl(host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x, delay2: 0x%x\n",
|
|
|
|
|
delay1, delay2);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "gdelay1: 0x%x, gdelay2: 0x%x\n",
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "gdelay1: 0x%x, gdelay2: 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3));
|
|
|
|
|
|
|
|
|
|
@@ -685,31 +640,37 @@ static int emmc_ds_data_alignment(struct mmc_host *mmc)
|
|
|
|
|
struct amlsd_platform *pdata = host->pdata;
|
|
|
|
|
u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
int i;
|
|
|
|
|
int i, line_x, temp = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
|
pr_info("i = %d, delay1: 0x%x, delay2: 0x%x\n",
|
|
|
|
|
for (line_x = 0; line_x < 8; line_x++) {
|
|
|
|
|
temp = fbinary(pdata->align[line_x]);
|
|
|
|
|
if (temp <= 4)
|
|
|
|
|
continue;
|
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "i = %d, delay1: 0x%x, delay2: 0x%x\n",
|
|
|
|
|
i, readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3));
|
|
|
|
|
delay1 += (1<<0)|(1<<6)|(1<<12)|(1<<18)|(1<<24);
|
|
|
|
|
delay2 += (1<<0)|(1<<6)|(1<<12)|(1<<24);
|
|
|
|
|
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
emmc_eyetest_log(mmc, 3);
|
|
|
|
|
emmc_eyetest_log(mmc, 8);
|
|
|
|
|
if (pdata->align[3] & 0xf)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i == 64) {
|
|
|
|
|
pr_warn("%s [%d] Don't find line delay which aligned with DS\n",
|
|
|
|
|
if (line_x < 5)
|
|
|
|
|
delay1 += 1 << (6 * line_x);
|
|
|
|
|
else
|
|
|
|
|
delay2 += 1 << (6 * (line_x - 5));
|
|
|
|
|
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
emmc_eyetest_log(mmc, line_x);
|
|
|
|
|
if (pdata->align[line_x] & 0xf)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i == 64) {
|
|
|
|
|
pr_warn("%s [%d] Don't find line delay which aligned with DS\n",
|
|
|
|
|
__func__, __LINE__);
|
|
|
|
|
return 1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void print_all_line_eyetest(struct mmc_host *mmc)
|
|
|
|
|
static void update_all_line_eyetest(struct mmc_host *mmc)
|
|
|
|
|
{
|
|
|
|
|
int line_x;
|
|
|
|
|
|
|
|
|
|
@@ -725,13 +686,13 @@ static int emmc_ds_core_align(struct mmc_host *mmc)
|
|
|
|
|
u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
u32 delay2_bak = delay2;
|
|
|
|
|
u32 count = 0;
|
|
|
|
|
u32 ds_count = 0;
|
|
|
|
|
u32 ds_count = 0, cmd_count = 0;
|
|
|
|
|
|
|
|
|
|
if (pdata->align[8] & 0xf)
|
|
|
|
|
return 0;
|
|
|
|
|
ds_count = fbinary(pdata->align[8]);
|
|
|
|
|
//BUG_ON(ds_count >= 4 && ds_count <= 8);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "ds_count:%d,delay1:0x%x,delay2:0x%x\n",
|
|
|
|
|
if (ds_count == 0)
|
|
|
|
|
if ((pdata->align[8] & 0xf0) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "ds_count:%d,delay1:0x%x,delay2:0x%x\n",
|
|
|
|
|
ds_count, readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3));
|
|
|
|
|
if (ds_count < 20) {
|
|
|
|
|
@@ -753,10 +714,14 @@ static int emmc_ds_core_align(struct mmc_host *mmc)
|
|
|
|
|
count = ((delay2>>18) & 0x3f) - ((delay2_bak>>18) & 0x3f);
|
|
|
|
|
pdata->ds_core = count;
|
|
|
|
|
delay1 += (count<<0)|(count<<6)|(count<<12)|(count<<18)|(count<<24);
|
|
|
|
|
delay2 += (count<<0)|(count<<6)|(count<<12)|(count<<24);
|
|
|
|
|
delay2 += (count<<0)|(count<<6)|(count<<12);
|
|
|
|
|
cmd_count = fbinary(pdata->align[9]);
|
|
|
|
|
if (cmd_count < (pdata->count / 2))
|
|
|
|
|
cmd_count = (pdata->count / 2) - cmd_count;
|
|
|
|
|
delay2 += (cmd_count<<24);
|
|
|
|
|
writel(delay1, host->base + SD_EMMC_DELAY1_V3);
|
|
|
|
|
writel(delay2, host->base + SD_EMMC_DELAY2_V3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG,
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3,
|
|
|
|
|
"ds_count:%d,delay1:0x%x,delay2:0x%x,count: %u\n",
|
|
|
|
|
ds_count, readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3), count);
|
|
|
|
|
@@ -773,40 +738,73 @@ static int emmc_ds_manual_sht(struct mmc_host *mmc)
|
|
|
|
|
u8 *blk_test = NULL;
|
|
|
|
|
u32 blksz = 512;
|
|
|
|
|
int i, err = 0;
|
|
|
|
|
u32 sta = 0, end = 0, flag = 0;
|
|
|
|
|
int match[32];
|
|
|
|
|
int best_start = -1, best_size = -1;
|
|
|
|
|
int cur_start = -1, cur_size = 0;
|
|
|
|
|
|
|
|
|
|
sd_emmc_debug = 0x2000;
|
|
|
|
|
blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL);
|
|
|
|
|
if (!blk_test)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
print_all_line_eyetest(mmc);
|
|
|
|
|
update_all_line_eyetest(mmc);
|
|
|
|
|
emmc_ds_core_align(mmc);
|
|
|
|
|
print_all_line_eyetest(mmc);
|
|
|
|
|
update_all_line_eyetest(mmc);
|
|
|
|
|
emmc_all_data_line_alignment(mmc);
|
|
|
|
|
print_all_line_eyetest(mmc);
|
|
|
|
|
update_all_line_eyetest(mmc);
|
|
|
|
|
emmc_ds_data_alignment(mmc);
|
|
|
|
|
print_all_line_eyetest(mmc);
|
|
|
|
|
update_all_line_eyetest(mmc);
|
|
|
|
|
host->is_tunning = 1;
|
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
|
gintf3->ds_sht_m += 1;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
err = aml_sd_emmc_cali_v3(mmc,
|
|
|
|
|
MMC_READ_MULTIPLE_BLOCK,
|
|
|
|
|
blk_test, blksz, 20);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x, err: %d\n",
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3), err);
|
|
|
|
|
if (!err && !flag) {
|
|
|
|
|
sta = i;
|
|
|
|
|
flag = 1;
|
|
|
|
|
} else if (err && flag) {
|
|
|
|
|
end = i-1;
|
|
|
|
|
break;
|
|
|
|
|
emmc_dbg(AMLSD_DBG_V3, "intf3: 0x%x, err[%d]: %d\n",
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3), i, err);
|
|
|
|
|
if (!err)
|
|
|
|
|
match[i] = 0;
|
|
|
|
|
else
|
|
|
|
|
match[i] = -1;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
|
if (match[i] == 0) {
|
|
|
|
|
if (cur_start < 0)
|
|
|
|
|
cur_start = i;
|
|
|
|
|
cur_size++;
|
|
|
|
|
} else {
|
|
|
|
|
if (cur_start >= 0) {
|
|
|
|
|
if (best_start < 0) {
|
|
|
|
|
best_start = cur_start;
|
|
|
|
|
best_size = cur_size;
|
|
|
|
|
} else {
|
|
|
|
|
if (best_size < cur_size) {
|
|
|
|
|
best_start = cur_start;
|
|
|
|
|
best_size = cur_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cur_start = -1;
|
|
|
|
|
cur_size = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
gintf3->ds_sht_m = (sta + end) / 2;
|
|
|
|
|
if (cur_start >= 0) {
|
|
|
|
|
if (best_start < 0) {
|
|
|
|
|
best_start = cur_start;
|
|
|
|
|
best_size = cur_size;
|
|
|
|
|
} else if (best_size < cur_size) {
|
|
|
|
|
best_start = cur_start;
|
|
|
|
|
best_size = cur_size;
|
|
|
|
|
}
|
|
|
|
|
cur_start = -1;
|
|
|
|
|
cur_size = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gintf3->ds_sht_m = (best_start + best_size) / 2;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "sta:%u, end:%u,ds_sht:%u, intf3:0x%x",
|
|
|
|
|
sta, end, gintf3->ds_sht_m,
|
|
|
|
|
pr_info("ds_sht:%u, window:%d, intf3:0x%x",
|
|
|
|
|
gintf3->ds_sht_m, best_size,
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3));
|
|
|
|
|
host->is_tunning = 0;
|
|
|
|
|
kfree(blk_test);
|
|
|
|
|
@@ -835,8 +833,6 @@ static unsigned int emmc_clktest(struct mmc_host *mmc)
|
|
|
|
|
gintf3->clktest_exp = 8;
|
|
|
|
|
gintf3->clktest_on_m = 1;
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_CFG));
|
|
|
|
|
|
|
|
|
|
clktest_log = readl(host->base + SD_EMMC_CLKTEST_LOG);
|
|
|
|
|
clktest = readl(host->base + SD_EMMC_CLKTEST_OUT);
|
|
|
|
|
@@ -865,19 +861,250 @@ static unsigned int emmc_clktest(struct mmc_host *mmc)
|
|
|
|
|
writel(intf3, host->base + SD_EMMC_INTF3);
|
|
|
|
|
vcfg |= (1 << 23);
|
|
|
|
|
writel(vcfg, host->base + SD_EMMC_CFG);
|
|
|
|
|
|
|
|
|
|
emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n",
|
|
|
|
|
readl(host->base + SD_EMMC_CFG));
|
|
|
|
|
pdata->count = count;
|
|
|
|
|
pdata->delay_cell = delay_cell;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int _aml_sd_emmc_execute_tuning(struct mmc_host *mmc, u32 opcode,
|
|
|
|
|
struct aml_tuning_data *tuning_data,
|
|
|
|
|
u32 adj_win_start)
|
|
|
|
|
{
|
|
|
|
|
#if 1 /* need finish later */
|
|
|
|
|
struct amlsd_host *host = mmc_priv(mmc);
|
|
|
|
|
struct amlsd_platform *pdata = host->pdata;
|
|
|
|
|
u32 vclk;
|
|
|
|
|
struct sd_emmc_clock_v3 *clkc = (struct sd_emmc_clock_v3 *)&(vclk);
|
|
|
|
|
u32 vctrl;
|
|
|
|
|
struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl;
|
|
|
|
|
u32 adjust = readl(host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
struct sd_emmc_adjust_v3 *gadjust =
|
|
|
|
|
(struct sd_emmc_adjust_v3 *)&adjust;
|
|
|
|
|
u32 clk_rate = 1000000000;
|
|
|
|
|
const u8 *blk_pattern = tuning_data->blk_pattern;
|
|
|
|
|
unsigned int blksz = tuning_data->blksz;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
u32 nmatch = 0;
|
|
|
|
|
int adj_delay = 0;
|
|
|
|
|
u8 *blk_test;
|
|
|
|
|
u8 tuning_num = 0;
|
|
|
|
|
u32 clock, clk_div;
|
|
|
|
|
u32 adj_delay_find;
|
|
|
|
|
int wrap_win_start = -1, wrap_win_size = 0;
|
|
|
|
|
int best_win_start = -1, best_win_size = 0;
|
|
|
|
|
int curr_win_start = -1, curr_win_size = 0;
|
|
|
|
|
|
|
|
|
|
writel(0, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
|
|
|
|
|
tunning:
|
|
|
|
|
spin_lock_irqsave(&host->mrq_lock, flags);
|
|
|
|
|
pdata->need_retuning = false;
|
|
|
|
|
spin_unlock_irqrestore(&host->mrq_lock, flags);
|
|
|
|
|
vclk = readl(host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
vctrl = readl(host->base + SD_EMMC_CFG);
|
|
|
|
|
clk_div = clkc->div;
|
|
|
|
|
clock = clk_rate / clk_div;/*200MHz, bus_clk */
|
|
|
|
|
pdata->mmc->actual_clock = ctrl->ddr ?
|
|
|
|
|
(clock / 2) : clock;/*100MHz in ddr */
|
|
|
|
|
|
|
|
|
|
if (ctrl->ddr == 1) {
|
|
|
|
|
blksz = 512;
|
|
|
|
|
opcode = 17;
|
|
|
|
|
}
|
|
|
|
|
blk_test = kmalloc(blksz, GFP_KERNEL);
|
|
|
|
|
if (!blk_test)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
host->is_tunning = 1;
|
|
|
|
|
pr_info("%s: clk %d %s tuning start\n",
|
|
|
|
|
mmc_hostname(mmc), (ctrl->ddr ? (clock / 2) : clock),
|
|
|
|
|
(ctrl->ddr ? "DDR mode" : "SDR mode"));
|
|
|
|
|
for (adj_delay = 0; adj_delay < clk_div; adj_delay++) {
|
|
|
|
|
gadjust->adj_delay = adj_delay;
|
|
|
|
|
gadjust->adj_enable = 1;
|
|
|
|
|
gadjust->cali_enable = 0;
|
|
|
|
|
gadjust->cali_rise = 0;
|
|
|
|
|
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
nmatch = aml_sd_emmc_tuning_transfer(mmc, opcode,
|
|
|
|
|
blk_pattern, blk_test, blksz);
|
|
|
|
|
/*get a ok adjust point!*/
|
|
|
|
|
if (nmatch == TUNING_NUM_PER_POINT) {
|
|
|
|
|
if (adj_delay == 0)
|
|
|
|
|
wrap_win_start = adj_delay;
|
|
|
|
|
|
|
|
|
|
if (wrap_win_start >= 0)
|
|
|
|
|
wrap_win_size++;
|
|
|
|
|
|
|
|
|
|
if (curr_win_start < 0)
|
|
|
|
|
curr_win_start = adj_delay;
|
|
|
|
|
|
|
|
|
|
curr_win_size++;
|
|
|
|
|
pr_info("%s: rx_tuning_result[%d] = %d\n",
|
|
|
|
|
mmc_hostname(host->mmc), adj_delay, nmatch);
|
|
|
|
|
} else {
|
|
|
|
|
if (curr_win_start >= 0) {
|
|
|
|
|
if (best_win_start < 0) {
|
|
|
|
|
best_win_start = curr_win_start;
|
|
|
|
|
best_win_size = curr_win_size;
|
|
|
|
|
} else {
|
|
|
|
|
if (best_win_size < curr_win_size) {
|
|
|
|
|
best_win_start = curr_win_start;
|
|
|
|
|
best_win_size = curr_win_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wrap_win_start = -1;
|
|
|
|
|
curr_win_start = -1;
|
|
|
|
|
curr_win_size = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
/* last point is ok! */
|
|
|
|
|
if (curr_win_start >= 0) {
|
|
|
|
|
if (best_win_start < 0) {
|
|
|
|
|
best_win_start = curr_win_start;
|
|
|
|
|
best_win_size = curr_win_size;
|
|
|
|
|
} else if (wrap_win_size > 0) {
|
|
|
|
|
/* Wrap around case */
|
|
|
|
|
if (curr_win_size + wrap_win_size > best_win_size) {
|
|
|
|
|
best_win_start = curr_win_start;
|
|
|
|
|
best_win_size = curr_win_size + wrap_win_size;
|
|
|
|
|
}
|
|
|
|
|
} else if (best_win_size < curr_win_size) {
|
|
|
|
|
best_win_start = curr_win_start;
|
|
|
|
|
best_win_size = curr_win_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
curr_win_start = -1;
|
|
|
|
|
curr_win_size = 0;
|
|
|
|
|
}
|
|
|
|
|
if (best_win_size <= 0) {
|
|
|
|
|
if ((tuning_num++ > MAX_TUNING_RETRY)
|
|
|
|
|
|| (clkc->div >= 10)) {
|
|
|
|
|
kfree(blk_test);
|
|
|
|
|
pr_info("%s: final result of tuning failed\n",
|
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
clkc->div += 1;
|
|
|
|
|
writel(vclk, host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
pdata->clkc = readl(host->base + SD_EMMC_CLOCK_V3);
|
|
|
|
|
pr_info("%s: tuning failed, reduce freq and retuning\n",
|
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
|
goto tunning;
|
|
|
|
|
} else {
|
|
|
|
|
pr_info("%s: best_win_start =%d, best_win_size =%d\n",
|
|
|
|
|
mmc_hostname(host->mmc), best_win_start, best_win_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((best_win_size != clk_div)
|
|
|
|
|
|| (aml_card_type_sdio(pdata)
|
|
|
|
|
&& (get_cpu_type() == MESON_CPU_MAJOR_ID_GXM))) {
|
|
|
|
|
adj_delay_find = best_win_start + (best_win_size - 1) / 2
|
|
|
|
|
+ (best_win_size - 1) % 2;
|
|
|
|
|
adj_delay_find = adj_delay_find % clk_div;
|
|
|
|
|
} else
|
|
|
|
|
adj_delay_find = 0;
|
|
|
|
|
|
|
|
|
|
/* fixme, for retry debug. */
|
|
|
|
|
if (aml_card_type_mmc(pdata)
|
|
|
|
|
&& (clk_div <= 5) && (adj_win_start != 100)
|
|
|
|
|
&& (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB)) {
|
|
|
|
|
pr_info("%s: adj_win_start %d\n",
|
|
|
|
|
mmc_hostname(host->mmc), adj_win_start);
|
|
|
|
|
adj_delay_find = adj_win_start % clk_div;
|
|
|
|
|
}
|
|
|
|
|
gadjust->adj_delay = adj_delay_find;
|
|
|
|
|
gadjust->adj_enable = 1;
|
|
|
|
|
gadjust->cali_enable = 0;
|
|
|
|
|
gadjust->cali_rise = 0;
|
|
|
|
|
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
host->is_tunning = 0;
|
|
|
|
|
|
|
|
|
|
pr_info("%s: sd_emmc_regs->gclock=0x%x,sd_emmc_regs->gadjust=0x%x\n",
|
|
|
|
|
mmc_hostname(host->mmc),
|
|
|
|
|
readl(host->base + SD_EMMC_CLOCK_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_ADJUST_V3));
|
|
|
|
|
kfree(blk_test);
|
|
|
|
|
/* do not dynamical tuning for no emmc device */
|
|
|
|
|
if ((pdata->is_in) && !aml_card_type_mmc(pdata))
|
|
|
|
|
schedule_delayed_work(&pdata->retuning, 15*HZ);
|
|
|
|
|
return ret;
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int aml_emmc_hs200_timming(struct mmc_host *mmc)
|
|
|
|
|
{
|
|
|
|
|
struct amlsd_host *host = mmc_priv(mmc);
|
|
|
|
|
u32 adjust = readl(host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
struct sd_emmc_adjust *gadjust =
|
|
|
|
|
(struct sd_emmc_adjust *)&adjust;
|
|
|
|
|
emmc_clktest(mmc);
|
|
|
|
|
update_all_line_eyetest(mmc);
|
|
|
|
|
emmc_all_data_line_alignment(mmc);
|
|
|
|
|
gadjust->cali_enable = 1;
|
|
|
|
|
gadjust->adj_auto = 1;
|
|
|
|
|
writel(adjust, host->base + SD_EMMC_ADJUST_V3);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode)
|
|
|
|
|
{
|
|
|
|
|
struct amlsd_host *host = mmc_priv(mmc);
|
|
|
|
|
struct amlsd_platform *pdata = host->pdata;
|
|
|
|
|
struct aml_tuning_data tuning_data;
|
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
u32 adj_win_start = 100;
|
|
|
|
|
u32 intf3;
|
|
|
|
|
|
|
|
|
|
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
|
|
|
|
|
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_8bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
|
|
|
|
|
} else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else if (opcode == MMC_SEND_TUNING_BLOCK) {
|
|
|
|
|
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
|
|
|
|
|
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
|
|
|
|
|
} else {
|
|
|
|
|
sd_emmc_err("Undefined command(%d) for tuning\n", opcode);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (aml_card_type_sdio(pdata))
|
|
|
|
|
err = _aml_sd_emmc_execute_tuning(mmc, opcode,
|
|
|
|
|
&tuning_data, adj_win_start);
|
|
|
|
|
else if (!(pdata->caps2 & MMC_CAP2_HS400)) {
|
|
|
|
|
intf3 = readl(host->base + SD_EMMC_INTF3);
|
|
|
|
|
intf3 |= (1<<22);
|
|
|
|
|
writel(intf3, (host->base + SD_EMMC_INTF3));
|
|
|
|
|
err = aml_emmc_hs200_timming(mmc);
|
|
|
|
|
} else {
|
|
|
|
|
intf3 = readl(host->base + SD_EMMC_INTF3);
|
|
|
|
|
intf3 |= (1<<22);
|
|
|
|
|
writel(intf3, (host->base + SD_EMMC_INTF3));
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pr_info("%s: gclock=0x%x, gdelay1=0x%x, gdelay2=0x%x,intf3=0x%x\n",
|
|
|
|
|
mmc_hostname(mmc), readl(host->base + SD_EMMC_CLOCK_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY1_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_DELAY2_V3),
|
|
|
|
|
readl(host->base + SD_EMMC_INTF3));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
int aml_post_hs400_timming(struct mmc_host *mmc)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
emmc_clktest(mmc);
|
|
|
|
|
ret = emmc_ds_manual_sht(mmc);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|