]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - drivers/mmc/host/sdhci-of-at91.c
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[mirror_ubuntu-hirsute-kernel.git] / drivers / mmc / host / sdhci-of-at91.c
index 9cb86fb25976f380530fe0029015bdfaf408d743..25f779e09d8e81e12b038157188ae446bf69eebe 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/kernel.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/slot-gpio.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #define                SDMMC_CACR_CAPWREN      BIT(0)
 #define                SDMMC_CACR_KEY          (0x46 << 8)
 
+#define SDHCI_AT91_PRESET_COMMON_CONF  0x400 /* drv type B, programmable clock mode */
+
 struct sdhci_at91_priv {
        struct clk *hclock;
        struct clk *gck;
        struct clk *mainck;
 };
 
+static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       u16 clk;
+       unsigned long timeout;
+
+       host->mmc->actual_clock = 0;
+
+       /*
+        * There is no requirement to disable the internal clock before
+        * changing the SD clock configuration. Moreover, disabling the
+        * internal clock, changing the configuration and re-enabling the
+        * internal clock causes some bugs. It can prevent to get the internal
+        * clock stable flag ready and an unexpected switch to the base clock
+        * when using presets.
+        */
+       clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+       clk &= SDHCI_CLOCK_INT_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+       if (clock == 0)
+               return;
+
+       clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
+
+       clk |= SDHCI_CLOCK_INT_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+       /* Wait max 20 ms */
+       timeout = 20;
+       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+               & SDHCI_CLOCK_INT_STABLE)) {
+               if (timeout == 0) {
+                       pr_err("%s: Internal clock never stabilised.\n",
+                              mmc_hostname(host->mmc));
+                       return;
+               }
+               timeout--;
+               mdelay(1);
+       }
+
+       clk |= SDHCI_CLOCK_CARD_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
 static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
-       .set_clock              = sdhci_set_clock,
+       .set_clock              = sdhci_at91_set_clock,
        .set_bus_width          = sdhci_set_bus_width,
        .reset                  = sdhci_reset,
        .set_uhs_signaling      = sdhci_set_uhs_signaling,
@@ -45,7 +94,6 @@ static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
 
 static const struct sdhci_pltfm_data soc_data_sama5d2 = {
        .ops = &sdhci_at91_sama5d2_ops,
-       .quirks2 = SDHCI_QUIRK2_NEED_DELAY_AFTER_INT_CLK_RST,
 };
 
 static const struct of_device_id sdhci_at91_dt_match[] = {
@@ -58,7 +106,7 @@ static int sdhci_at91_runtime_suspend(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct sdhci_at91_priv *priv = pltfm_host->priv;
+       struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
        int ret;
 
        ret = sdhci_runtime_suspend_host(host);
@@ -74,7 +122,7 @@ static int sdhci_at91_runtime_resume(struct device *dev)
 {
        struct sdhci_host *host = dev_get_drvdata(dev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct sdhci_at91_priv *priv = pltfm_host->priv;
+       struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
        int ret;
 
        ret = clk_prepare_enable(priv->mainck);
@@ -118,17 +166,19 @@ static int sdhci_at91_probe(struct platform_device *pdev)
        unsigned int                    clk_base, clk_mul;
        unsigned int                    gck_rate, real_gck_rate;
        int                             ret;
+       unsigned int                    preset_div;
 
        match = of_match_device(sdhci_at91_dt_match, &pdev->dev);
        if (!match)
                return -EINVAL;
        soc_data = match->data;
 
-       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv) {
-               dev_err(&pdev->dev, "unable to allocate private data\n");
-               return -ENOMEM;
-       }
+       host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv));
+       if (IS_ERR(host))
+               return PTR_ERR(host);
+
+       pltfm_host = sdhci_priv(host);
+       priv = sdhci_pltfm_priv(pltfm_host);
 
        priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
        if (IS_ERR(priv->mainck)) {
@@ -148,10 +198,6 @@ static int sdhci_at91_probe(struct platform_device *pdev)
                return PTR_ERR(priv->gck);
        }
 
-       host = sdhci_pltfm_init(pdev, soc_data, 0);
-       if (IS_ERR(host))
-               return PTR_ERR(host);
-
        /*
         * The mult clock is provided by as a generated clock by the PMC
         * controller. In order to set the rate of gck, we have to get the
@@ -188,12 +234,31 @@ static int sdhci_at91_probe(struct platform_device *pdev)
                         clk_mul, real_gck_rate);
        }
 
+       /*
+        * We have to set preset values because it depends on the clk_mul
+        * value. Moreover, SDR104 is supported in a degraded mode since the
+        * maximum sd clock value is 120 MHz instead of 208 MHz. For that
+        * reason, we need to use presets to support SDR104.
+        */
+       preset_div = DIV_ROUND_UP(real_gck_rate, 24000000) - 1;
+       writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
+              host->ioaddr + SDHCI_PRESET_FOR_SDR12);
+       preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1;
+       writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
+              host->ioaddr + SDHCI_PRESET_FOR_SDR25);
+       preset_div = DIV_ROUND_UP(real_gck_rate, 100000000) - 1;
+       writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
+              host->ioaddr + SDHCI_PRESET_FOR_SDR50);
+       preset_div = DIV_ROUND_UP(real_gck_rate, 120000000) - 1;
+       writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
+              host->ioaddr + SDHCI_PRESET_FOR_SDR104);
+       preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1;
+       writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
+              host->ioaddr + SDHCI_PRESET_FOR_DDR50);
+
        clk_prepare_enable(priv->mainck);
        clk_prepare_enable(priv->gck);
 
