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

Commit 4670e9e0 authored by Robin Murphy's avatar Robin Murphy Committed by Isaac J. Manjarres
Browse files

iommu/arm-smmu: Add context init implementation hook



Allocating and initialising a context for a domain is another point
where certain implementations are known to want special behaviour.
Currently the other half of the Cavium workaround comes into play here,
so let's finish the job to get the whole thing right out of the way.

Change-Id: I2fc1b383a5084d6a92e99002ed12b610e0cf7b04
Signed-off-by: default avatarRobin Murphy <robin.murphy@arm.com>
Signed-off-by: default avatarWill Deacon <will@kernel.org>
Git-commit: ba7e4a08
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git


[isaacm@codeaurora.org: resolved trivial merge conflicts]
Signed-off-by: default avatarIsaac J. Manjarres <isaacm@codeaurora.org>
parent 45eebc82
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -48,25 +48,60 @@ const struct arm_smmu_impl calxeda_impl = {
};


struct cavium_smmu {
	struct arm_smmu_device smmu;
	u32 id_base;
};

static int cavium_cfg_probe(struct arm_smmu_device *smmu)
{
	static atomic_t context_count = ATOMIC_INIT(0);
	struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu);
	/*
	 * Cavium CN88xx erratum #27704.
	 * Ensure ASID and VMID allocation is unique across all SMMUs in
	 * the system.
	 */
	smmu->cavium_id_base = atomic_fetch_add(smmu->num_context_banks,
						   &context_count);
	cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count);
	dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n");

	return 0;
}

int cavium_init_context(struct arm_smmu_domain *smmu_domain)
{
	struct cavium_smmu *cs = container_of(smmu_domain->smmu,
					      struct cavium_smmu, smmu);

	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2)
		smmu_domain->cfg.vmid += cs->id_base;
	else
		smmu_domain->cfg.asid += cs->id_base;

	return 0;
}

const struct arm_smmu_impl cavium_impl = {
	.cfg_probe = cavium_cfg_probe,
	.init_context = cavium_init_context,
};

struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu)
{
	struct cavium_smmu *cs;

	cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL);
	if (!cs)
		return ERR_PTR(-ENOMEM);

	cs->smmu = *smmu;
	cs->smmu.impl = &cavium_impl;

	devm_kfree(smmu->dev, smmu);

	return &cs->smmu;
}


#define ARM_MMU500_ACTLR_CPRE		(1 << 1)

@@ -126,8 +161,7 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
		smmu->impl = &arm_mmu500_impl;
		break;
	case CAVIUM_SMMUV2:
		smmu->impl = &cavium_impl;
		break;
		return cavium_smmu_impl_init(smmu);
	default:
		break;
	}
+7 −51
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/io-pgtable.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/interconnect.h>
@@ -144,25 +143,6 @@ struct arm_smmu_master_cfg {
#define for_each_cfg_sme(fw, i, idx) \
	for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i)

enum arm_smmu_context_fmt {
	ARM_SMMU_CTX_FMT_NONE,
	ARM_SMMU_CTX_FMT_AARCH64,
	ARM_SMMU_CTX_FMT_AARCH32_L,
	ARM_SMMU_CTX_FMT_AARCH32_S,
};

struct arm_smmu_cfg {
	u8				cbndx;
	u8				irptndx;
	union {
		u16			asid;
		u16			vmid;
	};
	u32				procid;
	enum arm_smmu_cbar_type		cbar;
	enum arm_smmu_context_fmt	fmt;
};
#define INVALID_IRPTNDX			0xff
#define INVALID_CBNDX			0xff
#define INVALID_ASID			0xffff
/*
@@ -175,42 +155,12 @@ struct arm_smmu_cfg {
#define ARM_SMMU_CB_VMID(smmu, cfg) ((u16)(smmu)->cavium_id_base + \
							(cfg)->cbndx + 1)

enum arm_smmu_domain_stage {
	ARM_SMMU_DOMAIN_S1 = 0,
	ARM_SMMU_DOMAIN_S2,
	ARM_SMMU_DOMAIN_NESTED,
	ARM_SMMU_DOMAIN_BYPASS,
};

struct arm_smmu_pte_info {
	void *virt_addr;
	size_t size;
	struct list_head entry;
};

struct arm_smmu_domain {
	struct arm_smmu_device		*smmu;
	struct device			*dev;
	struct io_pgtable_ops		*pgtbl_ops;
	const struct iommu_gather_ops	*tlb_ops;
	struct arm_smmu_cfg		cfg;
	enum arm_smmu_domain_stage	stage;
	bool				non_strict;
	struct mutex			init_mutex; /* Protects smmu pointer */
	spinlock_t			cb_lock; /* Serialises ATS1* ops */
	spinlock_t			sync_lock; /* Serialises TLB syncs */
	struct msm_io_pgtable_info	pgtbl_info;
	u64 attributes;
	u32				secure_vmid;
	struct list_head		pte_info_list;
	struct list_head		unassign_list;
	struct mutex			assign_lock;
	struct list_head		secure_pool_list;
	/* nonsecure pool protected by pgtbl_lock */
	struct list_head		nonsecure_pool;
	struct msm_iommu_domain		domain;
};

