aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/sdhci-s3c.c74
1 files changed, 70 insertions, 4 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index b19e7d435f8d..5fce14385910 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -53,6 +53,18 @@ struct sdhci_s3c {
53 struct clk *clk_bus[MAX_BUS_CLK]; 53 struct clk *clk_bus[MAX_BUS_CLK];
54}; 54};
55 55
56/**
57 * struct sdhci_s3c_driver_data - S3C SDHCI platform specific driver data
58 * @sdhci_quirks: sdhci host specific quirks.
59 *
60 * Specifies platform specific configuration of sdhci controller.
61 * Note: A structure for driver specific platform data is used for future
62 * expansion of its usage.
63 */
64struct sdhci_s3c_drv_data {
65 unsigned int sdhci_quirks;
66};
67
56static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) 68static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
57{ 69{
58 return sdhci_priv(host); 70 return sdhci_priv(host);
@@ -132,10 +144,10 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
132 return UINT_MAX; 144 return UINT_MAX;
133 145
134 /* 146 /*
135 * Clock divider's step is different as 1 from that of host controller 147 * If controller uses a non-standard clock division, find the best clock
136 * when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL. 148 * speed possible with selected clock source and skip the division.
137 */ 149 */
138 if (ourhost->pdata->clk_type) { 150 if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
139 rate = clk_round_rate(clksrc, wanted); 151 rate = clk_round_rate(clksrc, wanted);
140 return wanted - rate; 152 return wanted - rate;
141 } 153 }
@@ -272,6 +284,8 @@ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
272static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) 284static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
273{ 285{
274 struct sdhci_s3c *ourhost = to_s3c(host); 286 struct sdhci_s3c *ourhost = to_s3c(host);
287 unsigned long timeout;
288 u16 clk = 0;
275 289
276 /* don't bother if the clock is going off */ 290 /* don't bother if the clock is going off */
277 if (clock == 0) 291 if (clock == 0)
@@ -282,6 +296,25 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
282 clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); 296 clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
283 297
284 host->clock = clock; 298 host->clock = clock;
299
300 clk = SDHCI_CLOCK_INT_EN;
301 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
302
303 /* Wait max 20 ms */
304 timeout = 20;
305 while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
306 & SDHCI_CLOCK_INT_STABLE)) {
307 if (timeout == 0) {
308 printk(KERN_ERR "%s: Internal clock never "
309 "stabilised.\n", mmc_hostname(host->mmc));
310 return;
311 }
312 timeout--;
313 mdelay(1);
314 }
315
316 clk |= SDHCI_CLOCK_CARD_EN;
317 sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
285} 318}
286 319
287/** 320/**
@@ -382,9 +415,17 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
382 } 415 }
383} 416}
384 417
418static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
419 struct platform_device *pdev)
420{
421 return (struct sdhci_s3c_drv_data *)
422 platform_get_device_id(pdev)->driver_data;
423}
424
385static int __devinit sdhci_s3c_probe(struct platform_device *pdev) 425static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
386{ 426{
387 struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; 427 struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
428 struct sdhci_s3c_drv_data *drv_data;
388 struct device *dev = &pdev->dev; 429 struct device *dev = &pdev->dev;
389 struct sdhci_host *host; 430 struct sdhci_host *host;
390 struct sdhci_s3c *sc; 431 struct sdhci_s3c *sc;
@@ -414,6 +455,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
414 return PTR_ERR(host); 455 return PTR_ERR(host);
415 } 456 }
416 457
458 drv_data = sdhci_s3c_get_driver_data(pdev);
417 sc = sdhci_priv(host); 459 sc = sdhci_priv(host);
418 460
419 sc->host = host; 461 sc->host = host;
@@ -491,6 +533,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
491 /* Setup quirks for the controller */ 533 /* Setup quirks for the controller */
492 host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; 534 host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
493 host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; 535 host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
536 if (drv_data)
537 host->quirks |= drv_data->sdhci_quirks;
494 538
495#ifndef CONFIG_MMC_SDHCI_S3C_DMA 539#ifndef CONFIG_MMC_SDHCI_S3C_DMA
496 540
@@ -531,7 +575,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
531 * If controller does not have internal clock divider, 575 * If controller does not have internal clock divider,
532 * we can use overriding functions instead of default. 576 * we can use overriding functions instead of default.
533 */ 577 */
534 if (pdata->clk_type) { 578 if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
535 sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; 579 sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
536 sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; 580 sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
537 sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; 581 sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
@@ -647,9 +691,31 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {
647#define SDHCI_S3C_PMOPS NULL 691#define SDHCI_S3C_PMOPS NULL
648#endif 692#endif
649 693
694#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
695static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
696 .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK,
697};
698#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
699#else
700#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
701#endif
702
703static struct platform_device_id sdhci_s3c_driver_ids[] = {
704 {
705 .name = "s3c-sdhci",
706 .driver_data = (kernel_ulong_t)NULL,
707 }, {
708 .name = "exynos4-sdhci",
709 .driver_data = EXYNOS4_SDHCI_DRV_DATA,
710 },
711 { }
712};
713MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
714
650static struct platform_driver sdhci_s3c_driver = { 715static struct platform_driver sdhci_s3c_driver = {
651 .probe = sdhci_s3c_probe, 716 .probe = sdhci_s3c_probe,
652 .remove = __devexit_p(sdhci_s3c_remove), 717 .remove = __devexit_p(sdhci_s3c_remove),
718 .id_table = sdhci_s3c_driver_ids,
653 .driver = { 719 .driver = {
654 .owner = THIS_MODULE, 720 .owner = THIS_MODULE,
655 .name = "s3c-sdhci", 721 .name = "s3c-sdhci",