summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Salter <msalter@redhat.com>2014-11-10 21:35:11 -0500
committerDaniel Silverstone <daniel.silverstone@codethink.co.uk>2015-01-27 09:55:00 +0000
commit3e0245d07a82d677664b0e16db0c42f277029d28 (patch)
tree47b43be1de83f86e4d7a9c3a92b728bddab59c24
parent09fb88fb5dacee3f594dd950080c64c694d91a80 (diff)
downloadlinux-3e0245d07a82d677664b0e16db0c42f277029d28.tar.gz
DO NOT UPSTREAM - arm64: fix dma_ops for ACPI and PCI devices
Commit 2189064795dc3fb4101e5: arm64: Implement set_arch_dma_coherent_ops() to replace bus notifiers removed the bus notifiers from dma-mapping.c. This patch adds the notifier back for ACPI and PCI devices until a better permanent solution is worked out. Signed-off-by: Mark Salter <msalter@redhat.com>
-rw-r--r--arch/arm64/mm/dma-mapping.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index d92094203913..705a9ceac754 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -23,8 +23,14 @@
#include <linux/genalloc.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
#include <linux/vmalloc.h>
#include <linux/swiotlb.h>
+#include <linux/amba/bus.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
#include <asm/cacheflush.h>
@@ -423,10 +429,107 @@ out:
return -ENOMEM;
}
+#ifdef CONFIG_PCI
+static void arm64_of_set_dma_ops(void *_dev)
+{
+ struct device *dev = _dev;
+
+ /*
+ * PCI devices won't have an ACPI handle but the bridge will.
+ * Search up the device chain until we find an of_node
+ * to check.
+ */
+ while (dev) {
+ if (dev->of_node) {
+ if (of_dma_is_coherent(dev->of_node))
+ set_dma_ops(_dev, &coherent_swiotlb_dma_ops);
+ break;
+ }
+ dev = dev->parent;
+ }
+}
+#else
+static inline arm64_of_set_dma_ops(void *_dev) {}
+#endif
+
+
+#ifdef CONFIG_ACPI
+static void arm64_acpi_set_dma_ops(void *_dev)
+{
+ struct device *dev = _dev;
+
+ /*
+ * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64
+ * defaults to coherent. Set coherent ops if _CCA not found or _CCA
+ * found and non-zero.
+ *
+ * PCI devices won't have an of_node but the bridge will.
+ * Search up the device chain until we find an ACPI handle
+ * to check.
+ */
+ while (dev) {
+ if (ACPI_HANDLE(dev)) {
+ acpi_status status;
+ int coherent;
+ status = acpi_check_coherency(ACPI_HANDLE(dev),
+ &coherent);
+ if (ACPI_FAILURE(status) || coherent)
+ set_dma_ops(_dev, &coherent_swiotlb_dma_ops);
+ break;
+ }
+ dev = dev->parent;
+ }
+}
+#else
+static inline arm64_acpi_set_dma_ops(void *_dev) {}
+#endif
+
+static int dma_bus_notifier(struct notifier_block *nb,
+ unsigned long event, void *_dev)
+{
+ if (event != BUS_NOTIFY_ADD_DEVICE)
+ return NOTIFY_DONE;
+
+ if (acpi_disabled)
+ arm64_of_set_dma_ops(_dev);
+ else
+ arm64_acpi_set_dma_ops(_dev);
+
+ return NOTIFY_OK;
+}
+
+#ifdef CONFIG_ACPI
+static struct notifier_block platform_bus_nb = {
+ .notifier_call = dma_bus_notifier,
+};
+
+static struct notifier_block amba_bus_nb = {
+ .notifier_call = dma_bus_notifier,
+};
+#endif
+
+#ifdef CONFIG_PCI
+static struct notifier_block pci_bus_nb = {
+ .notifier_call = dma_bus_notifier,
+};
+#endif
+
static int __init swiotlb_late_init(void)
{
size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
+ /*
+ * These must be registered before of_platform_populate().
+ */
+#ifdef CONFIG_ACPI
+ bus_register_notifier(&platform_bus_type, &platform_bus_nb);
+ bus_register_notifier(&amba_bustype, &amba_bus_nb);
+#endif
+
+#ifdef CONFIG_PCI
+ bus_register_notifier(&pci_bus_type, &pci_bus_nb);
+#endif
+
dma_ops = &noncoherent_swiotlb_dma_ops;
return swiotlb_late_init_with_default_size(swiotlb_size);