Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932393Ab2KVUU4 (ORCPT ); Thu, 22 Nov 2012 15:20:56 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:59549 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756144Ab2KVTpp (ORCPT ); Thu, 22 Nov 2012 14:45:45 -0500 X-AuditID: cbfee61a-b7fa66d0000004cf-21-50ae0d8a8de2 From: Cho KyongHo To: linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, iommu@lists.linux-foundation.org, linux-kernel@vger.kernel.org Cc: "'Joerg Roedel'" , "'Sanghyun Lee'" , "'Kukjin Kim'" , sw0312.kim@samsung.com, "'Subash Patel'" , prathyush.k@samsung.com, rahul.sharma@samsung.com Subject: [PATCH v4 09/12] iommu/exynos: add support for runtime pm and suspend/resume Date: Thu, 22 Nov 2012 20:33:30 +0900 Message-id: <002a01cdc8a5$3240d0c0$96c27240$%cho@samsung.com> MIME-version: 1.0 Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7bit X-Mailer: Microsoft Office Outlook 12.0 Thread-index: Ac3IpTIpupm183UsTtmNPHjKHlCEzA== Content-language: ko DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrCIsWRmVeSWpSXmKPExsVy+t8zY90u3nUBBo1X5C0u75rDZjHj/D4m ByaPz5vkAhijuGxSUnMyy1KL9O0SuDLeX73EUjDNveJS60vWBsZ9Vl2MnBwSAiYS989fYoWw xSQu3FvP1sXIxSEksIxR4tSxqyxdjBxgRfPXBELEpzNK/NvSAlX0j1Hi8vyZ7CDdbAJaEqvn HmcESYgI9DJKXOj/ygTiMAv8YJSYvvkXC0iVsEC4xLLDFxlBbBYBVYnNqw6DxXkFbCUeTfjI CGELSvyYfA8szgw0df3O40wQtrzE5jVvmSFOUpd49FcXJCwioCfx4Nw1VogSEYl9L95BjReQ +Db5ENQHshKbDjBDfDmZXWLnIwMIW1Li4IobLBMYxWYhWTwLyeJZSBbPQrJhASPLKkbR1ILk guKk9FxDveLE3OLSvHS95PzcTYyQqJHawbiyweIQowAHoxIPr6bz2gAh1sSy4srcQ4wSHMxK Irz3uNcFCPGmJFZWpRblxxeV5qQWH2L0ATp8IrOUaHI+MKLzSuINjY1NzExMTcwtTc1NcQgr ifM2e6QECAmkJ5akZqemFqQWwYxj4uCUamC0mqHbv4LNq3N6eirrvzkSb15LHT+ZovFfrNo3 +cOmGIa0KavCQp/opps+5zYtv1j5TTZg/pE320MZtI69KioQfp3CfNtRwWGfwnx2YVflSL49 qy+1zyt7fOJORuTWfmfxS4v/bZv++sCrlk/SHJOu/dKuOVeW3XzqWHad6/Qd5QKy3l+PB0cr sRRnJBpqMRcVJwIAvgflTscCAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrCIsWRmVeSWpSXmKPExsVy+t9jQd0u3nUBBpNfS1lc3jWHzWLG+X1M DkwenzfJBTBGNTDaZKQmpqQWKaTmJeenZOal2yp5B8c7x5uaGRjqGlpamCsp5CXmptoqufgE 6Lpl5gCNVlIoS8wpBQoFJBYXK+nbYZoQGuKmawHTGKHrGxIE12NkgAYS1jFmvL96iaVgmnvF pdaXrA2M+6y6GDk4JARMJOavCexi5AQyxSQu3FvP1sXIxSEkMJ1R4t+WFijnH6PE5fkz2UGq 2AS0JFbPPc4IkhAR6GWUuND/lQnEYRb4wSgxffMvFpAqYYFwiWWHLzKC2CwCqhKbVx0Gi/MK 2Eo8mvCREcIWlPgx+R5YnBlo6vqdx5kgbHmJzWveMkOcpy7x6K8uSFhEQE/iwblrrBAlIhL7 XrxjnMAoMAvJpFlIJs1CMmkWkpYFjCyrGEVTC5ILipPScw31ihNzi0vz0vWS83M3MYKj8pnU DsaVDRaHGAU4GJV4eDWd1wYIsSaWFVfmHmKU4GBWEuG9x70uQIg3JbGyKrUoP76oNCe1+BCj D9CjE5mlRJPzgQkjryTe0NjEzMjSyMzCyMTcHIewkjhvs0dKgJBAemJJanZqakFqEcw4Jg5O qQbGxoVP/ffoF74V5JjetXR7rrwlu5+D/dp6vz9v14TuL7CaxJM++ZtArewM8zUeb9ZwXxF9 UeB0+5j4uZnnNDUUHPnSLDd5ZMkLXdXj/TrV6samPsZZU29U/G9RuP9sY1NH3eZk9yNfT/Cu qcqZ66g3xT9b4f4cO7vafexuFhfCFFyVzHQEf/kpsRRnJBpqMRcVJwIAYLsV8/cCAAA= X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 9041 Lines: 319 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 | 175 ++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 86 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 576f6b1..c2525ed 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -208,6 +208,7 @@ struct sysmmu_drvdata { struct iommu_domain *domain; sysmmu_fault_handler_t fault_handler; unsigned long pgtable; + bool runtime_active; void __iomem *sfrbases[0]; }; @@ -477,7 +478,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *drvdata) drvdata->pgtable = 0; drvdata->domain = NULL; - __sysmmu_disable_nocount(drvdata); + if (drvdata->runtime_active) + __sysmmu_disable_nocount(drvdata); dev_dbg(drvdata->sysmmu, "Disabled\n"); } else { @@ -490,30 +492,6 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *drvdata) 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 *drvdata = dev_get_drvdata(sysmmu); - disabled = __sysmmu_disable(drvdata); - if (disabled) - drvdata->master = NULL; - } - - spin_unlock_irqrestore(&owner->lock, flags); - - return disabled; -} - static void __sysmmu_enable_nocount(struct sysmmu_drvdata *drvdata) { int i; @@ -554,7 +532,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata *drvdata, drvdata->pgtable = pgtable; drvdata->domain = domain; - __sysmmu_enable_nocount(drvdata); + if (drvdata->runtime_active) + __sysmmu_enable_nocount(drvdata); dev_dbg(drvdata->sysmmu, "Enabled\n"); } else { @@ -610,42 +589,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)); - for_each_sysmmu(dev, sysmmu) - pm_runtime_put(sysmmu); + 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; + } + + spin_unlock_irqrestore(&owner->lock, flags); return disabled; } @@ -661,7 +629,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])) { @@ -895,6 +864,7 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) ret = __sysmmu_setup(dev, data); if (!ret) { + data->runtime_active = !pm_runtime_enabled(dev); data->sysmmu = dev; spin_lock_init(&data->lock); @@ -909,6 +879,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 * @@ -946,6 +974,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), } }; @@ -1007,13 +1036,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); @@ -1036,7 +1061,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; @@ -1061,20 +1085,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); @@ -1089,15 +1099,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; } @@ -1113,7 +1120,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(); @@ -1123,14 +1130,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__); } -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/