Commit e87d2db2 authored by yangbo lu's avatar yangbo lu Committed by Ulf Hansson

mmc: sdhci-of-esdhc: avoid clock glitch when frequency is changing

The eSDHC_PRSSTAT[SDSTB] bit indicates whether the internal card clock is
stable. This bit is for the host driver to poll clock status when changing
the clock frequency. It is recommended to clear eSDHC_SYSCTL[SDCLKEN]
to remove glitch on the card clock when the frequency is changing. This
patch is to disable SDCLKEN bit before changing frequency and enable it
after SDSTB bit is set.
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 c31165d7
...@@ -31,6 +31,10 @@ ...@@ -31,6 +31,10 @@
* eSDHC register definition * eSDHC register definition
*/ */
/* Present State Register */
#define ESDHC_PRSSTAT 0x24
#define ESDHC_CLOCK_STABLE 0x00000008
/* Protocol Control Register */ /* Protocol Control Register */
#define ESDHC_PROCTL 0x28 #define ESDHC_PROCTL 0x28
#define ESDHC_CTRL_4BITBUS (0x1 << 1) #define ESDHC_CTRL_4BITBUS (0x1 << 1)
...@@ -43,6 +47,7 @@ ...@@ -43,6 +47,7 @@
#define ESDHC_CLOCK_MASK 0x0000fff0 #define ESDHC_CLOCK_MASK 0x0000fff0
#define ESDHC_PREDIV_SHIFT 8 #define ESDHC_PREDIV_SHIFT 8
#define ESDHC_DIVIDER_SHIFT 4 #define ESDHC_DIVIDER_SHIFT 4
#define ESDHC_CLOCK_SDCLKEN 0x00000008
#define ESDHC_CLOCK_PEREN 0x00000004 #define ESDHC_CLOCK_PEREN 0x00000004
#define ESDHC_CLOCK_HCKEN 0x00000002 #define ESDHC_CLOCK_HCKEN 0x00000002
#define ESDHC_CLOCK_IPGEN 0x00000001 #define ESDHC_CLOCK_IPGEN 0x00000001
......
...@@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
int pre_div = 1; int pre_div = 1;
int div = 1; int div = 1;
u32 timeout;
u32 temp; u32 temp;
host->mmc->actual_clock = 0; host->mmc->actual_clock = 0;
...@@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
} }
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
| ESDHC_CLOCK_MASK); ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
while (host->max_clk / pre_div / 16 > clock && pre_div < 256) while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
...@@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
| (div << ESDHC_DIVIDER_SHIFT) | (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT)); | (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
mdelay(1);
/* Wait max 20 ms */
timeout = 20;
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) {
if (timeout == 0) {
pr_err("%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc));
return;
}
timeout--;
mdelay(1);
}
temp |= ESDHC_CLOCK_SDCLKEN;
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
} }
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
......
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