// SPDX-License-Identifier: GPL-2.0-only
/*
* PowerPC version derived from arch / arm / mm / consistent . c
* Copyright ( C ) 2001 Dan Malek ( dmalek @ jlc . net )
*
* Copyright ( C ) 2000 Russell King
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/highmem.h>
#include <linux/dma-direct.h>
#include <linux/dma-map-ops.h>
#include <asm /tlbflush.h>
#include <asm /dma.h>
/*
* make an area consistent .
*/
static void __dma_sync(void *vaddr, size_t size, int direction)
{
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 */
clean_dcache_range(start, end);
break ;
case DMA_BIDIRECTIONAL: /* writeback and invalidate */
flush_dcache_range(start, end);
break ;
}
}
#ifdef CONFIG_HIGHMEM
/*
* _ _ dma_sync_page ( ) 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
* 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)
{
size_t seg_size = min((size_t)(PAGE_SIZE - offset), size);
size_t cur_size = seg_size;
unsigned long flags, start, seg_offset = offset;
int nr_segs = 1 + ((size - seg_size) + PAGE_SIZE - 1 )/PAGE_SIZE;
int seg_nr = 0 ;
local_irq_save(flags);
do {
start = (unsigned long )kmap_atomic(page + seg_nr) + seg_offset;
/* Sync this buffer segment */
__dma_sync((void *)start, seg_size, direction);
kunmap_atomic((void *)start);
seg_nr++;
/* Calculate next buffer segment size */
seg_size = min((size_t)PAGE_SIZE, size - cur_size);
/* Add the segment size to our running total */
cur_size += seg_size;
seg_offset = 0 ;
} while (seg_nr < nr_segs);
local_irq_restore(flags);
}
#endif /* CONFIG_HIGHMEM */
/*
* _ _ dma_sync_page makes memory consistent . identical to _ _ dma_sync , but
* takes a struct page instead of a virtual address
*/
static void __dma_sync_page(phys_addr_t paddr, size_t size, int dir)
{
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);
#else
unsigned long start = (unsigned long )page_address(page) + offset;
__dma_sync((void *)start, size, dir);
#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);
}
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
__dma_sync_page(paddr, size, dir);
}
void arch_dma_prep_coherent(struct page *page, size_t size)
{
unsigned long kaddr = (unsigned long )page_address(page);
flush_dcache_range(kaddr, kaddr + size);
}
Messung V0.5 in Prozent C=91 H=87 G=88
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland