summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c73
1 files changed, 70 insertions, 3 deletions
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index acdc5520d02c..c5a8af4ca76b 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -488,6 +488,61 @@ static int meson_mmc_clk_init(struct meson_host *host)
return clk_prepare_enable(host->mmc_clk);
}
+static void meson_mmc_disable_resampling(struct meson_host *host)
+{
+ unsigned int val = readl(host->regs + host->data->adjust);
+
+ val &= ~ADJUST_ADJ_EN;
+ writel(val, host->regs + host->data->adjust);
+}
+
+static void meson_mmc_reset_resampling(struct meson_host *host)
+{
+ unsigned int val;
+
+ meson_mmc_disable_resampling(host);
+
+ val = readl(host->regs + host->data->adjust);
+ val &= ~ADJUST_ADJ_DELAY_MASK;
+ writel(val, host->regs + host->data->adjust);
+}
+
+static int meson_mmc_resampling_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ unsigned int val, dly, max_dly, i;
+ int ret;
+
+ /* Resampling is done using the source clock */
+ max_dly = DIV_ROUND_UP(clk_get_rate(host->mux_clk),
+ clk_get_rate(host->mmc_clk));
+
+ val = readl(host->regs + host->data->adjust);
+ val |= ADJUST_ADJ_EN;
+ writel(val, host->regs + host->data->adjust);
+
+ if (mmc->doing_retune)
+ dly = FIELD_GET(ADJUST_ADJ_DELAY_MASK, val) + 1;
+ else
+ dly = 0;
+
+ for (i = 0; i < max_dly; i++) {
+ val &= ~ADJUST_ADJ_DELAY_MASK;
+ val |= FIELD_PREP(ADJUST_ADJ_DELAY_MASK, (dly + i) % max_dly);
+ writel(val, host->regs + host->data->adjust);
+
+ ret = mmc_send_tuning(mmc, opcode, NULL);
+ if (!ret) {
+ dev_dbg(mmc_dev(mmc), "resampling delay: %u\n",
+ (dly + i) % max_dly);
+ return 0;
+ }
+ }
+
+ meson_mmc_reset_resampling(host);
+ return -EIO;
+}
+
static int meson_mmc_prepare_ios_clock(struct meson_host *host,
struct mmc_ios *ios)
{
@@ -507,6 +562,19 @@ static int meson_mmc_prepare_ios_clock(struct meson_host *host,
return meson_mmc_clk_set(host, ios->clock, ddr);
}
+static void meson_mmc_check_resampling(struct meson_host *host,
+ struct mmc_ios *ios)
+{
+ switch (ios->timing) {
+ case MMC_TIMING_LEGACY:
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_MMC_DDR52:
+ meson_mmc_disable_resampling(host);
+ break;
+ }
+}
+
static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct meson_host *host = mmc_priv(mmc);
@@ -533,9 +601,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
- /* disable signal resampling */
- writel(0, host->regs + host->data->adjust);
-
break;
case MMC_POWER_ON:
@@ -574,6 +639,7 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
val |= FIELD_PREP(CFG_BUS_WIDTH_MASK, bus_width);
writel(val, host->regs + SD_EMMC_CFG);
+ meson_mmc_check_resampling(host, ios);
err = meson_mmc_prepare_ios_clock(host, ios);
if (err)
dev_err(host->dev, "Failed to set clock: %d\n,", err);
@@ -963,6 +1029,7 @@ static const struct mmc_host_ops meson_mmc_ops = {
.get_cd = meson_mmc_get_cd,
.pre_req = meson_mmc_pre_req,
.post_req = meson_mmc_post_req,
+ .execute_tuning = meson_mmc_resampling_tuning,
.card_busy = meson_mmc_card_busy,
.start_signal_voltage_switch = meson_mmc_voltage_switch,
};