summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiam Mark <lmark@codeaurora.org>2016-08-08 10:14:01 -0700
committerLiam Mark <lmark@codeaurora.org>2016-08-11 14:06:53 -0700
commit1d79f44d300c61f085e9bdc70ebd8dcc4faa6f13 (patch)
tree71566a69be6bbc8658765cde675538f36c853d61
parente94b446eac88a43e42ecde105275d48b677ea5b3 (diff)
iommu/arm-smmu: support static context banks
Consider any pre-initialized stream match register table entries, which are of type translation, as static stream ID to context bank mappings. When an attach occurs for a stream ID which has a static context bank mapping ensure that the pre-initialized stream match register table entry is used for that stream ID. Contract for using static context banks: 1) Before doing the attach hardware translations must be stopped and hardware translations are only re-enabled once the required mappings have been added. 2) Before the attach the static CB must be in stage 1 context bank bypass (CBn_SCTLR.M == 0). CRs-Fixed: 1050659 Change-Id: I4037d7861657384eb5b4f67c52b2dbf4ad6e1c2c Signed-off-by: Liam Mark <lmark@codeaurora.org>
-rw-r--r--drivers/iommu/arm-smmu.c224
1 files changed, 187 insertions, 37 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 55eff5ae04e4..c69927bd4ff2 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -386,6 +386,7 @@ struct arm_smmu_device {
unsigned int *irqs;
struct list_head list;
+ struct list_head static_cbndx_list;
struct rb_root masters;
int num_clocks;
@@ -491,6 +492,18 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
{ 0, NULL},
};
+#define TYPE_TRANS (S2CR_TYPE_TRANS >> S2CR_TYPE_SHIFT)
+#define TYPE_BYPASS (S2CR_TYPE_BYPASS >> S2CR_TYPE_SHIFT)
+#define TYPE_FAULT (S2CR_TYPE_FAULT >> S2CR_TYPE_SHIFT)
+
+struct static_cbndx_entry {
+ struct list_head list;
+ u8 cbndx;
+ u8 smr_idx;
+ u16 sid;
+ u8 type;
+};
+
static int arm_smmu_enable_clocks_atomic(struct arm_smmu_device *smmu);
static void arm_smmu_disable_clocks_atomic(struct arm_smmu_device *smmu);
static void arm_smmu_prepare_pgtable(void *addr, void *cookie);
@@ -767,11 +780,103 @@ static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
return idx;
}
+static int __arm_smmu_set_bitmap(unsigned long *map, int idx)
+{
+ return test_and_set_bit(idx, map);
+}
+
+static struct static_cbndx_entry *arm_smmu_get_static_entry_from_sid(
+ struct arm_smmu_device *smmu, int sid)
+{
+ struct static_cbndx_entry *entry;
+
+ list_for_each_entry(entry, &smmu->static_cbndx_list, list) {
+ if (entry->sid == sid)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static struct static_cbndx_entry *arm_smmu_get_static_entry_from_context(
+ struct arm_smmu_device *smmu, int idx)
+{
+ struct static_cbndx_entry *entry;
+
+ list_for_each_entry(entry, &smmu->static_cbndx_list, list) {
+ if (entry->type == TYPE_TRANS && entry->cbndx == idx)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static struct static_cbndx_entry *arm_smmu_get_static_entry_from_smr(
+ struct arm_smmu_device *smmu, int idx)
+{
+ struct static_cbndx_entry *entry;
+
+ list_for_each_entry(entry, &smmu->static_cbndx_list, list) {
+ if (entry->smr_idx == idx)
+ return entry;
+ }
+
+ return NULL;
+}
+
+static int arm_smmu_alloc_smr_idx(struct arm_smmu_device *smmu, int start,
+ int end, int sid)
+{
+ struct static_cbndx_entry *entry = arm_smmu_get_static_entry_from_sid(
+ smmu, sid);
+
+ if (entry)
+ return entry->smr_idx;
+ else
+ return __arm_smmu_alloc_bitmap(smmu->smr_map, start, end);
+}
+
+static int arm_smmu_alloc_context_idx(struct arm_smmu_device *smmu, int start,
+ int end, u16 *streamids, int num_streamids)
+{
+ struct static_cbndx_entry *entry = NULL;
+ int i;
+
+ for (i = 0; i < num_streamids; ++i) {
+ entry = arm_smmu_get_static_entry_from_sid(smmu, streamids[i]);
+ if (entry && entry->type == TYPE_TRANS)
+ break;
+ }
+
+ if (entry && entry->type == TYPE_TRANS)
+ return entry->cbndx;
+ else
+ return __arm_smmu_alloc_bitmap(smmu->context_map, start, end);
+}
+
static void __arm_smmu_free_bitmap(unsigned long *map, int idx)
{
clear_bit(idx, map);
}
+static void arm_smmu_free_smr_idx(struct arm_smmu_device *smmu, int idx)
+{
+ struct static_cbndx_entry *entry = arm_smmu_get_static_entry_from_smr(
+ smmu, idx);
+
+ if (!entry)
+ __arm_smmu_free_bitmap(smmu->smr_map, idx);
+}
+
+static void arm_smmu_free_context_idx(struct arm_smmu_device *smmu, int idx)
+{
+ struct static_cbndx_entry *entry =
+ arm_smmu_get_static_entry_from_context(smmu, idx);
+
+ if (!entry)
+ __arm_smmu_free_bitmap(smmu->context_map, idx);
+}
+
static void arm_smmu_unprepare_clocks(struct arm_smmu_device *smmu)
{
int i;
@@ -1582,7 +1687,8 @@ static int arm_smmu_restore_sec_cfg(struct arm_smmu_device *smmu)
}
static int arm_smmu_init_domain_context(struct iommu_domain *domain,
- struct arm_smmu_device *smmu)
+ struct arm_smmu_device *smmu,
+ struct arm_smmu_master_cfg *master_cfg)
{
int irq, start, ret = 0;
unsigned long ias, oas;
@@ -1650,14 +1756,12 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
}
if (cfg->cbndx == INVALID_CBNDX) {
- ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
- smmu->num_context_banks);
+ ret = arm_smmu_alloc_context_idx(smmu, start,
+ smmu->num_context_banks, master_cfg->streamids,
+ master_cfg->num_streamids);
if (IS_ERR_VALUE(ret))
goto out;
cfg->cbndx = ret;
- } else {
- if (test_and_set_bit(cfg->cbndx, smmu->context_map))
- goto out;
}
if (smmu->version == ARM_SMMU_V1) {
@@ -1764,7 +1868,7 @@ free_irqs:
free_irq(irq, domain);
}
- __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+ arm_smmu_free_context_idx(smmu, cfg->cbndx);
smmu_domain->smmu = NULL;
}
@@ -1847,8 +1951,8 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
/* Allocate the SMRs on the SMMU */
for (i = 0; i < cfg->num_streamids; ++i) {
- int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
- smmu->num_mapping_groups);
+ int idx = arm_smmu_alloc_smr_idx(smmu, 0,
+ smmu->num_mapping_groups, cfg->streamids[i]);
if (IS_ERR_VALUE(idx)) {
dev_err(smmu->dev, "failed to allocate free SMR\n");
goto err_free_smrs;
@@ -1873,7 +1977,7 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
err_free_smrs:
while (--i >= 0)
- __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+ arm_smmu_free_smr_idx(smmu, smrs[i].idx);
kfree(smrs);
return -ENOSPC;
}
@@ -1893,7 +1997,7 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
u8 idx = smrs[i].idx;
writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
- __arm_smmu_free_bitmap(smmu->smr_map, idx);
+ arm_smmu_free_smr_idx(smmu, idx);
}
cfg->smrs = NULL;
@@ -2051,32 +2155,18 @@ out:
static int arm_smmu_populate_cb(struct arm_smmu_device *smmu,
struct arm_smmu_domain *smmu_domain, struct device *dev)
{
- void __iomem *gr0_base;
struct arm_smmu_master_cfg *cfg;
struct arm_smmu_cfg *smmu_cfg = &smmu_domain->cfg;
- int i;
- u32 sid;
+ struct static_cbndx_entry *entry;
- gr0_base = ARM_SMMU_GR0(smmu);
cfg = find_smmu_master_cfg(dev);
-
if (!cfg)
return -ENODEV;
- sid = cfg->streamids[0];
-
- for (i = 0; i < smmu->num_mapping_groups; i++) {
- u32 smr, s2cr;
- u8 cbndx;
-
- smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(i));
-
- if (sid == ((smr >> SMR_ID_SHIFT) & SMR_ID_MASK)) {
- s2cr = readl_relaxed(gr0_base + ARM_SMMU_GR0_S2CR(i));
- cbndx = (s2cr >> S2CR_CBNDX_SHIFT) & S2CR_CBNDX_MASK;
- smmu_cfg->cbndx = cbndx;
- return 0;
- }
+ entry = arm_smmu_get_static_entry_from_sid(smmu, cfg->streamids[0]);
+ if (entry && entry->type == TYPE_TRANS) {
+ smmu_cfg->cbndx = entry->cbndx;
+ return 0;
}
return -EINVAL;
@@ -2150,8 +2240,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
smmu_domain->slave_side_secure = true;
}
+ cfg = find_smmu_master_cfg(dev);
+ if (!cfg) {
+ ret = -ENODEV;
+ goto err_disable_clocks;
+ }
+
/* Ensure that the domain is finalised */
- ret = arm_smmu_init_domain_context(domain, smmu);
+ ret = arm_smmu_init_domain_context(domain, smmu, cfg);
if (IS_ERR_VALUE(ret))
goto err_disable_clocks;
@@ -2177,12 +2273,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
}
/* Looks ok, so add the device to the domain */
- cfg = find_smmu_master_cfg(dev);
- if (!cfg) {
- ret = -ENODEV;
- goto err_destroy_domain_context;
- }
-
ret = arm_smmu_domain_add_master(smmu_domain, cfg);
if (ret)
goto err_destroy_domain_context;
@@ -3603,6 +3693,62 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
return 0;
}
+static int arm_smmu_add_static_cbndx(struct arm_smmu_device *smmu, int sid,
+ int smr_idx)
+{
+ void __iomem *gr0_base;
+ u32 s2cr_reg;
+ struct static_cbndx_entry *entry;
+
+ entry = devm_kzalloc(smmu->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ gr0_base = ARM_SMMU_GR0(smmu);
+ s2cr_reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_S2CR(smr_idx));
+ entry->type = (s2cr_reg >> S2CR_TYPE_SHIFT) & S2CR_TYPE_MASK;
+ entry->smr_idx = smr_idx;
+ entry->sid = sid;
+
+ if (entry->type == TYPE_TRANS) {
+ entry->cbndx = (s2cr_reg >> S2CR_CBNDX_SHIFT) &
+ S2CR_CBNDX_MASK;
+ __arm_smmu_set_bitmap(smmu->context_map, entry->cbndx);
+ pr_debug("Static context bank: smr:%d, sid:%d, cbndx:%d\n",
+ smr_idx, sid, entry->cbndx);
+ }
+ __arm_smmu_set_bitmap(smmu->smr_map, smr_idx);
+ list_add(&entry->list, &smmu->static_cbndx_list);
+
+ return 0;
+}
+
+static int arm_smmu_init_static_cbndx_list(struct arm_smmu_device *smmu)
+{
+ int i, ret = 0;
+ void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+ for (i = 0; i < smmu->num_mapping_groups; ++i) {
+ u32 smr_reg, sid;
+
+ smr_reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(i));
+ if (smr_reg & SMR_VALID) {
+ u32 smr_mask = (smr_reg >> SMR_MASK_SHIFT) &
+ SMR_MASK_MASK;
+
+ if (smr_mask != 0)
+ dev_warn(smmu->dev,
+ "Static smr mask not supported\n");
+ sid = ((smr_reg >> SMR_ID_SHIFT) & SMR_ID_MASK);
+ ret = arm_smmu_add_static_cbndx(smmu, sid, i);
+ if (ret)
+ break;
+ }
+ }
+
+ return ret;
+}
+
static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,smmu-v1", .data = (void *)ARM_SMMU_V1 },
{ .compatible = "arm,smmu-v2", .data = (void *)ARM_SMMU_V2 },
@@ -3632,6 +3778,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
mutex_init(&smmu->attach_lock);
spin_lock_init(&smmu->atos_lock);
spin_lock_init(&smmu->clock_refs_lock);
+ INIT_LIST_HEAD(&smmu->static_cbndx_list);
of_id = of_match_node(arm_smmu_of_match, dev->of_node);
if (!of_id)
@@ -3713,6 +3860,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
smmu->sec_id = msm_dev_to_device_id(dev);
err = arm_smmu_device_cfg_probe(smmu);
+ if (!err)
+ err = arm_smmu_init_static_cbndx_list(smmu);
+
arm_smmu_disable_clocks(smmu);
if (err)
goto out_put_masters;