mediatek: add support for the new MT7623 Arm SoC
[openwrt/svn-archive/archive.git] / target / linux / mediatek / patches / 0036-mmc-mediatek-Add-PM-support-for-MMC-driver.patch
diff --git a/target/linux/mediatek/patches/0036-mmc-mediatek-Add-PM-support-for-MMC-driver.patch b/target/linux/mediatek/patches/0036-mmc-mediatek-Add-PM-support-for-MMC-driver.patch
new file mode 100644 (file)
index 0000000..e9ba31d
--- /dev/null
@@ -0,0 +1,209 @@
+From 4ca0e8a959569852b520b607d39ce6ceeeb0f518 Mon Sep 17 00:00:00 2001
+From: Chaotian Jing <chaotian.jing@mediatek.com>
+Date: Mon, 15 Jun 2015 19:20:49 +0800
+Subject: [PATCH 36/76] mmc: mediatek: Add PM support for MMC driver
+
+Add PM support for Mediatek MMC driver
+Save/restore registers when PM
+
+Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
+---
+ drivers/mmc/host/mtk-sd.c |   89 +++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 86 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
+index 952be2e..7c20f28 100644
+--- a/drivers/mmc/host/mtk-sd.c
++++ b/drivers/mmc/host/mtk-sd.c
+@@ -22,6 +22,8 @@
+ #include <linux/of_gpio.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/pm_runtime.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/spinlock.h>
+@@ -212,6 +214,7 @@
+ #define MSDC_ASYNC_FLAG (0x1 << 1)
+ #define MSDC_MMAP_FLAG (0x1 << 2)
++#define MTK_MMC_AUTOSUSPEND_DELAY     50
+ #define CMD_TIMEOUT         (HZ/10 * 5)       /* 100ms x5 */
+ #define DAT_TIMEOUT         (HZ    * 5)       /* 1000ms x5 */
+@@ -254,6 +257,15 @@ struct msdc_dma {
+       dma_addr_t bd_addr;     /* the physical address of bd array */
+ };
++struct msdc_save_para {
++      u32 msdc_cfg;
++      u32 iocon;
++      u32 sdc_cfg;
++      u32 pad_tune;
++      u32 patch_bit0;
++      u32 patch_bit1;
++};
++
+ struct msdc_host {
+       struct device *dev;
+       struct mmc_host *mmc;   /* mmc structure */
+@@ -286,6 +298,7 @@ struct msdc_host {
+       u32 sclk;               /* SD/MS bus clock frequency */
+       bool ddr;
+       bool vqmmc_enabled;
++      struct msdc_save_para save_para; /* used when gate HCLK */
+ };
+ static void sdr_set_bits(void __iomem *reg, u32 bs)
+@@ -677,6 +690,9 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
+       if (mrq->data)
+               msdc_unprepare_data(host, mrq);
+       mmc_request_done(host->mmc, mrq);
++
++      pm_runtime_mark_last_busy(host->dev);
++      pm_runtime_put_autosuspend(host->dev);
+ }
+ /* returns true if command is fully handled; returns false otherwise */
+@@ -831,6 +847,8 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
+       WARN_ON(host->mrq);
+       host->mrq = mrq;
++      pm_runtime_get_sync(host->dev);
++
+       if (mrq->data)
+               msdc_prepare_data(host, mrq);
+@@ -1145,6 +1163,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+       int ret;
+       u32 ddr = 0;
++      pm_runtime_get_sync(host->dev);
++
+       if (ios->timing == MMC_TIMING_UHS_DDR50 ||
+           ios->timing == MMC_TIMING_MMC_DDR52)
+               ddr = 1;
+@@ -1159,7 +1179,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+                                       ios->vdd);
+                       if (ret) {
+                               dev_err(host->dev, "Failed to set vmmc power!\n");
+-                              return;
++                              goto end;
+                       }
+               }
+               break;
+@@ -1187,6 +1207,10 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+       if (host->mclk != ios->clock || host->ddr != ddr)
+               msdc_set_mclk(host, ddr, ios->clock);
++
++end:
++      pm_runtime_mark_last_busy(host->dev);
++      pm_runtime_put_autosuspend(host->dev);
+ }
+ static struct mmc_host_ops mt_msdc_ops = {
+@@ -1310,12 +1334,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
+       if (ret)
+               goto release;
++      pm_runtime_set_active(host->dev);
++      pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY);
++      pm_runtime_use_autosuspend(host->dev);
++      pm_runtime_enable(host->dev);
+       ret = mmc_add_host(mmc);
++
+       if (ret)
+-              goto release;
++              goto end;
+       return 0;
+-
++end:
++      pm_runtime_disable(host->dev);
+ release:
+       platform_set_drvdata(pdev, NULL);
+       msdc_deinit_hw(host);
+@@ -1343,11 +1373,15 @@ static int msdc_drv_remove(struct platform_device *pdev)
+       mmc = platform_get_drvdata(pdev);
+       host = mmc_priv(mmc);
++      pm_runtime_get_sync(host->dev);
++
+       platform_set_drvdata(pdev, NULL);
+       mmc_remove_host(host->mmc);
+       msdc_deinit_hw(host);
+       msdc_gate_clock(host);
++      pm_runtime_disable(host->dev);
++      pm_runtime_put_noidle(host->dev);
+       dma_free_coherent(&pdev->dev,
+                       sizeof(struct mt_gpdma_desc),
+                       host->dma.gpd, host->dma.gpd_addr);
+@@ -1359,6 +1393,54 @@ static int msdc_drv_remove(struct platform_device *pdev)
+       return 0;
+ }
++#ifdef CONFIG_PM
++static void msdc_save_reg(struct msdc_host *host)
++{
++      host->save_para.msdc_cfg = readl(host->base + MSDC_CFG);
++      host->save_para.iocon = readl(host->base + MSDC_IOCON);
++      host->save_para.sdc_cfg = readl(host->base + SDC_CFG);
++      host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
++      host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
++      host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
++}
++
++static void msdc_restore_reg(struct msdc_host *host)
++{
++      writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
++      writel(host->save_para.iocon, host->base + MSDC_IOCON);
++      writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
++      writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
++      writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
++      writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
++}
++
++static int msdc_runtime_suspend(struct device *dev)
++{
++      struct mmc_host *mmc = dev_get_drvdata(dev);
++      struct msdc_host *host = mmc_priv(mmc);
++
++      msdc_save_reg(host);
++      msdc_gate_clock(host);
++      return 0;
++}
++
++static int msdc_runtime_resume(struct device *dev)
++{
++      struct mmc_host *mmc = dev_get_drvdata(dev);
++      struct msdc_host *host = mmc_priv(mmc);
++
++      msdc_ungate_clock(host);
++      msdc_restore_reg(host);
++      return 0;
++}
++#endif
++
++static const struct dev_pm_ops msdc_dev_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
++                              pm_runtime_force_resume)
++      SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
++};
++
+ static const struct of_device_id msdc_of_ids[] = {
+       {   .compatible = "mediatek,mt8135-mmc", },
+       {}
+@@ -1370,6 +1452,7 @@ static struct platform_driver mt_msdc_driver = {
+       .driver = {
+               .name = "mtk-msdc",
+               .of_match_table = msdc_of_ids,
++              .pm = &msdc_dev_pm_ops,
+       },
+ };
+-- 
+1.7.10.4
+