From patchwork Wed Dec 26 01:54:08 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cho KyongHo X-Patchwork-Id: 1910451 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id A5888DF23A for ; Wed, 26 Dec 2012 02:00:13 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TngET-0001zQ-FA; Wed, 26 Dec 2012 01:56:33 +0000 Received: from mailout3.samsung.com ([203.254.224.33]) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TngCO-0000tG-4S for linux-arm-kernel@lists.infradead.org; Wed, 26 Dec 2012 01:54:30 +0000 Received: from epcpsbgm1.samsung.com (epcpsbgm1 [203.254.230.26]) by mailout3.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MFM001IT7XPIIE0@mailout3.samsung.com> for linux-arm-kernel@lists.infradead.org; Wed, 26 Dec 2012 10:54:09 +0900 (KST) Received: from epcpsbgm1.samsung.com ( [203.254.230.51]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 89.36.01231.1C85AD05; Wed, 26 Dec 2012 10:54:09 +0900 (KST) X-AuditID: cbfee61a-b7fa66d0000004cf-61-50da58c1a1c2 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm1.samsung.com (EPCPMTA) with SMTP id 77.36.01231.0C85AD05; Wed, 26 Dec 2012 10:54:08 +0900 (KST) Received: from DOPULLIPCHO06 ([12.23.118.152]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0MFM000AK7Y8XN30@mmp1.samsung.com> for linux-arm-kernel@lists.infradead.org; Wed, 26 Dec 2012 10:54:08 +0900 (KST) From: Cho KyongHo To: 'Linux ARM Kernel' , 'Linux IOMMU' , 'Linux Kernel' , 'Linux Samsung SOC' Subject: [PATCH v6 08/12] iommu/exynos: add support for runtime pm and suspend/resume Date: Wed, 26 Dec 2012 10:54:08 +0900 Message-id: <003b01cde30b$e49c8220$add58660$%cho@samsung.com> MIME-version: 1.0 X-Mailer: Microsoft Office Outlook 12.0 Thread-index: Ac3jC+R69X3K1tFGSyGUhimqnNyILQ== Content-language: ko DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrHIsWRmVeSWpSXmKPExsVy+t8zY92DEbcCDJo3yFpsenyN1YHRY/OS +gDGKC6blNSczLLUIn27BK6MxVvbmAquulVMn/GdqYHxr2UXIyeHhICJRO+chSwQtpjEhXvr 2UBsIYFljBJrbsXA1Cx4+gEozgUUX8Qocf7bR2YIZzmTxNG17ewgVWwCWhKr5x5nBLFFBO4y Srw8zwNSxCzwl1Hi05lDYEXCAuES1578AytiEVCVuPVmPthqXgFbifZfTxghbEGJH5PvgcWZ BQwk3s/qY4Ww5SU2r3kLtJkD6CR1iUd/dUFMEQE9iTWT5CEqRCT2vXgHNV1A4tvkQywQ1bIS mw6AnSwh0M4usWneB3aIxyQlDq64wTKBUWwWksWzkCyehWTxLCQrFjCyrGIUTS1ILihOSs81 1CtOzC0uzUvXS87P3cQIiRSpHYwrGywOMQpwMCrx8G78fjNAiDWxrLgy9xCjBAezkgiv80eg EG9KYmVValF+fFFpTmrxIUYfoMsnMkuJJucDozivJN7Q2NjEzMTUxNzS1NwUh7CSOG+zR0qA kEB6YklqdmpqQWoRzDgmDk6pBsakEo+EJUnytxg0RbirH1U1ZotveT15n29qcZOXwgvPQD6N P5zX1/j2Rj/8232ufK/9nVeMSfvDH69bY3HWm+V972cWqzbNI89d/yit8V4sH9tscek8s/6U qDg1ZfmPlU3tvR/PHvHpWx094emKIIYg3v2p16/cj7NLyPEuEXnbt8v17tW4mUosxRmJhlrM RcWJACMSnbfBAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrHIsWRmVeSWpSXmKPExsVy+t9jAd0DEbcCDK5eVrLY9PgaqwOjx+Yl 9QGMUQ2MNhmpiSmpRQqpecn5KZl56bZK3sHxzvGmZgaGuoaWFuZKCnmJuam2Si4+AbpumTlA U5UUyhJzSoFCAYnFxUr6dpgmhIa46VrANEbo+oYEwfUYGaCBhHWMGYu3tjEVXHWrmD7jO1MD 41/LLkZODgkBE4kFTz+wQdhiEhfurQeyuTiEBBYxSpz/9pEZwlnOJHF0bTs7SBWbgJbE6rnH GUFsEYG7jBIvz/OAFDEL/GWU+HTmEFiRsEC4xLUn/8CKWARUJW69mc8CYvMK2Eq0/3rCCGEL SvyYfA8szixgIPF+Vh8rhC0vsXnNW6DNHEAnqUs8+qsLYooI6EmsmSQPUSEise/FO8YJjAKz kAyahWTQLCSDZiFpWcDIsopRNLUguaA4KT3XUK84Mbe4NC9dLzk/dxMjOBKfSe1gXNlgcYhR gINRiYd34/ebAUKsiWXFlbmHGCU4mJVEeJ0/AoV4UxIrq1KL8uOLSnNSiw8x+gD9OZFZSjQ5 H5gk8kriDY1NzIwsjcwsjEzMzXEIK4nzNnukBAgJpCeWpGanphakFsGMY+LglGpgZE66qXFD ZZdMpcNsAdEM89u/L/KKmyox7nHxW1oTEjVlVfzztUJrH/PXiyz9MNv5gqqK6qKzHcody+z5 Cs/wzdJL6nRs294lcaso6ODLHied9jcv2n9bvWU9y6tzy+KowUYtLeUtJ3VPv2XkOrn+Rq+6 /noxdqXqaUz8F1S3907bEbEs2sJbiaU4I9FQi7moOBEAjkYIovECAAA= X-CFilter-Loop: Reflected X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20121225_205425_006516_01619AA3 X-CRM114-Status: GOOD ( 16.39 ) X-Spam-Score: -7.5 (-------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-7.5 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [203.254.224.33 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.7 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.1 HDRS_LCASE Odd capitalization of message header 0.0 T_MANY_HDRS_LCASE Odd capitalization of multiple message headers Cc: 'Kukjin Kim' , 'Hyunwoong Kim' , 'Prathyush' , 'Joerg Roedel' , 'Subash Patel' , 'Rahul Sharma' X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org This change enables the client device drivers not to care about the state of System MMU since the internal state of System MMU is controlled by the runtime PM and suspend/resume callback functions. Signed-off-by: KyongHo Cho --- drivers/iommu/exynos-iommu.c | 176 ++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a0e5ee1..c3c5b7b 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -187,6 +187,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; unsigned long pgtable; + bool runtime_active; void __iomem *sfrbases[0]; }; @@ -409,7 +410,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) data->pgtable = 0; data->domain = NULL; - __sysmmu_disable_nocount(data); + if (data->runtime_active) + __sysmmu_disable_nocount(data); dev_dbg(data->sysmmu, "Disabled\n"); } else { @@ -422,30 +424,6 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data) return disabled; } -static bool __exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct device *sysmmu; - - BUG_ON(!has_sysmmu(dev)); - - spin_lock_irqsave(&owner->lock, flags); - - /* Every call to __sysmmu_disable() must return same result */ - for_each_sysmmu(dev, sysmmu) { - struct sysmmu_drvdata *data = dev_get_drvdata(sysmmu); - disabled = __sysmmu_disable(data); - if (disabled) - data->master = NULL; - } - - spin_unlock_irqrestore(&owner->lock, flags); - - return disabled; -} - static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) { int i; @@ -481,7 +459,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, data->pgtable = pgtable; data->domain = domain; - __sysmmu_enable_nocount(data); + if (data->runtime_active) + __sysmmu_enable_nocount(data); dev_dbg(data->sysmmu, "Enabled\n"); } else { @@ -537,42 +516,31 @@ static int __exynos_sysmmu_enable(struct device *dev, unsigned long pgtable, int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) { - int ret; - struct device *sysmmu; - BUG_ON(!memblock_is_memory(pgtable)); - for_each_sysmmu(dev, sysmmu) { - ret = pm_runtime_get_sync(sysmmu); - if (ret < 0) - break; - } - - if (ret < 0) { - struct device *start; - for_each_sysmmu_until(dev, start, sysmmu) - pm_runtime_put(start); - - return ret; - } - - ret = __exynos_sysmmu_enable(dev, pgtable, NULL); - if (ret < 0) - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); - - return ret; + return __exynos_sysmmu_enable(dev, pgtable, NULL); } bool exynos_sysmmu_disable(struct device *dev) { - bool disabled; + unsigned long flags; + bool disabled = true; + struct exynos_iommu_owner *owner = dev->archdata.iommu; struct device *sysmmu; - disabled = __exynos_sysmmu_disable(dev); + BUG_ON(!has_sysmmu(dev)); + + spin_lock_irqsave(&owner->lock, flags); + + /* Every call to __sysmmu_disable() must return same result */ + for_each_sysmmu(dev, sysmmu) { + struct sysmmu_drvdata *drvdata = dev_get_drvdata(sysmmu); + disabled = __sysmmu_disable(drvdata); + if (disabled) + drvdata->master = NULL; + } - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); + spin_unlock_irqrestore(&owner->lock, flags); return disabled; } @@ -588,7 +556,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova) data = dev_get_drvdata(sysmmu); spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data)) { + if (is_sysmmu_active(data) && + data->runtime_active) { int i; for (i = 0; i < data->nsfrs; i++) { if (sysmmu_block(data->sfrbases[i])) { @@ -817,7 +786,7 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) data->sysmmu = dev; spin_lock_init(&data->lock); - pm_runtime_enable(dev); + data->runtime_active = !pm_runtime_enabled(dev); platform_set_drvdata(pdev, data); @@ -828,6 +797,64 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM_SLEEP +static int sysmmu_suspend(struct device *dev) +{ + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long flags; + spin_lock_irqsave(&drvdata->lock, flags); + if (is_sysmmu_active(drvdata) && + (!pm_runtime_enabled(dev) || drvdata->runtime_active)) + __sysmmu_disable_nocount(drvdata); + spin_unlock_irqrestore(&drvdata->lock, flags); + return 0; +} + +static int sysmmu_resume(struct device *dev) +{ + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long flags; + spin_lock_irqsave(&drvdata->lock, flags); + if (is_sysmmu_active(drvdata) && + (!pm_runtime_enabled(dev) || drvdata->runtime_active)) { + __sysmmu_enable_nocount(drvdata); + } + spin_unlock_irqrestore(&drvdata->lock, flags); + return 0; +} +#endif + +#ifdef CONFIG_PM_RUNTIME +static int sysmmu_runtime_suspend(struct device *dev) +{ + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long flags; + spin_lock_irqsave(&drvdata->lock, flags); + if (is_sysmmu_active(drvdata)) + __sysmmu_disable_nocount(drvdata); + drvdata->runtime_active = false; + spin_unlock_irqrestore(&drvdata->lock, flags); + return 0; +} + +static int sysmmu_runtime_resume(struct device *dev) +{ + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev); + unsigned long flags; + spin_lock_irqsave(&drvdata->lock, flags); + drvdata->runtime_active = true; + if (is_sysmmu_active(drvdata)) + __sysmmu_enable_nocount(drvdata); + spin_unlock_irqrestore(&drvdata->lock, flags); + return 0; +} +#endif + +static const struct dev_pm_ops __pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sysmmu_suspend, sysmmu_resume) + SET_RUNTIME_PM_OPS(sysmmu_runtime_suspend, sysmmu_runtime_resume, NULL) +}; + /* * Descriptions of Device Tree node for System MMU * @@ -865,6 +892,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { .driver = { .owner = THIS_MODULE, .name = "exynos-sysmmu", + .pm = &__pm_ops, .of_match_table = of_match_ptr(sysmmu_of_match), } }; @@ -939,13 +967,9 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain) spin_lock_irqsave(&priv->lock, flags); list_for_each_entry_safe(owner, n, &priv->clients, client) { - struct device *sysmmu; - while (!__exynos_sysmmu_disable(owner->dev)) + while (!exynos_sysmmu_disable(owner->dev)) ; /* until System MMU is actually disabled */ list_del_init(&owner->client); - - for_each_sysmmu(owner->dev, sysmmu) - pm_runtime_put(sysmmu); } spin_unlock_irqrestore(&priv->lock, flags); @@ -968,7 +992,6 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, struct exynos_iommu_domain *priv = domain->priv; unsigned long flags; int ret; - struct device *sysmmu; if (WARN_ON(!list_empty(&owner->client))) { bool found = false; @@ -993,20 +1016,6 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return 0; } - for_each_sysmmu(dev, sysmmu) { - ret = pm_runtime_get_sync(sysmmu); - if (ret < 0) - break; - } - - if (ret < 0) { - struct device *start; - for_each_sysmmu_until(dev, start, sysmmu) - pm_runtime_put(start); - - return ret; - } - spin_lock_irqsave(&priv->lock, flags); ret = __exynos_sysmmu_enable(dev, __pa(priv->pgtable), domain); @@ -1021,15 +1030,12 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, spin_unlock_irqrestore(&priv->lock, flags); - if (ret < 0) { + if (ret < 0) dev_err(dev, "%s: Failed to attach IOMMU with pgtable %#lx\n", __func__, __pa(priv->pgtable)); - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); - } else { + else dev_dbg(dev, "%s: Attached new IOMMU with pgtable 0x%lx\n", __func__, __pa(priv->pgtable)); - } return ret; } @@ -1045,7 +1051,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, list_for_each_entry_safe(owner, n, &priv->clients, client) { if (owner == dev->archdata.iommu) { - if (__exynos_sysmmu_disable(dev)) + if (exynos_sysmmu_disable(dev)) list_del_init(&owner->client); else BUG(); @@ -1055,14 +1061,10 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, spin_unlock_irqrestore(&priv->lock, flags); - if (owner == dev->archdata.iommu) { - struct device *sysmmu; + if (owner == dev->archdata.iommu) dev_dbg(dev, "%s: Detached IOMMU with pgtable %#lx\n", __func__, __pa(priv->pgtable)); - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); - - } else + else dev_dbg(dev, "%s: No IOMMU is attached\n", __func__); }