iommu/arm-smmu: fix s2cr and smr teardown on device detach from domain

When we attach a device to a domain, we configure the SMRs (if we have
any) to match the Stream IDs for the corresponding SMMU master and
program the s2crs accordingly. However, on detach we tear down the s2crs
assuming stream-indexing (as opposed to stream-matching) and SMRs
assuming they are present.

This patch fixes the device detach code so that it operates as a
converse of the attach code.

Cc: <stable@vger.kernel.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
Will Deacon 2014-07-15 11:22:24 +01:00
parent 93b14135cf
commit 43b412bedb
1 changed files with 12 additions and 15 deletions

View File

@ -1116,6 +1116,9 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
void __iomem *gr0_base = ARM_SMMU_GR0(smmu); void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
struct arm_smmu_smr *smrs = cfg->smrs; struct arm_smmu_smr *smrs = cfg->smrs;
if (!smrs)
return;
/* Invalidate the SMRs before freeing back to the allocator */ /* Invalidate the SMRs before freeing back to the allocator */
for (i = 0; i < cfg->num_streamids; ++i) { for (i = 0; i < cfg->num_streamids; ++i) {
u8 idx = smrs[i].idx; u8 idx = smrs[i].idx;
@ -1128,20 +1131,6 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
kfree(smrs); kfree(smrs);
} }
static void arm_smmu_bypass_stream_mapping(struct arm_smmu_device *smmu,
struct arm_smmu_master_cfg *cfg)
{
int i;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
for (i = 0; i < cfg->num_streamids; ++i) {
u16 sid = cfg->streamids[i];
writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(sid));
}
}
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_master_cfg *cfg) struct arm_smmu_master_cfg *cfg)
{ {
@ -1168,13 +1157,21 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain, static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
struct arm_smmu_master_cfg *cfg) struct arm_smmu_master_cfg *cfg)
{ {
int i;
struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_device *smmu = smmu_domain->smmu;
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
/* /*
* We *must* clear the S2CR first, because freeing the SMR means * We *must* clear the S2CR first, because freeing the SMR means
* that it can be re-allocated immediately. * that it can be re-allocated immediately.
*/ */
arm_smmu_bypass_stream_mapping(smmu, cfg); for (i = 0; i < cfg->num_streamids; ++i) {
u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(idx));
}
arm_smmu_master_free_smrs(smmu, cfg); arm_smmu_master_free_smrs(smmu, cfg);
} }