Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux.git] / drivers / mmc / host / sdhci-s3c.c
index 52770d58cc47af3fcd29e1174d86ff47fa01da00..d61eb5a708331d54ad9c4ea948bf7cbb4533d8da 100644 (file)
@@ -51,12 +51,13 @@ struct sdhci_s3c {
        struct platform_device  *pdev;
        struct resource         *ioarea;
        struct s3c_sdhci_platdata *pdata;
-       unsigned int            cur_clk;
+       int                     cur_clk;
        int                     ext_cd_irq;
        int                     ext_cd_gpio;
 
        struct clk              *clk_io;
        struct clk              *clk_bus[MAX_BUS_CLK];
+       unsigned long           clk_rates[MAX_BUS_CLK];
 };
 
 /**
@@ -76,32 +77,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
        return sdhci_priv(host);
 }
 
-/**
- * get_curclk - convert ctrl2 register to clock source number
- * @ctrl2: Control2 register value.
- */
-static u32 get_curclk(u32 ctrl2)
-{
-       ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
-       ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
-
-       return ctrl2;
-}
-
-static void sdhci_s3c_check_sclk(struct sdhci_host *host)
-{
-       struct sdhci_s3c *ourhost = to_s3c(host);
-       u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
-
-       if (get_curclk(tmp) != ourhost->cur_clk) {
-               dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
-
-               tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
-               tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
-               writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
-       }
-}
-
 /**
  * sdhci_s3c_get_max_clk - callback to get maximum clock frequency.
  * @host: The SDHCI host instance.
@@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
 static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
 {
        struct sdhci_s3c *ourhost = to_s3c(host);
-       struct clk *busclk;
-       unsigned int rate, max;
-       int clk;
-
-       /* note, a reset will reset the clock source */
-
-       sdhci_s3c_check_sclk(host);
-
-       for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) {
-               busclk = ourhost->clk_bus[clk];
-               if (!busclk)
-                       continue;
+       unsigned long rate, max = 0;
+       int src;
 
-               rate = clk_get_rate(busclk);
+       for (src = 0; src < MAX_BUS_CLK; src++) {
+               rate = ourhost->clk_rates[src];
                if (rate > max)
                        max = rate;
        }
@@ -146,7 +112,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
        struct clk *clksrc = ourhost->clk_bus[src];
        int shift;
 
-       if (!clksrc)
+       if (IS_ERR(clksrc))
                return UINT_MAX;
 
        /*
@@ -158,13 +124,20 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
                return wanted - rate;
        }
 
-       rate = clk_get_rate(clksrc);
+       rate = ourhost->clk_rates[src];
 
-       for (shift = 0; shift < 8; ++shift) {
+       for (shift = 0; shift <= 8; ++shift) {
                if ((rate >> shift) <= wanted)
                        break;
        }
 
+       if (shift > 8) {
+               dev_dbg(&ourhost->pdev->dev,
+                       "clk %d: rate %ld, min rate %lu > wanted %u\n",
+                       src, rate, rate / 256, wanted);
+               return UINT_MAX;
+       }
+
        dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
                src, rate, wanted, rate >> shift);
 
@@ -209,20 +182,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
                struct clk *clk = ourhost->clk_bus[best_src];
 
                clk_prepare_enable(clk);
-               clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
-
-               /* turn clock off to card before changing clock source */
-               writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+               if (ourhost->cur_clk >= 0)
+                       clk_disable_unprepare(
+                                       ourhost->clk_bus[ourhost->cur_clk]);
 
                ourhost->cur_clk = best_src;
-               host->max_clk = clk_get_rate(clk);
-
-               ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
-               ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
-               ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
-               writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+               host->max_clk = ourhost->clk_rates[best_src];
        }
 
+       /* turn clock off to card before changing clock source */
+       writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+       ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+       ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+       ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+       writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+
        /* reprogram default hardware configuration */
        writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
                host->ioaddr + S3C64XX_SDHCI_CONTROL4);
