// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Marek Vasut */ #include #include #include #include #include #include #include #include #include #include #include #include "tmio-common.h" #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 #define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) #define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 #define RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff #define RENESAS_SDHI_SCC_TAPSET 0x804 #define RENESAS_SDHI_SCC_DT2FF 0x808 #define RENESAS_SDHI_SCC_CKSEL 0x80c #define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) #define RENESAS_SDHI_SCC_RVSCNTL 0x810 #define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) #define RENESAS_SDHI_SCC_RVSREQ 0x814 #define RENESAS_SDHI_SCC_RVSREQ_RVSERR BIT(2) #define RENESAS_SDHI_SCC_SMPCMP 0x818 #define RENESAS_SDHI_SCC_TMPPORT2 0x81c #define RENESAS_SDHI_SCC_TMPPORT2_HS400EN BIT(31) #define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) #define RENESAS_SDHI_MAX_TAP 3 static unsigned int renesas_sdhi_init_tuning(struct tmio_sd_priv *priv) { u32 reg; /* Initialize SCC */ tmio_sd_writel(priv, 0, TMIO_SD_INFO1); reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); reg &= ~TMIO_SD_CLKCTL_SCLKEN; tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); /* Set sampling clock selection range */ tmio_sd_writel(priv, (0x8 << RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) | RENESAS_SDHI_SCC_DTCNTL_TAPEN, RENESAS_SDHI_SCC_DTCNTL); 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); tmio_sd_writel(priv, 0x300 /* scc_tappos */, RENESAS_SDHI_SCC_DT2FF); reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); reg |= TMIO_SD_CLKCTL_SCLKEN; tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); /* Read TAPNUM */ return (tmio_sd_readl(priv, RENESAS_SDHI_SCC_DTCNTL) >> RENESAS_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & RENESAS_SDHI_SCC_DTCNTL_TAPNUM_MASK; } static void renesas_sdhi_reset_tuning(struct tmio_sd_priv *priv) { u32 reg; /* Reset SCC */ reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); reg &= ~TMIO_SD_CLKCTL_SCLKEN; tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); 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_TMPPORT2); reg &= ~(RENESAS_SDHI_SCC_TMPPORT2_HS400EN | RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_TMPPORT2); reg = tmio_sd_readl(priv, TMIO_SD_CLKCTL); reg |= TMIO_SD_CLKCTL_SCLKEN; tmio_sd_writel(priv, reg, TMIO_SD_CLKCTL); reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; tmio_sd_writel(priv, reg, RENESAS_SDHI_SCC_RVSCNTL); reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL); reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; 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) { /* Set sampling clock position */ tmio_sd_writel(priv, tap, RENESAS_SDHI_SCC_TAPSET); } static unsigned int renesas_sdhi_compare_scc_data(struct tmio_sd_priv *priv) { /* Get comparison of sampling data */ return tmio_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP); } static int renesas_sdhi_select_tuning(struct tmio_sd_priv *priv, unsigned int tap_num, unsigned int taps, unsigned int smpcmp) { unsigned long tap_cnt; /* counter of tuning success */ 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 */ unsigned long match_cnt;/* counter of matching data */ unsigned long i; bool select = false; u32 reg; /* Clear SCC_RVSREQ */ tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ); /* Merge the results */ for (i = 0; i < tap_num * 2; i++) { if (!(taps & BIT(i))) { taps &= ~BIT(i % tap_num); taps &= ~BIT((i % tap_num) + tap_num); } if (!(smpcmp & BIT(i))) { smpcmp &= ~BIT(i % tap_num); smpcmp &= ~BIT((i % tap_num) + tap_num); } } /* * Find the longest consecutive run of successful probes. If that * is more than RENESAS_SDHI_MAX_TAP probes long then use the * center index as the tap. */ tap_cnt = 0; ntap = 0; tap_start = 0; tap_end = 0; for (i = 0; i < tap_num * 2; i++) { if (taps & BIT(i)) ntap++; else { if (ntap > tap_cnt) { tap_start = i - ntap; tap_end = i - 1; tap_cnt = ntap; } ntap = 0; } } if (ntap > tap_cnt) { tap_start = i - ntap; tap_end = i - 1; tap_cnt = ntap; } /* * If all of the TAP is OK, the sampling clock position is selected by * identifying the change point of data. */ if (tap_cnt == tap_num * 2) { match_cnt = 0; ntap = 0; tap_start = 0; tap_end = 0; for (i = 0; i < tap_num * 2; i++) { if (smpcmp & BIT(i)) ntap++; else { if (ntap > match_cnt) { tap_start = i - ntap; tap_end = i - 1; match_cnt = ntap; } ntap = 0; } } if (ntap > match_cnt) { tap_start = i - ntap; tap_end = i - 1; match_cnt = ntap; } if (match_cnt) select = true; } else if (tap_cnt >= RENESAS_SDHI_MAX_TAP) select = true; if (select) priv->tap_set = ((tap_start + tap_end) / 2) % tap_num; else return -EIO; /* Set SCC */ tmio_sd_writel(priv, priv->tap_set, RENESAS_SDHI_SCC_TAPSET); /* Enable auto re-tuning */ 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; } int renesas_sdhi_execute_tuning(struct udevice *dev, uint opcode) { struct tmio_sd_priv *priv = dev_get_priv(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct mmc *mmc = upriv->mmc; unsigned int tap_num; unsigned int taps = 0, smpcmp = 0; int i, ret = 0; u32 caps; /* Only supported on Renesas RCar */ if (!(priv->caps & TMIO_SD_CAP_RCAR_UHS)) return -EINVAL; /* 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; tap_num = renesas_sdhi_init_tuning(priv); if (!tap_num) /* Tuning is not supported */ goto out; if (tap_num * 2 >= sizeof(taps) * 8) { dev_err(dev, "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); goto out; } /* Issue CMD19 twice for each tap */ for (i = 0; i < 2 * tap_num; i++) { renesas_sdhi_prepare_tuning(priv, i % tap_num); /* Force PIO for the tuning */ caps = priv->caps; priv->caps &= ~TMIO_SD_CAP_DMA_INTERNAL; ret = mmc_send_tuning(mmc, opcode, NULL); priv->caps = caps; if (ret == 0) taps |= BIT(i); ret = renesas_sdhi_compare_scc_data(priv); if (ret == 0) smpcmp |= BIT(i); mdelay(1); } ret = renesas_sdhi_select_tuning(priv, tap_num, taps, smpcmp); out: if (ret < 0) { dev_warn(dev, "Tuning procedure failed\n"); renesas_sdhi_reset_tuning(priv); } return ret; } #else static int renesas_sdhi_hs400(struct udevice *dev) { return 0; } #endif static int renesas_sdhi_set_ios(struct udevice *dev) { struct tmio_sd_priv *priv = dev_get_priv(dev); u32 tmp; int ret; /* 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); ret = renesas_sdhi_hs400(dev); if (ret) return ret; ret = tmio_sd_set_ios(dev); 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; } #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) static int renesas_sdhi_wait_dat0(struct udevice *dev, int state, int timeout) { int ret = -ETIMEDOUT; bool dat0_high; bool target_dat0_high = !!state; struct tmio_sd_priv *priv = dev_get_priv(dev); timeout = DIV_ROUND_UP(timeout, 10); /* check every 10 us. */ while (timeout--) { dat0_high = !!(tmio_sd_readl(priv, TMIO_SD_INFO2) & TMIO_SD_INFO2_DAT0); if (dat0_high == target_dat0_high) { ret = 0; break; } udelay(10); } return ret; } #endif 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) || \ CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) .execute_tuning = renesas_sdhi_execute_tuning, #endif #if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) .wait_dat0 = renesas_sdhi_wait_dat0, #endif }; #define RENESAS_GEN2_QUIRKS TMIO_SD_CAP_RCAR_GEN2 #define RENESAS_GEN3_QUIRKS \ TMIO_SD_CAP_64BIT | TMIO_SD_CAP_RCAR_GEN3 | TMIO_SD_CAP_RCAR_UHS static const struct udevice_id renesas_sdhi_match[] = { { .compatible = "renesas,sdhi-r8a7790", .data = RENESAS_GEN2_QUIRKS }, { .compatible = "renesas,sdhi-r8a7791", .data = RENESAS_GEN2_QUIRKS }, { .compatible = "renesas,sdhi-r8a7792", .data = RENESAS_GEN2_QUIRKS }, { .compatible = "renesas,sdhi-r8a7793", .data = RENESAS_GEN2_QUIRKS }, { .compatible = "renesas,sdhi-r8a7794", .data = RENESAS_GEN2_QUIRKS }, { .compatible = "renesas,sdhi-r8a7795", .data = RENESAS_GEN3_QUIRKS }, { .compatible = "renesas,sdhi-r8a7796", .data = RENESAS_GEN3_QUIRKS }, { .compatible = "renesas,sdhi-r8a77965", .data = RENESAS_GEN3_QUIRKS }, { .compatible = "renesas,sdhi-r8a77970", .data = RENESAS_GEN3_QUIRKS }, { .compatible = "renesas,sdhi-r8a77990", .data = RENESAS_GEN3_QUIRKS }, { .compatible = "renesas,sdhi-r8a77995", .data = RENESAS_GEN3_QUIRKS }, { /* 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; /* H3 ES1.x and M3W ES1.0 uses bit 17 for DTRAEND */ 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() == 0))) priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD; else priv->read_poll_flag = TMIO_SD_DMA_INFO1_END_RD2; } 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; 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); if (ret < 0) { dev_err(dev, "\"reg\" resource not found, ret=%i\n", ret); return ret; } if (fdt_resource_size(®_res) == 0x100) quirks |= TMIO_SD_CAP_16BIT; } 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 */ ret = clk_set_rate(&priv->clk, 200000000); if (ret < 0) { dev_err(dev, "failed to set rate for host clock\n"); clk_free(&priv->clk); return ret; } ret = clk_enable(&priv->clk); if (ret) { dev_err(dev, "failed to enable host clock\n"); return ret; } ret = tmio_sd_probe(dev, quirks); 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 return ret; } U_BOOT_DRIVER(renesas_sdhi) = { .name = "renesas-sdhi", .id = UCLASS_MMC, .of_match = renesas_sdhi_match, .bind = tmio_sd_bind, .probe = renesas_sdhi_probe, .priv_auto_alloc_size = sizeof(struct tmio_sd_priv), .platdata_auto_alloc_size = sizeof(struct tmio_sd_plat), .ops = &renesas_sdhi_ops, };