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

Commit da68882a authored by Patrick Daly's avatar Patrick Daly
Browse files

iommu: arm-smmu: Handoff SMR registers and context banks



The continuous splash screen feature requires the display hardware to
operate while the linux iommu driver probes. Therefore, we cannot simply
wipe out the SMR register settings programmed by the bootloader.

Detect which SMR registers are in use during probe, and which context banks
they are associated with. Reserve this context bank for the first linux
device whose stream-id matches the SMR register.

Only context banks which have SCTLR.M bit set to 0 are allowed; any
existing page-tables will be discarded.

Change-Id: Iecce61b38b36db01db4fd2d26b6d4451e68c04da
Signed-off-by: default avatarPatrick Daly <pdaly@codeaurora.org>
parent 937de539
Loading
Loading
Loading
Loading
+102 −8
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@
#define ARM_SMMU_GR0_SMR(n)		(0x800 + ((n) << 2))
#define SMR_VALID			(1 << 31)
#define SMR_MASK_SHIFT			16
#define SMR_MASK_MASK			0x7FFF
#define SMR_ID_SHIFT			0

#define ARM_SMMU_GR0_S2CR(n)		(0xc00 + ((n) << 2))
@@ -335,10 +336,12 @@ struct arm_smmu_s2cr {
	enum arm_smmu_s2cr_type		type;
	enum arm_smmu_s2cr_privcfg	privcfg;
	u8				cbndx;
	bool				cb_handoff;
};

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

struct arm_smmu_smr {
@@ -550,6 +553,10 @@ static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu);

static int arm_smmu_enable_s1_translations(struct arm_smmu_domain *smmu_domain);

static int arm_smmu_alloc_cb(struct iommu_domain *domain,
				struct arm_smmu_device *smmu,
				struct device *dev);

static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
{
	return container_of(dom, struct arm_smmu_domain, domain);
@@ -1612,14 +1619,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
	if (is_iommu_pt_coherent(smmu_domain))
		quirks |= IO_PGTABLE_QUIRK_PAGE_TABLE_COHERENT;

	/* Dynamic domains must set cbndx through domain attribute */
	if (!dynamic) {
		ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
				      smmu->num_context_banks);
	ret = arm_smmu_alloc_cb(domain, smmu, dev);
	if (ret < 0)
		goto out_unlock;
	cfg->cbndx = ret;
	}

	if (smmu->version < ARM_SMMU_V2) {
		cfg->irptndx = atomic_inc_return(&smmu->irptndx);
		cfg->irptndx %= smmu->num_context_irqs;
@@ -3260,6 +3264,92 @@ static int arm_smmu_id_size_to_bits(int size)
	}
}


/*
 * Some context banks needs to be transferred from bootloader to HLOS in a way
 * that allows ongoing traffic. The current expectation is that these context
 * banks operate in bypass mode.
 * Additionally, there must be exactly one device in devicetree with stream-ids
 * overlapping those used by the bootloader.
 */
static int arm_smmu_alloc_cb(struct iommu_domain *domain,
				struct arm_smmu_device *smmu,
				struct device *dev)
{
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
	u32 i, idx;
	int cb = -EINVAL;
	bool dynamic;

	/* Dynamic domains must set cbndx through domain attribute */
	dynamic = is_dynamic_domain(domain);
	if (dynamic)
		return INVALID_CBNDX;

	mutex_lock(&smmu->stream_map_mutex);
	for_each_cfg_sme(fwspec, i, idx) {
		if (smmu->s2crs[idx].cb_handoff)
			cb = smmu->s2crs[idx].cbndx;
	}

	if (cb < 0) {
		mutex_unlock(&smmu->stream_map_mutex);
		return __arm_smmu_alloc_bitmap(smmu->context_map,
						smmu->num_s2_context_banks,
						smmu->num_context_banks);
	}

	for (i = 0; i < smmu->num_mapping_groups; i++) {
		if (smmu->s2crs[i].cbndx == cb) {
			smmu->s2crs[i].cbndx = 0;
			smmu->s2crs[i].cb_handoff = false;
			smmu->s2crs[i].count -= 1;
		}
	}
	mutex_unlock(&smmu->stream_map_mutex);

	return cb;
}

static int arm_smmu_handoff_cbs(struct arm_smmu_device *smmu)
{
	u32 i, raw_smr, raw_s2cr;
	struct arm_smmu_smr smr;
	struct arm_smmu_s2cr s2cr;

	for (i = 0; i < smmu->num_mapping_groups; i++) {
		raw_smr = readl_relaxed(ARM_SMMU_GR0(smmu) +
					ARM_SMMU_GR0_SMR(i));
		if (!(raw_smr & SMR_VALID))
			continue;

		smr.mask = (raw_smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
		smr.id = (u16)raw_smr;
		smr.valid = true;

		raw_s2cr = readl_relaxed(ARM_SMMU_GR0(smmu) +
					ARM_SMMU_GR0_S2CR(i));
		s2cr.group = NULL;
		s2cr.count = 1;
		s2cr.type = (raw_s2cr >> S2CR_TYPE_SHIFT) & S2CR_TYPE_MASK;
		s2cr.privcfg = (raw_s2cr >> S2CR_PRIVCFG_SHIFT) &
				S2CR_PRIVCFG_MASK;
		s2cr.cbndx = (u8)raw_s2cr;
		s2cr.cb_handoff = true;

		if (s2cr.type != S2CR_TYPE_TRANS)
			continue;

		smmu->smrs[i] = smr;
		smmu->s2crs[i] = s2cr;
		bitmap_set(smmu->context_map, s2cr.cbndx, 1);
		dev_dbg(smmu->dev, "Handoff smr: %x s2cr: %x cb: %d\n",
			raw_smr, raw_s2cr, s2cr.cbndx);
	}

	return 0;
}

static int arm_smmu_parse_impl_def_registers(struct arm_smmu_device *smmu)
{
	struct device *dev = smmu->dev;
@@ -3844,6 +3934,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
	if (err)
		goto out_power_off;

	err = arm_smmu_handoff_cbs(smmu);
	if (err)
		goto out_power_off;

	err = arm_smmu_parse_impl_def_registers(smmu);
	if (err)
		goto out_power_off;