Skip to content

Commit

Permalink
xtensa: platform-specific handling of coherent memory
Browse files Browse the repository at this point in the history
Memory layout is not fixed for noMMU xtensa configurations. Platforms
that need to use coherent DMA should implement platform_vaddr_* helpers
that check address type (cached/uncached) and convert addresses between
these types.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
  • Loading branch information
Max Filippov committed Jul 11, 2018
1 parent adbfa4e commit 2cc15e8
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 21 deletions.
6 changes: 0 additions & 6 deletions arch/xtensa/include/asm/kmem_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,6 @@
#error XCHAL_KSEG_PADDR is not properly aligned to XCHAL_KSEG_ALIGNMENT
#endif

#else

#define XCHAL_KSEG_CACHED_VADDR __XTENSA_UL_CONST(0xd0000000)
#define XCHAL_KSEG_BYPASS_VADDR __XTENSA_UL_CONST(0xd8000000)
#define XCHAL_KSEG_SIZE __XTENSA_UL_CONST(0x08000000)

#endif

#ifndef CONFIG_KASAN
Expand Down
8 changes: 8 additions & 0 deletions arch/xtensa/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#define FIRST_USER_ADDRESS 0UL
#define FIRST_USER_PGD_NR (FIRST_USER_ADDRESS >> PGDIR_SHIFT)

#ifdef CONFIG_MMU
/*
* Virtual memory area. We keep a distance to other memory regions to be
* on the safe side. We also use this area for cache aliasing.
Expand All @@ -80,6 +81,13 @@
#define TLBTEMP_SIZE ICACHE_WAY_SIZE
#endif

#else

#define VMALLOC_START __XTENSA_UL_CONST(0)
#define VMALLOC_END __XTENSA_UL_CONST(0xffffffff)

#endif

/*
* For the Xtensa architecture, the PTE layout is as follows:
*
Expand Down
27 changes: 27 additions & 0 deletions arch/xtensa/include/asm/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,31 @@ extern void platform_calibrate_ccount (void);
*/
void cpu_reset(void) __attribute__((noreturn));

/*
* Memory caching is platform-dependent in noMMU xtensa configurations.
* The following set of functions should be implemented in platform code
* in order to enable coherent DMA memory operations when CONFIG_MMU is not
* enabled. Default implementations do nothing and issue a warning.
*/

/*
* Check whether p points to a cached memory.
*/
bool platform_vaddr_cached(const void *p);

/*
* Check whether p points to an uncached memory.
*/
bool platform_vaddr_uncached(const void *p);

/*
* Return pointer to an uncached view of the cached sddress p.
*/
void *platform_vaddr_to_uncached(void *p);

/*
* Return pointer to a cached view of the uncached sddress p.
*/
void *platform_vaddr_to_cached(void *p);

#endif /* _XTENSA_PLATFORM_H */
73 changes: 58 additions & 15 deletions arch/xtensa/kernel/pci-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/platform.h>

static void do_cache_op(phys_addr_t paddr, size_t size,
void (*fn)(unsigned long, unsigned long))
Expand Down Expand Up @@ -84,6 +85,58 @@ void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
}
}

#ifdef CONFIG_MMU
bool platform_vaddr_cached(const void *p)
{
unsigned long addr = (unsigned long)p;

return addr >= XCHAL_KSEG_CACHED_VADDR &&
addr - XCHAL_KSEG_CACHED_VADDR < XCHAL_KSEG_SIZE;
}

bool platform_vaddr_uncached(const void *p)
{
unsigned long addr = (unsigned long)p;

return addr >= XCHAL_KSEG_BYPASS_VADDR &&
addr - XCHAL_KSEG_BYPASS_VADDR < XCHAL_KSEG_SIZE;
}

void *platform_vaddr_to_uncached(void *p)
{
return p + XCHAL_KSEG_BYPASS_VADDR - XCHAL_KSEG_CACHED_VADDR;
}

void *platform_vaddr_to_cached(void *p)
{
return p + XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
}
#else
bool __attribute__((weak)) platform_vaddr_cached(const void *p)
{
WARN_ONCE(1, "Default %s implementation is used\n", __func__);
return true;
}

bool __attribute__((weak)) platform_vaddr_uncached(const void *p)
{
WARN_ONCE(1, "Default %s implementation is used\n", __func__);
return false;
}

void __attribute__((weak)) *platform_vaddr_to_uncached(void *p)
{
WARN_ONCE(1, "Default %s implementation is used\n", __func__);
return p;
}

void __attribute__((weak)) *platform_vaddr_to_cached(void *p)
{
WARN_ONCE(1, "Default %s implementation is used\n", __func__);
return p;
}
#endif

/*
* Note: We assume that the full memory space is always mapped to 'kseg'
* Otherwise we have to use page attributes (not implemented).
Expand All @@ -92,8 +145,6 @@ void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
gfp_t flag, unsigned long attrs)
{
unsigned long ret;
unsigned long uncached;
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
struct page *page = NULL;

Expand Down Expand Up @@ -134,29 +185,21 @@ void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
return p;
}
#endif
ret = (unsigned long)page_address(page);
BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);

uncached = ret + XCHAL_KSEG_BYPASS_VADDR - XCHAL_KSEG_CACHED_VADDR;
__invalidate_dcache_range(ret, size);

return (void *)uncached;
BUG_ON(!platform_vaddr_cached(page_address(page)));
__invalidate_dcache_range((unsigned long)page_address(page), size);
return platform_vaddr_to_uncached(page_address(page));
}

void arch_dma_free(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, unsigned long attrs)
{
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
unsigned long addr = (unsigned long)vaddr;
struct page *page;

if (attrs & DMA_ATTR_NO_KERNEL_MAPPING) {
page = vaddr;
} else if (addr >= XCHAL_KSEG_BYPASS_VADDR &&
addr - XCHAL_KSEG_BYPASS_VADDR < XCHAL_KSEG_SIZE) {
addr += XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
page = virt_to_page(addr);
} else if (platform_vaddr_uncached(vaddr)) {
page = virt_to_page(platform_vaddr_to_cached(vaddr));
} else {
#ifdef CONFIG_MMU
dma_common_free_remap(vaddr, size, VM_MAP);
Expand Down

0 comments on commit 2cc15e8

Please sign in to comment.