-       pltfm_host = sdhci_priv(host);
-       pltfm_host->priv = priv;
-
        ret = mmc_of_parse(host->mmc);
        if (ret)
                goto clocks_disable_unprepare;
@@ -210,6 +275,25 @@ static int sdhci_at91_probe(struct platform_device *pdev)
        if (ret)
                goto pm_runtime_disable;
 
+       /*
+        * When calling sdhci_runtime_suspend_host(), the sdhci layer makes
+        * the assumption that all the clocks of the controller are disabled.
+        * It means we can't get irq from it when it is runtime suspended.
+        * For that reason, it is not planned to wake-up on a card detect irq
+        * from the controller.
+        * If we want to use runtime PM and to be able to wake-up on card
+        * insertion, we have to use a GPIO for the card detection or we can
+        * use polling. Be aware that using polling will resume/suspend the
+        * controller between each attempt.
+        * Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries
+        * to enable polling via device tree with broken-cd property.
+        */
+       if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
+           IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc))) {
+               host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+               host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+       }
+
        pm_runtime_put_autosuspend(&pdev->dev);
 
        return 0;
@@ -231,7 +315,10 @@ static int sdhci_at91_remove(struct platform_device *pdev)
 {
        struct sdhci_host       *host = platform_get_drvdata(pdev);
        struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
-       struct sdhci_at91_priv  *priv = pltfm_host->priv;
+       struct sdhci_at91_priv  *priv = sdhci_pltfm_priv(pltfm_host);
+       struct clk *gck = priv->gck;
+       struct clk *hclock = priv->hclock;
+       struct clk *mainck = priv->mainck;
 
        pm_runtime_get_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -239,9 +326,9 @@ static int sdhci_at91_remove(struct platform_device *pdev)
 
        sdhci_pltfm_unregister(pdev);
 
-       clk_disable_unprepare(priv->gck);
-       clk_disable_unprepare(priv->hclock);
-       clk_disable_unprepare(priv->mainck);
+       clk_disable_unprepare(gck);
+       clk_disable_unprepare(hclock);
+       clk_disable_unprepare(mainck);
 
        return 0;
 }