diff mbox series

[12/30] iommu/of: Add iommu_of_xlate()

Message ID 12-v1-f82a05539a64+5042-iommu_fwspec_p2_jgg@nvidia.com (mailing list archive)
State Handled Elsewhere, archived
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 function can be called by drivers in their probe function if they
want to parse their own ID table, almost always because the driver
supports a multi-instance configuration and needs to extract the list of
iommu_driver's and data from the ID into some internal format.

The core code will find the iommu_driver for each ID table entry and
validate that it matches the driver's ops. A driver provided function is
called to handle the (iommu_driver, ID) tuple.

Before calling this function the driver should allocate its per-driver
private data and pass it through the opaque cookie priv argument.

Driver's should follow a typical pattern in their probe_device:

 static int apple_dart_of_xlate(struct iommu_device *iommu,
                                struct of_phandle_args *args, void *priv);
[..]

	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
 	if (!cfg) return ERR_PTR(-ENOMEM);

	ret = iommu_of_xlate(pinf, &apple_dart_iommu_ops, 1,
			     &apple_dart_of_xlate, cfg);
	if (ret) goto err_free;

	dev_iommu_priv_set(dev, cfg);
        return &??->iommu; // The first iommu_device parsed

Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
---
 drivers/iommu/of_iommu.c     | 58 ++++++++++++++++++++++++++++++++++++
 include/linux/iommu-driver.h | 13 ++++++++
 2 files changed, 71 insertions(+)
diff mbox series

Patch

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 37af32a6bc84e5..9c1d398aa2cd9c 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -285,6 +285,8 @@  struct parse_info {
 	struct iommu_probe_info *pinf;
 	const struct iommu_ops *ops;
 	int num_cells;
+	iommu_of_xlate_fn xlate_fn;
+	void *priv;
 };
 
 static struct iommu_device *parse_iommu(struct parse_info *info,
@@ -336,3 +338,59 @@  struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 	return iommu_fw_finish_get_single(pinf);
 }
 EXPORT_SYMBOL_GPL(__iommu_of_get_single_iommu);
+
+static int parse_of_xlate(struct of_phandle_args *iommu_spec, void *_info)
+{
+	struct parse_info *info = _info;
+	struct iommu_device *iommu;
+
+	iommu = parse_iommu(info, iommu_spec);
+	if (IS_ERR(iommu))
+		return PTR_ERR(iommu);
+	info->pinf->num_ids++;
+	return info->xlate_fn(iommu, iommu_spec, info->priv);
+}
+
+/**
+ * iommu_of_xlate - Parse all OF ids for an IOMMU
+ * @pinf: The iommu_probe_info
+ * @ops: The ops the iommu instance must have
+ * @num_cells: #iommu-cells value to enforce, -1 is no check
+ * @fn: Call for each Instance and ID
+ * @priv: Opaque cookie for fn
+ *
+ * Drivers that support multiple iommu instances must call this function to
+ * parse each instance from the OF table. fn will be called with the driver's
+ * iommu_driver instance and the raw of_phandle_args that contains the ID.
+ *
+ * Drivers that need to parse a complex ID format should also use this function.
+ */
+int iommu_of_xlate(struct iommu_probe_info *pinf, const struct iommu_ops *ops,
+		   int num_cells, iommu_of_xlate_fn fn, void *priv)
+{
+	struct parse_info info = { .pinf = pinf,
+				   .ops = ops,
+				   .num_cells = num_cells,
+				   .xlate_fn = fn,
+				   .priv = priv };
+
+	pinf->num_ids = 0;
+	return of_iommu_for_each_id(pinf->dev, pinf->of_master_np,
+				    pinf->of_map_id, parse_of_xlate, &info);
+}
+EXPORT_SYMBOL_GPL(iommu_of_xlate);
+
+/*
+ * Temporary approach to allow drivers to opt into the bus probe. It configures
+ * the iommu_probe_info to probe the dev->of_node. This is a bit hacky because
+ * it mutates the iommu_probe_info and thus assumes there is only one op in the
+ * system. Remove when we call probe from the bus always anyhow.
+ */
+void iommu_of_allow_bus_probe(struct iommu_probe_info *pinf)
+{
+	if (pinf->is_dma_configure)
+		return;
+	pinf->of_master_np = pinf->dev->of_node;
+	pinf->is_dma_configure = true;
+}
+EXPORT_SYMBOL_GPL(iommu_of_allow_bus_probe);
diff --git a/include/linux/iommu-driver.h b/include/linux/iommu-driver.h
index 597998a62b0dd6..622d6ad9056ce0 100644
--- a/include/linux/iommu-driver.h
+++ b/include/linux/iommu-driver.h
@@ -60,9 +60,16 @@  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);
 
+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);
+
 #if IS_ENABLED(CONFIG_OF_IOMMU)
 void of_iommu_get_resv_regions(struct device *dev, struct list_head *list);
 
+int iommu_of_xlate(struct iommu_probe_info *pinf, const struct iommu_ops *ops,
+		   int num_cells, iommu_of_xlate_fn fn, void *priv);
+
 struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 						 const struct iommu_ops *ops,
 						 int num_cells);
@@ -71,6 +78,12 @@  static inline void of_iommu_get_resv_regions(struct device *dev,
 					     struct list_head *list)
 {
 }
+static inline int iommu_of_xlate(struct iommu_probe_info *pinf,
+				 const struct iommu_ops *ops, int num_cells,
+				 iommu_of_xlate_fn fn, void *priv)
+{
+	return -ENODEV;
+}
 static inline
 struct iommu_device *__iommu_of_get_single_iommu(struct iommu_probe_info *pinf,
 						 const struct iommu_ops *ops,