Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 098ddd7f authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "iommu/arm-smmu: Keep track of S2CR state" into msm-4.8

parents 79cc7459 a754fd15
Loading
Loading
Loading
Loading
+97 −57
Original line number Diff line number Diff line
@@ -174,9 +174,20 @@
#define S2CR_CBNDX_MASK			0xff
#define S2CR_TYPE_SHIFT			16
#define S2CR_TYPE_MASK			0x3
#define S2CR_TYPE_TRANS			(0 << S2CR_TYPE_SHIFT)
#define S2CR_TYPE_BYPASS		(1 << S2CR_TYPE_SHIFT)
#define S2CR_TYPE_FAULT			(2 << S2CR_TYPE_SHIFT)
enum arm_smmu_s2cr_type {
	S2CR_TYPE_TRANS,
	S2CR_TYPE_BYPASS,
	S2CR_TYPE_FAULT,
};

#define S2CR_PRIVCFG_SHIFT		24
#define S2CR_PRIVCFG_MASK		0x3
enum arm_smmu_s2cr_privcfg {
	S2CR_PRIVCFG_DEFAULT,
	S2CR_PRIVCFG_DIPAN,
	S2CR_PRIVCFG_UNPRIV,
	S2CR_PRIVCFG_PRIV,
};

/* Context bank attribute registers */
#define ARM_SMMU_GR1_CBAR(n)		(0x0 + ((n) << 2))
@@ -322,6 +333,16 @@ struct arm_smmu_impl_def_reg {
	u32 value;
};

struct arm_smmu_s2cr {
	enum arm_smmu_s2cr_type		type;
	enum arm_smmu_s2cr_privcfg	privcfg;
	u8				cbndx;
};

#define s2cr_init_val (struct arm_smmu_s2cr){				\
	.type = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS,	\
}

