@@ -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;
@@ -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,
};
@@ -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
@@ -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)
new file mode 100644
@@ -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, ¶ms,
+ 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);
@@ -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);
@@ -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
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