diff mbox series

[25/30] iommu/iort: Add iommu_iort_get_single_iommu()

Message ID 25-v1-f82a05539a64+5042-iommu_fwspec_p2_jgg@nvidia.com
State New
Headers show
Series Make a new API for drivers to use to get their FW | expand

Commit Message

Jason Gunthorpe Nov. 30, 2023, 1:10 a.m. UTC
This API is basically the same as iommu_of_get_single_iommu(), except that
it will try to parse the ACPI IORT table if it is available.

The ACPI IORT table can return a flags value to indicate
IOMMU_FWSPEC_PCI_RC_ATS, return this through an output flags pointer.

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/acpi/arm64/iort.c    |  3 +-
 drivers/acpi/scan.c          |  1 +
 drivers/iommu/Makefile       |  1 +
 drivers/iommu/iommu.c        |  3 ++
 drivers/iommu/iort_iommu.c   | 98 ++++++++++++++++++++++++++++++++++++
 include/linux/acpi_iort.h    |  1 +
 include/linux/iommu-driver.h | 41 +++++++++++++++
 7 files changed, 146 insertions(+), 2 deletions(-)
 create mode 100644 drivers/iommu/iort_iommu.c
diff mbox series

Patch

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 798c0b344f4be8..6b2d50cc9ac180 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -79,8 +79,7 @@  static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
  *
  * Returns: fwnode_handle pointer on success, NULL on failure
  */
