mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
Merge remote-tracking branch 'lsk/v3.10/topic/dma-mapping' into lsk-v3.10-arm64-misc
This commit is contained in:
@@ -265,37 +265,19 @@ static void *
|
||||
__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
unsigned long addr;
|
||||
|
||||
/*
|
||||
* DMA allocation can be mapped to user space, so lets
|
||||
* set VM_USERMAP flags too.
|
||||
*/
|
||||
area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
addr = (unsigned long)area->addr;
|
||||
area->phys_addr = __pfn_to_phys(page_to_pfn(page));
|
||||
|
||||
if (ioremap_page_range(addr, addr + size, area->phys_addr, prot)) {
|
||||
vunmap((void *)addr);
|
||||
return NULL;
|
||||
}
|
||||
return (void *)addr;
|
||||
return dma_common_contiguous_remap(page, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
prot, caller);
|
||||
}
|
||||
|
||||
static void __dma_free_remap(void *cpu_addr, size_t size)
|
||||
{
|
||||
unsigned int flags = VM_ARM_DMA_CONSISTENT | VM_USERMAP;
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
if (!area || (area->flags & flags) != flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
dma_common_free_remap(cpu_addr, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP);
|
||||
}
|
||||
|
||||
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
|
||||
@@ -1167,29 +1149,8 @@ static void *
|
||||
__iommu_alloc_remap(struct page **pages, size_t size, gfp_t gfp, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
unsigned int i, nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
struct vm_struct *area;
|
||||
unsigned long p;
|
||||
|
||||
area = get_vm_area_caller(size, VM_ARM_DMA_CONSISTENT | VM_USERMAP,
|
||||
caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
area->nr_pages = nr_pages;
|
||||
p = (unsigned long)area->addr;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
phys_addr_t phys = __pfn_to_phys(page_to_pfn(pages[i]));
|
||||
if (ioremap_page_range(p, p + PAGE_SIZE, phys, prot))
|
||||
goto err;
|
||||
p += PAGE_SIZE;
|
||||
}
|
||||
return area->addr;
|
||||
err:
|
||||
unmap_kernel_range((unsigned long)area->addr, size);
|
||||
vunmap(area->addr);
|
||||
return dma_common_pages_remap(pages, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP, prot, caller);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1386,8 +1347,8 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
||||
}
|
||||
|
||||
if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
dma_common_free_remap(cpu_addr, size,
|
||||
VM_ARM_DMA_CONSISTENT | VM_USERMAP);
|
||||
}
|
||||
|
||||
__iommu_remove_mapping(dev, handle, size);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm-generic/dma-coherent.h>
|
||||
|
||||
/*
|
||||
@@ -267,3 +269,73 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_common_mmap);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
/*
|
||||
* remaps an array of PAGE_SIZE pages into another vm_area
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
void *dma_common_pages_remap(struct page **pages, size_t size,
|
||||
unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
|
||||
area = get_vm_area_caller(size, vm_flags, caller);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->pages = pages;
|
||||
|
||||
if (map_vm_area(area, prot, pages)) {
|
||||
vunmap(area->addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return area->addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* remaps an allocated contiguous region into another vm_area.
|
||||
* Cannot be used in non-sleeping contexts
|
||||
*/
|
||||
|
||||
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
||||
unsigned long vm_flags,
|
||||
pgprot_t prot, const void *caller)
|
||||
{
|
||||
int i;
|
||||
struct page **pages;
|
||||
void *ptr;
|
||||
unsigned long pfn;
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) << get_order(size), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return NULL;
|
||||
|
||||
for (i = 0, pfn = page_to_pfn(page); i < (size >> PAGE_SHIFT); i++)
|
||||
pages[i] = pfn_to_page(pfn + i);
|
||||
|
||||
ptr = dma_common_pages_remap(pages, size, vm_flags, prot, caller);
|
||||
|
||||
kfree(pages);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmaps a range previously mapped by dma_common_*_remap
|
||||
*/
|
||||
void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
|
||||
{
|
||||
struct vm_struct *area = find_vm_area(cpu_addr);
|
||||
|
||||
if (!area || (area->flags & vm_flags) != vm_flags) {
|
||||
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
unmap_kernel_range((unsigned long)cpu_addr, size);
|
||||
vunmap(cpu_addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -179,6 +179,15 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size);
|
||||
|
||||
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
||||
unsigned long vm_flags,
|
||||
pgprot_t prot, const void *caller);
|
||||
|
||||
void *dma_common_pages_remap(struct page **pages, size_t size,
|
||||
unsigned long vm_flags, pgprot_t prot,
|
||||
const void *caller);
|
||||
void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags);
|
||||
|
||||
/**
|
||||
* dma_mmap_attrs - map a coherent DMA allocation into user space
|
||||
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
||||
|
||||
@@ -106,6 +106,10 @@ extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo,
|
||||
extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
|
||||
unsigned long start, unsigned int nr, void *data);
|
||||
|
||||
extern unsigned long gen_pool_first_fit_order_align(unsigned long *map,
|
||||
unsigned long size, unsigned long start, unsigned int nr,
|
||||
void *data);
|
||||
|
||||
extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
|
||||
unsigned long start, unsigned int nr, void *data);
|
||||
|
||||
@@ -113,6 +117,9 @@ extern struct gen_pool *devm_gen_pool_create(struct device *dev,
|
||||
int min_alloc_order, int nid);
|
||||
extern struct gen_pool *dev_get_gen_pool(struct device *dev);
|
||||
|
||||
bool addr_in_gen_pool(struct gen_pool *pool, unsigned long start,
|
||||
size_t size);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
extern struct gen_pool *of_get_named_gen_pool(struct device_node *np,
|
||||
const char *propname, int index);
|
||||
|
||||
@@ -368,6 +368,35 @@ void gen_pool_for_each_chunk(struct gen_pool *pool,
|
||||
}
|
||||
EXPORT_SYMBOL(gen_pool_for_each_chunk);
|
||||
|
||||
/**
|
||||
* addr_in_gen_pool - checks if an address falls within the range of a pool
|
||||
* @pool: the generic memory pool
|
||||
* @start: start address
|
||||
* @size: size of the region
|
||||
*
|
||||
* Check if the range of addresses falls within the specified pool. Returns
|
||||
* true if the entire range is contained in the pool and false otherwise.
|
||||
*/
|
||||
bool addr_in_gen_pool(struct gen_pool *pool, unsigned long start,
|
||||
size_t size)
|
||||
{
|
||||
bool found = false;
|
||||
unsigned long end = start + size;
|
||||
struct gen_pool_chunk *chunk;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk) {
|
||||
if (start >= chunk->start_addr && start <= chunk->end_addr) {
|
||||
if (end <= chunk->end_addr) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* gen_pool_avail - get available free space of the pool
|
||||
* @pool: pool to get available free space
|
||||
@@ -446,6 +475,26 @@ unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size,
|
||||
}
|
||||
EXPORT_SYMBOL(gen_pool_first_fit);
|
||||
|
||||
/**
|
||||
* gen_pool_first_fit_order_align - find the first available region
|
||||
* of memory matching the size requirement. The region will be aligned
|
||||
* to the order of the size specified.
|
||||
* @map: The address to base the search on
|
||||
* @size: The bitmap size in bits
|
||||
* @start: The bitnumber to start searching at
|
||||
* @nr: The number of zeroed bits we're looking for
|
||||
* @data: additional data - unused
|
||||
*/
|
||||
unsigned long gen_pool_first_fit_order_align(unsigned long *map,
|
||||
unsigned long size, unsigned long start,
|
||||
unsigned int nr, void *data)
|
||||
{
|
||||
unsigned long align_mask = roundup_pow_of_two(nr) - 1;
|
||||
|
||||
return bitmap_find_next_zero_area(map, size, start, nr, align_mask);
|
||||
}
|
||||
EXPORT_SYMBOL(gen_pool_first_fit_order_align);
|
||||
|
||||
/**
|
||||
* gen_pool_best_fit - find the best fitting region of memory
|
||||
* macthing the size requirement (no alignment constraint)
|
||||
|
||||
Reference in New Issue
Block a user