struct arm_smmu_smr {
	u16				mask;
	u16				id;
@@ -406,6 +427,7 @@ struct arm_smmu_device {
	u16				streamid_mask;
	u16				smr_mask_mask;
	struct arm_smmu_smr		*smrs;
	struct arm_smmu_s2cr		*s2crs;

	unsigned long			va_size;
	unsigned long			ipa_size;
@@ -1929,6 +1951,23 @@ static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
}

static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
{
	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
	u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
		  (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;

	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
}

static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
{
	arm_smmu_write_s2cr(smmu, idx);
	if (smmu->smrs)
		arm_smmu_write_smr(smmu, idx);
}

static int arm_smmu_master_alloc_smes(struct arm_smmu_device *smmu,
				      struct arm_smmu_master_cfg *cfg)
{
@@ -1979,6 +2018,26 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
{
	int i;

	/*
	 * We *must* clear the S2CR first, because freeing the SMR means
	 * that it can be re-allocated immediately.
	 */
	for (i = 0; i < cfg->num_streamids; ++i) {
		int idx = cfg->smendx[i];

		/*
		 * An IOMMU group is torn down by the first device to be
		 * removed
		 */
		if (idx == INVALID_SMENDX)
			return;

		smmu->s2crs[idx] = s2cr_init_val;
		arm_smmu_write_s2cr(smmu, idx);
	}
	/* Sync S2CR updates before touching anything else */
	__iowmb();

	/* Invalidate the SMRs before freeing back to the allocator */
	for (i = 0; i < cfg->num_streamids; ++i) {
		if (smmu->smrs)
@@ -1991,9 +2050,16 @@ static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
				      struct arm_smmu_master_cfg *cfg)
{
	int i, ret;
	int i, ret = 0;
	struct arm_smmu_device *smmu = smmu_domain->smmu;
	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
	enum arm_smmu_s2cr_type type = S2CR_TYPE_TRANS;
	u8 cbndx = smmu_domain->cfg.cbndx;

	if (cfg->smendx[0] == INVALID_SMENDX)
		ret = arm_smmu_master_alloc_smes(smmu, cfg);
	if (ret)
		return ret;

	/*
	 * FIXME: This won't be needed once we have IOMMU-backed DMA ops
@@ -2002,51 +2068,22 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
	 * and a PCI device (i.e. a PCI host controller)
	 */
	if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
		return 0;

	/* Devices in an IOMMU group may already be configured */
	ret = arm_smmu_master_alloc_smes(smmu, cfg);
	if (ret)
		return ret == -EEXIST ? 0 : ret;
		type = S2CR_TYPE_BYPASS;

	for (i = 0; i < cfg->num_streamids; ++i) {
		u32 idx, s2cr;

		idx = cfg->smendx[i];
		s2cr = S2CR_TYPE_TRANS |
		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
	}

	return 0;
}

static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
					  struct arm_smmu_master_cfg *cfg)
{
	int i;
	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
	 * that it can be re-allocated immediately.
	 */
	for (i = 0; i < cfg->num_streamids; ++i) {
		int idx = cfg->smendx[i];
		u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;

		/*
		 * An IOMMU group is torn down by the first device to be
		 * removed
		 */
		if (idx == INVALID_SMENDX)
			return;
		/* Devices in an IOMMU group may already be configured */
		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
			break;

		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
		s2cr[idx].type = type;
		s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
		s2cr[idx].cbndx = cbndx;
		arm_smmu_write_s2cr(smmu, idx);
	}

	arm_smmu_master_free_smes(smmu, cfg);
	return 0;
}

static void arm_smmu_detach_dev(struct iommu_domain *domain,
@@ -2071,7 +2108,6 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain,
	}

	dev->archdata.iommu = NULL;
	arm_smmu_domain_remove_master(smmu_domain, cfg);

	/* Remove additional vote for atomic power */
	if (atomic_domain) {
@@ -2214,13 +2250,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
		goto out_power_off;
	}

	/* Detach the dev from its current domain */
	if (dev->archdata.iommu)
		arm_smmu_detach_dev(dev->archdata.iommu, dev);

	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
	if (!ret)
		dev->archdata.iommu = domain;

out_power_off:
	/*
@@ -2512,6 +2542,12 @@ static int arm_smmu_add_device(struct device *dev)

static void arm_smmu_remove_device(struct device *dev)
{
	struct arm_smmu_device *smmu = find_smmu_for_device(dev);
	struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);

	if (smmu && cfg)
		arm_smmu_master_free_smes(smmu, cfg);

	iommu_group_remove_device(dev);
}

@@ -3131,13 +3167,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
	 * invalid and all S2CRn as bypass unless overridden.
	 */
	if (!(smmu->options & ARM_SMMU_OPT_SKIP_INIT)) {
		for (i = 0; i < smmu->num_mapping_groups; ++i) {
			reg = disable_bypass ? S2CR_TYPE_FAULT
					: S2CR_TYPE_BYPASS;
			if (smmu->smrs)
				arm_smmu_write_smr(smmu, i);
			writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
		}
		for (i = 0; i < smmu->num_mapping_groups; ++i)
			arm_smmu_write_sme(smmu, i);

		arm_smmu_context_bank_reset(smmu);
	}
@@ -3394,6 +3425,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
	u32 id;
	bool cttw_dt, cttw_reg;
	int i;

	dev_dbg(smmu->dev, "probing hardware configuration...\n");
	dev_dbg(smmu->dev, "SMMUv%d with:\n",
@@ -3491,6 +3523,14 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
			   "\tstream matching with %lu register groups, mask 0x%x",
			   size, smmu->smr_mask_mask);
	}
	/* s2cr->type == 0 means translation, so initialise explicitly */
	smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs),
					 GFP_KERNEL);
	if (!smmu->s2crs)
		return -ENOMEM;
	for (i = 0; i < size; i++)
		smmu->s2crs[i] = s2cr_init_val;

	smmu->num_mapping_groups = size;

	if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {