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 <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/14349/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Paul Burton 2016-10-05 18:18:16 +01:00 committed by Ralf Baechle
parent cfa93fb9c2
commit 20d330645c
7 changed files with 31 additions and 2 deletions

View File

@ -1099,6 +1099,10 @@ config DMA_MAYBE_COHERENT
select DMA_NONCOHERENT select DMA_NONCOHERENT
bool bool
config DMA_PERDEV_COHERENT
bool
select DMA_MAYBE_COHERENT
config DMA_COHERENT config DMA_COHERENT
bool bool

View File

@ -11,6 +11,11 @@ struct dma_map_ops;
struct dev_archdata { struct dev_archdata {
/* DMA operations on that device */ /* DMA operations on that device */
struct dma_map_ops *dma_ops; 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 { struct pdev_archdata {

View File

@ -15,7 +15,9 @@ enum coherent_io_user_state {
IO_COHERENCE_DISABLED, 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 enum coherent_io_user_state coherentio;
extern int hw_coherentio; extern int hw_coherentio;
#else #else

View File

@ -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, extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction); 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 */ #endif /* _ASM_DMA_MAPPING_H */

View File

@ -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) static inline int plat_device_is_coherent(struct device *dev)
{ {
#ifdef CONFIG_DMA_PERDEV_COHERENT
return dev->archdata.dma_coherent;
#else
switch (coherentio) { switch (coherentio) {
default: default:
case IO_COHERENCE_DEFAULT: case IO_COHERENCE_DEFAULT:
@ -58,6 +61,7 @@ static inline int plat_device_is_coherent(struct device *dev)
case IO_COHERENCE_DISABLED: case IO_COHERENCE_DISABLED:
return 0; return 0;
} }
#endif
} }
#ifndef plat_post_dma_flush #ifndef plat_post_dma_flush

View File

@ -1935,8 +1935,12 @@ void r4k_cache_init(void)
__local_flush_icache_user_range = local_r4k_flush_icache_user_range; __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_NONCOHERENT) || defined(CONFIG_DMA_MAYBE_COHERENT)
# if defined(CONFIG_DMA_PERDEV_COHERENT)
if (0) {
# else
if ((coherentio == IO_COHERENCE_ENABLED) || if ((coherentio == IO_COHERENCE_ENABLED) ||
((coherentio == IO_COHERENCE_DEFAULT) && hw_coherentio)) { ((coherentio == IO_COHERENCE_DEFAULT) && hw_coherentio)) {
# endif
_dma_cache_wback_inv = (void *)cache_noop; _dma_cache_wback_inv = (void *)cache_noop;
_dma_cache_wback = (void *)cache_noop; _dma_cache_wback = (void *)cache_noop;
_dma_cache_inv = (void *)cache_noop; _dma_cache_inv = (void *)cache_noop;

View File

@ -24,7 +24,7 @@
#include <dma-coherence.h> #include <dma-coherence.h>
#ifdef CONFIG_DMA_MAYBE_COHERENT #if defined(CONFIG_DMA_MAYBE_COHERENT) && !defined(CONFIG_DMA_PERDEV_COHERENT)
/* User defined DMA coherency from command line. */ /* User defined DMA coherency from command line. */
enum coherent_io_user_state coherentio = IO_COHERENCE_DEFAULT; enum coherent_io_user_state coherentio = IO_COHERENCE_DEFAULT;
EXPORT_SYMBOL_GPL(coherentio); EXPORT_SYMBOL_GPL(coherentio);