dma-mapping: introduce DMA range map, supplanting dma_pfn_offset
The new field 'dma_range_map' in struct device is used to facilitate the use of single or multiple offsets between mapping regions of cpu addrs and dma addrs. It subsumes the role of "dev->dma_pfn_offset" which was only capable of holding a single uniform offset and had no region bounds checking. The function of_dma_get_range() has been modified so that it takes a single argument -- the device node -- and returns a map, NULL, or an error code. The map is an array that holds the information regarding the DMA regions. Each range entry contains the address offset, the cpu_start address, the dma_start address, and the size of the region. of_dma_configure() is the typical manner to set range offsets but there are a number of ad hoc assignments to "dev->dma_pfn_offset" in the kernel driver code. These cases now invoke the function dma_direct_set_offset(dev, cpu_addr, dma_addr, size). Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com> [hch: various interface cleanups] Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org> Tested-by: Mathieu Poirier <mathieu.poirier@linaro.org> Tested-by: Nathan Chancellor <natechancellor@gmail.com>
This commit is contained in:

committed by
Christoph Hellwig

parent
6eb0233ec2
commit
e0d072782c
@@ -13,6 +13,7 @@
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
|
||||
@@ -66,8 +67,12 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
|
||||
static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
|
||||
{
|
||||
return phys_to_dma_direct(dev, phys) + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
|
||||
|
||||
if (dma_addr == DMA_MAPPING_ERROR)
|
||||
return false;
|
||||
return dma_addr + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -461,3 +466,45 @@ bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
|
||||
return !dev_is_dma_coherent(dev) ||
|
||||
is_swiotlb_buffer(dma_to_phys(dev, dma_addr));
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_direct_set_offset - Assign scalar offset for a single DMA range.
|
||||
* @dev: device pointer; needed to "own" the alloced memory.
|
||||
* @cpu_start: beginning of memory region covered by this offset.
|
||||
* @dma_start: beginning of DMA/PCI region covered by this offset.
|
||||
* @size: size of the region.
|
||||
*
|
||||
* This is for the simple case of a uniform offset which cannot
|
||||
* be discovered by "dma-ranges".
|
||||
*
|
||||
* It returns -ENOMEM if out of memory, -EINVAL if a map
|
||||
* already exists, 0 otherwise.
|
||||
*
|
||||
* Note: any call to this from a driver is a bug. The mapping needs
|
||||
* to be described by the device tree or other firmware interfaces.
|
||||
*/
|
||||
int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
|
||||
dma_addr_t dma_start, u64 size)
|
||||
{
|
||||
struct bus_dma_region *map;
|
||||
u64 offset = (u64)cpu_start - (u64)dma_start;
|
||||
|
||||
if (dev->dma_range_map) {
|
||||
dev_err(dev, "attempt to add DMA range to existing map\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
map = kcalloc(2, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
map[0].cpu_start = cpu_start;
|
||||
map[0].dma_start = dma_start;
|
||||
map[0].offset = offset;
|
||||
map[0].size = size;
|
||||
dev->dma_range_map = map;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_direct_set_offset);
|
||||
|
Reference in New Issue
Block a user