Commit 19c3a0ef authored by yangbo lu's avatar yangbo lu Committed by Ulf Hansson

mmc: sdhci-of-esdhc: add peripheral clock support

eSDHC could select peripheral clock or platform clock as clock source by
the PCS bit of eSDHC Control Register, and this bit couldn't be reset by
software reset for all. In default, the platform clock is used. But we have
to use peripheral clock since it has a higher frequency to support eMMC
HS200 mode and SD UHS-I mode. This patch is to add peripheral clock support
and use it instead of platform clock if it's declared in eSDHC dts node.
Signed-off-by: default avatarYangbo Lu <yangbo.lu@nxp.com>
Acked-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent a72016a4
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
/* Control Register for DMA transfer */ /* Control Register for DMA transfer */
#define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_PERIPHERAL_CLK_SEL 0x00080000
#define ESDHC_DMA_SNOOP 0x00000040 #define ESDHC_DMA_SNOOP 0x00000040
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
#include <linux/clk.h>
#include <linux/ktime.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "sdhci-esdhc.h" #include "sdhci-esdhc.h"
...@@ -30,6 +32,7 @@ struct sdhci_esdhc { ...@@ -30,6 +32,7 @@ struct sdhci_esdhc {
u8 vendor_ver; u8 vendor_ver;
u8 spec_ver; u8 spec_ver;
bool quirk_incorrect_hostver; bool quirk_incorrect_hostver;
unsigned int peripheral_clock;
}; };
/** /**
...@@ -414,15 +417,25 @@ static int esdhc_of_enable_dma(struct sdhci_host *host) ...@@ -414,15 +417,25 @@ static int esdhc_of_enable_dma(struct sdhci_host *host)
static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host) static unsigned int esdhc_of_get_max_clock(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
if (esdhc->peripheral_clock)
return esdhc->peripheral_clock;
else
return pltfm_host->clock; return pltfm_host->clock;
} }
static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
...@@ -512,6 +525,33 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) ...@@ -512,6 +525,33 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
sdhci_writel(host, ctrl, ESDHC_PROCTL); 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) static void esdhc_reset(struct sdhci_host *host, u8 mask)
{ {
sdhci_reset(host, mask); sdhci_reset(host, mask);
...@@ -613,6 +653,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) ...@@ -613,6 +653,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct sdhci_esdhc *esdhc; struct sdhci_esdhc *esdhc;
struct device_node *np;
struct clk *clk;
u32 val;
u16 host_ver; u16 host_ver;
pltfm_host = sdhci_priv(host); pltfm_host = sdhci_priv(host);
...@@ -626,6 +669,32 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) ...@@ -626,6 +669,32 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
esdhc->quirk_incorrect_hostver = true; esdhc->quirk_incorrect_hostver = true;
else else
esdhc->quirk_incorrect_hostver = false; 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) static int sdhci_esdhc_probe(struct platform_device *pdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment