aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-s3c.c
diff options
context:
space:
mode:
authorJeongbae Seo <jeongbae.seo@samsung.com>2010-10-08 04:46:21 -0400
committerChris Ball <cjb@laptop.org>2011-01-08 23:52:22 -0500
commit253e0a7c3dc4bbbc69fc6bfd7c01bc4c9397a5e5 (patch)
tree5c56f6ed391fc7fcc96918d9ee657c319d81e962 /drivers/mmc/host/sdhci-s3c.c
parentb3824f2c6f16ef19060a53ef9345a124de175098 (diff)
mmc: sdhci-s3c: Support controllers with no internal clock divider
This patch supports controllers with no internal clock divider in SDHCI, such as the s5pc210 board. The external clock divider can be used to make a proper clock because SDHCI doesn't support internal clock divider by itself. If external clock divider type is selected, some functions related to clock control will be overrided by other functions. The current clock control index is added to let you know which clock bus is used for SDHCI when overriding functions. Checking functions are added into sdhci_s3c_consider_clock, because the clock divider step is different from that of host controller. Signed-off-by: Jeongbae Seo <jeongbae.seo@samsung.com> Cc: Jaehoon Chung <jh80.chung@samsung.com> Cc: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/sdhci-s3c.c')
-rw-r--r--drivers/mmc/host/sdhci-s3c.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index a7710f55784..17203586305 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -130,6 +130,15 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
130 if (!clksrc) 130 if (!clksrc)
131 return UINT_MAX; 131 return UINT_MAX;
132 132
133 /*
134 * Clock divider's step is different as 1 from that of host controller
135 * when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL.
136 */
137 if (ourhost->pdata->clk_type) {
138 rate = clk_round_rate(clksrc, wanted);
139 return wanted - rate;
140 }
141
133 rate = clk_get_rate(clksrc); 142 rate = clk_get_rate(clksrc);
134 143
135 for (div = 1; div < 256; div *= 2) { 144 for (div = 1; div < 256; div *= 2) {
@@ -232,6 +241,42 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
232 return min; 241 return min;
233} 242}
234 243
244/* sdhci_cmu_get_max_clk - callback to get maximum clock frequency.*/
245static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)
246{
247 struct sdhci_s3c *ourhost = to_s3c(host);
248
249 return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX);
250}
251
252/* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */
253static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
254{
255 struct sdhci_s3c *ourhost = to_s3c(host);
256
257 /*
258 * initial clock can be in the frequency range of
259 * 100KHz-400KHz, so we set it as max value.
260 */
261 return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000);
262}
263
264/* sdhci_cmu_set_clock - callback on clock change.*/
265static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
266{
267 struct sdhci_s3c *ourhost = to_s3c(host);
268
269 /* don't bother if the clock is going off */
270 if (clock == 0)
271 return;
272
273 sdhci_s3c_set_clock(host, clock);
274
275 clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
276
277 host->clock = clock;
278}
279
235static struct sdhci_ops sdhci_s3c_ops = { 280static struct sdhci_ops sdhci_s3c_ops = {
236 .get_max_clock = sdhci_s3c_get_max_clk, 281 .get_max_clock = sdhci_s3c_get_max_clk,
237 .set_clock = sdhci_s3c_set_clock, 282 .set_clock = sdhci_s3c_set_clock,
@@ -361,6 +406,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
361 406
362 clks++; 407 clks++;
363 sc->clk_bus[ptr] = clk; 408 sc->clk_bus[ptr] = clk;
409
410 /*
411 * save current clock index to know which clock bus
412 * is used later in overriding functions.
413 */
414 sc->cur_clk = ptr;
415
364 clk_enable(clk); 416 clk_enable(clk);
365 417
366 dev_info(dev, "clock source %d: %s (%ld Hz)\n", 418 dev_info(dev, "clock source %d: %s (%ld Hz)\n",
@@ -427,6 +479,16 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
427 /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */ 479 /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
428 host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; 480 host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
429 481
482 /*
483 * If controller does not have internal clock divider,
484 * we can use overriding functions instead of default.
485 */
486 if (pdata->clk_type) {
487 sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
488 sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
489 sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
490 }
491
430 /* It supports additional host capabilities if needed */ 492 /* It supports additional host capabilities if needed */
431 if (pdata->host_caps) 493 if (pdata->host_caps)
432 host->mmc->caps |= pdata->host_caps; 494 host->mmc->caps |= pdata->host_caps;