static bool using_legacy_binding, using_generic_binding;

struct arm_smmu_option_prop {
@@ -1958,6 +1908,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,

	cfg->cbndx = ret;

	smmu_domain->smmu = smmu;
	if (smmu->impl && smmu->impl->init_context) {
		ret = smmu->impl->init_context(smmu_domain);
		if (ret)
			goto out_clear_smmu;
	}

	pgtbl_info->pgtbl_cfg = (struct io_pgtable_cfg) {
		.quirks		= quirks,
		.pgsize_bitmap	= smmu->pgsize_bitmap,
@@ -1968,7 +1925,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
		.iommu_dev	= smmu->dev,
	};

	smmu_domain->smmu = smmu;
	smmu_domain->dev = dev;
	smmu_domain->pgtbl_ops = alloc_io_pgtable_ops(fmt,
						      &pgtbl_info->pgtbl_cfg,
+52 −2
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/iommu.h>
#include <linux/io-pgtable.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -341,8 +342,6 @@ struct arm_smmu_device {

	struct list_head		list;

	u32				cavium_id_base; /* Specific to Cavium */

	spinlock_t			global_sync_lock;

	/* IOMMU core code handle */
@@ -364,6 +363,56 @@ struct arm_smmu_device {
	void				*archdata;
};

enum arm_smmu_context_fmt {
	ARM_SMMU_CTX_FMT_NONE,
	ARM_SMMU_CTX_FMT_AARCH64,
	ARM_SMMU_CTX_FMT_AARCH32_L,
	ARM_SMMU_CTX_FMT_AARCH32_S,
};

struct arm_smmu_cfg {
	u8				cbndx;
	u8				irptndx;
	union {
		u16			asid;
		u16			vmid;
	};
	u32				procid;
	enum arm_smmu_cbar_type		cbar;
	enum arm_smmu_context_fmt	fmt;
};
#define INVALID_IRPTNDX			0xff

enum arm_smmu_domain_stage {
	ARM_SMMU_DOMAIN_S1 = 0,
	ARM_SMMU_DOMAIN_S2,
	ARM_SMMU_DOMAIN_NESTED,
	ARM_SMMU_DOMAIN_BYPASS,
};

struct arm_smmu_domain {
	struct arm_smmu_device		*smmu;
	struct device			*dev;
	struct io_pgtable_ops		*pgtbl_ops;
	const struct iommu_gather_ops	*tlb_ops;
	struct arm_smmu_cfg		cfg;
	enum arm_smmu_domain_stage	stage;
	bool				non_strict;
	struct mutex			init_mutex; /* Protects smmu pointer */
	spinlock_t			cb_lock; /* Serialises ATS1* ops */
	spinlock_t			sync_lock; /* Serialises TLB syncs */
	struct msm_io_pgtable_info	pgtbl_info;
	u64 attributes;
	u32				secure_vmid;
	struct list_head		pte_info_list;
	struct list_head		unassign_list;
	struct mutex			assign_lock;
	struct list_head		secure_pool_list;
	/* nonsecure pool protected by pgtbl_lock */
	struct list_head		nonsecure_pool;
	struct msm_iommu_domain		domain;
};


/* Implementation details, yay! */
struct arm_smmu_impl {
@@ -375,6 +424,7 @@ struct arm_smmu_impl {
			    u64 val);
	int (*cfg_probe)(struct arm_smmu_device *smmu);
	int (*reset)(struct arm_smmu_device *smmu);
	int (*init_context)(struct arm_smmu_domain *smmu_domain);
};

static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)