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:
Jim Quinlan
2020-09-17 18:43:40 +02:00
committed by Christoph Hellwig
parent 6eb0233ec2
commit e0d072782c
22 changed files with 285 additions and 118 deletions

View File

@@ -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);