From patchwork Thu Sep 29 08:12:31 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9355867 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8A0C56077A for ; Thu, 29 Sep 2016 08:13:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7BE95298B6 for ; Thu, 29 Sep 2016 08:13:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 706E8298BA; Thu, 29 Sep 2016 08:13:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9867E298B6 for ; Thu, 29 Sep 2016 08:13:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755368AbcI2INW (ORCPT ); Thu, 29 Sep 2016 04:13:22 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:27081 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753813AbcI2IMp (ORCPT ); Thu, 29 Sep 2016 04:12:45 -0400 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OE9002SRAT6D460@mailout1.w1.samsung.com>; Thu, 29 Sep 2016 09:12:42 +0100 (BST) Received: from eusmges3.samsung.com (unknown [203.254.199.242]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20160929081241eucas1p1b8f62221cb8d79935f107195fb1c003c~4vRGvPyUT1594315943eucas1p1M; Thu, 29 Sep 2016 08:12:41 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges3.samsung.com (EUCPMTA) with SMTP id 65.EF.11330.9FCCCE75; Thu, 29 Sep 2016 09:12:41 +0100 (BST) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20160929081240eucas1p231c1ab5c5936886cbec6bc3f5267c940~4vRFvPIvM2192921929eucas1p2a; Thu, 29 Sep 2016 08:12:40 +0000 (GMT) X-AuditID: cbfec7f2-f79556d000002c42-74-57ecccf914dd Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id F8.71.10494.DCCCCE75; Thu, 29 Sep 2016 09:11:57 +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 <0OE90036ZASY3B80@eusync4.samsung.com>; Thu, 29 Sep 2016 09:12:40 +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 , Tobias Jakobi Subject: [PATCH v4 2/2] iommu/exynos: Add proper runtime pm support Date: Thu, 29 Sep 2016 10:12:31 +0200 Message-id: <1475136751-31340-3-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1475136751-31340-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAzVSa0hTYRjuO+fs7DhaHuaoLzWjRX8KTVPow5UV+eMIggbhICtdelJpXthU sj9eKi+LVFziMKuJt7C8tKaY5X15YeJlNtJ0SuhQUytSKw0l3Zn/nofneZ/nfeGlcFEJz5WK S0hmlQlyhYQUEM29G8OeG4PLMu9Gsxi90TbwUPHXORLdr2ggUdFMIYF0HVKU97SRj5YWfFDB 7BKObG9nMTTWWkai1cdGgLTD7RhamHdDdUYrH40X2gAaNJl5KPvVTx7KrzOTF0XMXNdzjGmx VgJGX5tHMh3PXvMZzXgNYLZ7+YzBkkMw+YZawKzqPRhtTjMvVHBNcC6aVcSlssrTAZGC2OZJ G0iqj7z7wtiGZ4DWEDVwoiDtByc3f/M4fBCOTDeQaiCgRHQVgEZLFc6RVQBNi/3E3sTGzBeH UA3gZnaHYyQDg92mJmzXRdI+UL2itgtiOhPAtdxKbJfgdA8BS/O27I0u9GU4XKez5xL0Cbhk WSd3sZBm4MficpLr84ADvRq734kOgqMWmz0V0ot8WF7/b2cRaoccgfpOnPMHws/1f/gcdoHf +gwO7A7HNI8cNxQAmPXwFIe1AA6tCDkshT19o/YunD4Ai5pLHPFCmJst4iwMbLHkO1a7BCea VjDu+jIANXPfyULgrgP7aoGYTVHFx7AqXy+VPF6VkhDjFZUYrwc772La7vvVAtb7/bsBTQHJ fqF3/5JMxJOnqtLiuwGkcIlYeGZgWSYSRsvT7rHKxAhlioJVdQM3ipAcEn7QfZKJ6Bh5MnuH ZZNY5Z6KUU6uGeB4YtPVkJvVhoBW39GmcBqqa5xvnQ0ZsYbdcMrMTFoTmOv4aWX+40Pz8png qYgpf6VEupXwpPRC0eGezo4483xFvSm5K9gUraBua9o9g/5ekRZ7jb6Pe4nlrenyHzi3Tfy4 PpveFWmdywoPazMfe4eFBvotH003SAXTCiI06ryEUMXKfU7iSpX8P4n9WJ0qAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrOIsWRmVeSWpSXmKPExsVy+t/xa7pnz7wJN1jzRMFi44z1rBZTHz5h s2hevJ7NYtL9CSwWC/ZbW3TO3sBu8fqFoUX/49fMFk83P2ayuLxrDpvF594jjBYzzu9jsnjx XNpi7ZG77BY3JjxltDhz+hKrRdvqD6wWfWsvsTkIeTw5OI/JY8fdJYwem1Z1snnsn7uG3WPy jeWMHv+OsXtsudrO4tG3ZRWjx+dNch4z2rexBnBFudlkpCampBYppOYl56dk5qXbKoWGuOla KCnkJeam2ipF6PqGBCkplCXmlAJ5RgZowME5wD1YSd8uwS1j2+2njAXrEirmH9nL3MC4y7+L kZNDQsBE4uf9W8wQtpjEhXvr2boYuTiEBJYwSpx5e4cFwmlikvh1ZCo7SBWbgKFE19susCoR gUZGiW1bJ7KDOMwCx1kkLhzpYAWpEhZwlji/dgELiM0ioCrx+upXNhCbV8BD4ujUhWwQ++Qk Th6bDFbPKeApcfHqU7C4EFDNgSk72CYw8i5gZFjFKJJaWpybnltspFecmFtcmpeul5yfu4kR GGnbjv3csoOx613wIUYBDkYlHt6OU6/DhVgTy4orcw8xSnAwK4nwGp18Ey7Em5JYWZValB9f VJqTWnyI0RToqInMUqLJ+cAkkFcSb2hiaG5paGRsYWFuZKQkzjv1w5VwIYH0xJLU7NTUgtQi mD4mDk6pBsauOWKWPfzBuy6/Kdl1mM0heuM1p+Z3lydeLvVOF5SLc/IOTHlwS/Fp64m9TTOn 8MiFqVRNfXd8xeGTEuctJILWz/FbuuaYhWbdlOjCj5OOaV218k36eiT6t+2xnlK5ktDrDjeM /E1XBvN4fTx8XkLFsdiKn9fnk2n3t88KWtq+Zt1drvsnzVViKc5INNRiLipOBACGkTAPygIA AA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20160929081240eucas1p231c1ab5c5936886cbec6bc3f5267c940 X-Msg-Generator: CA X-Sender-IP: 182.198.249.180 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: 20160929081240eucas1p231c1ab5c5936886cbec6bc3f5267c940 X-RootMTR: 20160929081240eucas1p231c1ab5c5936886cbec6bc3f5267c940 References: <1475136751-31340-1-git-send-email-m.szyprowski@samsung.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index dfb44034b4ee..c8926e030713 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); @@ -388,7 +370,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; @@ -434,40 +416,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) @@ -484,17 +445,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 @@ -505,48 +468,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, @@ -555,7 +488,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); @@ -656,40 +589,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 = { @@ -793,9 +741,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); @@ -829,31 +780,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, @@ -864,7 +816,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; @@ -872,29 +823,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, @@ -1265,10 +1217,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_PM_RUNTIME); + return 0; }