123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- From b31046c51c72232363711f0c623df08bf28c37e4 Mon Sep 17 00:00:00 2001
- From: Yangbo Lu <[email protected]>
- Date: Mon, 25 Sep 2017 12:21:30 +0800
- Subject: [PATCH] mmc: layerscape support
- This is a integrated patch for layerscape mmc support.
- Adrian Hunter <[email protected]>
- Jaehoon Chung <[email protected]>
- Masahiro Yamada <[email protected]>
- Signed-off-by: Yangbo Lu <[email protected]>
- ---
- drivers/mmc/host/Kconfig | 1 +
- drivers/mmc/host/sdhci-esdhc.h | 52 +++++---
- drivers/mmc/host/sdhci-of-esdhc.c | 251 ++++++++++++++++++++++++++++++++++++--
- drivers/mmc/host/sdhci.c | 45 ++++---
- drivers/mmc/host/sdhci.h | 3 +
- 5 files changed, 306 insertions(+), 46 deletions(-)
- --- a/drivers/mmc/host/Kconfig
- +++ b/drivers/mmc/host/Kconfig
- @@ -144,6 +144,7 @@ config MMC_SDHCI_OF_ESDHC
- depends on MMC_SDHCI_PLTFM
- depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE
- select MMC_SDHCI_IO_ACCESSORS
- + select FSL_GUTS
- help
- This selects the Freescale eSDHC controller support.
-
- --- a/drivers/mmc/host/sdhci-esdhc.h
- +++ b/drivers/mmc/host/sdhci-esdhc.h
- @@ -24,30 +24,46 @@
- SDHCI_QUIRK_PIO_NEEDS_DELAY | \
- SDHCI_QUIRK_NO_HISPD_BIT)
-
- -#define ESDHC_PROCTL 0x28
- -
- -#define ESDHC_SYSTEM_CONTROL 0x2c
- -#define ESDHC_CLOCK_MASK 0x0000fff0
- -#define ESDHC_PREDIV_SHIFT 8
- -#define ESDHC_DIVIDER_SHIFT 4
- -#define ESDHC_CLOCK_PEREN 0x00000004
- -#define ESDHC_CLOCK_HCKEN 0x00000002
- -#define ESDHC_CLOCK_IPGEN 0x00000001
- -
- /* pltfm-specific */
- #define ESDHC_HOST_CONTROL_LE 0x20
-
- /*
- - * P2020 interpretation of the SDHCI_HOST_CONTROL register
- + * eSDHC register definition
- */
- -#define ESDHC_CTRL_4BITBUS (0x1 << 1)
- -#define ESDHC_CTRL_8BITBUS (0x2 << 1)
- -#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
- -
- -/* OF-specific */
- -#define ESDHC_DMA_SYSCTL 0x40c
- -#define ESDHC_DMA_SNOOP 0x00000040
-
- -#define ESDHC_HOST_CONTROL_RES 0x01
- +/* Present State Register */
- +#define ESDHC_PRSSTAT 0x24
- +#define ESDHC_CLOCK_STABLE 0x00000008
- +
- +/* Protocol Control Register */
- +#define ESDHC_PROCTL 0x28
- +#define ESDHC_VOLT_SEL 0x00000400
- +#define ESDHC_CTRL_4BITBUS (0x1 << 1)
- +#define ESDHC_CTRL_8BITBUS (0x2 << 1)
- +#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
- +#define ESDHC_HOST_CONTROL_RES 0x01
- +
- +/* System Control Register */
- +#define ESDHC_SYSTEM_CONTROL 0x2c
- +#define ESDHC_CLOCK_MASK 0x0000fff0
- +#define ESDHC_PREDIV_SHIFT 8
- +#define ESDHC_DIVIDER_SHIFT 4
- +#define ESDHC_CLOCK_SDCLKEN 0x00000008
- +#define ESDHC_CLOCK_PEREN 0x00000004
- +#define ESDHC_CLOCK_HCKEN 0x00000002
- +#define ESDHC_CLOCK_IPGEN 0x00000001
- +
- +/* Host Controller Capabilities Register 2 */
- +#define ESDHC_CAPABILITIES_1 0x114
- +
- +/* Tuning Block Control Register */
- +#define ESDHC_TBCTL 0x120
- +#define ESDHC_TB_EN 0x00000004
- +
- +/* Control Register for DMA transfer */
- +#define ESDHC_DMA_SYSCTL 0x40c
- +#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000
- +#define ESDHC_FLUSH_ASYNC_FIFO 0x00040000
- +#define ESDHC_DMA_SNOOP 0x00000040
-
- #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
- --- a/drivers/mmc/host/sdhci-of-esdhc.c
- +++ b/drivers/mmc/host/sdhci-of-esdhc.c
- @@ -16,8 +16,12 @@
- #include <linux/err.h>
- #include <linux/io.h>
- #include <linux/of.h>
- +#include <linux/of_address.h>
- #include <linux/delay.h>
- #include <linux/module.h>
- +#include <linux/sys_soc.h>
- +#include <linux/clk.h>
- +#include <linux/ktime.h>
- #include <linux/mmc/host.h>
- #include "sdhci-pltfm.h"
- #include "sdhci-esdhc.h"
- @@ -28,8 +32,12 @@
- struct sdhci_esdhc {
- u8 vendor_ver;
- u8 spec_ver;
- + bool quirk_incorrect_hostver;
- + unsigned int peripheral_clock;
- };
-
- +static void esdhc_clock_enable(struct sdhci_host *host, bool enable);
- +
- /**
- * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
- * to make it compatible with SD spec.
- @@ -80,6 +88,17 @@ static u32 esdhc_readl_fixup(struct sdhc
- return ret;
- }
-
- + /*
- + * DTS properties of mmc host are used to enable each speed mode
- + * according to soc and board capability. So clean up
- + * SDR50/SDR104/DDR50 support bits here.
- + */
- + if (spec_reg == SDHCI_CAPABILITIES_1) {
- + ret = value & (~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
- + SDHCI_SUPPORT_DDR50));
- + return ret;
- + }
- +
- ret = value;
- return ret;
- }
- @@ -87,6 +106,8 @@ static u32 esdhc_readl_fixup(struct sdhc
- static u16 esdhc_readw_fixup(struct sdhci_host *host,
- int spec_reg, u32 value)
- {
- + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
- u16 ret;
- int shift = (spec_reg & 0x2) * 8;
-
- @@ -94,6 +115,12 @@ static u16 esdhc_readw_fixup(struct sdhc
- ret = value & 0xffff;
- else
- ret = (value >> shift) & 0xffff;
- + /* Workaround for T4240-R1.0-R2.0 eSDHC which has incorrect
- + * vendor version and spec version information.
- + */
- + if ((spec_reg == SDHCI_HOST_VERSION) &&
- + (esdhc->quirk_incorrect_hostver))
- + ret = (VENDOR_V_23 << SDHCI_VENDOR_VER_SHIFT) | SDHCI_SPEC_200;
- return ret;
- }
-
- @@ -235,7 +262,11 @@ static u32 esdhc_be_readl(struct sdhci_h
- u32 ret;
- u32 value;
-
- - value = ioread32be(host->ioaddr + reg);
- + if (reg == SDHCI_CAPABILITIES_1)
- + value = ioread32be(host->ioaddr + ESDHC_CAPABILITIES_1);
- + else
- + value = ioread32be(host->ioaddr + reg);
- +
- ret = esdhc_readl_fixup(host, reg, value);
-
- return ret;
- @@ -246,7 +277,11 @@ static u32 esdhc_le_readl(struct sdhci_h
- u32 ret;
- u32 value;
-
- - value = ioread32(host->ioaddr + reg);
- + if (reg == SDHCI_CAPABILITIES_1)
- + value = ioread32(host->ioaddr + ESDHC_CAPABILITIES_1);
- + else
- + value = ioread32(host->ioaddr + reg);
- +
- ret = esdhc_readl_fixup(host, reg, value);
-
- return ret;
- @@ -404,15 +439,25 @@ static int esdhc_of_enable_dma(struct sd
- static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host)
- {
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
-
- - return pltfm_host->clock;
- + if (esdhc->peripheral_clock)
- + return esdhc->peripheral_clock;
- + else
- + return pltfm_host->clock;
- }
-
- static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
- {
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- + struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
- + unsigned int clock;
-
- - return pltfm_host->clock / 256 / 16;
- + if (esdhc->peripheral_clock)
- + clock = esdhc->peripheral_clock;
- + else
- + clock = pltfm_host->clock;
- + return clock / 256 / 16;
- }
-
- static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
- @@ -421,17 +466,34 @@ static void esdhc_of_set_clock(struct sd
- struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
- int pre_div = 1;
- int div = 1;
- + ktime_t timeout;
- u32 temp;
-
- host->mmc->actual_clock = 0;
-
- - if (clock == 0)
- + if (clock == 0) {
- + esdhc_clock_enable(host, false);
- return;
- + }
-
- /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
- if (esdhc->vendor_ver < VENDOR_V_23)
- pre_div = 2;
-
- + /*
- + * Limit SD clock to 167MHz for ls1046a according to its datasheet
- + */
- + if (clock > 167000000 &&
- + of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc"))
- + clock = 167000000;
- +
- + /*
- + * Limit SD clock to 125MHz for ls1012a according to its datasheet
- + */
- + if (clock > 125000000 &&
- + of_find_compatible_node(NULL, NULL, "fsl,ls1012a-esdhc"))
- + clock = 125000000;
- +
- /* Workaround to reduce the clock frequency for p1010 esdhc */
- if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
- if (clock > 20000000)
- @@ -441,8 +503,8 @@ static void esdhc_of_set_clock(struct sd
- }
-
- temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
- - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
- - | ESDHC_CLOCK_MASK);
- + temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
- + ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
- sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
-
- while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
- @@ -462,7 +524,20 @@ static void esdhc_of_set_clock(struct sd
- | (div << ESDHC_DIVIDER_SHIFT)
- | (pre_div << ESDHC_PREDIV_SHIFT));
- sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- - mdelay(1);
- +
- + /* Wait max 20 ms */
- + timeout = ktime_add_ms(ktime_get(), 20);
- + while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
- + if (ktime_after(ktime_get(), timeout)) {
- + pr_err("%s: Internal clock never stabilised.\n",
- + mmc_hostname(host->mmc));
- + return;
- + }
- + udelay(10);
- + }
- +
- + temp |= ESDHC_CLOCK_SDCLKEN;
- + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- }
-
- static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
- @@ -487,6 +562,33 @@ static void esdhc_pltfm_set_bus_width(st
- sdhci_writel(host, ctrl, ESDHC_PROCTL);
- }
-
- +static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
- +{
- + u32 val;
- + ktime_t timeout;
- +
- + val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
- +
- + if (enable)
- + val |= ESDHC_CLOCK_SDCLKEN;
- + else
- + val &= ~ESDHC_CLOCK_SDCLKEN;
- +
- + sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
- +
- + /* Wait max 20 ms */
- + timeout = ktime_add_ms(ktime_get(), 20);
- + val = ESDHC_CLOCK_STABLE;
- + while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) {
- + if (ktime_after(ktime_get(), timeout)) {
- + pr_err("%s: Internal clock never stabilised.\n",
- + mmc_hostname(host->mmc));
- + break;
- + }
- + udelay(10);
- + }
- +}
- +
- static void esdhc_reset(struct sdhci_host *host, u8 mask)
- {
- sdhci_reset(host, mask);
- @@ -495,6 +597,95 @@ static void esdhc_reset(struct sdhci_hos
- sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
- }
-
- +/* The SCFG, Supplemental Configuration Unit, provides SoC specific
- + * configuration and status registers for the device. There is a
- + * SDHC IO VSEL control register on SCFG for some platforms. It's
- + * used to support SDHC IO voltage switching.
- + */
- +static const struct of_device_id scfg_device_ids[] = {
- + { .compatible = "fsl,t1040-scfg", },
- + { .compatible = "fsl,ls1012a-scfg", },
- + { .compatible = "fsl,ls1046a-scfg", },
- + {}
- +};
- +
- +/* SDHC IO VSEL control register definition */
- +#define SCFG_SDHCIOVSELCR 0x408
- +#define SDHCIOVSELCR_TGLEN 0x80000000
- +#define SDHCIOVSELCR_VSELVAL 0x60000000
- +#define SDHCIOVSELCR_SDHC_VS 0x00000001
- +
- +static int esdhc_signal_voltage_switch(struct mmc_host *mmc,
- + struct mmc_ios *ios)
- +{
- + struct sdhci_host *host = mmc_priv(mmc);
- + struct device_node *scfg_node;
- + void __iomem *scfg_base = NULL;
- + u32 sdhciovselcr;
- + u32 val;
- +
- + /*
- + * Signal Voltage Switching is only applicable for Host Controllers
- + * v3.00 and above.
- + */
- + if (host->version < SDHCI_SPEC_300)
- + return 0;
- +
- + val = sdhci_readl(host, ESDHC_PROCTL);
- +
- + switch (ios->signal_voltage) {
- + case MMC_SIGNAL_VOLTAGE_330:
- + val &= ~ESDHC_VOLT_SEL;
- + sdhci_writel(host, val, ESDHC_PROCTL);
- + return 0;
- + case MMC_SIGNAL_VOLTAGE_180:
- + scfg_node = of_find_matching_node(NULL, scfg_device_ids);
- + if (scfg_node)
- + scfg_base = of_iomap(scfg_node, 0);
- + if (scfg_base) {
- + sdhciovselcr = SDHCIOVSELCR_TGLEN |
- + SDHCIOVSELCR_VSELVAL;
- + iowrite32be(sdhciovselcr,
- + scfg_base + SCFG_SDHCIOVSELCR);
- +
- + val |= ESDHC_VOLT_SEL;
- + sdhci_writel(host, val, ESDHC_PROCTL);
- + mdelay(5);
- +
- + sdhciovselcr = SDHCIOVSELCR_TGLEN |
- + SDHCIOVSELCR_SDHC_VS;
- + iowrite32be(sdhciovselcr,
- + scfg_base + SCFG_SDHCIOVSELCR);
- + iounmap(scfg_base);
- + } else {
- + val |= ESDHC_VOLT_SEL;
- + sdhci_writel(host, val, ESDHC_PROCTL);
- + }
- + return 0;
- + default:
- + return 0;
- + }
- +}
- +
- +static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
- +{
- + struct sdhci_host *host = mmc_priv(mmc);
- + u32 val;
- +
- + /* Use tuning block for tuning procedure */
- + esdhc_clock_enable(host, false);
- + val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
- + val |= ESDHC_FLUSH_ASYNC_FIFO;
- + sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
- +
- + val = sdhci_readl(host, ESDHC_TBCTL);
- + val |= ESDHC_TB_EN;
- + sdhci_writel(host, val, ESDHC_TBCTL);
- + esdhc_clock_enable(host, true);
- +
- + return sdhci_execute_tuning(mmc, opcode);
- +}
- +
- #ifdef CONFIG_PM_SLEEP
- static u32 esdhc_proctl;
- static int esdhc_of_suspend(struct device *dev)
- @@ -575,10 +766,19 @@ static const struct sdhci_pltfm_data sdh
- .ops = &sdhci_esdhc_le_ops,
- };
-
- +static struct soc_device_attribute soc_incorrect_hostver[] = {
- + { .family = "QorIQ T4240", .revision = "1.0", },
- + { .family = "QorIQ T4240", .revision = "2.0", },
- + { },
- +};
- +
- static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
- {
- struct sdhci_pltfm_host *pltfm_host;
- struct sdhci_esdhc *esdhc;
- + struct device_node *np;
- + struct clk *clk;
- + u32 val;
- u16 host_ver;
-
- pltfm_host = sdhci_priv(host);
- @@ -588,6 +788,36 @@ static void esdhc_init(struct platform_d
- esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
- SDHCI_VENDOR_VER_SHIFT;
- esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
- + if (soc_device_match(soc_incorrect_hostver))
- + esdhc->quirk_incorrect_hostver = true;
- + else
- + esdhc->quirk_incorrect_hostver = false;
- +
- + np = pdev->dev.of_node;
- + clk = of_clk_get(np, 0);
- + if (!IS_ERR(clk)) {
- + /*
- + * esdhc->peripheral_clock would be assigned with a value
- + * which is eSDHC base clock when use periperal clock.
- + * For ls1046a, the clock value got by common clk API is
- + * peripheral clock while the eSDHC base clock is 1/2
- + * peripheral clock.
- + */
- + if (of_device_is_compatible(np, "fsl,ls1046a-esdhc"))
- + esdhc->peripheral_clock = clk_get_rate(clk) / 2;
- + else
- + esdhc->peripheral_clock = clk_get_rate(clk);
- +
- + clk_put(clk);
- + }
- +
- + if (esdhc->peripheral_clock) {
- + esdhc_clock_enable(host, false);
- + val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
- + val |= ESDHC_PERIPHERAL_CLK_SEL;
- + sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
- + esdhc_clock_enable(host, true);
- + }
- }
-
- static int sdhci_esdhc_probe(struct platform_device *pdev)
- @@ -610,6 +840,11 @@ static int sdhci_esdhc_probe(struct plat
- if (IS_ERR(host))
- return PTR_ERR(host);
-
- + host->mmc_host_ops.start_signal_voltage_switch =
- + esdhc_signal_voltage_switch;
- + host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
- + host->tuning_delay = 1;
- +
- esdhc_init(pdev, host);
-
- sdhci_get_of_property(pdev);
- --- a/drivers/mmc/host/sdhci.c
- +++ b/drivers/mmc/host/sdhci.c
- @@ -1624,26 +1624,24 @@ static void sdhci_set_ios(struct mmc_hos
-
- ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
-
- - if ((ios->timing == MMC_TIMING_SD_HS ||
- - ios->timing == MMC_TIMING_MMC_HS)
- - && !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
- - ctrl |= SDHCI_CTRL_HISPD;
- - else
- - ctrl &= ~SDHCI_CTRL_HISPD;
- + if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) {
- + if ((ios->timing == MMC_TIMING_SD_HS ||
- + ios->timing == MMC_TIMING_MMC_HS ||
- + ios->timing == MMC_TIMING_MMC_HS400 ||
- + ios->timing == MMC_TIMING_MMC_HS200 ||
- + ios->timing == MMC_TIMING_MMC_DDR52 ||
- + ios->timing == MMC_TIMING_UHS_SDR50 ||
- + ios->timing == MMC_TIMING_UHS_SDR104 ||
- + ios->timing == MMC_TIMING_UHS_DDR50 ||
- + ios->timing == MMC_TIMING_UHS_SDR25))
- + ctrl |= SDHCI_CTRL_HISPD;
- + else
- + ctrl &= ~SDHCI_CTRL_HISPD;
- + }
-
- if (host->version >= SDHCI_SPEC_300) {
- u16 clk, ctrl_2;
-
- - /* In case of UHS-I modes, set High Speed Enable */
- - if ((ios->timing == MMC_TIMING_MMC_HS400) ||
- - (ios->timing == MMC_TIMING_MMC_HS200) ||
- - (ios->timing == MMC_TIMING_MMC_DDR52) ||
- - (ios->timing == MMC_TIMING_UHS_SDR50) ||
- - (ios->timing == MMC_TIMING_UHS_SDR104) ||
- - (ios->timing == MMC_TIMING_UHS_DDR50) ||
- - (ios->timing == MMC_TIMING_UHS_SDR25))
- - ctrl |= SDHCI_CTRL_HISPD;
- -
- if (!host->preset_enabled) {
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
- /*
- @@ -1956,7 +1954,7 @@ static int sdhci_prepare_hs400_tuning(st
- return 0;
- }
-
- -static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
- +int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
- {
- struct sdhci_host *host = mmc_priv(mmc);
- u16 ctrl;
- @@ -2015,6 +2013,9 @@ static int sdhci_execute_tuning(struct m
- return err;
- }
-
- + if (host->tuning_delay < 0)
- + host->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
- +
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- ctrl |= SDHCI_CTRL_EXEC_TUNING;
- if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND)
- @@ -2127,9 +2128,10 @@ static int sdhci_execute_tuning(struct m
-
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
-
- - /* eMMC spec does not require a delay between tuning cycles */
- - if (opcode == MMC_SEND_TUNING_BLOCK)
- - mdelay(1);
- + /* Spec does not require a delay between tuning cycles */
- + if (host->tuning_delay > 0)
- + mdelay(host->tuning_delay);
- +
- } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
-
- /*
- @@ -2165,6 +2167,7 @@ out_unlock:
- spin_unlock_irqrestore(&host->lock, flags);
- return err;
- }
- +EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
-
- static int sdhci_select_drive_strength(struct mmc_card *card,
- unsigned int max_dtr, int host_drv,
- @@ -2997,6 +3000,8 @@ struct sdhci_host *sdhci_alloc_host(stru
-
- host->flags = SDHCI_SIGNALING_330;
-
- + host->tuning_delay = -1;
- +
- return host;
- }
-
- --- a/drivers/mmc/host/sdhci.h
- +++ b/drivers/mmc/host/sdhci.h
- @@ -524,6 +524,8 @@ struct sdhci_host {
- #define SDHCI_TUNING_MODE_1 0
- #define SDHCI_TUNING_MODE_2 1
- #define SDHCI_TUNING_MODE_3 2
- + /* Delay (ms) between tuning commands */
- + int tuning_delay;
-
- unsigned long private[0] ____cacheline_aligned;
- };
- @@ -689,6 +691,7 @@ void sdhci_set_power_noreg(struct sdhci_
- void sdhci_set_bus_width(struct sdhci_host *host, int width);
- void sdhci_reset(struct sdhci_host *host, u8 mask);
- void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
- +int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
-
- #ifdef CONFIG_PM
- extern int sdhci_suspend_host(struct sdhci_host *host);
|