Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Yangbo Lu <yangbo.lu@nxp.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

yangbo lu and committed by
Ulf Hansson
e87d2db2 c31165d7

+23 -3
+5
drivers/mmc/host/sdhci-esdhc.h
··· 31 31 * eSDHC register definition 32 32 */ 33 33 34 + /* Present State Register */ 35 + #define ESDHC_PRSSTAT 0x24 36 + #define ESDHC_CLOCK_STABLE 0x00000008 37 + 34 38 /* Protocol Control Register */ 35 39 #define ESDHC_PROCTL 0x28 36 40 #define ESDHC_CTRL_4BITBUS (0x1 << 1) ··· 47 43 #define ESDHC_CLOCK_MASK 0x0000fff0 48 44 #define ESDHC_PREDIV_SHIFT 8 49 45 #define ESDHC_DIVIDER_SHIFT 4 46 + #define ESDHC_CLOCK_SDCLKEN 0x00000008 50 47 #define ESDHC_CLOCK_PEREN 0x00000004 51 48 #define ESDHC_CLOCK_HCKEN 0x00000002 52 49 #define ESDHC_CLOCK_IPGEN 0x00000001
+18 -3
drivers/mmc/host/sdhci-of-esdhc.c
··· 431 431 struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); 432 432 int pre_div = 1; 433 433 int div = 1; 434 + u32 timeout; 434 435 u32 temp; 435 436 436 437 host->mmc->actual_clock = 0; ··· 452 451 } 453 452 454 453 temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); 455 - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN 456 - | ESDHC_CLOCK_MASK); 454 + temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | 455 + ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); 457 456 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 458 457 459 458 while (host->max_clk / pre_div / 16 > clock && pre_div < 256) ··· 473 472 | (div << ESDHC_DIVIDER_SHIFT) 474 473 | (pre_div << ESDHC_PREDIV_SHIFT)); 475 474 sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 476 - mdelay(1); 475 + 476 + /* Wait max 20 ms */ 477 + timeout = 20; 478 + while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { 479 + if (timeout == 0) { 480 + pr_err("%s: Internal clock never stabilised.\n", 481 + mmc_hostname(host->mmc)); 482 + return; 483 + } 484 + timeout--; 485 + mdelay(1); 486 + } 487 + 488 + temp |= ESDHC_CLOCK_SDCLKEN; 489 + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 477 490 } 478 491 479 492 static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)