From patchwork Tue May 19 06:36:47 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chaotian Jing X-Patchwork-Id: 6434191 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id C4957C0432 for ; Tue, 19 May 2015 06:42:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A713D20384 for ; Tue, 19 May 2015 06:42:53 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9ABAF2037F for ; Tue, 19 May 2015 06:42:52 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YubCV-0000Js-Fr; Tue, 19 May 2015 06:40:27 +0000 Received: from [210.61.82.183] (helo=mailgw01.mediatek.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YubAZ-0006VH-Fb; Tue, 19 May 2015 06:38:31 +0000 X-Listener-Flag: 11101 Received: from mtkhts09.mediatek.inc [(172.21.101.70)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 1063332586; Tue, 19 May 2015 14:37:45 +0800 Received: from localhost.localdomain (10.17.3.153) by mtkhts09.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 14.3.181.6; Tue, 19 May 2015 14:37:44 +0800 From: Chaotian Jing To: Rob Herring , Matthias Brugger , Chris Ball , Ulf Hansson Subject: [PATCH v4 3/7] mmc: mediatek: Add PM support for MMC driver Date: Tue, 19 May 2015 14:36:47 +0800 Message-ID: <1432017411-2996-4-git-send-email-chaotian.jing@mediatek.com> X-Mailer: git-send-email 1.8.1.1.dirty In-Reply-To: <1432017411-2996-1-git-send-email-chaotian.jing@mediatek.com> References: <1432017411-2996-1-git-send-email-chaotian.jing@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150518_233828_025434_3BB34857 X-CRM114-Status: GOOD ( 14.91 ) X-Spam-Score: 1.3 (+) Cc: Mark Rutland , James Liao , srv_heupstream@mediatek.com, Arnd Bergmann , devicetree@vger.kernel.org, Hongzhou Yang , Catalin Marinas , bin.zhang@mediatek.com, linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Will Deacon , linux-gpio@vger.kernel.org, linux-mediatek@lists.infradead.org, Chaotian Jing , Sascha Hauer , "Joe.C" , Eddie Huang , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add PM support for Mediatek MMC driver Save/restore registers when PM Signed-off-by: Chaotian Jing --- drivers/mmc/host/mtk-sd.c | 99 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 26e9590..c0b2e2d 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -212,6 +213,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 +256,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 */ @@ -287,6 +298,8 @@ struct msdc_host { bool vqmmc_enabled; bool sclk_enabled; /* source clock enabled */ bool hclk_enabled; /* Hclk enabled */ + bool in_suspend; /* used for apply restore regs */ + struct msdc_save_para save_para; /* used when gate HCLK */ }; static void sdr_set_bits(void __iomem *reg, u32 bs) @@ -459,6 +472,30 @@ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, timeout); } +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); + host->in_suspend = true; +} + +static void msdc_restore_reg(struct msdc_host *host) +{ + if (host->in_suspend == false) + return; + 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); + host->in_suspend = false; +} + static void msdc_disable_src_clk(struct msdc_host *host) { if (host->sclk_enabled) { @@ -479,6 +516,10 @@ static void msdc_enable_src_clk(struct msdc_host *host) static void msdc_gate_clock(struct msdc_host *host) { + /* Save register first, only when clk is on */ + if (host->sclk_enabled) + msdc_save_reg(host); + msdc_disable_src_clk(host); if (!IS_ERR(host->h_clk) && host->hclk_enabled) { clk_disable_unprepare(host->h_clk); @@ -493,6 +534,8 @@ static void msdc_ungate_clock(struct msdc_host *host) host->hclk_enabled = true; } msdc_enable_src_clk(host); + /* Then restore register */ + msdc_restore_reg(host); } static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz) @@ -710,6 +753,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 */ @@ -866,6 +912,8 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq); spin_unlock_irqrestore(&host->lock, flags); + pm_runtime_get_sync(host->dev); + if (mrq->data) msdc_prepare_data(host, mrq); @@ -1130,7 +1178,8 @@ static void msdc_init_hw(struct msdc_host *host) sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1); writel(0xffff0089, host->base + MSDC_PATCH_BIT1); /* Configure to enable SDIO mode. - it's must otherwise sdio cmd5 failed */ + * it's must otherwise sdio cmd5 failed + */ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); /* disable detect SDIO device interrupt function */ @@ -1185,6 +1234,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; @@ -1200,7 +1251,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; @@ -1234,6 +1285,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 = { @@ -1355,12 +1410,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); @@ -1388,10 +1449,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); @@ -1403,6 +1469,30 @@ static int msdc_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int msdc_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct msdc_host *host = mmc_priv(mmc); + + 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); + return 0; +} +#endif + +static const struct dev_pm_ops msdc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL) +}; + static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt8135-mmc", }, {} @@ -1414,6 +1504,7 @@ static struct platform_driver mt_msdc_driver = { .driver = { .name = "mtk-msdc", .of_match_table = msdc_of_ids, + .pm = &msdc_dev_pm_ops, }, };