[PATCH 5/5] sdhci-s3c: Add support no internal clock divider in host controller

Jeongbae Seo jeongbae.seo at samsung.com
Thu Sep 16 04:37:03 EDT 2010


From: Hyuk Lee <hyuk1.lee at 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.lee at samsung.com>
Signed-off-by: Jeongbae Seo <jeongbae.seo at 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




More information about the linux-arm-kernel mailing list