From patchwork Tue Sep 13 12:49:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 76069 Delivered-To: patch@linaro.org Received: by 10.140.106.72 with SMTP id d66csp1345216qgf; Tue, 13 Sep 2016 05:49:43 -0700 (PDT) X-Received: by 10.98.150.213 with SMTP id s82mr1055220pfk.86.1473770983338; Tue, 13 Sep 2016 05:49:43 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l75si13519684pfb.232.2016.09.13.05.49.43; Tue, 13 Sep 2016 05:49:43 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-samsung-soc-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-samsung-soc-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-samsung-soc-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754653AbcIMMt3 (ORCPT + 4 others); Tue, 13 Sep 2016 08:49:29 -0400 Received: from mailout3.w1.samsung.com ([210.118.77.13]:46378 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751488AbcIMMtX (ORCPT ); Tue, 13 Sep 2016 08:49:23 -0400 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout3.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0ODG00LGC0Y8LI00@mailout3.w1.samsung.com>; Tue, 13 Sep 2016 13:49:21 +0100 (BST) Received: from eusmges1.samsung.com (unknown [203.254.199.239]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20160913124919eucas1p1ad997b218de485d4d4f1bf0ac7c687de~z4uEPZeFf2669126691eucas1p1r; Tue, 13 Sep 2016 12:49:19 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges1.samsung.com (EUCPMTA) with SMTP id 3A.95.23383.FC5F7D75; Tue, 13 Sep 2016 13:49:19 +0100 (BST) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20160913124918eucas1p29f49efbb142d416215482c29b53daff8~z4uDlZqpN1319313193eucas1p2X; Tue, 13 Sep 2016 12:49:18 +0000 (GMT) X-AuditID: cbfec7ef-f79e76d000005b57-aa-57d7f5cfd039 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id 62.AF.07726.0A5F7D75; Tue, 13 Sep 2016 13:48:32 +0100 (BST) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0ODG00I0K0Y0D650@eusync4.samsung.com>; Tue, 13 Sep 2016 13:49:18 +0100 (BST) From: Marek Szyprowski To: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux-foundation.org, linux-samsung-soc@vger.kernel.org Cc: Marek Szyprowski , Joerg Roedel , Inki Dae , Kukjin Kim , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz , "Rafael J. Wysocki" , Mark Brown , "Luis R. Rodriguez" , Greg Kroah-Hartman , Tomeu Vizoso , Lukas Wunner , Kevin Hilman Subject: [PATCH v3 2/2] iommu/exynos: Add proper runtime pm support Date: Tue, 13 Sep 2016 14:49:01 +0200 Message-id: <1473770941-8337-3-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1473770941-8337-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAzWSbUhTYRTHfe7u7u5Gk+sUffA1VvaipA768IQWSZaXDOyDMEowV17UUieb ivolU/NlmRMtvWDpwmViyvIlEUOd07RluqamQliYzqRpQtqHyiy3q99+h/M///85cEiepILv TaZmZDGqDEWalBDhPaO/Jk9Yfs7Jw+wfQ1EHa+Cjh4vLBCpqMhCo+nMVjnSD4ai8/oUA2Vdl SLtk5yFb1xKGpvseEWjz/ghArGUAQ6tffVD7yIIAzVfZAHo3PsVHle1TxFk3enmoAaN7F/SA 7mwtJ+jBx20Cumb+GaC7P5TidGV3K6A3O/1ptrSHf1l4VRSRxKSl5jCq0DOJopSdcis/cywx d2KlBC8A1lgNEJKQOgn1i4UCjj3h+08GQgNEpIRqBrB51Y5zxSaA271v8f2JDcMbvoOdqn/6 vYkCDP4xDjutCEoGNesaZ8ODugPgVpkecxQ86gEOtWwL5lC5U+fgROMK4WCcCoTbxbW7tiQp pqKhsf4ml+YPzaM1zjQhRcOZLTNw+EBqVgDvrdUChx5SfrDTyOP0UXBQY9/b1B1+G+veu80X lpcNYRxrASy8G8wxC+DkupjjcDg8ZnVm8ShXWN1Tx+PsxbCsRMJJaNjYWLFnHwltrwb43PH1 APbXLQmqgK8OuLQCDyZbnZ7MqGUhakW6OjsjOeSGMr0T7P7I+M7YRi+wFcWZAEUC6QFxwfNZ uYSvyFHnpZsAJHlSD3Hwjzm5RJykyMtnVMprquw0Rm0CPiQu9RL362bkEipZkcXcYphMRrXf xUihdwEIqCyO6Xu58tSuPdSk85Ne8T9y2Jo6bcEr46JbbruVdSlLMySm3PqhqoYvrsZ837XY PGV7i3n9QlZhUELAEhsdxi4Glpw+iGs6dF7H27SY+yHt+S2JiznyqKclxjM+6OKxBKFPQoCy Ne3v71OvnxDfWVtzhOxSvCX1epR1hqWluDpFIQviqdSK/yGP6J4fAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrDIsWRmVeSWpSXmKPExsVy+t/xa7oLvl4PNzj6Tt1i44z1rBZTHz5h s2hevJ7NYtL9CSwWC/ZbW3TO3sBu8fqFoUX/49fMFk83P2ayuLxrDpvF594jjBYzzu9jsnjx XNpi7ZG77BY3JjxltDhz+hKrRd/aS2wOgh5PDs5j8thxdwmjx6ZVnWwe++euYfeYfGM5o8eW q+0sHn1bVjF6fN4k5zGjfRtrAGeUm01GamJKapFCal5yfkpmXrqtUmiIm66FkkJeYm6qrVKE rm9IkJJCWWJOKZBnZIAGHJwD3IOV9O0S3DL+dV5kLTieUHH2WRtLA+NF/y5GTg4JAROJ9+tP sELYYhIX7q1n62Lk4hASWMIo8ej2cxaQhJBAE5PEyiMCIDabgKFE19susCIRgUZGiW1bJ7KD OMwC01gkpnR3gXUICzhLnJ3/jA3EZhFQlfjTMg1oBQcHr4C7xIHZWRDb5CROHpsMtplTwEPi ypeTjBDL3CU2/LvHOoGRdwEjwypGkdTS4tz03GJDveLE3OLSvHS95PzcTYzAmNp27OfmHYyX NgYfYhTgYFTi4W1YfS1ciDWxrLgy9xCjBAezkgiv9qfr4UK8KYmVValF+fFFpTmpxYcYTYFu msgsJZqcD4z3vJJ4QxNDc0tDI2MLC3MjIyVx3pIPV8KFBNITS1KzU1MLUotg+pg4OKUaGKtC +hsLd64o7Yi+9mSu96pSJcMVBo/deKyyfz+y6Hzs137Kf3lLicCNG7ffKqZzlisz9suuWbT5 nObUd+Z83ioZEotM1tkJS5yo7+YvNjmmV1T3e/IswR91u+t2zfHi8zKezbfN88a6I+bh+/ZE /5Ow2n5yy9TletcW3T/RMln0sE96/VqmG0osxRmJhlrMRcWJAMnq2Te/AgAA X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20160913124918eucas1p29f49efbb142d416215482c29b53daff8 X-Msg-Generator: CA X-Sender-IP: 182.198.249.179 X-Local-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRs=?= =?UTF-8?B?7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRtT?= =?UTF-8?B?YW1zdW5nIEVsZWN0cm9uaWNzG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Sender-Code: =?UTF-8?B?QzEwG0VIURtDMTBDRDAyQ0QwMjczOTI=?= CMS-TYPE: 201P X-HopCount: 7 X-CMS-RootMailID: 20160913124918eucas1p29f49efbb142d416215482c29b53daff8 X-RootMTR: 20160913124918eucas1p29f49efbb142d416215482c29b53daff8 References: <1473770941-8337-1-git-send-email-m.szyprowski@samsung.com> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org This patch uses recently introduced device links to track the runtime pm state of the master's device. This way each SYSMMU controller is runtime activated when its master's device is active and can save/restore its state instead of being enabled all the time. This way SYSMMU controllers no longer prevents respective power domains to be turned off when master's device is not used. Signed-off-by: Marek Szyprowski --- drivers/iommu/exynos-iommu.c | 225 ++++++++++++++++++------------------------- 1 file changed, 94 insertions(+), 131 deletions(-) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" 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/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b0fa4d432e71..34717a0b1902 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -206,6 +206,7 @@ static const struct sysmmu_fault_info sysmmu_v5_faults[] = { struct exynos_iommu_owner { struct list_head controllers; /* list of sysmmu_drvdata.owner_node */ struct iommu_domain *domain; /* domain this device is attached */ + struct mutex rpm_lock; /* for runtime pm of all sysmmus */ }; /* @@ -237,8 +238,8 @@ struct sysmmu_drvdata { struct clk *aclk; /* SYSMMU's aclk clock */ struct clk *pclk; /* SYSMMU's pclk clock */ struct clk *clk_master; /* master's device clock */ - int activations; /* number of calls to sysmmu_enable */ spinlock_t lock; /* lock for modyfying state */ + int active; /* current status */ struct exynos_iommu_domain *domain; /* domain we belong to */ struct list_head domain_node; /* node for domain clients list */ struct list_head owner_node; /* node for owner controllers list */ @@ -251,25 +252,6 @@ static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom) return container_of(dom, struct exynos_iommu_domain, domain); } -static bool set_sysmmu_active(struct sysmmu_drvdata *data) -{ - /* return true if the System MMU was not active previously - and it needs to be initialized */ - return ++data->activations == 1; -} - -static bool set_sysmmu_inactive(struct sysmmu_drvdata *data) -{ - /* return true if the System MMU is needed to be disabled */ - BUG_ON(data->activations < 1); - return --data->activations == 0; -} - -static bool is_sysmmu_active(struct sysmmu_drvdata *data) -{ - return data->activations > 0; -} - static void sysmmu_unblock(struct sysmmu_drvdata *data) { writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); @@ -389,7 +371,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) unsigned short reg_status, reg_clear; int ret = -ENOSYS; - WARN_ON(!is_sysmmu_active(data)); + WARN_ON(!data->active); if (MMU_MAJ_VER(data->version) < 5) { reg_status = REG_INT_STATUS; @@ -435,40 +417,19 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static void __sysmmu_disable_nocount(struct sysmmu_drvdata *data) +static void __sysmmu_disable(struct sysmmu_drvdata *data) { + unsigned long flags; + clk_enable(data->clk_master); + spin_lock_irqsave(&data->lock, flags); writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); writel(0, data->sfrbase + REG_MMU_CFG); - - __sysmmu_disable_clocks(data); -} - -static bool __sysmmu_disable(struct sysmmu_drvdata *data) -{ - bool disabled; - unsigned long flags; - - spin_lock_irqsave(&data->lock, flags); - - disabled = set_sysmmu_inactive(data); - - if (disabled) { - data->pgtable = 0; - data->domain = NULL; - - __sysmmu_disable_nocount(data); - - dev_dbg(data->sysmmu, "Disabled\n"); - } else { - dev_dbg(data->sysmmu, "%d times left to disable\n", - data->activations); - } - + data->active = false; spin_unlock_irqrestore(&data->lock, flags); - return disabled; + __sysmmu_disable_clocks(data); } static void __sysmmu_init_config(struct sysmmu_drvdata *data) @@ -485,17 +446,19 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) writel(cfg, data->sfrbase + REG_MMU_CFG); } -static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) +static void __sysmmu_enable(struct sysmmu_drvdata *data) { + unsigned long flags; + __sysmmu_enable_clocks(data); + spin_lock_irqsave(&data->lock, flags); writel(CTRL_BLOCK, data->sfrbase + REG_MMU_CTRL); - __sysmmu_init_config(data); - __sysmmu_set_ptbase(data, data->pgtable); - writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); + data->active = true; + spin_unlock_irqrestore(&data->lock, flags); /* * SYSMMU driver keeps master's clock enabled only for the short @@ -506,48 +469,18 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) clk_disable(data->clk_master); } -static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, - struct exynos_iommu_domain *domain) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&data->lock, flags); - if (set_sysmmu_active(data)) { - data->pgtable = pgtable; - data->domain = domain; - - __sysmmu_enable_nocount(data); - - dev_dbg(data->sysmmu, "Enabled\n"); - } else { - ret = (pgtable == data->pgtable) ? 1 : -EBUSY; - - dev_dbg(data->sysmmu, "already enabled\n"); - } - - if (WARN_ON(ret < 0)) - set_sysmmu_inactive(data); /* decrement count */ - - spin_unlock_irqrestore(&data->lock, flags); - - return ret; -} - static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { unsigned long flags; - spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data) && data->version >= MAKE_MMU_VER(3, 3)) { + if (data->active && data->version >= MAKE_MMU_VER(3, 3)) { clk_enable(data->clk_master); __sysmmu_tlb_invalidate_entry(data, iova, 1); clk_disable(data->clk_master); } spin_unlock_irqrestore(&data->lock, flags); - } static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, @@ -556,7 +489,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, unsigned long flags; spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data)) { + if (data->active) { unsigned int num_inv = 1; clk_enable(data->clk_master); @@ -657,40 +590,55 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - of_iommu_set_ops(dev->of_node, &exynos_iommu_ops); return 0; } -#ifdef CONFIG_PM_SLEEP static int exynos_sysmmu_suspend(struct device *dev) { struct sysmmu_drvdata *data = dev_get_drvdata(dev); + struct exynos_iommu_owner *owner; - dev_dbg(dev, "suspend\n"); - if (is_sysmmu_active(data)) { - __sysmmu_disable_nocount(data); - pm_runtime_put(dev); + if (!data->master) + return 0; + + owner = data->master->archdata.iommu; + + mutex_lock(&owner->rpm_lock); + if (data->domain) { + dev_dbg(data->sysmmu, "saving state\n"); + __sysmmu_disable(data); } + mutex_unlock(&owner->rpm_lock); + return 0; } static int exynos_sysmmu_resume(struct device *dev) { struct sysmmu_drvdata *data = dev_get_drvdata(dev); + struct exynos_iommu_owner *owner; - dev_dbg(dev, "resume\n"); - if (is_sysmmu_active(data)) { - pm_runtime_get_sync(dev); - __sysmmu_enable_nocount(data); + if (!data->master) + return 0; + + owner = data->master->archdata.iommu; + + mutex_lock(&owner->rpm_lock); + if (data->domain) { + dev_dbg(data->sysmmu, "restoring state\n"); + __sysmmu_enable(data); } + mutex_unlock(&owner->rpm_lock); + return 0; } -#endif static const struct dev_pm_ops sysmmu_pm_ops = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_sysmmu_suspend, exynos_sysmmu_resume) + SET_RUNTIME_PM_OPS(exynos_sysmmu_suspend, exynos_sysmmu_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static const struct of_device_id sysmmu_of_match[] __initconst = { @@ -794,9 +742,12 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain) spin_lock_irqsave(&domain->lock, flags); list_for_each_entry_safe(data, next, &domain->clients, domain_node) { - if (__sysmmu_disable(data)) - data->master = NULL; + spin_lock(&data->lock); + __sysmmu_disable(data); + data->pgtable = 0; + data->domain = NULL; list_del_init(&data->domain_node); + spin_unlock(&data->lock); } spin_unlock_irqrestore(&domain->lock, flags); @@ -830,31 +781,32 @@ static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, phys_addr_t pagetable = virt_to_phys(domain->pgtable); struct sysmmu_drvdata *data, *next; unsigned long flags; - bool found = false; if (!has_sysmmu(dev) || owner->domain != iommu_domain) return; + mutex_lock(&owner->rpm_lock); + + list_for_each_entry(data, &owner->controllers, owner_node) { + if (pm_runtime_active(data->sysmmu)) + __sysmmu_disable(data); + } + spin_lock_irqsave(&domain->lock, flags); list_for_each_entry_safe(data, next, &domain->clients, domain_node) { - if (data->master == dev) { - if (__sysmmu_disable(data)) { - data->master = NULL; - list_del_init(&data->domain_node); - } - pm_runtime_put(data->sysmmu); - found = true; - } + spin_lock(&data->lock); + data->pgtable = 0; + data->domain = NULL; + list_del_init(&data->domain_node); + spin_unlock(&data->lock); } + owner->domain = NULL; spin_unlock_irqrestore(&domain->lock, flags); - owner->domain = NULL; + mutex_unlock(&owner->rpm_lock); - if (found) - dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", - __func__, &pagetable); - else - dev_err(dev, "%s: No IOMMU is attached\n", __func__); + dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", __func__, + &pagetable); } static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, @@ -865,7 +817,6 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, struct sysmmu_drvdata *data; phys_addr_t pagetable = virt_to_phys(domain->pgtable); unsigned long flags; - int ret = -ENODEV; if (!has_sysmmu(dev)) return -ENODEV; @@ -873,29 +824,30 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, if (owner->domain) exynos_iommu_detach_device(owner->domain, dev); + mutex_lock(&owner->rpm_lock); + + spin_lock_irqsave(&domain->lock, flags); list_for_each_entry(data, &owner->controllers, owner_node) { - pm_runtime_get_sync(data->sysmmu); - ret = __sysmmu_enable(data, pagetable, domain); - if (ret >= 0) { - data->master = dev; - - spin_lock_irqsave(&domain->lock, flags); - list_add_tail(&data->domain_node, &domain->clients); - spin_unlock_irqrestore(&domain->lock, flags); - } + spin_lock(&data->lock); + data->pgtable = pagetable; + data->domain = domain; + list_add_tail(&data->domain_node, &domain->clients); + spin_unlock(&data->lock); } + owner->domain = iommu_domain; + spin_unlock_irqrestore(&domain->lock, flags); - if (ret < 0) { - dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n", - __func__, &pagetable); - return ret; + list_for_each_entry(data, &owner->controllers, owner_node) { + if (pm_runtime_active(data->sysmmu)) + __sysmmu_enable(data); } - owner->domain = iommu_domain; - dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa %s\n", - __func__, &pagetable, (ret == 0) ? "" : ", again"); + mutex_unlock(&owner->rpm_lock); - return ret; + dev_dbg(dev, "%s: Attached IOMMU with pgtable %pa\n", __func__, + &pagetable); + + return 0; } static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain, @@ -1266,10 +1218,21 @@ static int exynos_iommu_of_xlate(struct device *dev, return -ENOMEM; INIT_LIST_HEAD(&owner->controllers); + mutex_init(&owner->rpm_lock); dev->archdata.iommu = owner; } list_add_tail(&data->owner_node, &owner->controllers); + data->master = dev; + + /* + * SYSMMU will be runtime enabled via device link (dependency) to its + * master device, so there are no direct calls to pm_runtime_get/put + * in this driver. + */ + device_link_add(dev, data->sysmmu, DEVICE_LINK_AVAILABLE, + DEVICE_LINK_PERSISTENT | DEVICE_LINK_PM_RUNTIME); + return 0; }