@@ -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) {
@@ -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;
@@ -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,
@@ -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)
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@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(-)