From 9abf648436b49553e6cb4a7f03a021720616d75b Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 19 May 2020 11:06:43 +0800 Subject: [PATCH 1/6] mmc: fsl_esdhc: read register once for card inserted status No need to poll register for card inserted status. Signed-off-by: Yangbo Lu Acked-by: Peng Fan --- drivers/mmc/fsl_esdhc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index d5d955846c..704407981b 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc - * Copyright 2019 NXP Semiconductors + * Copyright 2019-2020 NXP * Andy Fleming * * Based vaguely on the pxa mmc code: @@ -630,16 +630,15 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) static int esdhc_getcd_common(struct fsl_esdhc_priv *priv) { struct fsl_esdhc *regs = priv->esdhc_regs; - int timeout = 1000; #ifdef CONFIG_ESDHC_DETECT_QUIRK if (CONFIG_ESDHC_DETECT_QUIRK) return 1; #endif - while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) - udelay(1000); + if (esdhc_read32(®s->prsstat) & PRSSTAT_CINS) + return 1; - return timeout > 0; + return 0; } static void fsl_esdhc_get_cfg_common(struct fsl_esdhc_priv *priv, From c927d65873c7706f436ab59a78d44a0eb80fe939 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 19 May 2020 11:06:44 +0800 Subject: [PATCH 2/6] mmc: fsl_esdhc: workaround for hardware 3.3v IO reliability issue When eSDHC operates at 3.3v, damage can accumulate in an internal level shifter at a higher than expected rate. The faster the interface runs, the more damage accumulates. This issue now is found on LX2160A eSDHC1 for only SD card. The hardware workaround is recommended to use an on-board level shifter that is 1.8v on SoC side and 3.3v on SD card side. For boards without hardware workaround, this option could be enabled, ensuring 1.8v IO voltage and disabling eSDHC if no card. This option assumes no hotplug, and u-boot has to make all the way to to linux to use 1.8v UHS-I speed mode if has card. If you do not want the workaround for better user experience, of course you can choose to not select it running eSDHC in unsafe mode. Signed-off-by: Yangbo Lu Acked-by: Peng Fan --- drivers/mmc/Kconfig | 15 +++++++++++++++ drivers/mmc/fsl_esdhc.c | 38 ++++++++++++++++++++++++++++++++++++-- include/fsl_esdhc.h | 2 ++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 3c4f057373..8f56572c39 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -727,6 +727,21 @@ config FSL_ESDHC This selects support for the eSDHC (Enhanced Secure Digital Host Controller) found on numerous Freescale/NXP SoCs. +config FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND + bool "enable eSDHC workaround for 3.3v IO reliability issue" + depends on FSL_ESDHC && DM_MMC + default n + help + When eSDHC operates at 3.3v, damage can accumulate in an internal + level shifter at a higher than expected rate. The faster the interface + runs, the more damage accumulates. This issue now is found on LX2160A + eSDHC1 for only SD card. The hardware workaround is recommended to use + an on-board level shifter that is 1.8v on SoC side and 3.3v on SD card + side. For boards without hardware workaround, this option could be + enabled, ensuring 1.8v IO voltage and disabling eSDHC if no card. + This option assumes no hotplug, and u-boot has to make all the way to + to linux to use 1.8v UHS-I speed mode if has card. + config FSL_ESDHC_IMX bool "Freescale/NXP i.MX eSDHC controller support" help diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 704407981b..a4b923ab99 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -723,13 +723,38 @@ __weak int esdhc_status_fixup(void *blob, const char *compat) return 0; } +#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND +static int fsl_esdhc_get_cd(struct udevice *dev); + +static void esdhc_disable_for_no_card(void *blob) +{ + struct udevice *dev; + + for (uclass_first_device(UCLASS_MMC, &dev); + dev; + uclass_next_device(&dev)) { + char esdhc_path[50]; + + if (fsl_esdhc_get_cd(dev)) + continue; + + snprintf(esdhc_path, sizeof(esdhc_path), "/soc/esdhc@%lx", + (unsigned long)dev_read_addr(dev)); + do_fixup_by_path(blob, esdhc_path, "status", "disabled", + sizeof("disabled"), 1); + } +} +#endif + void fdt_fixup_esdhc(void *blob, bd_t *bd) { const char *compat = "fsl,esdhc"; if (esdhc_status_fixup(blob, compat)) return; - +#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND + esdhc_disable_for_no_card(blob); +#endif do_fixup_by_compat_u32(blob, compat, "clock-frequency", gd->arch.sdhc_clk, 1); } @@ -848,6 +873,7 @@ static int fsl_esdhc_probe(struct udevice *dev) struct fsl_esdhc_priv *priv = dev_get_priv(dev); fdt_addr_t addr; struct mmc *mmc; + int ret; addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) @@ -881,7 +907,15 @@ static int fsl_esdhc_probe(struct udevice *dev) upriv->mmc = mmc; - return esdhc_init_common(priv, mmc); + ret = esdhc_init_common(priv, mmc); + if (ret) + return ret; + +#ifdef CONFIG_FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND + if (!fsl_esdhc_get_cd(dev)) + esdhc_setbits32(&priv->esdhc_regs->proctl, PROCTL_VOLT_SEL); +#endif + return 0; } static int fsl_esdhc_get_cd(struct udevice *dev) diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h index 8e8cd2cc1a..e148eaa880 100644 --- a/include/fsl_esdhc.h +++ b/include/fsl_esdhc.h @@ -4,6 +4,7 @@ *------------------------------------------------------------------- * * Copyright 2007-2008,2010-2011 Freescale Semiconductor, Inc + * Copyright 2020 NXP */ #ifndef __FSL_ESDHC_H__ @@ -98,6 +99,7 @@ #define PROCTL_DTW_4 0x00000002 #define PROCTL_DTW_8 0x00000004 #define PROCTL_D3CD 0x00000008 +#define PROCTL_VOLT_SEL 0x00000400 #define CMDARG 0x0002e008 From dbd8a8dfe7fa03300bd8d56b1d1e87d1089550b5 Mon Sep 17 00:00:00 2001 From: Arthur Li Date: Thu, 21 May 2020 17:24:22 -0700 Subject: [PATCH 3/6] mmc: ca_dw_mmc: Misc cleanup of driver - Rename DT compatible name - Remove uneccessary if-statement to support 8-bit buswidth - Remove redundant error msg - Use symbolic constants in switch statement Signed-off-by: Arthur Li Signed-off-by: Alex Nemirovsky CC: Peng Fan CC: Jaehoon Chung CC: Tom Rini --- drivers/mmc/ca_dw_mmc.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c index acbc850fcb..198c41f451 100644 --- a/drivers/mmc/ca_dw_mmc.c +++ b/drivers/mmc/ca_dw_mmc.c @@ -19,6 +19,7 @@ #define SD_CLK_SEL_200MHZ (0x2) #define SD_CLK_SEL_100MHZ (0x1) +#define SD_CLK_SEL_50MHZ (0x0) #define IO_DRV_SD_DS_OFFSET (16) #define IO_DRV_SD_DS_MASK (0xff << IO_DRV_SD_DS_OFFSET) @@ -44,15 +45,11 @@ static void ca_dwmci_clksel(struct dwmci_host *host) struct ca_dwmmc_priv_data *priv = host->priv; u32 val = readl(priv->sd_dll_reg); - if (host->bus_hz >= 200000000) { - val &= ~SD_CLK_SEL_MASK; + val &= ~SD_CLK_SEL_MASK; + if (host->bus_hz >= 200000000) val |= SD_CLK_SEL_200MHZ; - } else if (host->bus_hz >= 100000000) { - val &= ~SD_CLK_SEL_MASK; + else if (host->bus_hz >= 100000000) val |= SD_CLK_SEL_100MHZ; - } else { - val &= ~SD_CLK_SEL_MASK; - } writel(val, priv->sd_dll_reg); } @@ -77,14 +74,14 @@ unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq) u8 clk_div; switch (sd_clk_sel) { - case 2: - clk_div = 1; + case SD_CLK_SEL_50MHZ: + clk_div = 4; break; - case 1: + case SD_CLK_SEL_100MHZ: clk_div = 2; break; default: - clk_div = 4; + clk_div = 1; } return SD_SCLK_MAX / clk_div / (host->div + 1); @@ -100,9 +97,6 @@ static int ca_dwmmc_ofdata_to_platdata(struct udevice *dev) host->dev_index = 0; host->buswidth = dev_read_u32_default(dev, "bus-width", 1); - if (host->buswidth != 1 && host->buswidth != 4) - return -EINVAL; - host->bus_hz = dev_read_u32_default(dev, "max-frequency", 50000000); priv->ds = dev_read_u32_default(dev, "io_ds", 0x33); host->fifo_mode = dev_read_bool(dev, "fifo-mode"); @@ -118,10 +112,8 @@ static int ca_dwmmc_ofdata_to_platdata(struct udevice *dev) return -EINVAL; host->ioaddr = dev_read_addr_ptr(dev); - if (host->ioaddr == (void *)FDT_ADDR_T_NONE) { - printf("DWMMC: base address is invalid\n"); + if (!host->ioaddr) return -EINVAL; - } host->priv = priv; @@ -140,10 +132,8 @@ static int ca_dwmmc_probe(struct udevice *dev) memcpy(&ca_dwmci_dm_ops, &dm_dwmci_ops, sizeof(struct dm_mmc_ops)); dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, MIN_FREQ); - if (host->buswidth == 1) { - (&plat->cfg)->host_caps &= ~MMC_MODE_8BIT; - (&plat->cfg)->host_caps &= ~MMC_MODE_4BIT; - } + if (host->buswidth == 1) + (&plat->cfg)->host_caps &= ~(MMC_MODE_8BIT | MMC_MODE_4BIT); host->mmc = &plat->mmc; host->mmc->priv = &priv->host; @@ -164,7 +154,7 @@ static int ca_dwmmc_bind(struct udevice *dev) } static const struct udevice_id ca_dwmmc_ids[] = { - { .compatible = "snps,dw-cortina" }, + { .compatible = "cortina,ca-mmc" }, { } }; From 1ac64476440ee293e51f03d5b612b2b44b059976 Mon Sep 17 00:00:00 2001 From: Alex Nemirovsky Date: Thu, 21 May 2020 17:24:23 -0700 Subject: [PATCH 4/6] board: presidio-asic: update eMMC DT information Change DT compatibility name to match change in driver's name. Remove unused io_ds and fifo_mode fields from DT. Signed-off-by: Alex Nemirovsky CC: Peng Fan CC: Jaehoon Chung CC: Tom Rini --- arch/arm/dts/ca-presidio-engboard.dts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/dts/ca-presidio-engboard.dts b/arch/arm/dts/ca-presidio-engboard.dts index c03dacc54a..40c93d7042 100644 --- a/arch/arm/dts/ca-presidio-engboard.dts +++ b/arch/arm/dts/ca-presidio-engboard.dts @@ -10,11 +10,9 @@ #size-cells = <1>; mmc0: mmc@f4400000 { - compatible = "snps,dw-cortina"; + compatible = "cortina,ca-mmc"; reg = <0x0 0xf4400000 0x1000>; bus-width = <4>; - io_ds = <0x77>; - fifo-mode; sd_dll_ctrl = <0xf43200e8>; io_drv_ctrl = <0xf432004c>; }; From 406df85345f942d9348443983d81a01e013e920b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 22 May 2020 18:19:08 +0200 Subject: [PATCH 5/6] mmc: fsl_esdhc: Fix SDR104 and HS200 support The 3V3/1V8 switching could never have worked on any of the iMXes ever since 51313b49f2 ("mmc: fsl_esdhc: support SDR104 and HS200"), because that commit uses priv->vqmmc_dev when switching voltages on mode switch, while local vqmmc_dev in probe to store the regulator pointer. Those are two different variables with the same name. So the priv->vqmmc_dev was always NULL and thus voltage switch between modes never really suceeded. Fix this by assigning priv->vqmmc_dev with value of the vqmmc_dev in probe. Fixes: 51313b49f2 ("mmc: fsl_esdhc: support SDR104 and HS200") Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Jaehoon Chung Cc: Peng Fan Cc: Stefano Babic Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc_imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index 588d6a9d76..f3101a061a 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -1455,6 +1455,7 @@ static int fsl_esdhc_probe(struct udevice *dev) if (ret) { dev_dbg(dev, "no vqmmc-supply\n"); } else { + priv->vqmmc_dev = vqmmc_dev; ret = regulator_set_enable(vqmmc_dev, true); if (ret) { dev_err(dev, "fail to enable vqmmc-supply\n"); From 50a17a69be076316e4c539b75403acfabf9e4c4c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 22 May 2020 18:28:33 +0200 Subject: [PATCH 6/6] mmc: fsl_esdhc: Gracefully fail on unsupported voltage switch Unsupported voltage on voltage switch is not an error, do not print error message in such a case. This happens e.g. if the eMMC is already in 1V8 mode or when testing 1V2 mode operation on systems which only do 3V3/1V8 switching. Signed-off-by: Marek Vasut Cc: Fabio Estevam Cc: Jaehoon Chung Cc: Peng Fan Cc: Stefano Babic Reviewed-by: Jaehoon Chung --- drivers/mmc/fsl_esdhc_imx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index f3101a061a..f42e018434 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -791,7 +791,7 @@ static int esdhc_set_voltage(struct mmc *mmc) switch (mmc->signal_voltage) { case MMC_SIGNAL_VOLTAGE_330: if (priv->vs18_enable) - return -EIO; + return -ENOTSUPP; #if CONFIG_IS_ENABLED(DM_REGULATOR) if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) { ret = regulator_set_value(priv->vqmmc_dev, 3300000); @@ -972,7 +972,8 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) if (priv->signal_voltage != mmc->signal_voltage) { ret = esdhc_set_voltage(mmc); if (ret) { - printf("esdhc_set_voltage error %d\n", ret); + if (ret != -ENOTSUPP) + printf("esdhc_set_voltage error %d\n", ret); return ret; } }