From 20d330645cfb8cfecfb82b369e4d3084e429e68a Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 5 Oct 2016 18:18:16 +0100 Subject: [PATCH] MIPS: Support per-device DMA coherence On some MIPS systems, a subset of devices may have DMA coherent with CPU caches. For example in systems including a MIPS I/O Coherence Unit (IOCU), some devices may be connected to that IOCU whilst others are not. Prior to this patch, we have a plat_device_is_coherent() function but no implementation which does anything besides return a global true or false, optionally chosen at runtime. For devices such as those described above this is insufficient. Fix this by tracking DMA coherence on a per-device basis with a dma_coherent field in struct dev_archdata. Setting this from arch_setup_dma_ops() takes care of devices which set the dma-coherent property via device tree, and any PCI devices beneath a bridge described in DT, automatically. Signed-off-by: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/14349/ Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 4 ++++ arch/mips/include/asm/device.h | 5 +++++ arch/mips/include/asm/dma-coherence.h | 4 +++- arch/mips/include/asm/dma-mapping.h | 10 ++++++++++ arch/mips/include/asm/mach-generic/dma-coherence.h | 4 ++++ arch/mips/mm/c-r4k.c | 4 ++++ arch/mips/mm/dma-default.c | 2 +- 7 files changed, 31 insertions(+), 2 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 86d5b3930531..235643970517 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1099,6 +1099,10 @@ config DMA_MAYBE_COHERENT select DMA_NONCOHERENT bool +config DMA_PERDEV_COHERENT + bool + select DMA_MAYBE_COHERENT + config DMA_COHERENT bool diff --git a/arch/mips/include/asm/device.h b/arch/mips/include/asm/device.h index c94fafba9e62..21c2082a0dfb 100644 --- a/arch/mips/include/asm/device.h +++ b/arch/mips/include/asm/device.h @@ -11,6 +11,11 @@ struct dma_map_ops; struct dev_archdata { /* DMA operations on that device */ struct dma_map_ops *dma_ops; + +#ifdef CONFIG_DMA_PERDEV_COHERENT + /* Non-zero if DMA is coherent with CPU caches */ + bool dma_coherent; +#endif }; struct pdev_archdata { diff --git a/arch/mips/include/asm/dma-coherence.h b/arch/mips/include/asm/dma-coherence.h index 4fbce79fb57f..72d0eab02afc 100644 --- a/arch/mips/include/asm/dma-coherence.h +++ b/arch/mips/include/asm/dma-coherence.h @@ -15,7 +15,9 @@ enum coherent_io_user_state { IO_COHERENCE_DISABLED, }; -#ifdef CONFIG_DMA_MAYBE_COHERENT +#if defined(CONFIG_DMA_PERDEV_COHERENT) +/* Don't provide (hw_)coherentio to avoid misuse */ +#elif defined(CONFIG_DMA_MAYBE_COHERENT) extern enum coherent_io_user_state coherentio; extern int hw_coherentio; #else diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h index 12fa79e2f1b4..7aa71b9b0258 100644 --- a/arch/mips/include/asm/dma-mapping.h +++ b/arch/mips/include/asm/dma-mapping.h @@ -32,4 +32,14 @@ static inline void dma_mark_clean(void *addr, size_t size) {} extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction); +#define arch_setup_dma_ops arch_setup_dma_ops +static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, + u64 size, const struct iommu_ops *iommu, + bool coherent) +{ +#ifdef CONFIG_DMA_PERDEV_COHERENT + dev->archdata.dma_coherent = coherent; +#endif +} + #endif /* _ASM_DMA_MAPPING_H */ diff --git a/arch/mips/include/asm/mach-generic/dma-coherence.h b/arch/mips/include/asm/mach-generic/dma-coherence.h index 8484f82fc794..61addb1677e9 100644 --- a/arch/mips/include/asm/mach-generic/dma-coherence.h +++ b/arch/mips/include/asm/mach-generic/dma-coherence.h @@ -49,6 +49,9 @@ static inline int plat_dma_supported(struct device *dev, u64 mask) static inline int plat_device_is_coherent(struct device *dev) { +#ifdef CONFIG_DMA_PERDEV_COHERENT + return dev->archdata.dma_coherent; +#else switch (coherentio) { default: case IO_COHERENCE_DEFAULT: @@ -58,6 +61,7 @@ static inline int plat_device_is_coherent(struct device *dev) case IO_COHERENCE_DISABLED: return 0; } +#endif } #ifndef plat_post_dma_flush diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 78ac033a0f07..88cfaf81c958 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -1935,8 +1935,12 @@ void r4k_cache_init(void) __local_flush_icache_user_range = local_r4k_flush_icache_user_range; #if defined(CONFIG_DMA_NONCOHERENT) || defined(CONFIG_DMA_MAYBE_COHERENT) +# if defined(CONFIG_DMA_PERDEV_COHERENT) + if (0) { +# else if ((coherentio == IO_COHERENCE_ENABLED) || ((coherentio == IO_COHERENCE_DEFAULT) && hw_coherentio)) { +# endif _dma_cache_wback_inv = (void *)cache_noop; _dma_cache_wback = (void *)cache_noop; _dma_cache_inv = (void *)cache_noop; diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index 7ae4c55c935a..46d5696c4f27 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -24,7 +24,7 @@ #include -#ifdef CONFIG_DMA_MAYBE_COHERENT +#if defined(CONFIG_DMA_MAYBE_COHERENT) && !defined(CONFIG_DMA_PERDEV_COHERENT) /* User defined DMA coherency from command line. */ enum coherent_io_user_state coherentio = IO_COHERENCE_DEFAULT; EXPORT_SYMBOL_GPL(coherentio); -- 2.20.1