[U-Boot] [PATCH 1/6] mmc: uniphier-sd: Always check controller version

Handle the controller version even if quirks are set. The controller in Renesas Gen3 SoCs does provide the version register, which indicates a controller v10 and the controller does support internal DMA and /1024 divider.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 0d1203cb76..784bb883ce 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -817,16 +817,15 @@ static int uniphier_sd_probe(struct udevice *dev) return -EINVAL; }
- if (quirks) { + if (quirks) priv->caps = quirks; - } else { - priv->version = uniphier_sd_readl(priv, UNIPHIER_SD_VERSION) & - UNIPHIER_SD_VERSION_IP; - dev_dbg(dev, "version %x\n", priv->version); - if (priv->version >= 0x10) { - priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL; - priv->caps |= UNIPHIER_SD_CAP_DIV1024; - } + + priv->version = uniphier_sd_readl(priv, UNIPHIER_SD_VERSION) & + UNIPHIER_SD_VERSION_IP; + dev_dbg(dev, "version %x\n", priv->version); + if (priv->version >= 0x10) { + priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL; + priv->caps |= UNIPHIER_SD_CAP_DIV1024; }
if (fdt_get_property(gd->fdt_blob, dev_of_offset(dev), "non-removable",

Drop the ad-hoc DT caps parsing in favor of common framework function.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 784bb883ce..cf85ec23c7 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -799,24 +799,15 @@ static int uniphier_sd_probe(struct udevice *dev) return ret; }
- plat->cfg.name = dev->name; - plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; - - switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width", - 1)) { - case 8: - plat->cfg.host_caps |= MMC_MODE_8BIT; - break; - case 4: - plat->cfg.host_caps |= MMC_MODE_4BIT; - break; - case 1: - break; - default: - dev_err(dev, "Invalid "bus-width" value\n"); - return -EINVAL; + ret = mmc_of_parse(gd->fdt_blob, dev_of_offset(dev), &plat->cfg); + if (ret < 0) { + dev_err(dev, "failed to parse host caps\n"); + return ret; }
+ plat->cfg.name = dev->name; + plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + if (quirks) priv->caps = quirks;

Hi Marek,
2017-10-06 21:07 GMT+09:00 Marek Vasut marek.vasut@gmail.com:
Drop the ad-hoc DT caps parsing in favor of common framework function.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com
"git grep" could not find mmc_of_parse.
Are you waiting for this one?
http://patchwork.ozlabs.org/patch/816911/
drivers/mmc/uniphier-sd.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 784bb883ce..cf85ec23c7 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -799,24 +799,15 @@ static int uniphier_sd_probe(struct udevice *dev) return ret; }
plat->cfg.name = dev->name;
plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
1)) {
case 8:
plat->cfg.host_caps |= MMC_MODE_8BIT;
break;
case 4:
plat->cfg.host_caps |= MMC_MODE_4BIT;
break;
case 1:
break;
default:
dev_err(dev, "Invalid \"bus-width\" value\n");
return -EINVAL;
ret = mmc_of_parse(gd->fdt_blob, dev_of_offset(dev), &plat->cfg);
if (ret < 0) {
dev_err(dev, "failed to parse host caps\n");
return ret; }
plat->cfg.name = dev->name;
plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
if (quirks) priv->caps = quirks;
-- 2.11.0
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On 10/13/2017 01:36 PM, Masahiro Yamada wrote:
Hi Marek,
2017-10-06 21:07 GMT+09:00 Marek Vasut marek.vasut@gmail.com:
Drop the ad-hoc DT caps parsing in favor of common framework function.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com
"git grep" could not find mmc_of_parse.
Are you waiting for this one?
Yeah, I think Jaehoon is testing the patchset again . Hopefully this time there will be some result from the testing ...
drivers/mmc/uniphier-sd.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 784bb883ce..cf85ec23c7 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -799,24 +799,15 @@ static int uniphier_sd_probe(struct udevice *dev) return ret; }
plat->cfg.name = dev->name;
plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
1)) {
case 8:
plat->cfg.host_caps |= MMC_MODE_8BIT;
break;
case 4:
plat->cfg.host_caps |= MMC_MODE_4BIT;
break;
case 1:
break;
default:
dev_err(dev, "Invalid \"bus-width\" value\n");
return -EINVAL;
ret = mmc_of_parse(gd->fdt_blob, dev_of_offset(dev), &plat->cfg);
if (ret < 0) {
dev_err(dev, "failed to parse host caps\n");
return ret; }
plat->cfg.name = dev->name;
plat->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
if (quirks) priv->caps = quirks;
-- 2.11.0
U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Factor out the regulator handling into set_ios and add support for selecting pin configuration based on the voltage to support UHS modes.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index cf85ec23c7..24dc13cdf9 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -10,6 +10,7 @@ #include <fdtdec.h> #include <mmc.h> #include <dm.h> +#include <dm/pinctrl.h> #include <linux/compat.h> #include <linux/dma-direction.h> #include <linux/io.h> @@ -134,6 +135,9 @@ struct uniphier_sd_priv { #define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ #define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ #define UNIPHIER_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif };
static u64 uniphier_sd_readq(struct uniphier_sd_priv *priv, unsigned int reg) @@ -676,6 +680,24 @@ static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv, udelay(1000); }
+static void uniphier_sd_set_pins(struct udevice *dev) +{ + struct uniphier_sd_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); + + if (priv->vqmmc_dev) { + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + regulator_set_value(priv->vqmmc_dev, 1800000); + else + regulator_set_value(priv->vqmmc_dev, 3300000); + } + + if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + pinctrl_select_state(dev, "state_uhs"); + else + pinctrl_select_state(dev, "default"); +} + static int uniphier_sd_set_ios(struct udevice *dev) { struct uniphier_sd_priv *priv = dev_get_priv(dev); @@ -690,6 +712,7 @@ static int uniphier_sd_set_ios(struct udevice *dev) return ret; uniphier_sd_set_ddr_mode(priv, mmc); uniphier_sd_set_clk_rate(priv, mmc); + uniphier_sd_set_pins(dev);
return 0; } @@ -757,9 +780,6 @@ static int uniphier_sd_probe(struct udevice *dev) fdt_addr_t base; struct clk clk; int ret; -#ifdef CONFIG_DM_REGULATOR - struct udevice *vqmmc_dev; -#endif
base = devfdt_get_addr(dev); if (base == FDT_ADDR_T_NONE) @@ -770,12 +790,7 @@ static int uniphier_sd_probe(struct udevice *dev) return -ENOMEM;
#ifdef CONFIG_DM_REGULATOR - ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); - if (!ret) { - /* Set the regulator to 3.3V until we support 1.8V modes */ - regulator_set_value(vqmmc_dev, 3300000); - regulator_set_enable(vqmmc_dev, true); - } + ret = device_get_supply_regulator(dev, "vqmmc-supply", &priv->vqmmc_dev); #endif
ret = clk_get_by_index(dev, 0, &clk);

