ACPI / scan: Parse _CCA and setup device coherency
authorSuthikulpanit, Suravee <Suravee.Suthikulpanit@amd.com>
Wed, 10 Jun 2015 16:08:52 +0000 (11:08 -0500)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 15 Jun 2015 12:40:48 +0000 (14:40 +0200)
This patch implements support for ACPI _CCA object, which is introduced in
ACPIv5.1, can be used for specifying device DMA coherency attribute.

The parsing logic traverses device namespace to parse coherency
information, and stores it in acpi_device_flags. Then uses it to call
arch_setup_dma_ops() when creating each device enumerated in DSDT
during ACPI scan.

This patch also introduces acpi_dma_is_coherent(), which provides
an interface for device drivers to check the coherency information
similarly to the of_dma_is_coherent().

Signed-off-by: Mark Salter <msalter@redhat.com>
Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/Kconfig
drivers/acpi/acpi_platform.c
drivers/acpi/glue.c
drivers/acpi/scan.c
include/acpi/acpi_bus.h
include/linux/acpi.h

index ab2cbb51c6aaccde0ec2f97af343383c6f360dff..212735f3f074a19f3c0aac195c8186a421392733 100644 (file)
@@ -54,6 +54,9 @@ config ACPI_GENERIC_GSI
 config ACPI_SYSTEM_POWER_STATES_SUPPORT
        bool
 
+config ACPI_CCA_REQUIRED
+       bool
+
 config ACPI_SLEEP
        bool
        depends on SUSPEND || HIBERNATION
index 4bf75597f732e90def62ba607590e7eb8e451da4..06a67d5f28461a0ea5a29f7791f7865e45979082 100644 (file)
@@ -103,7 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
        pdevinfo.res = resources;
        pdevinfo.num_res = count;
        pdevinfo.fwnode = acpi_fwnode_handle(adev);
-       pdevinfo.dma_mask = DMA_BIT_MASK(32);
+       pdevinfo.dma_mask = acpi_check_dma(adev, NULL) ? DMA_BIT_MASK(32) : 0;
        pdev = platform_device_register_full(&pdevinfo);
        if (IS_ERR(pdev))
                dev_err(&adev->dev, "platform device creation failed: %ld\n",
index 39c485b0c25c0d6ddccabb35e3c7014fb92788a9..b9657af751d1051d9b44ed2a22e2c347dd9c9846 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/rwsem.h>
 #include <linux/acpi.h>
+#include <linux/dma-mapping.h>
 
 #include "internal.h"
 
@@ -167,6 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
        struct list_head *physnode_list;
        unsigned int node_id;
        int retval = -EINVAL;
+       bool coherent;
 
        if (has_acpi_companion(dev)) {
                if (acpi_dev) {
@@ -223,6 +225,9 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
        if (!has_acpi_companion(dev))
                ACPI_COMPANION_SET(dev, acpi_dev);
 
+       if (acpi_check_dma(acpi_dev, &coherent))
+               arch_setup_dma_ops(dev, 0, 0, NULL, coherent);
+
        acpi_physnode_link_name(physical_node_name, node_id);
        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
                                   physical_node_name);
index 0a099917a0064d57cd04b443d1f44af5a2385509..67509b23172c41bac739f0efa90c920964dc9afa 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kthread.h>
 #include <linux/dmi.h>
 #include <linux/nls.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/pgtable.h>
 
@@ -2111,6 +2112,39 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp)
        kfree(pnp->unique_id);
 }
 
+static void acpi_init_coherency(struct acpi_device *adev)
+{
+       unsigned long long cca = 0;
+       acpi_status status;
+       struct acpi_device *parent = adev->parent;
+
+       if (parent && parent->flags.cca_seen) {
+               /*
+                * From ACPI spec, OSPM will ignore _CCA if an ancestor
+                * already saw one.
+                */
+               adev->flags.cca_seen = 1;
+               cca = parent->flags.coherent_dma;
+       } else {
+               status = acpi_evaluate_integer(adev->handle, "_CCA",
+                                              NULL, &cca);
+               if (ACPI_SUCCESS(status))
+                       adev->flags.cca_seen = 1;
+               else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
+                       /*
+                        * If architecture does not specify that _CCA is
+                        * required for DMA-able devices (e.g. x86),
+                        * we default to _CCA=1.
+                        */
+                       cca = 1;
+               else
+                       acpi_handle_debug(adev->handle,
+                                         "ACPI device is missing _CCA.\n");
+       }
+
+       adev->flags.coherent_dma = cca;
+}
+
 void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
                             int type, unsigned long long sta)
 {
@@ -2129,6 +2163,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        device->flags.visited = false;
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
+       acpi_init_coherency(device);
 }
 
 void acpi_device_add_finalize(struct acpi_device *device)
index da079976971fe1afd234cec0cad2b4e231f40b05..54df54d00cd2add5125cf781b6d57f83236d4651 100644 (file)
@@ -209,7 +209,9 @@ struct acpi_device_flags {
        u32 hotplug_notify:1;
        u32 is_dock_station:1;
        u32 of_compatible_ok:1;
-       u32 reserved:22;
+       u32 coherent_dma:1;
+       u32 cca_seen:1;
+       u32 reserved:20;
 };
 
 /* File System */
@@ -381,6 +383,39 @@ struct acpi_device {
        void (*remove)(struct acpi_device *);
 };
 
+static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent)
+{
+       bool ret = false;
+
+       if (!adev)
+               return ret;
+
+       /**
+        * Currently, we only support _CCA=1 (i.e. coherent_dma=1)
+        * This should be equivalent to specifyig dma-coherent for
+        * a device in OF.
+        *
+        * For the case when _CCA=0 (i.e. coherent_dma=0 && cca_seen=1),
+        * There are two cases:
+        * case 1. Do not support and disable DMA.
+        * case 2. Support but rely on arch-specific cache maintenance for
+        *         non-coherence DMA operations.
+        * Currently, we implement case 1 above.
+        *
+        * For the case when _CCA is missing (i.e. cca_seen=0) and
+        * platform specifies ACPI_CCA_REQUIRED, we do not support DMA,
+        * and fallback to arch-specific default handling.
+        *
+        * See acpi_init_coherency() for more info.
+        */
+       if (adev->flags.coherent_dma) {
+               ret = true;
+               if (coherent)
+                       *coherent = adev->flags.coherent_dma;
+       }
+       return ret;
+}
+
 static inline bool is_acpi_node(struct fwnode_handle *fwnode)
 {
        return fwnode && fwnode->type == FWNODE_ACPI;
index e4da5e35e29cd8ee66f443ddd070276ea268e248..d46a48c21c6751704084d6be84e4701ab637c656 100644 (file)
@@ -569,6 +569,11 @@ static inline int acpi_device_modalias(struct device *dev,
        return -ENODEV;
 }
 
+static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent)
+{
+       return false;
+}
+
 #define ACPI_PTR(_ptr) (NULL)
 
 #endif /* !CONFIG_ACPI */