mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
Merge remote-tracking branch 'lsk/v3.10/topic/arm64-dma' into lsk-v3.10-arm64-misc
Conflicts: arch/arm64/Kconfig arch/arm64/mm/dma-mapping.c mm/Kconfig
This commit is contained in:
@@ -101,14 +101,23 @@ style to do this even if your device holds the default setting,
|
||||
because this shows that you did think about these issues wrt. your
|
||||
device.
|
||||
|
||||
The query is performed via a call to dma_set_mask():
|
||||
The query is performed via a call to dma_set_mask_and_coherent():
|
||||
|
||||
int dma_set_mask(struct device *dev, u64 mask);
|
||||
int dma_set_mask_and_coherent(struct device *dev, u64 mask);
|
||||
|
||||
The query for consistent allocations is performed via a call to
|
||||
dma_set_coherent_mask():
|
||||
which will query the mask for both streaming and coherent APIs together.
|
||||
If you have some special requirements, then the following two separate
|
||||
queries can be used instead:
|
||||
|
||||
int dma_set_coherent_mask(struct device *dev, u64 mask);
|
||||
The query for streaming mappings is performed via a call to
|
||||
dma_set_mask():
|
||||
|
||||
int dma_set_mask(struct device *dev, u64 mask);
|
||||
|
||||
The query for consistent allocations is performed via a call
|
||||
to dma_set_coherent_mask():
|
||||
|
||||
int dma_set_coherent_mask(struct device *dev, u64 mask);
|
||||
|
||||
Here, dev is a pointer to the device struct of your device, and mask
|
||||
is a bit mask describing which bits of an address your device
|
||||
@@ -137,7 +146,7 @@ exactly why.
|
||||
|
||||
The standard 32-bit addressing device would do something like this:
|
||||
|
||||
if (dma_set_mask(dev, DMA_BIT_MASK(32))) {
|
||||
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
|
||||
printk(KERN_WARNING
|
||||
"mydev: No suitable DMA available.\n");
|
||||
goto ignore_this_device;
|
||||
@@ -171,22 +180,20 @@ the case would look like this:
|
||||
|
||||
int using_dac, consistent_using_dac;
|
||||
|
||||
if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
|
||||
if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
|
||||
using_dac = 1;
|
||||
consistent_using_dac = 1;
|
||||
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
|
||||
} else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) {
|
||||
} else if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
|
||||
using_dac = 0;
|
||||
consistent_using_dac = 0;
|
||||
dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||
} else {
|
||||
printk(KERN_WARNING
|
||||
"mydev: No suitable DMA available.\n");
|
||||
goto ignore_this_device;
|
||||
}
|
||||
|
||||
dma_set_coherent_mask() will always be able to set the same or a
|
||||
smaller mask as dma_set_mask(). However for the rare case that a
|
||||
The coherent coherent mask will always be able to set the same or a
|
||||
smaller mask as the streaming mask. However for the rare case that a
|
||||
device driver only uses consistent allocations, one would have to
|
||||
check the return value from dma_set_coherent_mask().
|
||||
|
||||
@@ -199,9 +206,9 @@ address you might do something like:
|
||||
goto ignore_this_device;
|
||||
}
|
||||
|
||||
When dma_set_mask() is successful, and returns zero, the kernel saves
|
||||
away this mask you have provided. The kernel will use this
|
||||
information later when you make DMA mappings.
|
||||
When dma_set_mask() or dma_set_mask_and_coherent() is successful, and
|
||||
returns zero, the kernel saves away this mask you have provided. The
|
||||
kernel will use this information later when you make DMA mappings.
|
||||
|
||||
There is a case which we are aware of at this time, which is worth
|
||||
mentioning in this documentation. If your device supports multiple
|
||||
|
||||
@@ -141,6 +141,14 @@ won't change the current mask settings. It is more intended as an
|
||||
internal API for use by the platform than an external API for use by
|
||||
driver writers.
|
||||
|
||||
int
|
||||
dma_set_mask_and_coherent(struct device *dev, u64 mask)
|
||||
|
||||
Checks to see if the mask is possible and updates the device
|
||||
streaming and coherent DMA mask parameters if it is.
|
||||
|
||||
Returns: 0 if successful and a negative error if not.
|
||||
|
||||
int
|
||||
dma_set_mask(struct device *dev, u64 mask)
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
#define ASMARM_DMA_CONTIGUOUS_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#ifdef CONFIG_CMA
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm-generic/dma-contiguous.h>
|
||||
|
||||
void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ static int __init atomic_pool_init(void)
|
||||
if (!pages)
|
||||
goto no_pages;
|
||||
|
||||
if (IS_ENABLED(CONFIG_CMA))
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA))
|
||||
ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page,
|
||||
atomic_pool_init);
|
||||
else
|
||||
@@ -670,7 +670,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||
addr = __alloc_simple_buffer(dev, size, gfp, &page);
|
||||
else if (!(gfp & __GFP_WAIT))
|
||||
addr = __alloc_from_pool(size, &page);
|
||||
else if (!IS_ENABLED(CONFIG_CMA))
|
||||
else if (!IS_ENABLED(CONFIG_DMA_CMA))
|
||||
addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller);
|
||||
else
|
||||
addr = __alloc_from_contiguous(dev, size, prot, &page, caller);
|
||||
@@ -759,7 +759,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
__dma_free_buffer(page, size);
|
||||
} else if (__free_from_pool(cpu_addr, size)) {
|
||||
return;
|
||||
} else if (!IS_ENABLED(CONFIG_CMA)) {
|
||||
} else if (!IS_ENABLED(CONFIG_DMA_CMA)) {
|
||||
__dma_free_remap(cpu_addr, size);
|
||||
__dma_free_buffer(page, size);
|
||||
} else {
|
||||
|
||||
@@ -28,6 +28,7 @@ config ARM64
|
||||
select HAVE_DEBUG_KMEMLEAK
|
||||
select HAVE_DMA_API_DEBUG
|
||||
select HAVE_DMA_ATTRS
|
||||
select HAVE_DMA_CONTIGUOUS
|
||||
select HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
select HAVE_GENERIC_DMA_COHERENT
|
||||
select HAVE_GENERIC_HARDIRQS
|
||||
@@ -82,7 +83,7 @@ config GENERIC_CSUM
|
||||
config GENERIC_CALIBRATE_DELAY
|
||||
def_bool y
|
||||
|
||||
config ZONE_DMA32
|
||||
config ZONE_DMA
|
||||
def_bool y
|
||||
|
||||
config ARCH_DMA_ADDR_T_64BIT
|
||||
|
||||
@@ -84,6 +84,13 @@ static inline void flush_cache_page(struct vm_area_struct *vma,
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache maintenance functions used by the DMA API. No to be used directly.
|
||||
*/
|
||||
extern void __dma_map_area(const void *, size_t, int);
|
||||
extern void __dma_unmap_area(const void *, size_t, int);
|
||||
extern void __dma_flush_range(const void *, const void *);
|
||||
|
||||
/*
|
||||
* Copy user data from/to a page which is mapped into a different
|
||||
* processes address space. Really, we want to allow our "user
|
||||
|
||||
28
arch/arm64/include/asm/dma-contiguous.h
Normal file
28
arch/arm64/include/asm/dma-contiguous.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _ASM_DMA_CONTIGUOUS_H
|
||||
#define _ASM_DMA_CONTIGUOUS_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
static inline void
|
||||
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -25,7 +25,10 @@
|
||||
|
||||
#define ARCH_HAS_DMA_GET_REQUIRED_MASK
|
||||
|
||||
#define DMA_ERROR_CODE (~(dma_addr_t)0)
|
||||
extern struct dma_map_ops *dma_ops;
|
||||
extern struct dma_map_ops coherent_swiotlb_dma_ops;
|
||||
extern struct dma_map_ops noncoherent_swiotlb_dma_ops;
|
||||
|
||||
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
|
||||
{
|
||||
@@ -35,6 +38,11 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
|
||||
return dev->archdata.dma_ops;
|
||||
}
|
||||
|
||||
static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
|
||||
{
|
||||
dev->archdata.dma_ops = ops;
|
||||
}
|
||||
|
||||
#include <asm-generic/dma-mapping-common.h>
|
||||
|
||||
static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
|
||||
@@ -81,8 +89,12 @@ static inline void dma_mark_clean(void *addr, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags)
|
||||
#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL)
|
||||
#define dma_free_coherent(d, s, h, f) dma_free_attrs(d, s, h, f, NULL)
|
||||
|
||||
static inline void *dma_alloc_attrs(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
void *vaddr;
|
||||
@@ -90,13 +102,14 @@ static inline void *dma_alloc_coherent(struct device *dev, size_t size,
|
||||
if (dma_alloc_from_coherent(dev, size, dma_handle, &vaddr))
|
||||
return vaddr;
|
||||
|
||||
vaddr = ops->alloc(dev, size, dma_handle, flags, NULL);
|
||||
vaddr = ops->alloc(dev, size, dma_handle, flags, attrs);
|
||||
debug_dma_alloc_coherent(dev, size, *dma_handle, vaddr);
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static inline void dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dev_addr)
|
||||
static inline void dma_free_attrs(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dev_addr,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
@@ -104,7 +117,7 @@ static inline void dma_free_coherent(struct device *dev, size_t size,
|
||||
return;
|
||||
|
||||
debug_dma_free_coherent(dev, size, vaddr, dev_addr);
|
||||
ops->free(dev, size, vaddr, dev_addr, NULL);
|
||||
ops->free(dev, size, vaddr, dev_addr, attrs);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -303,7 +303,7 @@ static int __init arm64_device_init(void)
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(arm64_device_init);
|
||||
arch_initcall_sync(arm64_device_init);
|
||||
|
||||
static DEFINE_PER_CPU(struct cpu, cpu_data);
|
||||
|
||||
|
||||
@@ -166,3 +166,88 @@ ENTRY(__flush_dcache_area)
|
||||
dsb sy
|
||||
ret
|
||||
ENDPROC(__flush_dcache_area)
|
||||
|
||||
/*
|
||||
* __dma_inv_range(start, end)
|
||||
* - start - virtual start address of region
|
||||
* - end - virtual end address of region
|
||||
*/
|
||||
__dma_inv_range:
|
||||
dcache_line_size x2, x3
|
||||
sub x3, x2, #1
|
||||
tst x1, x3 // end cache line aligned?
|
||||
bic x1, x1, x3
|
||||
b.eq 1f
|
||||
dc civac, x1 // clean & invalidate D / U line
|
||||
1: tst x0, x3 // start cache line aligned?
|
||||
bic x0, x0, x3
|
||||
b.eq 2f
|
||||
dc civac, x0 // clean & invalidate D / U line
|
||||
b 3f
|
||||
2: dc ivac, x0 // invalidate D / U line
|
||||
3: add x0, x0, x2
|
||||
cmp x0, x1
|
||||
b.lo 2b
|
||||
dsb sy
|
||||
ret
|
||||
ENDPROC(__dma_inv_range)
|
||||
|
||||
/*
|
||||
* __dma_clean_range(start, end)
|
||||
* - start - virtual start address of region
|
||||
* - end - virtual end address of region
|
||||
*/
|
||||
__dma_clean_range:
|
||||
dcache_line_size x2, x3
|
||||
sub x3, x2, #1
|
||||
bic x0, x0, x3
|
||||
1: dc cvac, x0 // clean D / U line
|
||||
add x0, x0, x2
|
||||
cmp x0, x1
|
||||
b.lo 1b
|
||||
dsb sy
|
||||
ret
|
||||
ENDPROC(__dma_clean_range)
|
||||
|
||||
/*
|
||||
* __dma_flush_range(start, end)
|
||||
* - start - virtual start address of region
|
||||
* - end - virtual end address of region
|
||||
*/
|
||||
ENTRY(__dma_flush_range)
|
||||
dcache_line_size x2, x3
|
||||
sub x3, x2, #1
|
||||
bic x0, x0, x3
|
||||
1: dc civac, x0 // clean & invalidate D / U line
|
||||
add x0, x0, x2
|
||||
cmp x0, x1
|
||||
b.lo 1b
|
||||
dsb sy
|
||||
ret
|
||||
ENDPROC(__dma_flush_range)
|
||||
|
||||
/*
|
||||
* __dma_map_area(start, size, dir)
|
||||
* - start - kernel virtual start address
|
||||
* - size - size of region
|
||||
* - dir - DMA direction
|
||||
*/
|
||||
ENTRY(__dma_map_area)
|
||||
add x1, x1, x0
|
||||
cmp w2, #DMA_FROM_DEVICE
|
||||
b.eq __dma_inv_range
|
||||
b __dma_clean_range
|
||||
ENDPROC(__dma_map_area)
|
||||
|
||||
/*
|
||||
* __dma_unmap_area(start, size, dir)
|
||||
* - start - kernel virtual start address
|
||||
* - size - size of region
|
||||
* - dir - DMA direction
|
||||
*/
|
||||
ENTRY(__dma_unmap_area)
|
||||
add x1, x1, x0
|
||||
cmp w2, #DMA_TO_DEVICE
|
||||
b.ne __dma_inv_range
|
||||
ret
|
||||
ENDPROC(__dma_unmap_area)
|
||||
|
||||
@@ -21,34 +21,277 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/swiotlb.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
struct dma_map_ops *dma_ops;
|
||||
EXPORT_SYMBOL(dma_ops);
|
||||
|
||||
static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
|
||||
bool coherent)
|
||||
{
|
||||
if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
|
||||
return pgprot_writecombine(prot);
|
||||
else if (!coherent)
|
||||
return pgprot_dmacoherent(prot);
|
||||
return prot;
|
||||
}
|
||||
|
||||
static void *__dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) &&
|
||||
dev->coherent_dma_mask <= DMA_BIT_MASK(32))
|
||||
flags |= GFP_DMA32;
|
||||
return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
|
||||
flags |= GFP_DMA;
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA)) {
|
||||
struct page *page;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT,
|
||||
get_order(size));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
return page_address(page);
|
||||
} else {
|
||||
return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void __dma_free_coherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
swiotlb_free_coherent(dev, size, vaddr, dma_handle);
|
||||
if (dev == NULL) {
|
||||
WARN_ONCE(1, "Use an actual device structure for DMA allocation\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DMA_CMA)) {
|
||||
phys_addr_t paddr = dma_to_phys(dev, dma_handle);
|
||||
|
||||
dma_release_from_contiguous(dev,
|
||||
phys_to_page(paddr),
|
||||
size >> PAGE_SHIFT);
|
||||
} else {
|
||||
swiotlb_free_coherent(dev, size, vaddr, dma_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dma_map_ops coherent_swiotlb_dma_ops = {
|
||||
static void *__dma_alloc_noncoherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct page *page, **map;
|
||||
void *ptr, *coherent_ptr;
|
||||
int order, i;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
order = get_order(size);
|
||||
|
||||
ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs);
|
||||
if (!ptr)
|
||||
goto no_mem;
|
||||
map = kmalloc(sizeof(struct page *) << order, flags & ~GFP_DMA);
|
||||
if (!map)
|
||||
goto no_map;
|
||||
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
__dma_flush_range(ptr, ptr + size);
|
||||
|
||||
/* create a coherent mapping */
|
||||
page = virt_to_page(ptr);
|
||||
for (i = 0; i < (size >> PAGE_SHIFT); i++)
|
||||
map[i] = page + i;
|
||||
coherent_ptr = vmap(map, size >> PAGE_SHIFT, VM_MAP,
|
||||
__get_dma_pgprot(attrs, pgprot_default, false));
|
||||
kfree(map);
|
||||
if (!coherent_ptr)
|
||||
goto no_map;
|
||||
|
||||
return coherent_ptr;
|
||||
|
||||
no_map:
|
||||
__dma_free_coherent(dev, size, ptr, *dma_handle, attrs);
|
||||
no_mem:
|
||||
*dma_handle = ~0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __dma_free_noncoherent(struct device *dev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle));
|
||||
|
||||
vunmap(vaddr);
|
||||
__dma_free_coherent(dev, size, swiotlb_addr, dma_handle, attrs);
|
||||
}
|
||||
|
||||
static dma_addr_t __swiotlb_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size,
|
||||
enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
dma_addr_t dev_addr;
|
||||
|
||||
dev_addr = swiotlb_map_page(dev, page, offset, size, dir, attrs);
|
||||
__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);
|
||||
|
||||
return dev_addr;
|
||||
}
|
||||
|
||||
|
||||
static void __swiotlb_unmap_page(struct device *dev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);
|
||||
swiotlb_unmap_page(dev, dev_addr, size, dir, attrs);
|
||||
}
|
||||
|
||||
static int __swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
|
||||
int nelems, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i, ret;
|
||||
|
||||
ret = swiotlb_map_sg_attrs(dev, sgl, nelems, dir, attrs);
|
||||
for_each_sg(sgl, sg, ret, i)
|
||||
__dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)),
|
||||
sg->length, dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __swiotlb_unmap_sg_attrs(struct device *dev,
|
||||
struct scatterlist *sgl, int nelems,
|
||||
enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
__dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)),
|
||||
sg->length, dir);
|
||||
swiotlb_unmap_sg_attrs(dev, sgl, nelems, dir, attrs);
|
||||
}
|
||||
|
||||
static void __swiotlb_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dev_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);
|
||||
swiotlb_sync_single_for_cpu(dev, dev_addr, size, dir);
|
||||
}
|
||||
|
||||
static void __swiotlb_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dev_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
swiotlb_sync_single_for_device(dev, dev_addr, size, dir);
|
||||
__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);
|
||||
}
|
||||
|
||||
static void __swiotlb_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sgl, int nelems,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
__dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)),
|
||||
sg->length, dir);
|
||||
swiotlb_sync_sg_for_cpu(dev, sgl, nelems, dir);
|
||||
}
|
||||
|
||||
static void __swiotlb_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sgl, int nelems,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
swiotlb_sync_sg_for_device(dev, sgl, nelems, dir);
|
||||
for_each_sg(sgl, sg, nelems, i)
|
||||
__dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)),
|
||||
sg->length, dir);
|
||||
}
|
||||
|
||||
/* vma->vm_page_prot must be set appropriately before calling this function */
|
||||
static int __dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size)
|
||||
{
|
||||
int ret = -ENXIO;
|
||||
unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >>
|
||||
PAGE_SHIFT;
|
||||
unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned long pfn = dma_to_phys(dev, dma_addr) >> PAGE_SHIFT;
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
|
||||
if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
|
||||
return ret;
|
||||
|
||||
if (off < nr_pages && nr_vma_pages <= (nr_pages - off)) {
|
||||
ret = remap_pfn_range(vma, vma->vm_start,
|
||||
pfn + off,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __swiotlb_mmap_noncoherent(struct device *dev,
|
||||
struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, false);
|
||||
return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
|
||||
}
|
||||
|
||||
static int __swiotlb_mmap_coherent(struct device *dev,
|
||||
struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
/* Just use whatever page_prot attributes were specified */
|
||||
return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
|
||||
}
|
||||
|
||||
struct dma_map_ops noncoherent_swiotlb_dma_ops = {
|
||||
.alloc = __dma_alloc_noncoherent,
|
||||
.free = __dma_free_noncoherent,
|
||||
.mmap = __swiotlb_mmap_noncoherent,
|
||||
.map_page = __swiotlb_map_page,
|
||||
.unmap_page = __swiotlb_unmap_page,
|
||||
.map_sg = __swiotlb_map_sg_attrs,
|
||||
.unmap_sg = __swiotlb_unmap_sg_attrs,
|
||||
.sync_single_for_cpu = __swiotlb_sync_single_for_cpu,
|
||||
.sync_single_for_device = __swiotlb_sync_single_for_device,
|
||||
.sync_sg_for_cpu = __swiotlb_sync_sg_for_cpu,
|
||||
.sync_sg_for_device = __swiotlb_sync_sg_for_device,
|
||||
.dma_supported = swiotlb_dma_supported,
|
||||
.mapping_error = swiotlb_dma_mapping_error,
|
||||
};
|
||||
EXPORT_SYMBOL(noncoherent_swiotlb_dma_ops);
|
||||
|
||||
struct dma_map_ops coherent_swiotlb_dma_ops = {
|
||||
.alloc = __dma_alloc_coherent,
|
||||
.free = __dma_free_coherent,
|
||||
.mmap = __swiotlb_mmap_coherent,
|
||||
.map_page = swiotlb_map_page,
|
||||
.unmap_page = swiotlb_unmap_page,
|
||||
.map_sg = swiotlb_map_sg_attrs,
|
||||
@@ -60,13 +303,48 @@ static struct dma_map_ops coherent_swiotlb_dma_ops = {
|
||||
.dma_supported = swiotlb_dma_supported,
|
||||
.mapping_error = swiotlb_dma_mapping_error,
|
||||
};
|
||||
EXPORT_SYMBOL(coherent_swiotlb_dma_ops);
|
||||
|
||||
void __init arm64_swiotlb_init(void)
|
||||
static int dma_bus_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *_dev)
|
||||
{
|
||||
dma_ops = &coherent_swiotlb_dma_ops;
|
||||
swiotlb_init(1);
|
||||
struct device *dev = _dev;
|
||||
|
||||
if (event != BUS_NOTIFY_ADD_DEVICE)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "dma-coherent"))
|
||||
set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block platform_bus_nb = {
|
||||
.notifier_call = dma_bus_notifier,
|
||||
};
|
||||
|
||||
static struct notifier_block amba_bus_nb = {
|
||||
.notifier_call = dma_bus_notifier,
|
||||
};
|
||||
|
||||
extern int swiotlb_late_init_with_default_size(size_t default_size);
|
||||
|
||||
static int __init swiotlb_late_init(void)
|
||||
{
|
||||
size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
|
||||
|
||||
/*
|
||||
* These must be registered before of_platform_populate().
|
||||
*/
|
||||
bus_register_notifier(&platform_bus_type, &platform_bus_nb);
|
||||
bus_register_notifier(&amba_bustype, &amba_bus_nb);
|
||||
|
||||
dma_ops = &noncoherent_swiotlb_dma_ops;
|
||||
|
||||
return swiotlb_late_init_with_default_size(swiotlb_size);
|
||||
}
|
||||
arch_initcall(swiotlb_late_init);
|
||||
|
||||
#define PREALLOC_DMA_DEBUG_ENTRIES 4096
|
||||
|
||||
static int __init dma_debug_do_init(void)
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -66,22 +68,22 @@ static int __init early_initrd(char *p)
|
||||
}
|
||||
early_param("initrd", early_initrd);
|
||||
|
||||
#define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT)
|
||||
|
||||
static void __init zone_sizes_init(unsigned long min, unsigned long max)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
|
||||
unsigned long max_dma32 = min;
|
||||
unsigned long max_dma = min;
|
||||
|
||||
memset(zone_size, 0, sizeof(zone_size));
|
||||
|
||||
#ifdef CONFIG_ZONE_DMA32
|
||||
/* 4GB maximum for 32-bit only capable devices */
|
||||
max_dma32 = max(min, min(max, MAX_DMA32_PFN));
|
||||
zone_size[ZONE_DMA32] = max_dma32 - min;
|
||||
#endif
|
||||
zone_size[ZONE_NORMAL] = max - max_dma32;
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA)) {
|
||||
unsigned long max_dma_phys =
|
||||
(unsigned long)dma_to_phys(NULL, DMA_BIT_MASK(32) + 1);
|
||||
max_dma = max(min, min(max, max_dma_phys >> PAGE_SHIFT));
|
||||
zone_size[ZONE_DMA] = max_dma - min;
|
||||
}
|
||||
zone_size[ZONE_NORMAL] = max - max_dma;
|
||||
|
||||
memcpy(zhole_size, zone_size, sizeof(zhole_size));
|
||||
|
||||
@@ -91,15 +93,15 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
|
||||
|
||||
if (start >= max)
|
||||
continue;
|
||||
#ifdef CONFIG_ZONE_DMA32
|
||||
if (start < max_dma32) {
|
||||
unsigned long dma_end = min(end, max_dma32);
|
||||
zhole_size[ZONE_DMA32] -= dma_end - start;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) && start < max_dma) {
|
||||
unsigned long dma_end = min(end, max_dma);
|
||||
zhole_size[ZONE_DMA] -= dma_end - start;
|
||||
}
|
||||
#endif
|
||||
if (end > max_dma32) {
|
||||
|
||||
if (end > max_dma) {
|
||||
unsigned long normal_end = min(end, max);
|
||||
unsigned long normal_start = max(start, max_dma32);
|
||||
unsigned long normal_start = max(start, max_dma);
|
||||
zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
|
||||
}
|
||||
}
|
||||
@@ -172,6 +174,8 @@ void __init arm64_memblock_init(void)
|
||||
memblock_reserve(base, size);
|
||||
}
|
||||
|
||||
dma_contiguous_reserve(0);
|
||||
|
||||
memblock_allow_resize();
|
||||
memblock_dump_all();
|
||||
}
|
||||
@@ -282,8 +286,6 @@ void __init mem_init(void)
|
||||
unsigned long reserved_pages, free_pages;
|
||||
struct memblock_region *reg;
|
||||
|
||||
arm64_swiotlb_init();
|
||||
|
||||
max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;
|
||||
|
||||
#ifndef CONFIG_SPARSEMEM_VMEMMAP
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm-generic/dma-contiguous.h>
|
||||
|
||||
static inline void
|
||||
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { }
|
||||
|
||||
@@ -202,11 +202,9 @@ config DMA_SHARED_BUFFER
|
||||
APIs extension; the file's descriptor can then be passed on to other
|
||||
driver.
|
||||
|
||||
config CMA
|
||||
bool "Contiguous Memory Allocator"
|
||||
depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK
|
||||
select MIGRATION
|
||||
select MEMORY_ISOLATION
|
||||
config DMA_CMA
|
||||
bool "DMA Contiguous Memory Allocator"
|
||||
depends on HAVE_DMA_CONTIGUOUS && CMA
|
||||
help
|
||||
This enables the Contiguous Memory Allocator which allows drivers
|
||||
to allocate big physically-contiguous blocks of memory for use with
|
||||
@@ -215,17 +213,7 @@ config CMA
|
||||
For more information see <include/linux/dma-contiguous.h>.
|
||||
If unsure, say "n".
|
||||
|
||||
if CMA
|
||||
|
||||
config CMA_DEBUG
|
||||
bool "CMA debug messages (DEVELOPMENT)"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
Turns on debug messages in CMA. This produces KERN_DEBUG
|
||||
messages for every CMA call as well as various messages while
|
||||
processing calls such as dma_alloc_from_contiguous().
|
||||
This option does not affect warning and error messages.
|
||||
|
||||
if DMA_CMA
|
||||
comment "Default contiguous memory area size:"
|
||||
|
||||
config CMA_SIZE_MBYTES
|
||||
|
||||
@@ -6,7 +6,7 @@ obj-y := core.o bus.o dd.o syscore.o \
|
||||
attribute_container.o transport_class.o \
|
||||
topology.o
|
||||
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
|
||||
obj-$(CONFIG_CMA) += dma-contiguous.o
|
||||
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
|
||||
obj-y += power/
|
||||
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
|
||||
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
|
||||
|
||||
@@ -96,7 +96,7 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve() - reserve area for contiguous memory handling
|
||||
* dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
*
|
||||
* This function reserves memory from early allocator. It should be
|
||||
@@ -124,22 +124,29 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (selected_size) {
|
||||
if (selected_size && !dma_contiguous_default_area) {
|
||||
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
|
||||
(unsigned long)selected_size / SZ_1M);
|
||||
|
||||
dma_declare_contiguous(NULL, selected_size, 0, limit);
|
||||
dma_contiguous_reserve_area(selected_size, 0, limit,
|
||||
&dma_contiguous_default_area);
|
||||
}
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(cma_mutex);
|
||||
|
||||
static __init int cma_activate_area(unsigned long base_pfn, unsigned long count)
|
||||
static int __init cma_activate_area(struct cma *cma)
|
||||
{
|
||||
unsigned long pfn = base_pfn;
|
||||
unsigned i = count >> pageblock_order;
|
||||
int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long);
|
||||
unsigned long base_pfn = cma->base_pfn, pfn = base_pfn;
|
||||
unsigned i = cma->count >> pageblock_order;
|
||||
struct zone *zone;
|
||||
|
||||
cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
|
||||
|
||||
if (!cma->bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
WARN_ON_ONCE(!pfn_valid(pfn));
|
||||
zone = page_zone(pfn_to_page(pfn));
|
||||
|
||||
@@ -153,92 +160,53 @@ static __init int cma_activate_area(unsigned long base_pfn, unsigned long count)
|
||||
}
|
||||
init_cma_reserved_pageblock(pfn_to_page(base_pfn));
|
||||
} while (--i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init struct cma *cma_create_area(unsigned long base_pfn,
|
||||
unsigned long count)
|
||||
{
|
||||
int bitmap_size = BITS_TO_LONGS(count) * sizeof(long);
|
||||
struct cma *cma;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
pr_debug("%s(base %08lx, count %lx)\n", __func__, base_pfn, count);
|
||||
|
||||
cma = kmalloc(sizeof *cma, GFP_KERNEL);
|
||||
if (!cma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cma->base_pfn = base_pfn;
|
||||
cma->count = count;
|
||||
cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
|
||||
|
||||
if (!cma->bitmap)
|
||||
goto no_mem;
|
||||
|
||||
ret = cma_activate_area(base_pfn, count);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
pr_debug("%s: returned %p\n", __func__, (void *)cma);
|
||||
return cma;
|
||||
|
||||
error:
|
||||
kfree(cma->bitmap);
|
||||
no_mem:
|
||||
kfree(cma);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct cma_reserved {
|
||||
phys_addr_t start;
|
||||
unsigned long size;
|
||||
struct device *dev;
|
||||
} cma_reserved[MAX_CMA_AREAS] __initdata;
|
||||
static unsigned cma_reserved_count __initdata;
|
||||
static struct cma cma_areas[MAX_CMA_AREAS];
|
||||
static unsigned cma_area_count;
|
||||
|
||||
static int __init cma_init_reserved_areas(void)
|
||||
{
|
||||
struct cma_reserved *r = cma_reserved;
|
||||
unsigned i = cma_reserved_count;
|
||||
int i;
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
for (; i; --i, ++r) {
|
||||
struct cma *cma;
|
||||
cma = cma_create_area(PFN_DOWN(r->start),
|
||||
r->size >> PAGE_SHIFT);
|
||||
if (!IS_ERR(cma))
|
||||
dev_set_cma_area(r->dev, cma);
|
||||
for (i = 0; i < cma_area_count; i++) {
|
||||
int ret = cma_activate_area(&cma_areas[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(cma_init_reserved_areas);
|
||||
|
||||
/**
|
||||
* dma_declare_contiguous() - reserve area for contiguous memory handling
|
||||
* for particular device
|
||||
* @dev: Pointer to device structure.
|
||||
* @size: Size of the reserved memory.
|
||||
* @base: Start address of the reserved memory (optional, 0 for any).
|
||||
* dma_contiguous_reserve_area() - reserve custom contiguous area
|
||||
* @size: Size of the reserved area (in bytes),
|
||||
* @base: Base address of the reserved area optional, use 0 for any
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
* @res_cma: Pointer to store the created cma region.
|
||||
*
|
||||
* This function reserves memory for specified device. It should be
|
||||
* called by board specific code when early allocator (memblock or bootmem)
|
||||
* is still activate.
|
||||
* This function reserves memory from early allocator. It should be
|
||||
* called by arch specific code once the early allocator (memblock or bootmem)
|
||||
* has been activated and all other subsystems have already allocated/reserved
|
||||
* memory. This function allows to create custom reserved areas for specific
|
||||
* devices.
|
||||
*/
|
||||
int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
phys_addr_t base, phys_addr_t limit)
|
||||
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
|
||||
phys_addr_t limit, struct cma **res_cma)
|
||||
{
|
||||
struct cma_reserved *r = &cma_reserved[cma_reserved_count];
|
||||
struct cma *cma = &cma_areas[cma_area_count];
|
||||
phys_addr_t alignment;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__,
|
||||
(unsigned long)size, (unsigned long)base,
|
||||
(unsigned long)limit);
|
||||
|
||||
/* Sanity checks */
|
||||
if (cma_reserved_count == ARRAY_SIZE(cma_reserved)) {
|
||||
if (cma_area_count == ARRAY_SIZE(cma_areas)) {
|
||||
pr_err("Not enough slots for CMA reserved regions!\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
@@ -256,7 +224,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
if (base) {
|
||||
if (memblock_is_region_reserved(base, size) ||
|
||||
memblock_reserve(base, size) < 0) {
|
||||
base = -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
@@ -266,7 +234,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
*/
|
||||
phys_addr_t addr = __memblock_alloc_base(size, alignment, limit);
|
||||
if (!addr) {
|
||||
base = -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
} else {
|
||||
base = addr;
|
||||
@@ -277,10 +245,11 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
* Each reserved area must be initialised later, when more kernel
|
||||
* subsystems (like slab allocator) are available.
|
||||
*/
|
||||
r->start = base;
|
||||
r->size = size;
|
||||
r->dev = dev;
|
||||
cma_reserved_count++;
|
||||
cma->base_pfn = PFN_DOWN(base);
|
||||
cma->count = size >> PAGE_SHIFT;
|
||||
*res_cma = cma;
|
||||
cma_area_count++;
|
||||
|
||||
pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
|
||||
(unsigned long)base);
|
||||
|
||||
@@ -289,7 +258,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
return 0;
|
||||
err:
|
||||
pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M);
|
||||
return base;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#ifndef ASM_DMA_CONTIGUOUS_H
|
||||
#define ASM_DMA_CONTIGUOUS_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#ifdef CONFIG_CMA
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
|
||||
static inline struct cma *dev_get_cma_area(struct device *dev)
|
||||
{
|
||||
if (dev && dev->cma_area)
|
||||
return dev->cma_area;
|
||||
return dma_contiguous_default_area;
|
||||
}
|
||||
|
||||
static inline void dev_set_cma_area(struct device *dev, struct cma *cma)
|
||||
{
|
||||
if (dev)
|
||||
dev->cma_area = cma;
|
||||
if (!dev && !dma_contiguous_default_area)
|
||||
dma_contiguous_default_area = cma;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -698,7 +698,7 @@ struct device {
|
||||
|
||||
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
|
||||
override */
|
||||
#ifdef CONFIG_CMA
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
struct cma *cma_area; /* contiguous memory area for dma
|
||||
allocations */
|
||||
#endif
|
||||
|
||||
@@ -57,7 +57,7 @@ struct cma;
|
||||
struct page;
|
||||
struct device;
|
||||
|
||||
#ifdef CONFIG_CMA
|
||||
#ifdef CONFIG_DMA_CMA
|
||||
|
||||
/*
|
||||
* There is always at least global CMA area and a few optional device
|
||||
@@ -67,9 +67,53 @@ struct device;
|
||||
|
||||
extern struct cma *dma_contiguous_default_area;
|
||||
|
||||
static inline struct cma *dev_get_cma_area(struct device *dev)
|
||||
{
|
||||
if (dev && dev->cma_area)
|
||||
return dev->cma_area;
|
||||
return dma_contiguous_default_area;
|
||||
}
|
||||
|
||||
static inline void dev_set_cma_area(struct device *dev, struct cma *cma)
|
||||
{
|
||||
if (dev)
|
||||
dev->cma_area = cma;
|
||||
}
|
||||
|
||||
static inline void dma_contiguous_set_default(struct cma *cma)
|
||||
{
|
||||
dma_contiguous_default_area = cma;
|
||||
}
|
||||
|
||||
void dma_contiguous_reserve(phys_addr_t addr_limit);
|
||||
int dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
phys_addr_t base, phys_addr_t limit);
|
||||
|
||||
int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
|
||||
phys_addr_t limit, struct cma **res_cma);
|
||||
|
||||
/**
|
||||
* dma_declare_contiguous() - reserve area for contiguous memory handling
|
||||
* for particular device
|
||||
* @dev: Pointer to device structure.
|
||||
* @size: Size of the reserved memory.
|
||||
* @base: Start address of the reserved memory (optional, 0 for any).
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
*
|
||||
* This function reserves memory for specified device. It should be
|
||||
* called by board specific code when early allocator (memblock or bootmem)
|
||||
* is still activate.
|
||||
*/
|
||||
|
||||
static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
phys_addr_t base, phys_addr_t limit)
|
||||
{
|
||||
struct cma *cma;
|
||||
int ret;
|
||||
ret = dma_contiguous_reserve_area(size, base, limit, &cma);
|
||||
if (ret == 0)
|
||||
dev_set_cma_area(dev, cma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct page *dma_alloc_from_contiguous(struct device *dev, int count,
|
||||
unsigned int order);
|
||||
@@ -80,8 +124,22 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages,
|
||||
|
||||
#define MAX_CMA_AREAS (0)
|
||||
|
||||
static inline struct cma *dev_get_cma_area(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void dev_set_cma_area(struct device *dev, struct cma *cma) { }
|
||||
|
||||
static inline void dma_contiguous_set_default(struct cma *cma) { }
|
||||
|
||||
static inline void dma_contiguous_reserve(phys_addr_t limit) { }
|
||||
|
||||
static inline int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base,
|
||||
phys_addr_t limit, struct cma **res_cma) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline
|
||||
int dma_declare_contiguous(struct device *dev, phys_addr_t size,
|
||||
phys_addr_t base, phys_addr_t limit)
|
||||
|
||||
@@ -97,6 +97,30 @@ static inline int dma_set_coherent_mask(struct device *dev, u64 mask)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set both the DMA mask and the coherent DMA mask to the same thing.
|
||||
* Note that we don't check the return value from dma_set_coherent_mask()
|
||||
* as the DMA API guarantees that the coherent DMA mask can be set to
|
||||
* the same or smaller than the streaming DMA mask.
|
||||
*/
|
||||
static inline int dma_set_mask_and_coherent(struct device *dev, u64 mask)
|
||||
{
|
||||
int rc = dma_set_mask(dev, mask);
|
||||
if (rc == 0)
|
||||
dma_set_coherent_mask(dev, mask);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to the above, except it deals with the case where the device
|
||||
* does not have dev->dma_mask appropriately setup.
|
||||
*/
|
||||
static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
|
||||
{
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
return dma_set_mask_and_coherent(dev, mask);
|
||||
}
|
||||
|
||||
extern u64 dma_get_required_mask(struct device *dev);
|
||||
|
||||
static inline unsigned int dma_get_max_seg_size(struct device *dev)
|
||||
|
||||
24
mm/Kconfig
24
mm/Kconfig
@@ -480,3 +480,27 @@ config FRONTSWAP
|
||||
|
||||
config GENERIC_EARLY_IOREMAP
|
||||
bool
|
||||
|
||||
config CMA
|
||||
bool "Contiguous Memory Allocator"
|
||||
depends on HAVE_MEMBLOCK
|
||||
select MIGRATION
|
||||
select MEMORY_ISOLATION
|
||||
help
|
||||
This enables the Contiguous Memory Allocator which allows other
|
||||
subsystems to allocate big physically-contiguous blocks of memory.
|
||||
CMA reserves a region of memory and allows only movable pages to
|
||||
be allocated from it. This way, the kernel can use the memory for
|
||||
pagecache and when a subsystem requests for contiguous area, the
|
||||
allocated pages are migrated away to serve the contiguous request.
|
||||
|
||||
If unsure, say "n".
|
||||
|
||||
config CMA_DEBUG
|
||||
bool "CMA debug messages (DEVELOPMENT)"
|
||||
depends on DEBUG_KERNEL && CMA
|
||||
help
|
||||
Turns on debug messages in CMA. This produces KERN_DEBUG
|
||||
messages for every CMA call as well as various messages while
|
||||
processing calls such as dma_alloc_from_contiguous().
|
||||
This option does not affect warning and error messages.
|
||||
|
||||
Reference in New Issue
Block a user