600 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			600 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From b31046c51c72232363711f0c623df08bf28c37e4 Mon Sep 17 00:00:00 2001
 | |
| From: Yangbo Lu <yangbo.lu@nxp.com>
 | |
| 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 <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 | 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);
 | 
