[PATCH 16/30] iommu/tegra: Route tegra_dev_iommu_get_stream_id() through an op

Jason Gunthorpe jgg at nvidia.com
Wed Nov 29 17:10:23 PST 2023


This special function exists because fwspec->ids is intended to be private
to the driver but tegra needs to program the FW ID into registers on the
initiating units. The function allows such units, only for tegra, to get
the IDs they are supposed to program.

The tegra HW that needs this function only supports tegra-smmu and
arm-smmu, so implement the function there.

This makes way to moving the id list into the private memory of the
driver.

Signed-off-by: Jason Gunthorpe <jgg at nvidia.com>
---
 drivers/iommu/arm/arm-smmu/arm-smmu.c | 11 +++++++++++
 drivers/iommu/of_iommu.c              | 18 ++++++++++++++++++
 drivers/iommu/tegra-smmu.c            | 11 +++++++++++
 include/linux/iommu.h                 | 21 +++++++--------------
 4 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index adc7937fd8a3a3..02b8dc4f366aa9 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1551,6 +1551,16 @@ static int arm_smmu_def_domain_type(struct device *dev)
 	return 0;
 }
 
+static bool arm_smmu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+	if (fwspec->num_ids != 1)
+		return false;
+	*stream_id = fwspec->ids[0] & 0xffff;
+	return true;
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1561,6 +1571,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.of_xlate		= arm_smmu_of_xlate,
 	.get_resv_regions	= arm_smmu_get_resv_regions,
 	.def_domain_type	= arm_smmu_def_domain_type,
+	.tegra_dev_iommu_get_stream_id = arm_smmu_get_stream_id,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 	.owner			= THIS_MODULE,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 9c1d398aa2cd9c..8d5495f03dbbcb 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -8,6 +8,7 @@
 #include <linux/export.h>
 #include <linux/iommu.h>
 #include <linux/iommu-driver.h>
+#include "iommu-priv.h"
 #include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -281,6 +282,23 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
 }
 EXPORT_SYMBOL(of_iommu_get_resv_regions);
 
+#if IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) || IS_ENABLED(CONFIG_ARM_SMMU)
+/*
+ * Newer generations of Tegra SoCs require devices' stream IDs to be directly
+ * programmed into some registers. These are always paired with a Tegra SMMU or
+ * ARM SMMU which provides an implementation of this op.
+ */
+bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	const struct iommu_ops *ops = dev_iommu_ops(dev);
+
+	if (!ops || !ops->tegra_dev_iommu_get_stream_id)
+		return false;
+	return ops->tegra_dev_iommu_get_stream_id(dev, stream_id);
+}
+EXPORT_SYMBOL_GPL(tegra_dev_iommu_get_stream_id);
+#endif
+
 struct parse_info {
 	struct iommu_probe_info *pinf;
 	const struct iommu_ops *ops;
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 310871728ab4b6..cf563db3e3b48d 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -989,6 +989,16 @@ static int tegra_smmu_def_domain_type(struct device *dev)
 	return IOMMU_DOMAIN_IDENTITY;
 }
 
+static bool tegra_smmu_get_stream_id(struct device *dev, u32 *stream_id)
+{
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+
+	if (fwspec->num_ids != 1)
+		return false;
+	*stream_id = fwspec->ids[0] & 0xffff;
+	return true;
+}
+
 static const struct iommu_ops tegra_smmu_ops = {
 	.identity_domain = &tegra_smmu_identity_domain,
 	.def_domain_type = &tegra_smmu_def_domain_type,
@@ -996,6 +1006,7 @@ static const struct iommu_ops tegra_smmu_ops = {
 	.probe_device = tegra_smmu_probe_device,
 	.device_group = tegra_smmu_device_group,
 	.of_xlate = tegra_smmu_of_xlate,
+	.tegra_dev_iommu_get_stream_id = tegra_smmu_get_stream_id,
 	.pgsize_bitmap = SZ_4K,
 	.default_domain_ops = &(const struct iommu_domain_ops) {
 		.attach_dev	= tegra_smmu_attach_dev,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f0aaf55db3c09b..0ba12e0e450705 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -412,6 +412,9 @@ struct iommu_ops {
 	int (*def_domain_type)(struct device *dev);
 	void (*remove_dev_pasid)(struct device *dev, ioasid_t pasid);
 
+	bool (*tegra_dev_iommu_get_stream_id)(struct device *dev,
+					      u32 *stream_id);
+
 	const struct iommu_domain_ops *default_domain_ops;
 	unsigned long pgsize_bitmap;
 	struct module *owner;
@@ -1309,26 +1312,16 @@ static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, struct msi_m
 
 #endif	/* CONFIG_IOMMU_DMA */
 
-/*
- * Newer generations of Tegra SoCs require devices' stream IDs to be directly programmed into
- * some registers. These are always paired with a Tegra SMMU or ARM SMMU, for which the contents
- * of the struct iommu_fwspec are known. Use this helper to formalize access to these internals.
- */
 #define TEGRA_STREAM_ID_BYPASS 0x7f
 
+#if IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) || IS_ENABLED(CONFIG_ARM_SMMU)
+bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id);
+#else
 static inline bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id)
 {
-#ifdef CONFIG_IOMMU_API
-	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-
-	if (fwspec && fwspec->num_ids == 1) {
-		*stream_id = fwspec->ids[0] & 0xffff;
-		return true;
-	}
-#endif
-
 	return false;
 }
+#endif
 
 #ifdef CONFIG_IOMMU_SVA
 static inline void mm_pasid_init(struct mm_struct *mm)
-- 
2.42.0




More information about the Linux-mediatek mailing list