From: Thierry Reding <treding@xxxxxxxxxx> Add an IOMMU device registry for drivers to register with and implement a method for users of the IOMMU API to attach to an IOMMU device. This allows to support deferred probing and gives the IOMMU API a convenient hook to perform early initialization of a device if necessary. Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> --- drivers/iommu/iommu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 27 +++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 806b55d056b7..5e9e82c73bbf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -29,8 +29,12 @@ #include <linux/idr.h> #include <linux/notifier.h> #include <linux/err.h> +#include <linux/of.h> #include <trace/events/iommu.h> +static DEFINE_MUTEX(iommus_lock); +static LIST_HEAD(iommus); + static struct kset *iommu_group_kset; static struct ida iommu_group_ida; static struct mutex iommu_group_mutex; @@ -1004,3 +1008,92 @@ int iommu_domain_set_attr(struct iommu_domain *domain, return ret; } EXPORT_SYMBOL_GPL(iommu_domain_set_attr); + +int iommu_add(struct iommu *iommu) +{ + mutex_lock(&iommus_lock); + list_add_tail(&iommu->list, &iommus); + mutex_unlock(&iommus_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iommu_add); + +void iommu_remove(struct iommu *iommu) +{ + mutex_lock(&iommus_lock); + list_del_init(&iommu->list); + mutex_unlock(&iommus_lock); +} +EXPORT_SYMBOL_GPL(iommu_remove); + +static int of_iommu_attach(struct device *dev) +{ + struct of_phandle_iter iter; + struct iommu *iommu; + + mutex_lock(&iommus_lock); + + of_property_for_each_phandle_with_args(iter, dev->of_node, "iommus", + "#iommu-cells", 0) { + bool found = false; + int err; + + /* skip disabled IOMMUs */ + if (!of_device_is_available(iter.out_args.np)) + continue; + + list_for_each_entry(iommu, &iommus, list) { + if (iommu->dev->of_node == iter.out_args.np) { + err = iommu->ops->attach(iommu, dev); + if (err < 0) { + } + + found = true; + } + } + + if (!found) { + mutex_unlock(&iommus_lock); + return -EPROBE_DEFER; + } + } + + mutex_unlock(&iommus_lock); + + return 0; +} + +static int of_iommu_detach(struct device *dev) +{ + /* TODO: implement */ + return -ENOSYS; +} + +int iommu_attach(struct device *dev) +{ + int err = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = of_iommu_attach(dev); + if (!err) + return 0; + } + + return err; +} +EXPORT_SYMBOL_GPL(iommu_attach); + +int iommu_detach(struct device *dev) +{ + int err = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = of_iommu_detach(dev); + if (!err) + return 0; + } + + return err; +} +EXPORT_SYMBOL_GPL(iommu_detach); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 284a4683fdc1..ac2ceef194d4 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -43,6 +43,17 @@ struct notifier_block; typedef int (*iommu_fault_handler_t)(struct iommu_domain *, struct device *, unsigned long, int, void *); +struct iommu { + struct device *dev; + + struct list_head list; + + const struct iommu_ops *ops; +}; + +int iommu_add(struct iommu *iommu); +void iommu_remove(struct iommu *iommu); + struct iommu_domain_geometry { dma_addr_t aperture_start; /* First address that can be mapped */ dma_addr_t aperture_end; /* Last address that can be mapped */ @@ -130,6 +141,9 @@ struct iommu_ops { /* Get the numer of window per domain */ u32 (*domain_get_windows)(struct iommu_domain *domain); + int (*attach)(struct iommu *iommu, struct device *dev); + int (*detach)(struct iommu *iommu, struct device *dev); + unsigned long pgsize_bitmap; }; @@ -192,6 +206,10 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t offset, u64 size, int prot); extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); + +int iommu_attach(struct device *dev); +int iommu_detach(struct device *dev); + /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -396,6 +414,15 @@ static inline int iommu_domain_set_attr(struct iommu_domain *domain, return -EINVAL; } +static inline int iommu_attach(struct device *dev) +{ + return 0; +} + +static inline int iommu_detach(struct device *dev) +{ + return 0; +} #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ -- 2.0.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html