You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
17 KiB
594 lines
17 KiB
From 4215d5757595e7ec7ca146c2b901beb177f415d8 Mon Sep 17 00:00:00 2001
|
|
From: Yangbo Lu <yangbo.lu@nxp.com>
|
|
Date: Wed, 17 Jan 2018 15:37:13 +0800
|
|
Subject: [PATCH 24/30] mmc: layerscape support
|
|
|
|
This is an integrated patch for layerscape mmc support.
|
|
|
|
Adrian Hunter <adrian.hunter@intel.com>
|
|
Jaehoon Chung <jh80.chung@samsung.com>
|
|
Masahiro Yamada <yamada.masahiro@socionext.com>
|
|
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
|
---
|
|
drivers/mmc/host/Kconfig | 1 +
|
|
drivers/mmc/host/sdhci-esdhc.h | 52 +++++---
|
|
drivers/mmc/host/sdhci-of-esdhc.c | 265 ++++++++++++++++++++++++++++++++++++--
|
|
drivers/mmc/host/sdhci.c | 45 ++++---
|
|
drivers/mmc/host/sdhci.h | 3 +
|
|
5 files changed, 320 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,12 +466,15 @@ 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)
|
|
@@ -454,9 +502,15 @@ static void esdhc_of_set_clock(struct sd
|
|
clock -= 5000000;
|
|
}
|
|
|
|
+ /* Workaround to reduce the clock frequency for ls1021a esdhc */
|
|
+ if (of_find_compatible_node(NULL, NULL, "fsl,ls1021a-esdhc")) {
|
|
+ if (clock == 50000000)
|
|
+ clock = 46500000;
|
|
+ }
|
|
+
|
|
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)
|
|
@@ -476,7 +530,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)
|
|
@@ -501,12 +568,136 @@ 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)
|
|
{
|
|
+ u32 val;
|
|
+
|
|
sdhci_reset(host, mask);
|
|
|
|
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
|
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
|
+
|
|
+ if (mask & SDHCI_RESET_ALL) {
|
|
+ val = sdhci_readl(host, ESDHC_TBCTL);
|
|
+ val &= ~ESDHC_TB_EN;
|
|
+ sdhci_writel(host, val, ESDHC_TBCTL);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* 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
|
|
@@ -589,10 +780,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);
|
|
@@ -602,6 +802,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)
|
|
@@ -624,6 +854,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
|
|
@@ -1631,26 +1631,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);
|
|
/*
|
|
@@ -1963,7 +1961,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;
|
|
@@ -2022,6 +2020,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)
|
|
@@ -2134,9 +2135,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);
|
|
|
|
/*
|
|
@@ -2172,6 +2174,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,
|
|
@@ -3004,6 +3007,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);
|
|
|