From patchwork Tue Apr 19 10:52:01 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 717821 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3JAqHND000619 for ; Tue, 19 Apr 2011 10:52:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754609Ab1DSKwQ (ORCPT ); Tue, 19 Apr 2011 06:52:16 -0400 Received: from moutng.kundenserver.de ([212.227.126.186]:53156 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754464Ab1DSKwQ (ORCPT ); Tue, 19 Apr 2011 06:52:16 -0400 Received: from axis700.grange (pD9EB8F0A.dip0.t-ipconnect.de [217.235.143.10]) by mrelayeu.kundenserver.de (node=mreu0) with ESMTP (Nemesis) id 0Le960-1PVM8j39NU-00q4XY; Tue, 19 Apr 2011 12:52:01 +0200 Received: by axis700.grange (Postfix, from userid 1000) id 631F5189B89; Tue, 19 Apr 2011 12:52:01 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by axis700.grange (Postfix) with ESMTP id 5969A189B88; Tue, 19 Apr 2011 12:52:01 +0200 (CEST) Date: Tue, 19 Apr 2011 12:52:01 +0200 (CEST) From: Guennadi Liakhovetski X-X-Sender: lyakh@axis700.grange To: linux-sh@vger.kernel.org cc: linux-mmc@vger.kernel.org, Magnus Damm , Simon Horman , Alan Stern , "Rafael J. Wysocki" Subject: [PATCH 2/3 v2] MMC: add runtime and system power-management support to the MMCIF driver In-Reply-To: Message-ID: References: MIME-Version: 1.0 X-Provags-ID: V02:K0:uKZ2oNPNIP2sWZSWGsjwAWap0gxT6VjAFEUIqNcch8v 7qmJoE4B73xgt8SKbYLddyQ6a+rqld3YaOtAYGtMZbRZYtvfJg 6IPVu4gcAiEJPFAlm923vSZfAhOhKiqIxBHmX98XeYHF8BXAd+ 2j3hG7t96r/d7CLFGb4/zkJhr5Tych/PPT9B1gQdnyRA5WuxaG t1DizeAiTWxeIXUOHDusexKpvTsAX6TtlhdsMsg28E= Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 19 Apr 2011 10:52:19 +0000 (UTC) Adding support for runtime power-management to the MMCIF driver allows it to save power as long as no card is present. System-wide power management has been verified with experimental PM patches on AP4- based systems. Signed-off-by: Guennadi Liakhovetski --- With this patch and with the patch to mmc/core/bus.c, that I've sent a couple of minutes ago http://article.gmane.org/gmane.linux.kernel.mmc/7433 PM works correctly with all possible modprobe / rmmod, card-insert / eject, and STR scenarios, that I could come up with. The part in sh_mmcif_remove() is a bit rough though... -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d3871b6..23eb0ec 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #define DRIVER_NAME "sh_mmcif" @@ -173,6 +174,7 @@ struct sh_mmcif_host { struct completion intr_wait; enum mmcif_state state; spinlock_t lock; + bool power; /* DMA support */ struct dma_chan *chan_rx; @@ -877,11 +879,21 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode == MMC_POWER_UP) { if (p->set_pwr) p->set_pwr(host->pd, ios->power_mode); + if (!host->power) { + pm_runtime_get_sync(&host->pd->dev); + host->power = true; + } } else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) { /* clock stop */ sh_mmcif_clock_control(host, 0); - if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) - p->down_pwr(host->pd); + if (ios->power_mode == MMC_POWER_OFF) { + if (host->power) { + pm_runtime_put(&host->pd->dev); + host->power = false; + } + if (p->down_pwr) + p->down_pwr(host->pd); + } host->state = STATE_IDLE; return; } @@ -1053,6 +1065,12 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_sync_reset(host); platform_set_drvdata(pdev, host); + pm_runtime_enable(&pdev->dev); + host->power = false; + ret = pm_runtime_resume(&pdev->dev); + if (ret < 0) + goto clean_up2; + /* See if we also get DMA */ sh_mmcif_request_dma(host, pd); @@ -1063,13 +1081,13 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); - goto clean_up2; + goto clean_up3; } ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host); if (ret) { free_irq(irq[0], host); dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto clean_up2; + goto clean_up3; } sh_mmcif_detect(host->mmc); @@ -1079,7 +1097,10 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; +clean_up3: + pm_runtime_suspend(&pdev->dev); clean_up2: + pm_runtime_disable(&pdev->dev); clk_disable(host->hclk); clean_up1: mmc_free_host(mmc); @@ -1112,15 +1133,54 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) clk_disable(host->hclk); mmc_free_host(host->mmc); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_suspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); return 0; } +#ifdef CONFIG_PM +static int sh_mmcif_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mmcif_host *host = platform_get_drvdata(pdev); + int ret = mmc_suspend_host(host->mmc); + + if (!ret) { + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + clk_disable(host->hclk); + } + + return ret; +} + +static int sh_mmcif_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mmcif_host *host = platform_get_drvdata(pdev); + + clk_enable(host->hclk); + + return mmc_resume_host(host->mmc); +} +#else +#define sh_mmcif_suspend NULL +#define sh_mmcif_resume NULL +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { + .suspend = sh_mmcif_suspend, + .resume = sh_mmcif_resume, +}; + static struct platform_driver sh_mmcif_driver = { .probe = sh_mmcif_probe, .remove = sh_mmcif_remove, .driver = { .name = DRIVER_NAME, + .pm = &sh_mmcif_dev_pm_ops, }, };