of/acpi: Configure dma operations at probe time for platform/amba/pci bus devices
authorSricharan R <sricharan@codeaurora.org>
Mon, 10 Apr 2017 11:21:01 +0000 (16:51 +0530)
committerJoerg Roedel <jroedel@suse.de>
Thu, 20 Apr 2017 14:31:06 +0000 (16:31 +0200)
Configuring DMA ops at probe time will allow deferring device probe when
the IOMMU isn't available yet. The dma_configure for the device is
now called from the generic device_attach callback just before the
bus/driver probe is called. This way, configuring the DMA ops for the
device would be called at the same place for all bus_types, hence the
deferred probing mechanism should work for all buses as well.

pci_bus_add_devices    (platform/amba)(_device_create/driver_register)
       |                         |
pci_bus_add_device     (device_add/driver_register)
       |                         |
device_attach           device_initial_probe
       |                         |
__device_attach_driver    __device_attach_driver
       |
driver_probe_device
       |
really_probe
       |
dma_configure

Similarly on the device/driver_unregister path __device_release_driver is
called which inturn calls dma_deconfigure.

This patch changes the dma ops configuration to probe time for
both OF and ACPI based platform/amba/pci bus devices.

Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Tested-by: Hanjun Guo <hanjun.guo@linaro.org>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Bjorn Helgaas <bhelgaas@google.com> (drivers/pci part)
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/acpi/glue.c
drivers/base/dd.c
drivers/base/dma-mapping.c
drivers/of/platform.c
drivers/pci/probe.c
include/linux/dma-mapping.h

index fb19e1cdb6415ac8d1913e7017106911dcb7a7dd..c05f24107bfcdbe798aaddddb288970ee87a66dc 100644 (file)
@@ -176,7 +176,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) {
@@ -233,10 +232,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)
-               acpi_dma_configure(dev, attr);
-
        acpi_physnode_link_name(physical_node_name, node_id);
        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
                                   physical_node_name);
index a1fbf55c4d3abbea786ef5585b96d769ca3a144a..4882f06d12dfe3b6e8ab7393a5e95c0ab7892d20 100644 (file)
@@ -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>
@@ -356,6 +357,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));
@@ -417,6 +422,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);
@@ -826,6 +833,8 @@ static void __device_release_driver(struct device *dev, struct device *parent)
                        drv->remove(dev);
 
                device_links_driver_cleanup(dev);
+               dma_deconfigure(dev);
+
                devres_release_all(dev);
                dev->driver = NULL;
                dev_set_drvdata(dev, NULL);
index efd71cf4fdead7c5f0b88dcdb3c7dee800b7cb65..449b948c7427fb1a8e6c6e8e636e5c57f5888d42 100644 (file)
@@ -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,41 @@ 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;
+       enum dev_dma_attr attr;
+
+       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) {
+               of_dma_configure(dev, dma_dev->of_node);
+       } else if (has_acpi_companion(dma_dev)) {
+               attr = acpi_get_dma_attr(to_acpi_device_node(dma_dev->fwnode));
+               if (attr != DEV_DMA_NOT_SUPPORTED)
+                       acpi_dma_configure(dev, attr);
+       }
+
+       if (bridge)
+               pci_put_host_bridge_device(bridge);
+
+       return 0;
+}
+
+void dma_deconfigure(struct device *dev)
+{
+       of_dma_deconfigure(dev);
+       acpi_dma_deconfigure(dev);
+}
index 5344db50aa651cbb08ab93fa320a7b323df118c8..2aa4ebbde9cd661efa88066b21eec1b2d9ac0a49 100644 (file)
@@ -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>
@@ -186,11 +187,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;
        }
@@ -248,7 +247,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);
@@ -542,7 +540,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;
index dfc9a27941418976c0fadee7e6628531fbb47613..5a8dd43db33684f20285298548625184ea1d8235 100644 (file)
@@ -1893,33 +1893,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
-                       acpi_dma_configure(&dev->dev, attr);
-       }
-
-       pci_put_host_bridge_device(bridge);
-}
-
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
 {
        int ret;
@@ -1933,7 +1906,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);
index 0977317c6835c2526428f61c12fcfab976650b99..4f3eecedca2d7c75c683cc2f08d64be9ac95118d 100644 (file)
@@ -728,6 +728,18 @@ dma_mark_declared_memory_occupied(struct device *dev,
 }
 #endif /* CONFIG_HAVE_GENERIC_DMA_COHERENT */
 
+#ifdef CONFIG_HAS_DMA
+int dma_configure(struct device *dev);
+void dma_deconfigure(struct device *dev);
+#else
+static inline int dma_configure(struct device *dev)
+{
+       return 0;
+}
+
+static inline void dma_deconfigure(struct device *dev) {}
+#endif
+
 /*
  * Managed DMA API
  */