-static inline struct fwnode_handle *iort_get_fwnode(
-			struct acpi_iort_node *node)
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node)
 {
 	struct iort_fwnode *curr;
 	struct fwnode_handle *fwnode = NULL;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 9ec01196573b6e..eb7406cdc9a464 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1571,6 +1571,7 @@  static int acpi_iommu_configure_id(struct device *dev, const u32 *id_in)
 	struct iommu_probe_info pinf = {
 		.dev = dev,
 		.is_dma_configure = true,
+		.acpi_map_id = id_in,
 		.is_acpi = true,
 	};
 
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 9c35b106cecb2e..ebf6c151a97746 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -11,6 +11,7 @@  obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
 obj-$(CONFIG_IOMMU_IO_PGTABLE_DART) += io-pgtable-dart.o
 obj-$(CONFIG_IOMMU_IOVA) += iova.o
 obj-$(CONFIG_OF_IOMMU)	+= of_iommu.o
+obj-$(CONFIG_ACPI_IORT)	+= iort_iommu.o
 obj-$(CONFIG_ACPI_VIOT)	+= viot_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index caf14a53ed1952..7468a64778931b 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3030,6 +3030,9 @@  iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 	if (!pinf->num_ids)
 		pinf->cached_single_iommu = true;
 
+	if (pinf->is_acpi)
+		pinf->acpi_fwnode = fwnode;
+
 	if (!iommu || iommu->fwnode != fwnode) {
 		iommu = iommu_device_from_fwnode(fwnode);
 		if (!iommu)
diff --git a/drivers/iommu/iort_iommu.c b/drivers/iommu/iort_iommu.c
new file mode 100644
index 00000000000000..9a997b0fd5d5f1
--- /dev/null
+++ b/drivers/iommu/iort_iommu.c
@@ -0,0 +1,98 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES
+ */
+#include <linux/acpi_iort.h>
+#include <acpi/actbl2.h>
+
+#include <linux/iommu.h>
+#include <linux/iommu-driver.h>
+
+struct parse_info {
+	struct iommu_probe_info *pinf;
+	const struct iommu_ops *ops;
+	u32 *ids;
+};
+
+static bool iort_iommu_driver_enabled(struct iommu_probe_info *pinf, u8 type)
+{
+	switch (type) {
+	case ACPI_IORT_NODE_SMMU_V3:
+		return IS_ENABLED(CONFIG_ARM_SMMU_V3);
+	case ACPI_IORT_NODE_SMMU:
+		return IS_ENABLED(CONFIG_ARM_SMMU);
+	default:
+		dev_warn(pinf->dev,
+			 FW_WARN
+			 "IORT node type %u does not describe an SMMU\n",
+			 type);
+		return false;
+	}
+}
+
+static int parse_single_iommu(struct acpi_iort_node *iort_iommu, u32 streamid,
+			      void *_info)
+{
+	struct parse_info *info = _info;
+	struct iommu_probe_info *pinf = info->pinf;
+	struct fwnode_handle *fwnode;
+	struct iommu_device *iommu;
+
+	fwnode = iort_get_fwnode(iort_iommu);
+	if (!fwnode)
+		return -ENODEV;
+
+	iommu = iommu_device_from_fwnode_pinf(pinf, info->ops, fwnode);
+	if (IS_ERR(iommu)) {
+		if (iommu == ERR_PTR(-EPROBE_DEFER) &&
+		    !iort_iommu_driver_enabled(pinf, iort_iommu->type))
+			return -ENODEV;
+		return PTR_ERR(iommu);
+	}
+	iommu_fw_cache_id(pinf, streamid);
+	return 0;
+}
+
+static int parse_read_ids(struct acpi_iort_node *iommu, u32 streamid,
+			  void *_info)
+{
+	struct parse_info *info = _info;
+
+	*info->ids = streamid;
+	(*info->ids)++;
+	return 0;
+}
+
+static int iort_get_u32_ids(struct iommu_probe_info *pinf, u32 *ids)
+{
+	struct parse_info info = { .pinf = pinf, .ids = ids };
+	struct iort_params params;
+
+	return iort_iommu_for_each_id(pinf->dev, pinf->acpi_map_id, &params,
+				      parse_read_ids, &info);
+}
+
+struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params)
+{
+	struct parse_info info = { .pinf = pinf, .ops = ops };
+	struct iort_params unused_params;
+	int err;
+
+	if (!pinf->is_dma_configure || !pinf->is_acpi)
+		return ERR_PTR(-ENODEV);
+
+	if (!params)
+		params = &unused_params;
+
+	iommu_fw_clear_cache(pinf);
+	err = iort_iommu_for_each_id(pinf->dev, pinf->acpi_map_id, params,
+				     parse_single_iommu, &info);
+	if (err)
+		return ERR_PTR(err);
+	pinf->get_u32_ids = iort_get_u32_ids;
+	return iommu_fw_finish_get_single(pinf);
+}
+EXPORT_SYMBOL(__iommu_iort_get_single_iommu);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 13f0cefb930693..bacba2a76c3acb 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -40,6 +40,7 @@  typedef int (*iort_for_each_fn)(struct acpi_iort_node *iommu, u32 streamid,
 int iort_iommu_for_each_id(struct device *dev, const u32 *id_in,
 			   struct iort_params *params, iort_for_each_fn fn,
 			   void *info);
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node);
 
 #ifdef CONFIG_ACPI_IORT
 u32 iort_msi_map_id(struct device *dev, u32 id);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index ce0ba1f35bb5dc..c4e133cdef2c78 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -19,6 +19,7 @@ 
 struct of_phandle_args;
 struct fwnode_handle;
 struct iommu_device;
+struct iort_params;
 struct iommu_ops;
 
 /*
@@ -39,7 +40,9 @@  struct iommu_probe_info {
 	struct list_head *deferred_group_list;
 	struct iommu_device *cached_iommu;
 	struct device_node *of_master_np;
+	struct fwnode_handle *acpi_fwnode;
 	const u32 *of_map_id;
+	const u32 *acpi_map_id;
 	int (*get_u32_ids)(struct iommu_probe_info *pinf, u32 *ids);
 	unsigned int num_ids;
 	u32 cached_ids[8];
@@ -63,6 +66,21 @@  iommu_device_from_fwnode_pinf(struct iommu_probe_info *pinf,
 			      struct fwnode_handle *fwnode);
 struct iommu_device *iommu_fw_finish_get_single(struct iommu_probe_info *pinf);
 
+/**
+ * iommu_fw_acpi_fwnode - Get an ACPI fwnode_handle
+ * @pinf: The iommu_probe_info
+ *
+ * Return the ACPI version of the fwnode describing the iommu data that is
+ * associated with the device being probed.
+ */
+static inline struct fwnode_handle *
+iommu_fw_acpi_fwnode(struct iommu_probe_info *pinf)
+{
+	if (!pinf->is_acpi)
+		return NULL;
+	return pinf->acpi_fwnode;
+}
+
 typedef int (*iommu_of_xlate_fn)(struct iommu_device *iommu,
 				struct of_phandle_args *args, void *priv);
 void iommu_of_allow_bus_probe(struct iommu_probe_info *pinf);
@@ -213,4 +231,27 @@  __iommu_viot_get_single_iommu(struct iommu_probe_info *pinf,
 			      __iommu_of_get_single_iommu(pinf, ops, -1)), \
 		drv_struct, member)
 
+#if IS_ENABLED(CONFIG_ACPI_IORT)
+struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params);
+#else
+static inline struct iommu_device *
+__iommu_iort_get_single_iommu(struct iommu_probe_info *pinf,
+			      const struct iommu_ops *ops,
+			      struct iort_params *params)
+{
+	return ERR_PTR(-ENODEV);
+}
+#endif
+#define iommu_iort_get_single_iommu(pinf, ops, params, drv_struct, member)    \
+	({                                                                    \
+		memset(params, 0, sizeof(*(params)));                         \
+		container_of_err(__iommu_first(__iommu_iort_get_single_iommu( \
+						       pinf, ops, params),    \
+					       __iommu_of_get_single_iommu(   \
+						       pinf, ops, -1)),       \
+				 drv_struct, member)                          \
+	})
 #endif