From: Hyuk Lee <hyuk1....@samsung.com>

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....@samsung.com>
Signed-off-by: Jeongbae Seo <jeongbae....@samsung.com>
---
 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-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to