forked from luck/tmp_suning_uos_patched
drivers: dma-coherent: Account dma_pfn_offset when used with device tree
dma_declare_coherent_memory() and friends are designed to account difference in CPU and device addresses. However, when it is used with reserved memory regions there is assumption that CPU and device have the same view on address space. This assumption gets invalid when reserved memory for coherent DMA allocations is referenced by device with non-empty "dma-range" property. Simply feeding device address as rmem->base + dev->dma_pfn_offset would not work due to reserved memory region can be shared, so this patch turns device address to be expressed with help of CPU address and device's dma_pfn_offset in case memory reservation has been done via device tree; non device tree users continue to use the old scheme. Cc: Michal Nazarewicz <mina86@mina86.com> Cc: Marek Szyprowski <m.szyprowski@samsung.com> Cc: Roger Quadros <rogerq@ti.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Tested-by: Benjamin Gaignard <benjamin.gaignard@linaro.org> Tested-by: Andras Szemzo <sza@esh.hu> Tested-by: Alexandre TORGUE <alexandre.torgue@st.com> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
25f1e18870
commit
c41f9ea998
|
@ -16,8 +16,18 @@ struct dma_coherent_mem {
|
||||||
int flags;
|
int flags;
|
||||||
unsigned long *bitmap;
|
unsigned long *bitmap;
|
||||||
spinlock_t spinlock;
|
spinlock_t spinlock;
|
||||||
|
bool use_dev_dma_pfn_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline dma_addr_t dma_get_device_base(struct device *dev,
|
||||||
|
struct dma_coherent_mem * mem)
|
||||||
|
{
|
||||||
|
if (mem->use_dev_dma_pfn_offset)
|
||||||
|
return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT;
|
||||||
|
else
|
||||||
|
return mem->device_base;
|
||||||
|
}
|
||||||
|
|
||||||
static bool dma_init_coherent_memory(
|
static bool dma_init_coherent_memory(
|
||||||
phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags,
|
phys_addr_t phys_addr, dma_addr_t device_addr, size_t size, int flags,
|
||||||
struct dma_coherent_mem **mem)
|
struct dma_coherent_mem **mem)
|
||||||
|
@ -133,7 +143,7 @@ void *dma_mark_declared_memory_occupied(struct device *dev,
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
spin_lock_irqsave(&mem->spinlock, flags);
|
spin_lock_irqsave(&mem->spinlock, flags);
|
||||||
pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
|
pos = PFN_DOWN(device_addr - dma_get_device_base(dev, mem));
|
||||||
err = bitmap_allocate_region(mem->bitmap, pos, get_order(size));
|
err = bitmap_allocate_region(mem->bitmap, pos, get_order(size));
|
||||||
spin_unlock_irqrestore(&mem->spinlock, flags);
|
spin_unlock_irqrestore(&mem->spinlock, flags);
|
||||||
|
|
||||||
|
@ -186,7 +196,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
|
||||||
/*
|
/*
|
||||||
* Memory was found in the per-device area.
|
* Memory was found in the per-device area.
|
||||||
*/
|
*/
|
||||||
*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
|
*dma_handle = dma_get_device_base(dev, mem) + (pageno << PAGE_SHIFT);
|
||||||
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
||||||
dma_memory_map = (mem->flags & DMA_MEMORY_MAP);
|
dma_memory_map = (mem->flags & DMA_MEMORY_MAP);
|
||||||
spin_unlock_irqrestore(&mem->spinlock, flags);
|
spin_unlock_irqrestore(&mem->spinlock, flags);
|
||||||
|
@ -299,6 +309,7 @@ static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
|
||||||
&rmem->base, (unsigned long)rmem->size / SZ_1M);
|
&rmem->base, (unsigned long)rmem->size / SZ_1M);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
mem->use_dev_dma_pfn_offset = true;
|
||||||
rmem->priv = mem;
|
rmem->priv = mem;
|
||||||
dma_assign_coherent_memory(dev, mem);
|
dma_assign_coherent_memory(dev, mem);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user