@@ -254,17 +229,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
 static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
 {
        struct sdhci_s3c *ourhost = to_s3c(host);
-       unsigned int delta, min = UINT_MAX;
+       unsigned long rate, min = ULONG_MAX;
        int src;
 
        for (src = 0; src < MAX_BUS_CLK; src++) {
-               delta = sdhci_s3c_consider_clock(ourhost, src, 0);
-               if (delta == UINT_MAX)
+               rate = ourhost->clk_rates[src] / 256;
+               if (!rate)
                        continue;
-               /* delta is a negative value in this case */
-               if (-delta < min)
-                       min = -delta;
+               if (rate < min)
+                       min = rate;
        }
+
        return min;
 }
 
@@ -272,20 +247,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
 static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)
 {
        struct sdhci_s3c *ourhost = to_s3c(host);
+       unsigned long rate, max = 0;
+       int src;
 
-       return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX);
+       for (src = 0; src < MAX_BUS_CLK; src++) {
+               struct clk *clk;
+
+               clk = ourhost->clk_bus[src];
+               if (IS_ERR(clk))
+                       continue;
+
+               rate = clk_round_rate(clk, ULONG_MAX);
+               if (rate > max)
+                       max = rate;
+       }
+
+       return max;
 }
 
 /* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */
 static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
 {
        struct sdhci_s3c *ourhost = to_s3c(host);
+       unsigned long rate, min = ULONG_MAX;
+       int src;
 
-       /*
-        * initial clock can be in the frequency range of
-        * 100KHz-400KHz, so we set it as max value.
-        */
-       return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000);
+       for (src = 0; src < MAX_BUS_CLK; src++) {
+               struct clk *clk;
+
+               clk = ourhost->clk_bus[src];
+               if (IS_ERR(clk))
+                       continue;
+
+               rate = clk_round_rate(clk, 0);
+               if (rate < min)
+                       min = rate;
+       }
+
+       return min;
 }
 
 /* sdhci_cmu_set_clock - callback on clock change.*/
@@ -552,6 +551,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
        sc->host = host;
        sc->pdev = pdev;
        sc->pdata = pdata;
+       sc->cur_clk = -1;
 
        platform_set_drvdata(pdev, host);
 
@@ -566,25 +566,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
        clk_prepare_enable(sc->clk_io);
 
        for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
-               struct clk *clk;
                char name[14];
 
                snprintf(name, 14, "mmc_busclk.%d", ptr);
-               clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
+               sc->clk_bus[ptr] = devm_clk_get(dev, name);
+               if (IS_ERR(sc->clk_bus[ptr]))
                        continue;
 
                clks++;
-               sc->clk_bus[ptr] = clk;
-
-               /*
-                * save current clock index to know which clock bus
-                * is used later in overriding functions.
-                */
-               sc->cur_clk = ptr;
+               sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);
 
                dev_info(dev, "clock source %d: %s (%ld Hz)\n",
-                        ptr, name, clk_get_rate(clk));
+                               ptr, name, sc->clk_rates[ptr]);
        }
 
        if (clks == 0) {
@@ -593,10 +586,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
                goto err_no_busclks;
        }
 
-#ifndef CONFIG_PM_RUNTIME
-       clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
-#endif
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(host->ioaddr)) {
@@ -709,10 +698,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
        return 0;
 
  err_req_regs:
-#ifndef CONFIG_PM_RUNTIME
-       clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
-
  err_no_busclks:
        clk_disable_unprepare(sc->clk_io);
 
@@ -743,9 +728,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
        pm_runtime_dont_use_autosuspend(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
-#ifndef CONFIG_PM_RUNTIME
-       clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
        clk_disable_unprepare(sc->clk_io);
 
        sdhci_free_host(host);
@@ -779,7 +761,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
 
        ret = sdhci_runtime_suspend_host(host);
 
-       clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
+       if (ourhost->cur_clk >= 0)
+               clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
        clk_disable_unprepare(busclk);
        return ret;
 }
@@ -792,7 +775,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
        int ret;
 
        clk_prepare_enable(busclk);
-       clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
+       if (ourhost->cur_clk >= 0)
+               clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
        ret = sdhci_runtime_resume_host(host);
        return ret;
 }