aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorTomasz Figa <tomasz.figa@gmail.com>2014-01-11 16:39:05 -0500
committerChris Ball <chris@printf.net>2014-03-03 10:23:36 -0500
commit3ac147facfbd234acf3a990968de9746a399f133 (patch)
treeac929a35cdc6480f09afec0dfac831488e100710 /drivers/mmc
parent222a13c5d0c67333e443a1ea2fcc746ad61f8d68 (diff)
mmc: sdhci-s3c: Fix handling of bus clock switching
Currently the driver assumes at probe that controller is configured for last valid enumerated bus clock. This assumption is completely wrong, as there is no way to ensure such configuration until the hardware gets first configured (by calling sdhci_s3c_set_clock()). This patch modifies the driver to set current clock at probe to unknown state (represented by negative value) and make sure that the hardware gets actually configured to selected clock in sdhci_s3c_set_clock(). Signed-off-by: Tomasz Figa <tomasz.figa@gmail.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Jaehoon Chung <jh80.chung@samsung.com> Acked-by; Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Chris Ball <chris@printf.net>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-s3c.c78
1 files changed, 17 insertions, 61 deletions
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 7e14db07eb30..bad0e00a3e8f 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -51,7 +51,7 @@ struct sdhci_s3c {
51 struct platform_device *pdev; 51 struct platform_device *pdev;
52 struct resource *ioarea; 52 struct resource *ioarea;
53 struct s3c_sdhci_platdata *pdata; 53 struct s3c_sdhci_platdata *pdata;
54 unsigned int cur_clk; 54 int cur_clk;
55 int ext_cd_irq; 55 int ext_cd_irq;
56 int ext_cd_gpio; 56 int ext_cd_gpio;
57 57
@@ -78,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
78} 78}
79 79
80/** 80/**
81 * get_curclk - convert ctrl2 register to clock source number
82 * @ctrl2: Control2 register value.
83 */
84static u32 get_curclk(u32 ctrl2)
85{
86 ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
87 ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
88
89 return ctrl2;
90}
91
92static void sdhci_s3c_check_sclk(struct sdhci_host *host)
93{
94 struct sdhci_s3c *ourhost = to_s3c(host);
95 u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
96
97 if (get_curclk(tmp) != ourhost->cur_clk) {
98 dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
99
100 tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
101 tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
102 writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
103 }
104}
105
106/**
107 * sdhci_s3c_get_max_clk - callback to get maximum clock frequency. 81 * sdhci_s3c_get_max_clk - callback to get maximum clock frequency.
108 * @host: The SDHCI host instance. 82 * @host: The SDHCI host instance.
109 * 83 *
@@ -115,11 +89,6 @@ static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
115 unsigned long rate, max = 0; 89 unsigned long rate, max = 0;
116 int src; 90 int src;
117 91
118 /* note, a reset will reset the clock source */
119
120 sdhci_s3c_check_sclk(host);
121
122
123 for (src = 0; src < MAX_BUS_CLK; src++) { 92 for (src = 0; src < MAX_BUS_CLK; src++) {
124 rate = ourhost->clk_rates[src]; 93 rate = ourhost->clk_rates[src];
125 if (rate > max) 94 if (rate > max)
@@ -206,20 +175,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
206 struct clk *clk = ourhost->clk_bus[best_src]; 175 struct clk *clk = ourhost->clk_bus[best_src];
207 176
208 clk_prepare_enable(clk); 177 clk_prepare_enable(clk);
209 clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); 178 if (ourhost->cur_clk >= 0)
210 179 clk_disable_unprepare(
211 /* turn clock off to card before changing clock source */ 180 ourhost->clk_bus[ourhost->cur_clk]);
212 writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
213 181
214 ourhost->cur_clk = best_src; 182 ourhost->cur_clk = best_src;
215 host->max_clk = ourhost->clk_rates[best_src]; 183 host->max_clk = ourhost->clk_rates[best_src];
216
217 ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
218 ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
219 ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
220 writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
221 } 184 }
222 185
186 /* turn clock off to card before changing clock source */
187 writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
188
189 ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
190 ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
191 ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
192 writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
193
223 /* reprogram default hardware configuration */ 194 /* reprogram default hardware configuration */
224 writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA, 195 writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
225 host->ioaddr + S3C64XX_SDHCI_CONTROL4); 196 host->ioaddr + S3C64XX_SDHCI_CONTROL4);
@@ -573,6 +544,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
573 sc->host = host; 544 sc->host = host;
574 sc->pdev = pdev; 545 sc->pdev = pdev;
575 sc->pdata = pdata; 546 sc->pdata = pdata;
547 sc->cur_clk = -1;
576 548
577 platform_set_drvdata(pdev, host); 549 platform_set_drvdata(pdev, host);
578 550
@@ -595,13 +567,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
595 continue; 567 continue;
596 568
597 clks++; 569 clks++;
598
599 /*
600 * save current clock index to know which clock bus
601 * is used later in overriding functions.
602 */
603 sc->cur_clk = ptr;
604
605 sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]); 570 sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);
606 571
607 dev_info(dev, "clock source %d: %s (%ld Hz)\n", 572 dev_info(dev, "clock source %d: %s (%ld Hz)\n",
@@ -614,10 +579,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
614 goto err_no_busclks; 579 goto err_no_busclks;
615 } 580 }
616 581
617#ifndef CONFIG_PM_RUNTIME
618 clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
619#endif
620
621 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 582 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
622 host->ioaddr = devm_ioremap_resource(&pdev->dev, res); 583 host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
623 if (IS_ERR(host->ioaddr)) { 584 if (IS_ERR(host->ioaddr)) {
@@ -730,10 +691,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
730 return 0; 691 return 0;
731 692
732 err_req_regs: 693 err_req_regs:
733#ifndef CONFIG_PM_RUNTIME
734 clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
735#endif
736
737 err_no_busclks: 694 err_no_busclks:
738 clk_disable_unprepare(sc->clk_io); 695 clk_disable_unprepare(sc->clk_io);
739 696
@@ -764,9 +721,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
764 pm_runtime_dont_use_autosuspend(&pdev->dev); 721 pm_runtime_dont_use_autosuspend(&pdev->dev);
765 pm_runtime_disable(&pdev->dev); 722 pm_runtime_disable(&pdev->dev);
766 723
767#ifndef CONFIG_PM_RUNTIME
768 clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
769#endif
770 clk_disable_unprepare(sc->clk_io); 724 clk_disable_unprepare(sc->clk_io);
771 725
772 sdhci_free_host(host); 726 sdhci_free_host(host);
@@ -800,7 +754,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
800 754
801 ret = sdhci_runtime_suspend_host(host); 755 ret = sdhci_runtime_suspend_host(host);
802 756
803 clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); 757 if (ourhost->cur_clk >= 0)
758 clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
804 clk_disable_unprepare(busclk); 759 clk_disable_unprepare(busclk);
805 return ret; 760 return ret;
806} 761}
@@ -813,7 +768,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
813 int ret; 768 int ret;
814 769
815 clk_prepare_enable(busclk); 770 clk_prepare_enable(busclk);
816 clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); 771 if (ourhost->cur_clk >= 0)
772 clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
817 ret = sdhci_runtime_resume_host(host); 773 ret = sdhci_runtime_resume_host(host);
818 return ret; 774 return ret;
819} 775}