diff options
Diffstat (limited to 'drivers/mmc/renesas-sdhi.c')
-rw-r--r-- | drivers/mmc/renesas-sdhi.c | 149 |
1 files changed, 130 insertions, 19 deletions
diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index e7f96f8bf22..733b6d62f5d 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -17,7 +17,9 @@ #include "tmio-common.h" -#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) /* SCC registers */ #define RENESAS_SDHI_SCC_DTCNTL 0x800 @@ -107,6 +109,56 @@ static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); } +static int renesas_sdhi_hs400(struct udevice *dev) +{ + struct tmio_sd_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); + bool hs400 = (mmc->selected_mode == MMC_HS_400); + int ret, taps = hs400 ? priv->nrtaps : 8; + u32 reg; + + if (taps == 4) /* HS400 on 4tap SoC needs different clock */ + ret = clk_set_rate(&priv->clk, 400000000); + else + ret = clk_set_rate(&priv->clk, 200000000); + if (ret < 0) + return ret; + + tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_TMPPORT2); + if (hs400) { + reg |= RENESAS_SDHI_SCC_TMPPORT2_HS400EN | + RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL; + } else { + reg &= ~(RENESAS_SDHI_SCC_TMPPORT2_HS400EN | + RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); + } + + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); + + tmio_sd_writel(priv, (taps << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) | + RENESAS_SDHI_SCC_DTCNTL_TAPEN, + RENESAS_SDHI_SCC_DTCNTL); + + if (taps == 4) { + tmio_sd_writel(priv, priv->tap_set >> 1, + RENESAS_SDHI_SCC_TAPSET); + } else { + tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); + } + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_CKSEL); + reg |= RENESAS_SDHI_SCC_CKSEL_DTSEL; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_CKSEL); + + reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); + reg |= RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); + + return 0; +} + static void renesas_sdhi_prepare_tuning(struct tmio_sd_priv *priv, unsigned long tap) { @@ -125,7 +177,6 @@ static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, unsigned int smpcmp) { unsigned long tap_cnt; /* counter of tuning success */ - unsigned long tap_set; /* tap position */ unsigned long tap_start;/* start position of tuning success */ unsigned long tap_end; /* end position of tuning success */ unsigned long ntap; /* temporary counter of tuning success */ @@ -209,12 +260,12 @@ static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, select = true; if (select) - tap_set = ((tap_start + tap_end) / 2) % tap_num; + priv->tap_set = ((tap_start + tap_end) / 2) % tap_num; else return -EIO; /* Set SCC */ - tmio_sd_writel(priv, tap_set, RENESAS_SDHI_SCC_TAPSET); + tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); /* Enable auto re-tuning */ reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); @@ -240,6 +291,7 @@ int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) /* clock tuning is not needed for upto 52MHz */ if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == MMC_HS_400) || (mmc->selected_mode == UHS_SDR104) || (mmc->selected_mode == UHS_SDR50))) return 0; @@ -287,19 +339,42 @@ out: return ret; } +#else +static int renesas_sdhi_hs400(struct udevice *dev) +{ + return 0; +} #endif static int renesas_sdhi_set_ios(struct udevice *dev) { - int ret = tmio_sd_set_ios(dev); + struct tmio_sd_priv *priv = dev_get_priv(dev); + u32 tmp; + int ret; - mdelay(10); + /* Stop the clock before changing its rate to avoid a glitch signal */ + tmp = tmio_sd_readl(priv, TMIO_SD_CLKCTL); + tmp &= ~TMIO_SD_CLKCTL_SCLKEN; + tmio_sd_writel(priv, tmp, TMIO_SD_CLKCTL); -#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) - struct tmio_sd_priv *priv = dev_get_priv(dev); + ret = renesas_sdhi_hs400(dev); + if (ret) + return ret; + + ret = tmio_sd_set_ios(dev); - if (priv->caps & TMIO_SD_CAP_RCAR_UHS) + mdelay(10); + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) + struct mmc *mmc = mmc_get_mmc_dev(dev); + if ((priv->caps & TMIO_SD_CAP_RCAR_UHS) && + (mmc->selected_mode != UHS_SDR104) && + (mmc->selected_mode != MMC_HS_200) && + (mmc->selected_mode != MMC_HS_400)) { renesas_sdhi_reset_tuning(priv); + } #endif return ret; @@ -331,7 +406,9 @@ static const struct dm_mmc_ops renesas_sdhi_ops = { .send_cmd = tmio_sd_send_cmd, .set_ios = renesas_sdhi_set_ios, .get_cd = tmio_sd_get_cd, -#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) .execute_tuning = renesas_sdhi_execute_tuning, #endif #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) @@ -358,15 +435,45 @@ static const struct udevice_id renesas_sdhi_match[] = { { /* sentinel */ } }; +static ulong renesas_sdhi_clk_get_rate(struct tmio_sd_priv *priv) +{ + return clk_get_rate(&priv->clk); +} + +static void renesas_sdhi_filter_caps(struct udevice *dev) +{ + struct tmio_sd_plat *plat = dev_get_platdata(dev); + struct tmio_sd_priv *priv = dev_get_priv(dev); + + if (!(priv->caps & TMIO_SD_CAP_RCAR_GEN3)) + return; + + /* HS400 is not supported on H3 ES1.x and M3W ES1.0,ES1.1 */ + if (((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && + (rmobile_get_cpu_rev_integer() <= 1)) || + ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7796) && + (rmobile_get_cpu_rev_integer() == 1) && + (rmobile_get_cpu_rev_fraction() <= 1))) + plat->cfg.host_caps &= ~MMC_MODE_HS400; + + /* H3 ES2.0 uses 4 tuning taps */ + if ((rmobile_get_cpu_type() == RMOBILE_CPU_TYPE_R8A7795) && + (rmobile_get_cpu_rev_integer() == 2)) + priv->nrtaps = 4; + else + priv->nrtaps = 8; +} + static int renesas_sdhi_probe(struct udevice *dev) { struct tmio_sd_priv *priv = dev_get_priv(dev); u32 quirks = dev_get_driver_data(dev); struct fdt_resource reg_res; - struct clk clk; DECLARE_GLOBAL_DATA_PTR; int ret; + priv->clk_get_rate = renesas_sdhi_clk_get_rate; + if (quirks == RENESAS_GEN2_QUIRKS) { ret = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg", 0, ®_res); @@ -380,29 +487,33 @@ static int renesas_sdhi_probe(struct udevice *dev) quirks |= TMIO_SD_CAP_16BIT; } - ret = clk_get_by_index(dev, 0, &clk); + ret = clk_get_by_index(dev, 0, &priv->clk); if (ret < 0) { dev_err(dev, "failed to get host clock\n"); return ret; } /* set to max rate */ - priv->mclk = clk_set_rate(&clk, ULONG_MAX); - if (IS_ERR_VALUE(priv->mclk)) { + ret = clk_set_rate(&priv->clk, 200000000); + if (ret < 0) { dev_err(dev, "failed to set rate for host clock\n"); - clk_free(&clk); - return priv->mclk; + clk_free(&priv->clk); + return ret; } - ret = clk_enable(&clk); - clk_free(&clk); + ret = clk_enable(&priv->clk); if (ret) { dev_err(dev, "failed to enable host clock\n"); return ret; } ret = tmio_sd_probe(dev, quirks); -#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + + renesas_sdhi_filter_caps(dev); + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || \ + CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) if (!ret && (priv->caps & TMIO_SD_CAP_RCAR_UHS)) renesas_sdhi_reset_tuning(priv); #endif |