aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorThomas Abraham <thomas.abraham@linaro.org>2012-02-16 08:23:58 -0500
committerChris Ball <cjb@laptop.org>2012-04-05 19:57:49 -0400
commit3119936a289db88cf749143fa5ef6b4a4712e3c0 (patch)
tree53ed713ce8c9435c6daa976fc77b4c366e72d277 /drivers/mmc
parentdd775ae2549217d3ae09363e3edb305d0fa19928 (diff)
mmc: sdhci-s3c: Remove usage of clk_type member in platform data
SDHCI controllers on Exynos4 do not include the sdclk divider as per the sdhci controller specification. This case can be represented using the sdhci quirk SDHCI_QUIRK_NONSTANDARD_CLOCK instead of using an additional enum type definition 'clk_types'. Hence, usage of clk_type member in platform data is removed and the sdhci quirk is used. In addition to that, since this qurik is SoC specific, driver data is introduced to represent controllers on SoC's that require this quirk. Cc: Ben Dooks <ben-linux@fluff.org> Cc: Jeongbae Seo <jeongbae.seo@samsung.com> Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-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",