aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2020-01-20 00:21:12 +0000
committerLinaro CI <ci_notify@linaro.org>2020-01-20 00:21:12 +0000
commit968ea8a050f9056a7de01d8dde02b67d2caf3b98 (patch)
tree6f77d17be8063a5afc8c3281544d301d796888a0
parentbcfdd79084bc457a0638d97868ecde4681b3ce21 (diff)
parent430d2c7fd33abaf17066c689500be0ccce49cf71 (diff)
Merge remote-tracking branch 'iommu/tracking-qcomlt-iommu' into integration-linux-qcomlt
-rw-r--r--arch/arm64/boot/dts/qcom/sdm845.dtsi1
-rw-r--r--drivers/iommu/arm-smmu-impl.c31
-rw-r--r--drivers/iommu/arm-smmu.c96
-rw-r--r--drivers/iommu/arm-smmu.h1
4 files changed, 118 insertions, 11 deletions
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index ddb1f23c936f..ee67977cb9fd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -3012,6 +3012,7 @@
compatible = "qcom,sdm845-smmu-500", "arm,mmu-500";
reg = <0 0x15000000 0 0x80000>;
#iommu-cells = <2>;
+ qcom,smmu-500-fw-impl-safe-errata;
#global-interrupts = <1>;
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c
index b2fe72a8f019..518c4752a7c8 100644
--- a/drivers/iommu/arm-smmu-impl.c
+++ b/drivers/iommu/arm-smmu-impl.c
@@ -5,6 +5,7 @@
#define pr_fmt(fmt) "arm-smmu: " fmt
#include <linux/bitfield.h>
+#include <linux/qcom_scm.h>
#include <linux/of.h>
#include "arm-smmu.h"
@@ -47,7 +48,6 @@ static const struct arm_smmu_impl calxeda_impl = {
.write_reg = arm_smmu_write_ns,
};
-
struct cavium_smmu {
struct arm_smmu_device smmu;
u32 id_base;
@@ -147,6 +147,32 @@ static const struct arm_smmu_impl arm_mmu500_impl = {
.reset = arm_mmu500_reset,
};
+static int qcom_mmu500_cfg_probe(struct arm_smmu_device *smmu)
+{
+ int err;
+
+ if (of_property_read_bool(smmu->dev->of_node,
+ "qcom,smmu-500-fw-impl-safe-errata")) {
+ /*
+ * To address performance degradation in non-real time clients,
+ * such as USB and UFS, turn off wait-for-safe on sdm845 platforms,
+ * such as MTP, whose firmwares implement corresponding secure monitor
+ * call handlers.
+ */
+ err = qcom_scm_qsmmu500_wait_safe_toggle(0);
+ if (err)
+ dev_warn(smmu->dev, "Failed to turn off SAFE logic\n");
+
+ printk(KERN_ERR "%s() ===================================\n", __func__);
+ }
+
+ return 0;
+}
+
+static const struct arm_smmu_impl qcom_mmu500_impl = {
+ .cfg_probe = qcom_mmu500_cfg_probe,
+ .reset = arm_mmu500_reset,
+};
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
{
@@ -160,6 +186,9 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
case ARM_MMU500:
smmu->impl = &arm_mmu500_impl;
break;
+ case QCOM_MMU500:
+ smmu->impl = &qcom_mmu500_impl;
+ break;
case CAVIUM_SMMUV2:
return cavium_smmu_impl_init(smmu);
default:
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 4f1a350d9529..0700bdd9d43b 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -79,6 +79,7 @@ struct arm_smmu_s2cr {
enum arm_smmu_s2cr_type type;
enum arm_smmu_s2cr_privcfg privcfg;
u8 cbndx;
+ bool pinned;
};
#define s2cr_init_val (struct arm_smmu_s2cr){ \
@@ -220,9 +221,19 @@ static int arm_smmu_register_legacy_master(struct device *dev,
return err;
}
-static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
+static int __arm_smmu_alloc_cb(struct arm_smmu_device *smmu, int start,
+ struct device *dev)
{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ unsigned long *map = smmu->context_map;
+ int end = smmu->num_context_banks;
int idx;
+ int i;
+
+ for_each_cfg_sme(fwspec, i, idx) {
+ if (smmu->s2crs[idx].pinned)
+ return smmu->s2crs[idx].cbndx;
+ }
do {
idx = find_next_zero_bit(map, end, start);
@@ -642,7 +653,8 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
}
static int arm_smmu_init_domain_context(struct iommu_domain *domain,
- struct arm_smmu_device *smmu)
+ struct arm_smmu_device *smmu,
+ struct device *dev)
{
int irq, start, ret = 0;
unsigned long ias, oas;
@@ -756,8 +768,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
ret = -EINVAL;
goto out_unlock;
}
- ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
- smmu->num_context_banks);
+ ret = __arm_smmu_alloc_cb(smmu, start, dev);
if (ret < 0)
goto out_unlock;
@@ -946,23 +957,35 @@ static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu)
{
u32 smr;
+ int idx;
if (!smmu->smrs)
return;
+ for (idx = 0; idx < smmu->num_mapping_groups; idx++) {
+ smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx));
+ if (!(smr & SMR_VALID))
+ break;
+ }
+
+ if (idx == smmu->num_mapping_groups) {
+ dev_err(smmu->dev, "Unable to compute streamid_mask\n");
+ return;
+ }
+
/*
* SMR.ID bits may not be preserved if the corresponding MASK
* bits are set, so check each one separately. We can reject
* masters later if they try to claim IDs outside these masks.
*/
smr = FIELD_PREP(SMR_ID, smmu->streamid_mask);
- arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr);
- smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0));
+ arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), smr);
+ smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx));
smmu->streamid_mask = FIELD_GET(SMR_ID, smr);
smr = FIELD_PREP(SMR_MASK, smmu->streamid_mask);
- arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr);
- smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0));
+ arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), smr);
+ smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx));
smmu->smr_mask_mask = FIELD_GET(SMR_MASK, smr);
}
@@ -1010,12 +1033,19 @@ static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
{
+ bool pinned = smmu->s2crs[idx].pinned;
+ u8 cbndx = smmu->s2crs[idx].cbndx;;
+
if (--smmu->s2crs[idx].count)
return false;
smmu->s2crs[idx] = s2cr_init_val;
- if (smmu->smrs)
+ if (pinned) {
+ smmu->s2crs[idx].pinned = true;
+ smmu->s2crs[idx].cbndx = cbndx;
+ } else if (smmu->smrs) {
smmu->smrs[idx].valid = false;
+ }
return true;
}
@@ -1149,7 +1179,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return ret;
/* Ensure that the domain is finalised */
- ret = arm_smmu_init_domain_context(domain, smmu);
+ ret = arm_smmu_init_domain_context(domain, smmu, dev);
if (ret < 0)
goto rpm_put;
@@ -1667,6 +1697,48 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg);
}
+static int arm_smmu_read_smr_state(struct arm_smmu_device *smmu)
+{
+ u32 smr, s2cr;
+ u32 mask;
+ u32 type;
+ u32 cbndx;
+ u32 privcfg;
+ u32 id;
+ int i;
+
+ for (i = 0; i < smmu->num_mapping_groups; i++) {
+ smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
+ mask = FIELD_GET(SMR_MASK, smr);
+ id = FIELD_GET(SMR_ID, smr);
+
+ s2cr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_S2CR(i));
+ type = FIELD_GET(S2CR_TYPE, s2cr);
+ cbndx = FIELD_GET(S2CR_CBNDX, s2cr);
+ privcfg = FIELD_GET(S2CR_PRIVCFG, s2cr);
+
+ smmu->smrs[i].mask = mask;
+ smmu->smrs[i].id = id;
+ smmu->smrs[i].valid = !!(smr & SMR_VALID);
+
+ smmu->s2crs[i].group = NULL;
+ smmu->s2crs[i].count = 0;
+ smmu->s2crs[i].type = type;
+ smmu->s2crs[i].privcfg = privcfg;
+ smmu->s2crs[i].cbndx = cbndx;
+
+ if (!(smr & SMR_VALID))
+ continue;
+
+ smmu->s2crs[i].pinned = true;
+ bitmap_set(smmu->context_map, cbndx, 1);
+
+ dev_err(smmu->dev, "Handoff smr: %x s2cr: %x cb: %d\n", smr, s2cr, cbndx);
+ }
+
+ return 0;
+}
+
static int arm_smmu_id_size_to_bits(int size)
{
switch (size) {
@@ -1880,6 +1952,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n",
smmu->ipa_size, smmu->pa_size);
+ arm_smmu_read_smr_state(smmu);
+
if (smmu->impl && smmu->impl->cfg_probe)
return smmu->impl->cfg_probe(smmu);
@@ -1900,6 +1974,7 @@ ARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU);
ARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500);
ARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2);
ARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2);
+ARM_SMMU_MATCH_DATA(qcom_mmu500, ARM_SMMU_V2, QCOM_MMU500);
static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 },
@@ -1909,6 +1984,7 @@ static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,mmu-500", .data = &arm_mmu500 },
{ .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 },
{ .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 },
+ { .compatible = "qcom,sdm845-smmu-500", .data = &qcom_mmu500 },
{ },
};
diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
index 62b9f0cec49b..df9edf7e079a 100644
--- a/drivers/iommu/arm-smmu.h
+++ b/drivers/iommu/arm-smmu.h
@@ -222,6 +222,7 @@ enum arm_smmu_implementation {
ARM_MMU500,
CAVIUM_SMMUV2,
QCOM_SMMUV2,
+ QCOM_MMU500,
};
struct arm_smmu_device {