From: Hyuk Lee <hyuk1.lee@xxxxxxxxxxx> This patch adds to support no internal clock divider in SDHCI. The external clock divider can be used to make a proper clock because SDHCI doesn't support internal clock divider by itself. Signed-off-by: Hyuk Lee <hyuk1.lee@xxxxxxxxxxx> Signed-off-by: Jeongbae Seo <jeongbae.seo@xxxxxxxxxxx> --- drivers/mmc/host/sdhci-s3c.c | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 63 insertions(+), 0 deletions(-) diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 71ad416..6160960 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -96,6 +96,13 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host) unsigned int rate, max; int clk; + /* + * There is only one clock source(sclk) if there is no clock divider + * in the host controller + */ + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) + return clk_round_rate(ourhost->clk_bus[2], UINT_MAX); + /* note, a reset will reset the clock source */ sdhci_s3c_check_sclk(host); @@ -130,6 +137,15 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, if (!clksrc) return UINT_MAX; + /* + * There is only one clock source(sclk) if there is no clock divider + * in the host controller + */ + if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + rate = clk_round_rate(clksrc, wanted); + return wanted - rate; + } + rate = clk_get_rate(clksrc); for (div = 1; div < 256; div *= 2) { @@ -159,6 +175,7 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) int best_src = 0; int src; u32 ctrl; + unsigned int timeout; /* don't bother if the clock is going off. */ if (clock == 0) @@ -204,6 +221,35 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) (ourhost->pdata->cfg_card)(ourhost->pdev, host->ioaddr, &ios, NULL); } + + /* + * There is only one clock source(sclk) if there is no clock divider + * in the host controller + */ + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + + writew(SDHCI_CLOCK_INT_EN, host->ioaddr + SDHCI_CLOCK_CONTROL); + + /* Wait max 20 ms */ + timeout = 20; + while (!((sdhci_readw(host, SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + printk(KERN_ERR "%s: clock never stabilised.\n" + , mmc_hostname(host->mmc)); + return; + } + timeout--; + mdelay(1); + } + + writew(SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_CARD_EN, + host->ioaddr + SDHCI_CLOCK_CONTROL); + + host->clock = clock; + } } /** @@ -221,6 +267,13 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host) unsigned int delta, min = UINT_MAX; int src; + /* + * There is only one clock source(sclk) if there is no clock divider + * in the host controller + */ + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) + return clk_round_rate(ourhost->clk_bus[2], 400000); + for (src = 0; src < MAX_BUS_CLK; src++) { delta = sdhci_s3c_consider_clock(ourhost, src, 0); if (delta == UINT_MAX) @@ -425,6 +478,16 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; + /* + * If controller does not have internal clock divider, + * we need to use another method with setup a quirk. + */ + if (pdata->clk_type) + host->quirks |= SDHCI_QUIRK_NONSTANDARD_CLOCK; + + if (pdata->host_caps) + host->mmc->caps |= pdata->host_caps; + ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); -- 1.6.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html