iommu/arm-smmu: program implementation defined registers on attach

Some platforms require certain implementation-defined registers to be
programmed when first attaching to the SMMU. Add support for this via
specifying register offset, value pairs in the device tree.

Change-Id: Iac2fe42684c3849a24d0d1251a206954262257c5
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 9b8612e8..ebb16d4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -307,6 +307,11 @@
 	QCOM_SMMUV2,
 };
 
+struct arm_smmu_impl_def_reg {
+	u32 offset;
+	u32 value;
+};
+
 struct arm_smmu_smr {
 	u8				idx;
 	u16				mask;
@@ -372,6 +377,9 @@
 	struct rb_root			masters;
 
 	u32				cavium_id_base; /* Specific to Cavium */
+	/* Specific to QCOM */
+	struct arm_smmu_impl_def_reg	*impl_def_attach_registers;
+	unsigned int			num_impl_def_attach_registers;
 };
 
 enum arm_smmu_context_fmt {
@@ -1538,6 +1546,16 @@
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 };
 
+static void arm_smmu_impl_def_programming(struct arm_smmu_device *smmu)
+{
+	int i;
+	struct arm_smmu_impl_def_reg *regs = smmu->impl_def_attach_registers;
+
+	for (i = 0; i < smmu->num_impl_def_attach_registers; ++i)
+		writel_relaxed(regs[i].value,
+			ARM_SMMU_GR0(smmu) + regs[i].offset);
+}
+
 static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
@@ -1592,6 +1610,9 @@
 		}
 	}
 
+	/* Program implementation defined registers */
+	arm_smmu_impl_def_programming(smmu);
+
 	/* Invalidate the TLB, just in case */
 	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
 	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
@@ -1644,6 +1665,52 @@
 	}
 }
 
+static int arm_smmu_parse_impl_def_registers(struct arm_smmu_device *smmu)
+{
+	struct device *dev = smmu->dev;
+	int i, ntuples, ret;
+	u32 *tuples;
+	struct arm_smmu_impl_def_reg *regs, *regit;
+
+	if (!of_find_property(dev->of_node, "attach-impl-defs", &ntuples))
+		return 0;
+
+	ntuples /= sizeof(u32);
+	if (ntuples % 2) {
+		dev_err(dev,
+			"Invalid number of attach-impl-defs registers: %d\n",
+			ntuples);
+		return -EINVAL;
+	}
+
+	regs = devm_kmalloc(
+		dev, sizeof(*smmu->impl_def_attach_registers) * ntuples,
+		GFP_KERNEL);
+	if (!regs)
+		return -ENOMEM;
+
+	tuples = devm_kmalloc(dev, sizeof(u32) * ntuples * 2, GFP_KERNEL);
+	if (!tuples)
+		return -ENOMEM;
+
+	ret = of_property_read_u32_array(dev->of_node, "attach-impl-defs",
+					tuples, ntuples);
+	if (ret)
+		return ret;
+
+	for (i = 0, regit = regs; i < ntuples; i += 2, ++regit) {
+		regit->offset = tuples[i];
+		regit->value = tuples[i + 1];
+	}
+
+	devm_kfree(dev, tuples);
+
+	smmu->impl_def_attach_registers = regs;
+	smmu->num_impl_def_attach_registers = ntuples / 2;
+
+	return 0;
+}
+
 static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 {
 	unsigned long size;
@@ -1974,6 +2041,10 @@
 
 	kfree(masterspec);
 
+	err = arm_smmu_parse_impl_def_registers(smmu);
+	if (err)
+		goto out_put_masters;
+
 	parse_driver_options(smmu);
 
 	if (smmu->version == ARM_SMMU_V2 &&