diff options
author | Mark Salter <msalter@redhat.com> | 2014-11-10 21:35:11 -0500 |
---|---|---|
committer | Daniel Silverstone <daniel.silverstone@codethink.co.uk> | 2015-01-27 09:55:00 +0000 |
commit | 3e0245d07a82d677664b0e16db0c42f277029d28 (patch) | |
tree | 47b43be1de83f86e4d7a9c3a92b728bddab59c24 | |
parent | 09fb88fb5dacee3f594dd950080c64c694d91a80 (diff) | |
download | linux-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.c | 103 |
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); |