iommu/arm-smmu: add support for specifying regulators

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).

Change-Id: I87191d325423f160ddd4b71f5bf3a92f4942b821
Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 6c523e8..6764dae 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -400,6 +400,8 @@
 	int				num_clocks;
 	struct clk			**clocks;
 
+	struct regulator		*gdsc;
+
 	/* Protects power_count */
 	struct mutex			power_lock;
 	int				power_count;
@@ -748,6 +750,22 @@
 	spin_unlock_irqrestore(&smmu->clock_refs_lock, flags);
 }
 
+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);
+}
+
 static int arm_smmu_power_on_slow(struct arm_smmu_device *smmu)
 {
 	int ret;
@@ -759,12 +777,22 @@
 		return 0;
 	}
 
+	ret = arm_smmu_enable_regulators(smmu);
+	if (ret)
+		goto out_unlock;
+
 	ret = arm_smmu_prepare_clocks(smmu);
-	if (!ret)
-		smmu->power_count += 1;
+	if (ret)
+		goto out_disable_regulators;
 
+	smmu->power_count += 1;
 	mutex_unlock(&smmu->power_lock);
+	return 0;
 
+out_disable_regulators:
+	arm_smmu_disable_regulators(smmu);
+out_unlock:
+	mutex_unlock(&smmu->power_lock);
 	return ret;
 }
 
@@ -780,6 +808,7 @@
 	}
 
 	arm_smmu_unprepare_clocks(smmu);
+	arm_smmu_disable_regulators(smmu);
 
 	mutex_unlock(&smmu->power_lock);
 }
@@ -2685,6 +2714,20 @@
 	return 0;
 }
 
+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_device_cfg_probe(struct arm_smmu_device *smmu)
 {
 	unsigned long size;
@@ -2993,6 +3036,10 @@
 	if (err)
 		return err;
 
+	err = arm_smmu_init_regulators(smmu);
+	if (err)
+		return err;
+
 	err = arm_smmu_power_on(smmu);
 	if (err)
 		return err;