summaryrefslogtreecommitdiffstats
path: root/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch')
-rwxr-xr-xmeta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch414
1 files changed, 414 insertions, 0 deletions
diff --git a/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch
new file mode 100755
index 000000000..34b40a147
--- /dev/null
+++ b/meta-agl-bsp/meta-renesas/recipes-kernel/linux/linux/hibernation/0008-Add-rcar-mmc-hibernation-code.patch
@@ -0,0 +1,414 @@
+From 9d1d9be70ed3cf6670ae12a1caed337833f7bba8 Mon Sep 17 00:00:00 2001
+From: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+Date: Thu, 18 May 2017 17:38:11 +0900
+Subject: [PATCH 08/15] Add rcar mmc hibernation code
+
+Signed-off-by: Yuichi Kusakabe <yuichi.kusakabe@jp.fujitsu.com>
+---
+ drivers/mmc/host/sh_mmcif.c | 65 +++++++++++++++++++++-
+ drivers/mmc/host/sh_mobile_sdhi.c | 112 +++++++++++++++++++++++++++++++++++++-
+ drivers/mmc/host/tmio_mmc.h | 1 +
+ drivers/mmc/host/tmio_mmc_pio.c | 49 ++++++++++++-----
+ 4 files changed, 210 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
+index 7290e6e..4ecf62c 100644
+--- a/drivers/mmc/host/sh_mmcif.c
++++ b/drivers/mmc/host/sh_mmcif.c
+@@ -232,6 +232,7 @@ struct sh_mmcif_host {
+ struct platform_device *pd;
+ struct clk *hclk;
+ unsigned int clk;
++ int clkrate;
+ int bus_width;
+ unsigned char timing;
+ bool sd_error;
+@@ -257,6 +258,8 @@ struct sh_mmcif_host {
+ struct dma_chan *chan_tx;
+ struct completion dma_complete;
+ bool dma_active;
++#define N_REGS 10
++ u32 regs[N_REGS];
+ };
+
+ static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
+@@ -1457,6 +1460,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
+ }
+ }
+
++ host->clkrate = clk_get_rate(host->hclk);
++
+ ret = sh_mmcif_clk_update(host);
+ if (ret < 0)
+ goto eclkupdate;
+@@ -1503,6 +1508,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
+ dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
+ dev_dbg(&pdev->dev, "chip ver H'%04x\n",
+ sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
++ device_enable_async_suspend(&pdev->dev);
+ return ret;
+
+ emmcaddh:
+@@ -1574,15 +1580,68 @@ static int sh_mmcif_suspend(struct device *dev)
+ sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
+ pm_runtime_put(dev);
+
+- return 0;
++ return mmc_suspend_host(host->mmc);
+ }
+
+ static int sh_mmcif_resume(struct device *dev)
+ {
+- return 0;
++ struct sh_mmcif_host *host = dev_get_drvdata(dev);
++ return mmc_resume_host(host->mmc);
++}
++#endif
++
++#ifdef CONFIG_PM
++static int sh_mmcif_restore(struct device *dev)
++{
++ struct sh_mmcif_host *host = dev_get_drvdata(dev);
++ int ret;
++ ret = clk_set_rate(host->hclk, host->clkrate);
++ if (ret < 0)
++ goto eclkupdate;
++ ret = sh_mmcif_clk_update(host);
++ if (ret < 0)
++ goto eclkupdate;
++ ret = pm_runtime_resume(dev);
++ if (ret < 0)
++ goto eresume;
++ sh_mmcif_sync_reset(host);
++#ifdef CONFIG_MACH_FTEN
++ sh_mmcif_writel(host->addr, 0x00000080, 0x00000100);
++#endif
++ sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
++ clk_disable_unprepare(host->hclk);
++ dev_info(dev, "restore: chip ver H'%04x\n",
++ sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
++ sh_mmcif_writel(host->addr, MMCIF_CE_CMD_CTRL, host->regs[0]);
++ sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, host->regs[1]);
++ sh_mmcif_writel(host->addr, MMCIF_CE_CLK_CTRL, host->regs[2]);
++ sh_mmcif_writel(host->addr, MMCIF_CE_BUF_ACC, host->regs[3]);
++ sh_mmcif_release_dma(host);
++ return mmc_resume_host(host->mmc);
++eclkupdate:
++ pr_info("Can't set clock\n");
++ return -EINVAL;
++eresume:
++ pr_info("Can't resume PM\n");
++ return -ENODEV;
+ }
++
++static int sh_mmcif_freeze(struct device *dev)
++{
++ struct sh_mmcif_host *host = dev_get_drvdata(dev);
++ int ret = mmc_suspend_host(host->mmc);
++ host->regs[0] = sh_mmcif_readl(host->addr, MMCIF_CE_CMD_CTRL);
++ host->regs[1] = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET);
++ host->regs[2] = sh_mmcif_readl(host->addr, MMCIF_CE_CLK_CTRL);
++ host->regs[3] = sh_mmcif_readl(host->addr, MMCIF_CE_BUF_ACC);
++ return ret;
++}
++#else
++#define sh_mmcif_restore NULL
++#define sh_mmcif_freeze NULL
+ #endif
+
++
+ static const struct of_device_id mmcif_of_match[] = {
+ { .compatible = "renesas,sh-mmcif" },
+ { }
+@@ -1591,6 +1650,8 @@ MODULE_DEVICE_TABLE(of, mmcif_of_match);
+
+ static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
++ .restore = sh_mmcif_restore,
++ .freeze = sh_mmcif_freeze,
+ };
+
+ static struct platform_driver sh_mmcif_driver = {
+diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
+index 1b59cdf..c7f3abf 100644
+--- a/drivers/mmc/host/sh_mobile_sdhi.c
++++ b/drivers/mmc/host/sh_mobile_sdhi.c
+@@ -156,6 +156,8 @@ struct sh_mobile_sdhi {
+ struct tmio_mmc_dma dma_priv;
+ unsigned int type;
+ struct sh_mobile_sdhi_vlt vlt;
++ int wifi_xrst;
++ int save_clk_rate;
+ };
+
+ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
+@@ -647,6 +649,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "cannot set clock rate: %d\n", ret);
++ priv->save_clk_rate = clk_rate;
+
+ clk_disable_unprepare(priv->clk);
+ }
+@@ -841,6 +844,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
+ }
+ }
+
++ device_enable_async_suspend(&pdev->dev);
+ dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
+ mmc_hostname(host->mmc), (unsigned long)
+ (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
+@@ -865,17 +869,123 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
++#ifdef CONFIG_MACH_FTEN_DT
++ int ret;
++ struct sh_mobile_sdhi *priv = container_of(host->pdata,
++ struct sh_mobile_sdhi,
++ mmc_data);
++#endif
+
+ tmio_mmc_host_remove(host);
+
+ if (p && p->cleanup)
+ p->cleanup(pdev);
+
++#ifdef CONFIG_MACH_FTEN_DT
++ ret = gpio_request(priv->wifi_xrst, "sh_mobile_sdhi");
++ if (ret != 0) {
++ dev_err(&pdev->dev,
++ "gpio_request(%d) failed(%d) remove\n",
++ priv->wifi_xrst, ret);
++ goto skip_wifi;
++ }
++ ret = gpio_direction_output(priv->wifi_xrst, 0);
++ if (ret != 0) {
++ dev_err(&pdev->dev,
++ "gpio_direction_output(%d) failed(%d) remove\n",
++ priv->wifi_xrst, ret);
++ }
++ gpio_free(priv->wifi_xrst);
++skip_wifi:
++#endif
++
++ return 0;
++}
++
++static int sh_mobile_sdhi_restore_noirq(struct device *dev)
++{
++ struct mmc_host *mmc = dev_get_drvdata(dev);
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++
++ sd_ctrl_write32(host, CTL_IRQ_MASK, 0x8b7f031d);
++ sd_ctrl_write32(host, CTL_STATUS, 0);
++#if 0
++ sh_mobile_sdhi_enable_sdbuf_acc32(host, false);
++ /* FIXME - should we set stop clock reg here */
++ sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
++ /* implicit BUG_ON(!res) */
++ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
++ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
++ msleep(2);
++ sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
++ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
++ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
++ msleep(2);
++ sd_ctrl_write32(host, CTL_IRQ_MASK, 0x8b7f031d);
++ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0040);
++ sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80E0);
++ sd_ctrl_write16(host, CTL_DMA_ENABLE, 0x1002);
++#endif
++ return 0;
++}
++
++static int sh_mobile_sdhi_restore(struct device *dev)
++{
++ struct mmc_host *mmc = dev_get_drvdata(dev);
++ struct tmio_mmc_host *host = mmc_priv(mmc);
++ struct sh_mobile_sdhi *priv = container_of(host->pdata,
++ struct sh_mobile_sdhi,
++ mmc_data);
++#if defined(CONFIG_MACH_FTEN_DT) || defined(CONFIG_PM_SLEEP)
++ int ret;
++#endif
++ int dma_size;
++ host->restore = true;
++
++#ifdef CONFIG_MACH_FTEN_DT
++ /* priv->wifi_xrst is 0 or more. */
++ if (priv->wifi_xrst >= 0) {
++ ret = gpio_request(priv->wifi_xrst, "sh_mobile_sdhi");
++ if (ret != 0) {
++ dev_err(dev, "gpio_request(%d) failed(%d) restore\n",
++ priv->wifi_xrst, ret);
++ goto skip_wifi;
++ }
++ ret = gpio_direction_output(priv->wifi_xrst, 1);
++ if (ret != 0) {
++ dev_err(dev, "gpio_direction_output(%d) failed(%d) restore\n",
++ priv->wifi_xrst, ret);
++ }
++ gpio_free(priv->wifi_xrst);
++ }
++skip_wifi:
++#endif
++
++ dma_size = sh_mobile_sdhi_get_xmit_size(priv->type,
++ priv->dma_priv.alignment_shift);
++
++ sd_ctrl_write16(host, SD_DMACR(priv->type), dma_size);
++
++#ifdef CONFIG_PM_SLEEP
++ ret = tmio_mmc_host_resume(dev);
++ host->restore = false;
++ return ret;
++#else
++ host->restore = false;
+ return 0;
++#endif
+ }
+
+ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
+- SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
++#ifdef CONFIG_PM_SLEEP
++ .suspend = tmio_mmc_host_suspend,
++ .resume = tmio_mmc_host_resume,
++ .freeze = tmio_mmc_host_suspend,
++ .thaw = tmio_mmc_host_resume,
++ .poweroff = tmio_mmc_host_suspend,
++#endif
++ .restore = sh_mobile_sdhi_restore,
++ .restore_noirq = sh_mobile_sdhi_restore_noirq,
+ SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume,
+ NULL)
+diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
+index c5b12ad..3efe03d 100644
+--- a/drivers/mmc/host/tmio_mmc.h
++++ b/drivers/mmc/host/tmio_mmc.h
+@@ -104,6 +104,7 @@ struct tmio_mmc_host {
+ bool resuming;
+ bool done_tuning;
+ struct completion completion;
++ bool restore;
+ };
+
+ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
+diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
+index 09c0c08..514af15 100644
+--- a/drivers/mmc/host/tmio_mmc_pio.c
++++ b/drivers/mmc/host/tmio_mmc_pio.c
+@@ -167,8 +167,20 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
+ if (host->set_clk_div)
+ host->set_clk_div(host->pdev, (clk>>22) & 1);
+
++#ifdef CONFIG_MACH_FTEN
++ clk |= sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL) & 0x0100;
++ if (host->pdata->flags & TMIO_MMC_SDCLK_AUTO_CONTROL &&
++ new_clock > host->mmc->f_init)
++ clk |= SDCLKOFFEN;
++ dev_dbg(&host->pdev->dev,
++ "clock=%d, clk=%08x, new_clock=%d, f_init=%d\n",
++ clock, clk, new_clock, host->mmc->f_init);
++ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x3ff);
++#else
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & 0x1ff);
+- msleep(10);
++#endif
++ if (!host->restore)
++ msleep(2);
+ }
+
+ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
+@@ -176,13 +188,15 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
+ /* implicit BUG_ON(!res) */
+ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
+ sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
+- if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
++ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++ && !host->restore)
+ msleep(10);
+ }
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~0x0100 &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+- if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
++ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++ && !host->restore)
+ msleep(10);
+ }
+
+@@ -190,14 +204,16 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
+ {
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+- if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+- msleep(10);
++ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++ && !host->restore)
++ msleep(2);
+
+ /* implicit BUG_ON(!res) */
+ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
+ sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
+- if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP))
+- msleep(10);
++ if (!(host->pdata->flags & TMIO_MMC_CLK_NO_SLEEP)
++ && !host->restore)
++ msleep(2);
+ }
+ }
+
+@@ -208,11 +224,11 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
+ /* implicit BUG_ON(!res) */
+ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
+ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
+- msleep(10);
++ msleep(2);
+ sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+ if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
+ sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
+- msleep(10);
++ msleep(2);
+ }
+
+ static void tmio_mmc_reset_work(struct work_struct *work)
+@@ -1134,16 +1150,21 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+ * is kept positive, so no suspending actually takes place.
+ */
+ if (ios->power_mode == MMC_POWER_ON && ios->clock) {
++ int reset_needed = 0;
+ if (host->power != TMIO_MMC_ON_RUN) {
+ tmio_mmc_clk_update(mmc);
+ pm_runtime_get_sync(dev);
+- if (host->resuming) {
+- tmio_mmc_reset(host);
+- host->resuming = false;
+- }
++ if (host->resuming)
++ reset_needed = 1;
+ }
++
+ if (host->power == TMIO_MMC_OFF_STOP)
++ reset_needed = 1;
++ if (reset_needed) {
+ tmio_mmc_reset(host);
++ if (host->resuming)
++ host->resuming = false;
++ }
+ tmio_mmc_set_clock(host, ios->clock);
+ if (host->power == TMIO_MMC_OFF_STOP)
+ /* power up SD card and the bus */
+@@ -1497,7 +1518,7 @@ int tmio_mmc_host_resume(struct device *dev)
+
+ /* The MMC core will perform the complete set up */
+ host->resuming = true;
+- return mmc_resume_host(mmc);
++ return mmc_resume_host(mmc);
+ }
+ EXPORT_SYMBOL(tmio_mmc_host_resume);
+ #endif
+--
+1.8.3.1
+