diff options
author | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2017-01-31 15:03:18 +0000 |
---|---|---|
committer | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2017-01-31 15:03:18 +0000 |
commit | 7d51bb385aa397e608e7b2ddecf2b1a463675963 (patch) | |
tree | ed072a6cbf3f356d2b4b4e84c74f114cd4bb3ead | |
parent | 590da71a3e64a0886ff9f23a37452a31074df8ab (diff) | |
parent | 20e9b84317ad3e8dee6ed0c4b5e789715df3f55d (diff) |
Merge branch 'tracking-qcomlt-iommu-probe-deferal' into integration-linux-qcomlt-v4.9integration-linux-qcomlt-v4.9
-rw-r--r-- | arch/arm64/mm/dma-mapping.c | 132 | ||||
-rw-r--r-- | drivers/acpi/glue.c | 6 | ||||
-rw-r--r-- | drivers/base/dd.c | 9 | ||||
-rw-r--r-- | drivers/base/dma-mapping.c | 34 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu-v3.c | 35 | ||||
-rw-r--r-- | drivers/iommu/arm-smmu.c | 47 | ||||
-rw-r--r-- | drivers/iommu/iommu.c | 40 | ||||
-rw-r--r-- | drivers/iommu/msm_iommu.c | 17 | ||||
-rw-r--r-- | drivers/iommu/msm_iommu.h | 3 | ||||
-rw-r--r-- | drivers/iommu/of_iommu.c | 139 | ||||
-rw-r--r-- | drivers/of/address.c | 20 | ||||
-rw-r--r-- | drivers/of/device.c | 36 | ||||
-rw-r--r-- | drivers/of/platform.c | 10 | ||||
-rw-r--r-- | drivers/pci/probe.c | 29 | ||||
-rw-r--r-- | include/linux/dma-mapping.h | 3 | ||||
-rw-r--r-- | include/linux/iommu.h | 14 | ||||
-rw-r--r-- | include/linux/of_device.h | 10 | ||||
-rw-r--r-- | include/linux/of_iommu.h | 12 |
18 files changed, 269 insertions, 327 deletions
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 3f74d0d98de63..19858af32075c 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -800,140 +800,32 @@ static struct dma_map_ops iommu_dma_ops = { .mapping_error = iommu_dma_mapping_error, }; -/* - * TODO: Right now __iommu_setup_dma_ops() gets called too early to do - * everything it needs to - the device is only partially created and the - * IOMMU driver hasn't seen it yet, so it can't have a group. Thus we - * need this delayed attachment dance. Once IOMMU probe ordering is sorted - * to move the arch_setup_dma_ops() call later, all the notifier bits below - * become unnecessary, and will go away. - */ -struct iommu_dma_notifier_data { - struct list_head list; - struct device *dev; - const struct iommu_ops *ops; - u64 dma_base; - u64 size; -}; -static LIST_HEAD(iommu_dma_masters); -static DEFINE_MUTEX(iommu_dma_notifier_lock); - -static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops, - u64 dma_base, u64 size) -{ - struct iommu_domain *domain = iommu_get_domain_for_dev(dev); - - /* - * If the IOMMU driver has the DMA domain support that we require, - * then the IOMMU core will have already configured a group for this - * device, and allocated the default domain for that group. - */ - if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) { - pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", - dev_name(dev)); - return false; - } - - dev->archdata.dma_ops = &iommu_dma_ops; - return true; -} - -static void queue_iommu_attach(struct device *dev, const struct iommu_ops *ops, - u64 dma_base, u64 size) -{ - struct iommu_dma_notifier_data *iommudata; - - iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL); - if (!iommudata) - return; - - iommudata->dev = dev; - iommudata->ops = ops; - iommudata->dma_base = dma_base; - iommudata->size = size; - - mutex_lock(&iommu_dma_notifier_lock); - list_add(&iommudata->list, &iommu_dma_masters); - mutex_unlock(&iommu_dma_notifier_lock); -} - -static int __iommu_attach_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct iommu_dma_notifier_data *master, *tmp; - - if (action != BUS_NOTIFY_BIND_DRIVER) - return 0; - - mutex_lock(&iommu_dma_notifier_lock); - list_for_each_entry_safe(master, tmp, &iommu_dma_masters, list) { - if (data == master->dev && do_iommu_attach(master->dev, - master->ops, master->dma_base, master->size)) { - list_del(&master->list); - kfree(master); - break; - } - } - mutex_unlock(&iommu_dma_notifier_lock); - return 0; -} - -static int __init register_iommu_dma_ops_notifier(struct bus_type *bus) -{ - struct notifier_block *nb = kzalloc(sizeof(*nb), GFP_KERNEL); - int ret; - - if (!nb) - return -ENOMEM; - - nb->notifier_call = __iommu_attach_notifier; - - ret = bus_register_notifier(bus, nb); - if (ret) { - pr_warn("Failed to register DMA domain notifier; IOMMU DMA ops unavailable on bus '%s'\n", - bus->name); - kfree(nb); - } - return ret; -} - static int __init __iommu_dma_init(void) { - int ret; - - ret = iommu_dma_init(); - if (!ret) - ret = register_iommu_dma_ops_notifier(&platform_bus_type); - if (!ret) - ret = register_iommu_dma_ops_notifier(&amba_bustype); -#ifdef CONFIG_PCI - if (!ret) - ret = register_iommu_dma_ops_notifier(&pci_bus_type); -#endif - return ret; + return iommu_dma_init(); } arch_initcall(__iommu_dma_init); static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, const struct iommu_ops *ops) { - struct iommu_group *group; + struct iommu_domain *domain; if (!ops) return; + /* - * TODO: As a concession to the future, we're ready to handle being - * called both early and late (i.e. after bus_add_device). Once all - * the platform bus code is reworked to call us late and the notifier - * junk above goes away, move the body of do_iommu_attach here. + * The IOMMU core code allocates the default DMA domain, which the + * underlying IOMMU driver needs to support via the dma-iommu layer. */ - group = iommu_group_get(dev); - if (group) { - do_iommu_attach(dev, ops, dma_base, size); - iommu_group_put(group); - } else { - queue_iommu_attach(dev, ops, dma_base, size); + domain = iommu_get_domain_for_dev(dev); + if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) { + pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n", + dev_name(dev)); + return; } + + dev->archdata.dma_ops = &iommu_dma_ops; } void arch_teardown_dma_ops(struct device *dev) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 5ea5dc219f563..458445fc19da5 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -168,7 +168,6 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) struct list_head *physnode_list; unsigned int node_id; int retval = -EINVAL; - enum dev_dma_attr attr; if (has_acpi_companion(dev)) { if (acpi_dev) { @@ -225,11 +224,6 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) if (!has_acpi_companion(dev)) ACPI_COMPANION_SET(dev, acpi_dev); - attr = acpi_get_dma_attr(acpi_dev); - if (attr != DEV_DMA_NOT_SUPPORTED) - arch_setup_dma_ops(dev, 0, 0, NULL, - attr == DEV_DMA_COHERENT); - acpi_physnode_link_name(physical_node_name, node_id); retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, physical_node_name); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d76cd97a98b6b..01cba11d85968 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -19,6 +19,7 @@ #include <linux/device.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/kthread.h> #include <linux/wait.h> @@ -351,6 +352,10 @@ re_probe: if (ret) goto pinctrl_bind_failed; + ret = dma_configure(dev); + if (ret) + goto dma_failed; + if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); @@ -412,6 +417,8 @@ re_probe: goto done; probe_failed: + dma_deconfigure(dev); +dma_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); @@ -796,6 +803,8 @@ static void __device_release_driver(struct device *dev) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); + + dma_deconfigure(dev); devres_release_all(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 8f8b68c80986c..cc2cf0b25e3bf 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -7,9 +7,11 @@ * This file is released under the GPLv2. */ +#include <linux/acpi.h> #include <linux/dma-mapping.h> #include <linux/export.h> #include <linux/gfp.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -341,3 +343,35 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags) vunmap(cpu_addr); } #endif + +/* + * Common configuration to enable DMA API use for a device + */ +#include <linux/pci.h> + +int dma_configure(struct device *dev) +{ + struct device *bridge = NULL, *dma_dev = dev; + int ret = 0; + + if (dev_is_pci(dev)) { + bridge = pci_get_host_bridge_device(to_pci_dev(dev)); + dma_dev = bridge; + if (IS_ENABLED(CONFIG_OF) && dma_dev->parent && + dma_dev->parent->of_node) + dma_dev = dma_dev->parent; + } + + if (dma_dev->of_node) + ret = of_dma_configure(dev, dma_dev->of_node); + + if (bridge) + pci_put_host_bridge_device(bridge); + + return ret; +} + +void dma_deconfigure(struct device *dev) +{ + of_dma_deconfigure(dev); +} diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index e6f9b2d745ca0..8ddbf513ec93e 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -2680,40 +2680,9 @@ static struct platform_driver arm_smmu_driver = { .probe = arm_smmu_device_dt_probe, .remove = arm_smmu_device_remove, }; +module_platform_driver(arm_smmu_driver); -static int __init arm_smmu_init(void) -{ - static bool registered; - int ret = 0; - - if (!registered) { - ret = platform_driver_register(&arm_smmu_driver); - registered = !ret; - } - return ret; -} - -static void __exit arm_smmu_exit(void) -{ - return platform_driver_unregister(&arm_smmu_driver); -} - -subsys_initcall(arm_smmu_init); -module_exit(arm_smmu_exit); - -static int __init arm_smmu_of_init(struct device_node *np) -{ - int ret = arm_smmu_init(); - - if (ret) - return ret; - - if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root)) - return -ENODEV; - - return 0; -} -IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", NULL); MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations"); MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 8f7281444551d..5a04d5489f48c 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -2050,45 +2050,14 @@ static struct platform_driver arm_smmu_driver = { .probe = arm_smmu_device_dt_probe, .remove = arm_smmu_device_remove, }; - -static int __init arm_smmu_init(void) -{ - static bool registered; - int ret = 0; - - if (!registered) { - ret = platform_driver_register(&arm_smmu_driver); - registered = !ret; - } - return ret; -} - -static void __exit arm_smmu_exit(void) -{ - return platform_driver_unregister(&arm_smmu_driver); -} - -subsys_initcall(arm_smmu_init); -module_exit(arm_smmu_exit); - -static int __init arm_smmu_of_init(struct device_node *np) -{ - int ret = arm_smmu_init(); - - if (ret) - return ret; - - if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root)) - return -ENODEV; - - return 0; -} -IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init); -IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", arm_smmu_of_init); -IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", arm_smmu_of_init); -IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", arm_smmu_of_init); -IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", arm_smmu_of_init); -IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", arm_smmu_of_init); +module_platform_driver(arm_smmu_driver); + +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", NULL); +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", NULL); +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", NULL); +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", NULL); +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", NULL); +IOMMU_OF_DECLARE(cavium_smmuv2, "cavium,smmu-v2", NULL); MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>"); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 9a2f1960873b6..8d3e847d4845f 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1615,6 +1615,46 @@ out: return ret; } +struct iommu_instance { + struct list_head list; + struct fwnode_handle *fwnode; + const struct iommu_ops *ops; +}; +static LIST_HEAD(iommu_instance_list); +static DEFINE_SPINLOCK(iommu_instance_lock); + +void iommu_register_instance(struct fwnode_handle *fwnode, + const struct iommu_ops *ops) +{ + struct iommu_instance *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + + if (WARN_ON(!iommu)) + return; + + of_node_get(to_of_node(fwnode)); + INIT_LIST_HEAD(&iommu->list); + iommu->fwnode = fwnode; + iommu->ops = ops; + spin_lock(&iommu_instance_lock); + list_add_tail(&iommu->list, &iommu_instance_list); + spin_unlock(&iommu_instance_lock); +} + +const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode) +{ + struct iommu_instance *instance; + const struct iommu_ops *ops = NULL; + + spin_lock(&iommu_instance_lock); + list_for_each_entry(instance, &iommu_instance_list, list) + if (instance->fwnode == fwnode) { + ops = instance->ops; + break; + } + spin_unlock(&iommu_instance_lock); + return ops; +} + int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, const struct iommu_ops *ops) { diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index b09692bb5b0a2..1110b72f5df53 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -411,6 +411,7 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) } __disable_clocks(iommu); list_add(&iommu->dom_node, &priv->list_attached); + iommu->domain = domain; } } @@ -614,8 +615,8 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id) goto fail; } - pr_err("Unexpected IOMMU page fault!\n"); - pr_err("base = %08x\n", (unsigned int)iommu->base); + pr_debug("Unexpected IOMMU page fault!\n"); + pr_debug("base = %08x\n", (unsigned int)iommu->base); ret = __enable_clocks(iommu); if (ret) @@ -624,10 +625,16 @@ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id) for (i = 0; i < iommu->ncb; i++) { fsr = GET_FSR(iommu->base, i); if (fsr) { - pr_err("Fault occurred in context %d.\n", i); - pr_err("Interesting registers:\n"); - print_ctx_regs(iommu->base, i); + int ret = report_iommu_fault(iommu->domain, + to_msm_priv(iommu->domain)->dev, + GET_FAR(iommu->base, i), 0); + if (ret == -ENOSYS) { + pr_err("Fault occurred in context %d.\n", i); + pr_err("Interesting registers:\n"); + print_ctx_regs(iommu->base, i); + } SET_FSR(iommu->base, i, 0x4000000F); + SET_RESUME(iommu->base, i, 1); } } __disable_clocks(iommu); diff --git a/drivers/iommu/msm_iommu.h b/drivers/iommu/msm_iommu.h index 4ca25d50d6796..c53016c830370 100644 --- a/drivers/iommu/msm_iommu.h +++ b/drivers/iommu/msm_iommu.h @@ -56,6 +56,8 @@ * dom_node: list head for domain * ctx_list: list of 'struct msm_iommu_ctx_dev' * context_map: Bitmap to track allocated context banks + * domain: iommu domain that this iommu dev is a member of, + * ie. whose msm_priv::list_attached are we on? */ struct msm_iommu_dev { void __iomem *base; @@ -68,6 +70,7 @@ struct msm_iommu_dev { struct list_head dom_node; struct list_head ctx_list; DECLARE_BITMAP(context_map, IOMMU_MAX_CBS); + struct iommu_domain *domain; }; /** diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 5b82862f571f2..9529d6ccdea37 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -23,6 +23,7 @@ #include <linux/of.h> #include <linux/of_iommu.h> #include <linux/of_pci.h> +#include <linux/pci.h> #include <linux/slab.h> static const struct of_device_id __iommu_of_table_sentinel @@ -96,42 +97,33 @@ int of_get_dma_window(struct device_node *dn, const char *prefix, int index, } EXPORT_SYMBOL_GPL(of_get_dma_window); -struct of_iommu_node { - struct list_head list; - struct device_node *np; +static const struct iommu_ops +*of_iommu_xlate(struct device *dev, struct of_phandle_args *iommu_spec) +{ const struct iommu_ops *ops; -}; -static LIST_HEAD(of_iommu_list); -static DEFINE_SPINLOCK(of_iommu_lock); + struct fwnode_handle *fwnode = &iommu_spec->np->fwnode; + int err; -void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops) -{ - struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); - - if (WARN_ON(!iommu)) - return; - - of_node_get(np); - 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); -} + ops = iommu_get_instance(fwnode); + if ((ops && !ops->of_xlate) || + (!ops && !of_match_node(&__iommu_of_table, iommu_spec->np))) + return NULL; -const struct iommu_ops *of_iommu_get_ops(struct device_node *np) -{ - struct of_iommu_node *node; - const struct iommu_ops *ops = NULL; + err = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); + if (err) + return ERR_PTR(err); + /* + * The otherwise-empty fwspec handily serves to indicate the specific + * IOMMU device we're waiting for, which will be useful if we ever get + * a proper probe-ordering dependency mechanism in future. + */ + if (!ops) + return ERR_PTR(-EPROBE_DEFER); + + err = ops->of_xlate(dev, iommu_spec); + if (err) + return ERR_PTR(err); - 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; } @@ -144,10 +136,11 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) } static const struct iommu_ops -*of_pci_iommu_configure(struct pci_dev *pdev, struct device_node *bridge_np) +*of_pci_iommu_init(struct pci_dev *pdev, struct device_node *bridge_np) { const struct iommu_ops *ops; struct of_phandle_args iommu_spec; + int err; /* * Start by tracing the RID alias down the PCI topology as @@ -162,56 +155,76 @@ static const struct iommu_ops * bus into the system beyond, and which IOMMU it ends up at. */ iommu_spec.np = NULL; - if (of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map", - "iommu-map-mask", &iommu_spec.np, iommu_spec.args)) - return NULL; + err = of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map", + "iommu-map-mask", &iommu_spec.np, + iommu_spec.args); + if (err) + return ERR_PTR(err); - ops = of_iommu_get_ops(iommu_spec.np); - if (!ops || !ops->of_xlate || - iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) || - ops->of_xlate(&pdev->dev, &iommu_spec)) - ops = NULL; + ops = of_iommu_xlate(&pdev->dev, &iommu_spec); of_node_put(iommu_spec.np); return ops; } -const struct iommu_ops *of_iommu_configure(struct device *dev, - struct device_node *master_np) +static const struct iommu_ops +*of_platform_iommu_init(struct device *dev, struct device_node *np) { struct of_phandle_args iommu_spec; - struct device_node *np; const struct iommu_ops *ops = NULL; int idx = 0; - if (dev_is_pci(dev)) - return of_pci_iommu_configure(to_pci_dev(dev), master_np); - /* * 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(master_np, "iommus", - "#iommu-cells", idx, - &iommu_spec)) { - np = iommu_spec.np; - ops = of_iommu_get_ops(np); - - if (!ops || !ops->of_xlate || - iommu_fwspec_init(dev, &np->fwnode, ops) || - ops->of_xlate(dev, &iommu_spec)) - goto err_put_node; - - of_node_put(np); + while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells", + idx, &iommu_spec)) { + ops = of_iommu_xlate(dev, &iommu_spec); + of_node_put(iommu_spec.np); idx++; + if (IS_ERR_OR_NULL(ops)) + break; } return ops; +} + +const struct iommu_ops *of_iommu_configure(struct device *dev, + struct device_node *master_np) +{ + const struct iommu_ops *ops; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + + if (!master_np) + return NULL; + + if (fwspec) { + if (fwspec->ops) + return fwspec->ops; -err_put_node: - of_node_put(np); - return NULL; + /* In the deferred case, start again from scratch */ + iommu_fwspec_free(dev); + } + + if (dev_is_pci(dev)) + ops = of_pci_iommu_init(to_pci_dev(dev), master_np); + else + ops = of_platform_iommu_init(dev, master_np); + /* + * If we have reason to believe the IOMMU driver missed the initial + * add_device callback for dev, replay it to get things in order. + */ + if (!IS_ERR_OR_NULL(ops) && ops->add_device && + dev->bus && !dev->iommu_group) { + int err = ops->add_device(dev); + + if (err) + ops = ERR_PTR(err); + } + + return ops; } static int __init of_iommu_init(void) @@ -222,7 +235,7 @@ static int __init of_iommu_init(void) for_each_matching_node_and_match(np, matches, &match) { const of_iommu_init_fn init_fn = match->data; - if (init_fn(np)) + if (init_fn && init_fn(np)) pr_err("Failed to initialise IOMMU %s\n", of_node_full_name(np)); } diff --git a/drivers/of/address.c b/drivers/of/address.c index 02b2903fe9d27..6aeb81643f47d 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -819,8 +819,8 @@ EXPORT_SYMBOL(of_io_request_and_map); * CPU addr (phys_addr_t) : pna cells * size : nsize cells * - * It returns -ENODEV if "dma-ranges" property was not found - * for this device in DT. + * Return 0 on success, -ENODEV if the "dma-ranges" property was not found for + * this device in DT, or -EINVAL if the CPU address or size is invalid. */ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) { @@ -880,6 +880,22 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz *dma_addr = dmaaddr; *size = of_read_number(ranges + naddr + pna, nsize); + /* + * DT nodes sometimes incorrectly set the size as a mask. Work around + * those incorrect DT by computing the size as mask + 1. + */ + if (*size & 1) { + pr_warn("%s: size 0x%llx for dma-range in node(%s) set as mask\n", + __func__, *size, np->full_name); + *size = *size + 1; + } + + if (!*size) { + pr_err("%s: invalid size zero for dma-range in node(%s)\n", + __func__, np->full_name); + ret = -EINVAL; + goto out; + } pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", *dma_addr, *paddr, *size); diff --git a/drivers/of/device.c b/drivers/of/device.c index fd5cfad7c4030..d58595c4db620 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -82,7 +82,7 @@ int of_device_add(struct platform_device *ofdev) * can use a platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE events * to fix up DMA configuration. */ -void of_dma_configure(struct device *dev, struct device_node *np) +int of_dma_configure(struct device *dev, struct device_node *np) { u64 dma_addr, paddr, size; int ret; @@ -107,24 +107,9 @@ void of_dma_configure(struct device *dev, struct device_node *np) ret = of_dma_get_range(np, &dma_addr, &paddr, &size); if (ret < 0) { dma_addr = offset = 0; - size = dev->coherent_dma_mask + 1; + size = max(dev->coherent_dma_mask, dev->coherent_dma_mask + 1); } else { offset = PFN_DOWN(paddr - dma_addr); - - /* - * Add a work around to treat the size as mask + 1 in case - * it is defined in DT as a mask. - */ - if (size & 1) { - dev_warn(dev, "Invalid size 0x%llx for dma-range\n", - size); - size = size + 1; - } - - if (!size) { - dev_err(dev, "Adjusted size 0x%llx invalid\n", size); - return; - } dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); } @@ -144,13 +129,30 @@ void of_dma_configure(struct device *dev, struct device_node *np) coherent ? " " : " not "); iommu = of_iommu_configure(dev, np); + if (IS_ERR(iommu)) + return PTR_ERR(iommu); + dev_dbg(dev, "device is%sbehind an iommu\n", iommu ? " " : " not "); arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); + + return 0; } EXPORT_SYMBOL_GPL(of_dma_configure); +/** + * of_dma_deconfigure - Clean up DMA configuration + * @dev: Device for which to clean up DMA configuration + * + * Clean up all configuration performed by of_dma_configure_ops() and free all + * resources that have been allocated. + */ +void of_dma_deconfigure(struct device *dev) +{ + arch_teardown_dma_ops(dev); +} + int of_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index e4bf07d20f9bb..b0a2f9e9b8624 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_iommu.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -155,11 +156,6 @@ struct platform_device *of_device_alloc(struct device_node *np, } EXPORT_SYMBOL(of_device_alloc); -static void of_dma_deconfigure(struct device *dev) -{ - arch_teardown_dma_ops(dev); -} - /** * of_platform_device_create_pdata - Alloc, initialize and register an of_device * @np: pointer to node to create device for @@ -188,11 +184,9 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; - of_dma_configure(&dev->dev, dev->dev.of_node); of_msi_configure(&dev->dev, dev->dev.of_node); if (of_device_add(dev) != 0) { - of_dma_deconfigure(&dev->dev); platform_device_put(dev); goto err_clear_flag; } @@ -250,7 +244,6 @@ static struct amba_device *of_amba_device_create(struct device_node *node, dev_set_name(&dev->dev, "%s", bus_id); else of_device_make_bus_id(&dev->dev); - of_dma_configure(&dev->dev, dev->dev.of_node); /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); @@ -544,7 +537,6 @@ static int of_platform_device_destroy(struct device *dev, void *data) amba_device_unregister(to_amba_device(dev)); #endif - of_dma_deconfigure(dev); of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); return 0; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 104c46d531218..437256b7d52ac 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1743,34 +1743,6 @@ static void pci_set_msi_domain(struct pci_dev *dev) dev_set_msi_domain(&dev->dev, d); } -/** - * pci_dma_configure - Setup DMA configuration - * @dev: ptr to pci_dev struct of the PCI device - * - * Function to update PCI devices's DMA configuration using the same - * info from the OF node or ACPI node of host bridge's parent (if any). - */ -static void pci_dma_configure(struct pci_dev *dev) -{ - struct device *bridge = pci_get_host_bridge_device(dev); - - if (IS_ENABLED(CONFIG_OF) && - bridge->parent && bridge->parent->of_node) { - of_dma_configure(&dev->dev, bridge->parent->of_node); - } else if (has_acpi_companion(bridge)) { - struct acpi_device *adev = to_acpi_device_node(bridge->fwnode); - enum dev_dma_attr attr = acpi_get_dma_attr(adev); - - if (attr == DEV_DMA_NOT_SUPPORTED) - dev_warn(&dev->dev, "DMA not supported.\n"); - else - arch_setup_dma_ops(&dev->dev, 0, 0, NULL, - attr == DEV_DMA_COHERENT); - } - - pci_put_host_bridge_device(bridge); -} - void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { int ret; @@ -1784,7 +1756,6 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dev->dev.dma_mask = &dev->dma_mask; dev->dev.dma_parms = &dev->dma_parms; dev->dev.coherent_dma_mask = 0xffffffffull; - pci_dma_configure(dev); pci_set_dma_max_seg_size(dev, 65536); pci_set_dma_seg_boundary(dev, 0xffffffff); diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 08528afdf58b3..6f3e6cafe6ae0 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -702,6 +702,9 @@ dma_mark_declared_memory_occupied(struct device *dev, } #endif /* CONFIG_HAVE_GENERIC_DMA_COHERENT */ +int dma_configure(struct device *dev); +void dma_deconfigure(struct device *dev); + /* * Managed DMA API */ diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 436dc21318af7..f2960e4de3440 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -351,6 +351,9 @@ int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, const struct iommu_ops *ops); void iommu_fwspec_free(struct device *dev); int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids); +void iommu_register_instance(struct fwnode_handle *fwnode, + const struct iommu_ops *ops); +const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode); #else /* CONFIG_IOMMU_API */ @@ -580,6 +583,17 @@ static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids, return -ENODEV; } +static inline void iommu_register_instance(struct fwnode_handle *fwnode, + const struct iommu_ops *ops) +{ +} + +static inline +const struct iommu_ops *iommu_get_instance(struct fwnode_handle *fwnode) +{ + return NULL; +} + #endif /* CONFIG_IOMMU_API */ #endif /* __LINUX_IOMMU_H */ diff --git a/include/linux/of_device.h b/include/linux/of_device.h index cc7dd687a89dd..6dca65cbb3f3c 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -55,7 +55,8 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) return of_node_get(cpu_dev->of_node); } -void of_dma_configure(struct device *dev, struct device_node *np); +int of_dma_configure(struct device *dev, struct device_node *np); +void of_dma_deconfigure(struct device *dev); #else /* CONFIG_OF */ static inline int of_driver_match_device(struct device *dev, @@ -98,7 +99,12 @@ static inline struct device_node *of_cpu_device_node_get(int cpu) { return NULL; } -static inline void of_dma_configure(struct device *dev, struct device_node *np) + +static inline int of_dma_configure(struct device *dev, struct device_node *np) +{ + return 0; +} +static inline void of_dma_deconfigure(struct device *dev) {} #endif /* CONFIG_OF */ diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h index e80b9c762a035..6a7fc50510999 100644 --- a/include/linux/of_iommu.h +++ b/include/linux/of_iommu.h @@ -31,8 +31,16 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev, #endif /* CONFIG_OF_IOMMU */ -void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops); -const struct iommu_ops *of_iommu_get_ops(struct device_node *np); +static inline void of_iommu_set_ops(struct device_node *np, + const struct iommu_ops *ops) +{ + iommu_register_instance(&np->fwnode, ops); +} + +static inline const struct iommu_ops *of_iommu_get_ops(struct device_node *np) +{ + return iommu_get_instance(&np->fwnode); +} extern struct of_device_id __iommu_of_table; |