[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20230327121317.4081816-6-arnd@kernel.org>
Date: Mon, 27 Mar 2023 14:13:01 +0200
From: Arnd Bergmann <arnd@...nel.org>
To: linux-kernel@...r.kernel.org
Cc: Arnd Bergmann <arnd@...db.de>, Vineet Gupta <vgupta@...nel.org>,
Russell King <linux@...linux.org.uk>,
Neil Armstrong <neil.armstrong@...aro.org>,
Linus Walleij <linus.walleij@...aro.org>,
Catalin Marinas <catalin.marinas@....com>,
Will Deacon <will@...nel.org>, Guo Ren <guoren@...nel.org>,
Brian Cain <bcain@...cinc.com>,
Geert Uytterhoeven <geert@...ux-m68k.org>,
Michal Simek <monstr@...str.eu>,
Thomas Bogendoerfer <tsbogend@...ha.franken.de>,
Dinh Nguyen <dinguyen@...nel.org>,
Stafford Horne <shorne@...il.com>,
Helge Deller <deller@....de>,
Michael Ellerman <mpe@...erman.id.au>,
Christophe Leroy <christophe.leroy@...roup.eu>,
Paul Walmsley <paul.walmsley@...ive.com>,
Palmer Dabbelt <palmer@...belt.com>,
Rich Felker <dalias@...c.org>,
John Paul Adrian Glaubitz <glaubitz@...sik.fu-berlin.de>,
"David S. Miller" <davem@...emloft.net>,
Max Filippov <jcmvbkbc@...il.com>,
Christoph Hellwig <hch@....de>,
Robin Murphy <robin.murphy@....com>,
Lad Prabhakar <prabhakar.mahadev-lad.rj@...renesas.com>,
Conor Dooley <conor.dooley@...rochip.com>,
linux-snps-arc@...ts.infradead.org,
linux-arm-kernel@...ts.infradead.org, linux-oxnas@...ups.io,
linux-csky@...r.kernel.org, linux-hexagon@...r.kernel.org,
linux-m68k@...ts.linux-m68k.org, linux-mips@...r.kernel.org,
linux-openrisc@...r.kernel.org, linux-parisc@...r.kernel.org,
linuxppc-dev@...ts.ozlabs.org, linux-riscv@...ts.infradead.org,
linux-sh@...r.kernel.org, sparclinux@...r.kernel.org,
linux-xtensa@...ux-xtensa.org
Subject: [PATCH 05/21] powerpc: dma-mapping: split out cache operation logic
From: Arnd Bergmann <arnd@...db.de>
The powerpc arch_sync_dma_for_device()/arch_sync_dma_for_cpu() functions
behave differently from all other architectures, at least for some of
the operations.
As a preparation for making the behavior more consistent, reorder the
logic in which they decide whether to flush, invalidate or clean the.
No change in behavior is intended.
Signed-off-by: Arnd Bergmann <arnd@...db.de>
---
arch/powerpc/mm/dma-noncoherent.c | 91 +++++++++++++++++++++----------
1 file changed, 63 insertions(+), 28 deletions(-)
diff --git a/arch/powerpc/mm/dma-noncoherent.c b/arch/powerpc/mm/dma-noncoherent.c
index 30260b5d146d..f10869d27de5 100644
--- a/arch/powerpc/mm/dma-noncoherent.c
+++ b/arch/powerpc/mm/dma-noncoherent.c
@@ -16,31 +16,28 @@
#include <asm/tlbflush.h>
#include <asm/dma.h>
+enum dma_cache_op {
+ DMA_CACHE_CLEAN,
+ DMA_CACHE_INVAL,
+ DMA_CACHE_FLUSH,
+};
+
/*
* make an area consistent.
*/
-static void __dma_sync(void *vaddr, size_t size, int direction)
+static void __dma_op(void *vaddr, size_t size, enum dma_cache_op op)
{
unsigned long start = (unsigned long)vaddr;
unsigned long end = start + size;
- switch (direction) {
- case DMA_NONE:
- BUG();
- case DMA_FROM_DEVICE:
- /*
- * invalidate only when cache-line aligned otherwise there is
- * the potential for discarding uncommitted data from the cache
- */
- if ((start | end) & (L1_CACHE_BYTES - 1))
- flush_dcache_range(start, end);
- else
- invalidate_dcache_range(start, end);
- break;
- case DMA_TO_DEVICE: /* writeback only */
+ switch (op) {
+ case DMA_CACHE_CLEAN:
clean_dcache_range(start, end);
break;
- case DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ case DMA_CACHE_INVAL:
+ invalidate_dcache_range(start, end);
+ break;
+ case DMA_CACHE_FLUSH:
flush_dcache_range(start, end);
break;
}
@@ -48,16 +45,16 @@ static void __dma_sync(void *vaddr, size_t size, int direction)
#ifdef CONFIG_HIGHMEM
/*
- * __dma_sync_page() implementation for systems using highmem.
+ * __dma_highmem_op() implementation for systems using highmem.
* In this case, each page of a buffer must be kmapped/kunmapped
- * in order to have a virtual address for __dma_sync(). This must
+ * in order to have a virtual address for __dma_op(). This must
* not sleep so kmap_atomic()/kunmap_atomic() are used.
*
* Note: yes, it is possible and correct to have a buffer extend
* beyond the first page.
*/
-static inline void __dma_sync_page_highmem(struct page *page,
- unsigned long offset, size_t size, int direction)
+static inline void __dma_highmem_op(struct page *page,
+ unsigned long offset, size_t size, enum dma_cache_op op)
{
size_t seg_size = min((size_t)(PAGE_SIZE - offset), size);
size_t cur_size = seg_size;
@@ -71,7 +68,7 @@ static inline void __dma_sync_page_highmem(struct page *page,
start = (unsigned long)kmap_atomic(page + seg_nr) + seg_offset;
/* Sync this buffer segment */
- __dma_sync((void *)start, seg_size, direction);
+ __dma_op((void *)start, seg_size, op);
kunmap_atomic((void *)start);
seg_nr++;
@@ -88,32 +85,70 @@ static inline void __dma_sync_page_highmem(struct page *page,
#endif /* CONFIG_HIGHMEM */
/*
- * __dma_sync_page makes memory consistent. identical to __dma_sync, but
- * takes a struct page instead of a virtual address
+ * __dma_phys_op makes memory consistent. identical to __dma_op, but
+ * takes a phys_addr_t instead of a virtual address
*/
-static void __dma_sync_page(phys_addr_t paddr, size_t size, int dir)
+static void __dma_phys_op(phys_addr_t paddr, size_t size, enum dma_cache_op op)
{
struct page *page = pfn_to_page(paddr >> PAGE_SHIFT);
unsigned offset = paddr & ~PAGE_MASK;
#ifdef CONFIG_HIGHMEM
- __dma_sync_page_highmem(page, offset, size, dir);
+ __dma_highmem_op(page, offset, size, op);
#else
unsigned long start = (unsigned long)page_address(page) + offset;
- __dma_sync((void *)start, size, dir);
+ __dma_op((void *)start, size, op);
#endif
}
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
- __dma_sync_page(paddr, size, dir);
+ switch (direction) {
+ case DMA_NONE:
+ BUG();
+ case DMA_FROM_DEVICE:
+ /*
+ * invalidate only when cache-line aligned otherwise there is
+ * the potential for discarding uncommitted data from the cache
+ */
+ if ((start | end) & (L1_CACHE_BYTES - 1))
+ __dma_phys_op(start, end, DMA_CACHE_FLUSH);
+ else
+ __dma_phys_op(start, end, DMA_CACHE_INVAL);
+ break;
+ case DMA_TO_DEVICE: /* writeback only */
+ __dma_phys_op(start, end, DMA_CACHE_CLEAN);
+ break;
+ case DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ __dma_phys_op(start, end, DMA_CACHE_FLUSH);
+ break;
+ }
}
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
- __dma_sync_page(paddr, size, dir);
+ switch (direction) {
+ case DMA_NONE:
+ BUG();
+ case DMA_FROM_DEVICE:
+ /*
+ * invalidate only when cache-line aligned otherwise there is
+ * the potential for discarding uncommitted data from the cache
+ */
+ if ((start | end) & (L1_CACHE_BYTES - 1))
+ __dma_phys_op(start, end, DMA_CACHE_FLUSH);
+ else
+ __dma_phys_op(start, end, DMA_CACHE_INVAL);
+ break;
+ case DMA_TO_DEVICE: /* writeback only */
+ __dma_phys_op(start, end, DMA_CACHE_CLEAN);
+ break;
+ case DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ __dma_phys_op(start, end, DMA_CACHE_FLUSH);
+ break;
+ }
}
void arch_dma_prep_coherent(struct page *page, size_t size)
--
2.39.2
Powered by blists - more mailing lists