]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/iommu/of_iommu.c
iommu: store DT-probed IOMMU data privately
[karo-tx-linux.git] / drivers / iommu / of_iommu.c
index e550ccb7634e91d312cd9377ff499499d37e38ed..af1dc6a1c0a1fba38df9d7a6f7b4556251182dea 100644 (file)
  */
 
 #include <linux/export.h>
+#include <linux/iommu.h>
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/slab.h>
+
+static const struct of_device_id __iommu_of_table_sentinel
+       __used __section(__iommu_of_table_end);
 
 /**
  * of_get_dma_window - Parse *dma-window property and returns 0 if found.
@@ -89,3 +94,87 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
        return 0;
 }
 EXPORT_SYMBOL_GPL(of_get_dma_window);
+
+struct of_iommu_node {
+       struct list_head list;
+       struct device_node *np;
+       struct iommu_ops *ops;
+};
+static LIST_HEAD(of_iommu_list);
+static DEFINE_SPINLOCK(of_iommu_lock);
+
+void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops)
+{
+       struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+
+       if (WARN_ON(!iommu))
+               return;
+
+       INIT_LIST_HEAD(&iommu->list);
+       iommu->np = np;
+       iommu->ops = ops;
+       spin_lock(&of_iommu_lock);
+       list_add_tail(&iommu->list, &of_iommu_list);
+       spin_unlock(&of_iommu_lock);
+}
+
+struct iommu_ops *of_iommu_get_ops(struct device_node *np)
+{
+       struct of_iommu_node *node;
+       struct iommu_ops *ops = NULL;
+
+       spin_lock(&of_iommu_lock);
+       list_for_each_entry(node, &of_iommu_list, list)
+               if (node->np == np) {
+                       ops = node->ops;
+                       break;
+               }
+       spin_unlock(&of_iommu_lock);
+       return ops;
+}
+
+struct iommu_ops *of_iommu_configure(struct device *dev)
+{
+       struct of_phandle_args iommu_spec;
+       struct device_node *np;
+       struct iommu_ops *ops = NULL;
+       int idx = 0;
+
+       /*
+        * We don't currently walk up the tree looking for a parent IOMMU.
+        * See the `Notes:' section of
+        * Documentation/devicetree/bindings/iommu/iommu.txt
+        */
+       while (!of_parse_phandle_with_args(dev->of_node, "iommus",
+                                          "#iommu-cells", idx,
+                                          &iommu_spec)) {
+               np = iommu_spec.np;
+               ops = of_iommu_get_ops(np);
+
+               if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+                       goto err_put_node;
+
+               of_node_put(np);
+               idx++;
+       }
+
+       return ops;
+
+err_put_node:
+       of_node_put(np);
+       return NULL;
+}
+
+void __init of_iommu_init(void)
+{
+       struct device_node *np;
+       const struct of_device_id *match, *matches = &__iommu_of_table;
+
+       for_each_matching_node_and_match(np, matches, &match) {
+               const of_iommu_init_fn init_fn = match->data;
+
+               if (init_fn(np))
+                       pr_err("Failed to initialise IOMMU %s\n",
+                               of_node_full_name(np));
+       }
+}