Add a quirk to identify that the controller is Renesas RCar variant of the Matsushita SD IP and another quirk indicating it can support Renesas RCar HS200/HS400/SDR104 modes.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 24dc13cdf9..58af0b9954 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -135,6 +135,8 @@ struct uniphier_sd_priv { #define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ #define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ #define UNIPHIER_SD_CAP_64BIT BIT(3) /* Controller is 64bit */ +#define UNIPHIER_SD_CAP_RCAR BIT(4) /* Renesas RCar version of IP */ +#define UNIPHIER_SD_CAP_RCAR_UHS BIT(5) /* Renesas RCar UHS/SDR modes */ #ifdef CONFIG_DM_REGULATOR struct udevice *vqmmc_dev; #endif @@ -852,9 +854,18 @@ static int uniphier_sd_probe(struct udevice *dev) }
static const struct udevice_id uniphier_sd_match[] = { - { .compatible = "renesas,sdhi-r8a7795", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "renesas,sdhi-r8a7796", .data = UNIPHIER_SD_CAP_64BIT }, - { .compatible = "socionext,uniphier-sdhc", .data = 0 }, + { + .compatible = "renesas,sdhi-r8a7795", + .data = UNIPHIER_SD_CAP_64BIT | UNIPHIER_SD_CAP_RCAR | + UNIPHIER_SD_CAP_RCAR_UHS + }, { + .compatible = "renesas,sdhi-r8a7796", + .data = UNIPHIER_SD_CAP_64BIT | UNIPHIER_SD_CAP_RCAR | + UNIPHIER_SD_CAP_RCAR_UHS + }, { + .compatible = "socionext,uniphier-sdhc", + .data = 0 + }, { /* sentinel */ } };

On the Renesas version of the IP, the /1 divider is realized by setting the clock register [7:0] to 0xff instead of setting bit 10 of the register. Check the quirk and handle accordingly.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 58af0b9954..4456d0ed43 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -75,6 +75,7 @@ DECLARE_GLOBAL_DATA_PTR; #define UNIPHIER_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ #define UNIPHIER_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ #define UNIPHIER_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ +#define UNIPHIER_SD_CLKCTL_RCAR_DIV1 0xff /* SDCLK = CLK (RCar ver.) */ #define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ #define UNIPHIER_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ #define UNIPHIER_SD_SIZE 0x04c /* block size */ @@ -641,7 +642,8 @@ static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv, divisor = DIV_ROUND_UP(priv->mclk, mmc->clock);
if (divisor <= 1) - val = UNIPHIER_SD_CLKCTL_DIV1; + val = (priv->caps & UNIPHIER_SD_CAP_RCAR) ? + UNIPHIER_SD_CLKCTL_RCAR_DIV1 : UNIPHIER_SD_CLKCTL_DIV1; else if (divisor <= 2) val = UNIPHIER_SD_CLKCTL_DIV2; else if (divisor <= 4)

Add code for PHY tuning required for SDR104/HS200 support on Renesas RCar.
Signed-off-by: Marek Vasut marek.vasut+renesas@gmail.com Cc: Jaehoon Chung jh80.chung@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com --- drivers/mmc/uniphier-sd.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+)
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 4456d0ed43..bb52315ec9 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -143,6 +143,8 @@ struct uniphier_sd_priv { #endif };
+static void uniphier_sd_reset_tuning(struct uniphier_sd_priv *priv); + static u64 uniphier_sd_readq(struct uniphier_sd_priv *priv, unsigned int reg) { if (priv->caps & UNIPHIER_SD_CAP_64BIT) @@ -596,6 +598,7 @@ static int uniphier_sd_set_bus_width(struct uniphier_sd_priv *priv, u32 val, tmp;
switch (mmc->bus_width) { + case 0: case 1: val = UNIPHIER_SD_OPTION_WIDTH_1; break; @@ -694,6 +697,7 @@ static void uniphier_sd_set_pins(struct udevice *dev) regulator_set_value(priv->vqmmc_dev, 1800000); else regulator_set_value(priv->vqmmc_dev, 3300000); + regulator_set_enable(priv->vqmmc_dev, true); }
if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) @@ -718,6 +722,9 @@ static int uniphier_sd_set_ios(struct udevice *dev) uniphier_sd_set_clk_rate(priv, mmc); uniphier_sd_set_pins(dev);
+ if (priv->caps & UNIPHIER_SD_CAP_RCAR_UHS) + uniphier_sd_reset_tuning(priv); + return 0; }
@@ -732,10 +739,280 @@ static int uniphier_sd_get_cd(struct udevice *dev) UNIPHIER_SD_INFO1_CD); }
+/* + * Renesas RCar SDR104 / HS200 + */ + +/* SCC registers */ +#define SH_MOBILE_SDHI_SCC_DTCNTL 0x800 +#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0) +#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 +#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff +#define SH_MOBILE_SDHI_SCC_TAPSET 0x804 +#define SH_MOBILE_SDHI_SCC_DT2FF 0x808 +#define SH_MOBILE_SDHI_SCC_CKSEL 0x80c +#define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL BIT(0) +#define SH_MOBILE_SDHI_SCC_RVSCNTL 0x810 +#define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0) +#define SH_MOBILE_SDHI_SCC_RVSREQ 0x814 +#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) +#define SH_MOBILE_SDHI_SCC_SMPCMP 0x818 +#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x81c + +#define SH_MOBILE_SDHI_MAX_TAP 3 + +static unsigned int uniphier_sd_init_tuning(struct uniphier_sd_priv *priv) +{ + u32 reg; + + /* Initialize SCC */ + uniphier_sd_writel(priv, 0, UNIPHIER_SD_INFO1); + + reg = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); + reg &= ~UNIPHIER_SD_CLKCTL_SCLKEN; + uniphier_sd_writel(priv, reg, UNIPHIER_SD_CLKCTL); + + /* Set sampling clock selection range */ + uniphier_sd_writel(priv, 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT, + SH_MOBILE_SDHI_SCC_DTCNTL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_DTCNTL); + reg |= SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_DTCNTL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_CKSEL); + reg |= SH_MOBILE_SDHI_SCC_CKSEL_DTSEL; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_CKSEL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_RVSCNTL); + reg &= ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_RVSCNTL); + + uniphier_sd_writel(priv, 0x300 /* scc_tappos */, + SH_MOBILE_SDHI_SCC_DT2FF); + + reg = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); + reg |= UNIPHIER_SD_CLKCTL_SCLKEN; + uniphier_sd_writel(priv, reg, UNIPHIER_SD_CLKCTL); + + /* Read TAPNUM */ + return (uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_DTCNTL) >> + SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & + SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK; +} + +static void uniphier_sd_reset_tuning(struct uniphier_sd_priv *priv) +{ + u32 reg; + + /* Reset SCC */ + reg = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); + reg &= ~UNIPHIER_SD_CLKCTL_SCLKEN; + uniphier_sd_writel(priv, reg, UNIPHIER_SD_CLKCTL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_CKSEL); + reg &= ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_CKSEL); + + reg = uniphier_sd_readl(priv, UNIPHIER_SD_CLKCTL); + reg |= UNIPHIER_SD_CLKCTL_SCLKEN; + uniphier_sd_writel(priv, reg, UNIPHIER_SD_CLKCTL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_RVSCNTL); + reg &= ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_RVSCNTL); + + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_RVSCNTL); + reg &= ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_RVSCNTL); +} + +static void uniphier_sd_prepare_tuning(struct uniphier_sd_priv *priv, + unsigned long tap) +{ + /* Set sampling clock position */ + uniphier_sd_writel(priv, tap, SH_MOBILE_SDHI_SCC_TAPSET); +} + +static unsigned int sh_mobile_sdhi_compare_scc_data(struct uniphier_sd_priv *priv) +{ + /* Get comparison of sampling data */ + return uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_SMPCMP); +} + +static int uniphier_sd_select_tuning(struct uniphier_sd_priv *priv, + unsigned int tap_num, unsigned int taps, + 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 */ + unsigned long match_cnt;/* counter of matching data */ + unsigned long i; + bool select = false; + u32 reg; + + /* Clear SCC_RVSREQ */ + uniphier_sd_writel(priv, 0, SH_MOBILE_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 SH_MOBILE_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 >= SH_MOBILE_SDHI_MAX_TAP) + select = true; + + if (select) + tap_set = ((tap_start + tap_end) / 2) % tap_num; + else + return -EIO; + + /* Set SCC */ + uniphier_sd_writel(priv, tap_set, SH_MOBILE_SDHI_SCC_TAPSET); + + /* Enable auto re-tuning */ + reg = uniphier_sd_readl(priv, SH_MOBILE_SDHI_SCC_RVSCNTL); + reg |= SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN; + uniphier_sd_writel(priv, reg, SH_MOBILE_SDHI_SCC_RVSCNTL); + + return 0; +} + +static int uniphier_sd_execute_tuning(struct udevice *dev, uint opcode) +{ + struct uniphier_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 & UNIPHIER_SD_CAP_RCAR_UHS)) + return -EINVAL; + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + (mmc->selected_mode == UHS_SDR50))) + return 0; + + tap_num = uniphier_sd_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++) { + uniphier_sd_prepare_tuning(priv, i % tap_num); + + /* Force PIO for the tuning */ + caps = priv->caps; + priv->caps &= ~UNIPHIER_SD_CAP_DMA_INTERNAL; + + ret = mmc_send_tuning(mmc, opcode, NULL); + + priv->caps = caps; + + if (ret == 0) + taps |= BIT(i); + + ret = sh_mobile_sdhi_compare_scc_data(priv); + if (ret == 0) + smpcmp |= BIT(i); + + mdelay(1); + } + + ret = uniphier_sd_select_tuning(priv, tap_num, taps, smpcmp); + +out: + if (ret < 0) { + dev_warn(dev, "Tuning procedure failed\n"); + uniphier_sd_reset_tuning(priv); + } + + return ret; +} + static const struct dm_mmc_ops uniphier_sd_ops = { .send_cmd = uniphier_sd_send_cmd, .set_ios = uniphier_sd_set_ios, .get_cd = uniphier_sd_get_cd, + .execute_tuning = uniphier_sd_execute_tuning, };
static void uniphier_sd_host_init(struct uniphier_sd_priv *priv) @@ -852,6 +1129,9 @@ static int uniphier_sd_probe(struct udevice *dev)
upriv->mmc = &plat->mmc;
+ if (priv->caps & UNIPHIER_SD_CAP_RCAR_UHS) + uniphier_sd_reset_tuning(priv); + return 0; }
participants (2)
-
Marek Vasut
-
Masahiro Yamada