summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitchel Humpherys <mitchelh@codeaurora.org>2014-08-14 17:44:49 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:11:11 -0700
commitdbb94a856816edc4b911aea42e5169e0b992d2a2 (patch)
treee129e86898935f7e985410a9ae5a166df57bafdc
parent1ac0dcf474288ef34f260e2372fd4dbc8a25827f (diff)
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>
-rw-r--r--Documentation/devicetree/bindings/iommu/arm,smmu.txt8
-rw-r--r--drivers/iommu/arm-smmu.c69
2 files changed, 77 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 4a2e4bacd914..9e6b4a6bf72e 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -74,6 +74,10 @@ conditions.
- vdd-supply : Phandle of the regulator that should be powered on during
SMMU register access.
+- attach-impl-defs : global registers to program at device attach
+ time. This should be a list of 2-tuples of the format:
+ <offset reg_value>.
+
Example:
smmu {
@@ -93,4 +97,8 @@ Example:
*/
mmu-masters = <&dma0 0xd01d 0xd01e>,
<&dma1 0xd11c>;
+
+ attach-impl-defs = <0x124 0x3>,
+ <0x128 0xa5>,
+ <0x12c 0x1>;
};
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 04ef1920cf7c..212deec25113 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -383,6 +383,11 @@ enum smmu_model_id {
SMMU_MODEL_QCOM_V2,
};
+struct arm_smmu_impl_def_reg {
+ u32 offset;
+ u32 value;
+};
+
struct arm_smmu_device {
struct device *dev;
@@ -432,6 +437,9 @@ struct arm_smmu_device {
struct mutex attach_lock;
unsigned int attach_count;
+
+ struct arm_smmu_impl_def_reg *impl_def_attach_registers;
+ unsigned int num_impl_def_attach_registers;
};
struct arm_smmu_cfg {
@@ -1378,6 +1386,16 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
arm_smmu_disable_clocks(smmu);
}
+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);
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1403,6 +1421,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
arm_smmu_enable_regulators(smmu);
arm_smmu_enable_clocks(smmu);
arm_smmu_device_reset(smmu);
+ arm_smmu_impl_def_programming(smmu);
} else {
arm_smmu_enable_clocks(smmu);
}
@@ -2109,6 +2128,52 @@ static int arm_smmu_init_clocks(struct arm_smmu_device *smmu)
return 0;
}
+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;
@@ -2349,6 +2414,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
dev_notice(dev, "registered %d master devices\n", num_masters);
+ err = arm_smmu_parse_impl_def_registers(smmu);
+ if (err)
+ goto out_put_masters;
+
err = arm_smmu_init_regulators(smmu);
if (err)
goto out_put_masters;