diff options
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 74 |
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 | */ | ||
64 | struct sdhci_s3c_drv_data { | ||
65 | unsigned int sdhci_quirks; | ||
66 | }; | ||
67 | |||
56 | static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) | 68 | static 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) | |||
272 | static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) | 284 | static 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 | ||
418 | static 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 | |||
385 | static int __devinit sdhci_s3c_probe(struct platform_device *pdev) | 425 | static 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) | ||
695 | static 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 | |||
703 | static 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 | }; | ||
713 | MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids); | ||
714 | |||
650 | static struct platform_driver sdhci_s3c_driver = { | 715 | static 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", |