From patchwork Wed Aug 13 00:51:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mitchel Humpherys X-Patchwork-Id: 4715601 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8E9FDC0338 for ; Wed, 13 Aug 2014 00:54:19 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8F62920166 for ; Wed, 13 Aug 2014 00:54:18 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8552D20160 for ; Wed, 13 Aug 2014 00:54:17 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XHMnn-0006d5-9U; Wed, 13 Aug 2014 00:52:31 +0000 Received: from smtp.codeaurora.org ([198.145.11.231]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XHMng-0006Vf-H8 for linux-arm-kernel@lists.infradead.org; Wed, 13 Aug 2014 00:52:25 +0000 Received: from smtp.codeaurora.org (localhost [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id E9A0613F8A0; Wed, 13 Aug 2014 00:52:07 +0000 (UTC) Received: by smtp.codeaurora.org (Postfix, from userid 486) id DD82C13F8B1; Wed, 13 Aug 2014 00:52:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from mitchelh-linux.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) (using TLSv1.2 with cipher AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: mitchelh@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 3F1C613F8A0; Wed, 13 Aug 2014 00:52:07 +0000 (UTC) From: Mitchel Humpherys To: linux-arm-kernel@lists.infradead.org, iommu@lists.linux-foundation.org, Will Deacon , devicetree@vger.kernel.org Subject: [PATCH 2/6] iommu/arm-smmu: add support for specifying regulators Date: Tue, 12 Aug 2014 17:51:35 -0700 Message-Id: <1407891099-24641-3-git-send-email-mitchelh@codeaurora.org> X-Mailer: git-send-email 2.0.4 In-Reply-To: <1407891099-24641-1-git-send-email-mitchelh@codeaurora.org> References: <1407891099-24641-1-git-send-email-mitchelh@codeaurora.org> X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140812_175224_627430_93F0D593 X-CRM114-Status: GOOD ( 24.45 ) X-Spam-Score: -0.7 (/) Cc: Mitchel Humpherys X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP On some power-constrained platforms it's useful to disable power when a device is not in use. Add support for specifying regulators for SMMUs and only leave power on as long as the SMMU is in use (attached). Signed-off-by: Mitchel Humpherys --- .../devicetree/bindings/iommu/arm,smmu.txt | 3 + drivers/iommu/arm-smmu.c | 102 ++++++++++++++++++--- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index ceae3fe207..dbc1ddad79 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -59,6 +59,9 @@ conditions. Documentation/devicetree/bindings/clock/clock-bindings.txt for more info. +- vdd-supply : Phandle of the regulator that should be powered on during + SMMU register access. + Example: smmu { diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index e123d75db3..7fdc58d8f8 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -402,6 +402,11 @@ struct arm_smmu_device { int num_clocks; struct clk **clocks; + + struct regulator *gdsc; + + struct mutex attach_lock; + unsigned int attach_count; }; struct arm_smmu_cfg { @@ -617,6 +622,22 @@ static void arm_smmu_disable_clocks(struct arm_smmu_device *smmu) clk_disable_unprepare(smmu->clocks[i]); } +static int arm_smmu_enable_regulators(struct arm_smmu_device *smmu) +{ + if (!smmu->gdsc) + return 0; + + return regulator_enable(smmu->gdsc); +} + +static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu) +{ + if (!smmu->gdsc) + return 0; + + return regulator_disable(smmu->gdsc); +} + /* Wait for any pending TLB invalidations to complete */ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) { @@ -1275,6 +1296,8 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, arm_smmu_disable_clocks(smmu); } +static void arm_smmu_device_reset(struct arm_smmu_device *smmu); + static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { int ret; @@ -1293,7 +1316,15 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) return -EEXIST; } - arm_smmu_enable_clocks(smmu); + mutex_lock(&smmu->attach_lock); + if (!smmu->attach_count++) { + arm_smmu_enable_regulators(smmu); + arm_smmu_enable_clocks(smmu); + arm_smmu_device_reset(smmu); + } else { + arm_smmu_enable_clocks(smmu); + } + mutex_unlock(&smmu->attach_lock); /* * Sanity check the domain. We don't support domains across @@ -1304,7 +1335,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) /* Now that we have a master, we can finalise the domain */ ret = arm_smmu_init_domain_context(domain, smmu); if (IS_ERR_VALUE(ret)) - goto disable_clocks; + goto err_disable_clocks; dom_smmu = smmu_domain->smmu; } @@ -1314,28 +1345,46 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) "cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n", dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev)); ret = -EINVAL; - goto disable_clocks; + goto err_disable_clocks; } /* Looks ok, so add the device to the domain */ cfg = find_smmu_master_cfg(dev); if (!cfg) { ret = -ENODEV; - goto disable_clocks; + goto err_disable_clocks; } ret = arm_smmu_domain_add_master(smmu_domain, cfg); if (!ret) dev->archdata.iommu = domain; -disable_clocks: arm_smmu_disable_clocks(smmu); return ret; + +err_disable_clocks: + arm_smmu_disable_clocks(smmu); + mutex_lock(&smmu->attach_lock); + if (!--smmu->attach_count) + arm_smmu_disable_regulators(smmu); + mutex_unlock(&smmu->attach_lock); + return ret; +} + +static void arm_smmu_power_off(struct arm_smmu_device *smmu) +{ + /* Turn the thing off */ + arm_smmu_enable_clocks(smmu); + writel_relaxed(sCR0_CLIENTPD, + ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); + arm_smmu_disable_clocks(smmu); + arm_smmu_disable_regulators(smmu); } static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) { struct arm_smmu_domain *smmu_domain = domain->priv; struct arm_smmu_master_cfg *cfg; + struct arm_smmu_device *smmu = smmu_domain->smmu; cfg = find_smmu_master_cfg(dev); if (!cfg) @@ -1343,6 +1392,10 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) dev->archdata.iommu = NULL; arm_smmu_domain_remove_master(smmu_domain, cfg); + mutex_lock(&smmu->attach_lock); + if (!--smmu->attach_count) + arm_smmu_power_off(smmu); + mutex_unlock(&smmu->attach_lock); } static bool arm_smmu_pte_is_contiguous_range(unsigned long addr, @@ -1844,6 +1897,20 @@ static int arm_smmu_id_size_to_bits(int size) } } +static int arm_smmu_init_regulators(struct arm_smmu_device *smmu) +{ + struct device *dev = smmu->dev; + + if (!of_get_property(dev->of_node, "vdd-supply", NULL)) + return 0; + + smmu->gdsc = devm_regulator_get(dev, "vdd"); + if (IS_ERR(smmu->gdsc)) + return PTR_ERR(smmu->gdsc); + + return 0; +} + static int arm_smmu_init_clocks(struct arm_smmu_device *smmu) { const char *cname; @@ -2065,6 +2132,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) return -ENOMEM; } smmu->dev = dev; + mutex_init(&smmu->attach_lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); smmu->base = devm_ioremap_resource(dev, res); @@ -2124,13 +2192,19 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } dev_notice(dev, "registered %d master devices\n", i); + err = arm_smmu_init_regulators(smmu); + if (err) + goto out_put_masters; + err = arm_smmu_init_clocks(smmu); if (err) goto out_put_masters; + arm_smmu_enable_regulators(smmu); arm_smmu_enable_clocks(smmu); - err = arm_smmu_device_cfg_probe(smmu); + arm_smmu_disable_clocks(smmu); + arm_smmu_disable_regulators(smmu); if (err) goto out_disable_clocks; @@ -2163,8 +2237,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) list_add(&smmu->list, &arm_smmu_devices); spin_unlock(&arm_smmu_devices_lock); - arm_smmu_device_reset(smmu); - arm_smmu_disable_clocks(smmu); return 0; out_free_irqs: @@ -2173,6 +2245,7 @@ out_free_irqs: out_disable_clocks: arm_smmu_disable_clocks(smmu); + arm_smmu_disable_regulators(smmu); out_put_masters: for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { @@ -2216,10 +2289,15 @@ static int arm_smmu_device_remove(struct platform_device *pdev) for (i = 0; i < smmu->num_global_irqs; ++i) free_irq(smmu->irqs[i], smmu); - /* Turn the thing off */ - arm_smmu_enable_clocks(smmu); - writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); - arm_smmu_disable_clocks(smmu); + mutex_lock(&smmu->attach_lock); + /* + * If all devices weren't detached for some reason, we're + * still powered on. Power off now. + */ + if (smmu->attach_count) + arm_smmu_power_off(smmu); + mutex_unlock(&smmu->attach_lock); + return 0; }