mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge branch 'for-v3.7' of git://git.linaro.org/people/mszyprowski/linux-dma-mapping
Pull CMA and DMA-mapping updates from Marek Szyprowski: "This time the pull request is rather small, because the further redesign patches were not ready on time. This pull request consists of the patches which extend ARM DMA-mapping subsystem with support for CPU coherent (ACP) DMA busses. The first client of the new version is HighBank SATA driver. The second part of the pull request includes various cleanup for both CMA common code and ARM DMA-mapping subsystem." Fix up trivial add-add conflict due to the "dma-coherent" DT property being added next to the "calxeda,port-phys" property for the Calxeda AHCI controller. * 'for-v3.7' of git://git.linaro.org/people/mszyprowski/linux-dma-mapping: ARM: dma-mapping: Remove unsed var at arm_coherent_iommu_unmap_page ARM: highbank: add coherent DMA setup ARM: kill off arch_is_coherent ARM: add coherent iommu dma ops ARM: add coherent dma ops ARM: dma-mapping: Refrain noisy console message ARM: dma-mapping: Small logical clean up drivers: dma-contiguous: refactor dma_alloc_from_contiguous()
This commit is contained in:
commit
ca41cc96b2
@ -12,6 +12,7 @@ Optional properties:
|
||||
- calxeda,port-phys: phandle-combophy and lane assignment, which maps each
|
||||
SATA port to a combophy and a lane within that
|
||||
combophy
|
||||
- dma-coherent : Present if dma operations are coherent
|
||||
|
||||
Example:
|
||||
sata@ffe08000 {
|
||||
|
@ -9,6 +9,9 @@ Required properties:
|
||||
region.
|
||||
- interrupts: interrupt number to the cpu.
|
||||
|
||||
Optional properties:
|
||||
- dma-coherent : Present if dma operations are coherent
|
||||
|
||||
Example:
|
||||
|
||||
pdma0: pdma@12680000 {
|
||||
|
@ -6,6 +6,9 @@ Required properties:
|
||||
- interrupts : Should contain 3 xgmac interrupts. The 1st is main interrupt.
|
||||
The 2nd is pwr mgt interrupt. The 3rd is low power state interrupt.
|
||||
|
||||
Optional properties:
|
||||
- dma-coherent : Present if dma operations are coherent
|
||||
|
||||
Example:
|
||||
|
||||
ethernet@fff50000 {
|
||||
|
@ -124,6 +124,7 @@
|
||||
calxeda,port-phys = <&combophy5 0 &combophy0 0
|
||||
&combophy0 1 &combophy0 2
|
||||
&combophy0 3>;
|
||||
dma-coherent;
|
||||
};
|
||||
|
||||
sdhci@ffe0e000 {
|
||||
|
@ -44,10 +44,9 @@
|
||||
#define rmb() dsb()
|
||||
#define wmb() mb()
|
||||
#else
|
||||
#include <asm/memory.h>
|
||||
#define mb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
|
||||
#define rmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
|
||||
#define wmb() do { if (arch_is_coherent()) dmb(); else barrier(); } while (0)
|
||||
#define mb() barrier()
|
||||
#define rmb() barrier()
|
||||
#define wmb() barrier()
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#define DMA_ERROR_CODE (~0)
|
||||
extern struct dma_map_ops arm_dma_ops;
|
||||
extern struct dma_map_ops arm_coherent_dma_ops;
|
||||
|
||||
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
|
||||
{
|
||||
|
@ -275,14 +275,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
|
||||
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
|
||||
#define virt_addr_valid(kaddr) ((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory)
|
||||
|
||||
/*
|
||||
* Optional coherency support. Currently used only by selected
|
||||
* Intel XSC3-based systems.
|
||||
*/
|
||||
#ifndef arch_is_coherent
|
||||
#define arch_is_coherent() 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include <asm-generic/memory_model.h>
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
@ -23,6 +24,7 @@
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/amba/bus.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/smp_plat.h>
|
||||
@ -149,11 +151,61 @@ static void highbank_power_off(void)
|
||||
cpu_do_idle();
|
||||
}
|
||||
|
||||
static int highbank_platform_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *__dev)
|
||||
{
|
||||
struct resource *res;
|
||||
int reg = -1;
|
||||
struct device *dev = __dev;
|
||||
|
||||
if (event != BUS_NOTIFY_ADD_DEVICE)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "calxeda,hb-ahci"))
|
||||
reg = 0xc;
|
||||
else if (of_device_is_compatible(dev->of_node, "calxeda,hb-sdhci"))
|
||||
reg = 0x18;
|
||||
else if (of_device_is_compatible(dev->of_node, "arm,pl330"))
|
||||
reg = 0x20;
|
||||
else if (of_device_is_compatible(dev->of_node, "calxeda,hb-xgmac")) {
|
||||
res = platform_get_resource(to_platform_device(dev),
|
||||
IORESOURCE_MEM, 0);
|
||||
if (res) {
|
||||
if (res->start == 0xfff50000)
|
||||
reg = 0;
|
||||
else if (res->start == 0xfff51000)
|
||||
reg = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg < 0)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "dma-coherent")) {
|
||||
writel(0xff31, sregs_base + reg);
|
||||
set_dma_ops(dev, &arm_coherent_dma_ops);
|
||||
} else
|
||||
writel(0, sregs_base + reg);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block highbank_amba_nb = {
|
||||
.notifier_call = highbank_platform_notifier,
|
||||
};
|
||||
|
||||
static struct notifier_block highbank_platform_nb = {
|
||||
.notifier_call = highbank_platform_notifier,
|
||||
};
|
||||
|
||||
static void __init highbank_init(void)
|
||||
{
|
||||
pm_power_off = highbank_power_off;
|
||||
highbank_pm_init();
|
||||
|
||||
bus_register_notifier(&platform_bus_type, &highbank_platform_nb);
|
||||
bus_register_notifier(&amba_bustype, &highbank_amba_nb);
|
||||
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
}
|
||||
|
||||
|
@ -73,11 +73,18 @@ static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_cpu_to_dev(page, offset, size, dir);
|
||||
return pfn_to_dma(dev, page_to_pfn(page)) + offset;
|
||||
}
|
||||
|
||||
static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
return pfn_to_dma(dev, page_to_pfn(page)) + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_dma_unmap_page - unmap a buffer previously mapped through dma_map_page()
|
||||
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
||||
@ -96,7 +103,7 @@ static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
|
||||
handle & ~PAGE_MASK, size, dir);
|
||||
}
|
||||
@ -106,7 +113,6 @@ static void arm_dma_sync_single_for_cpu(struct device *dev,
|
||||
{
|
||||
unsigned int offset = handle & (PAGE_SIZE - 1);
|
||||
struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset));
|
||||
if (!arch_is_coherent())
|
||||
__dma_page_dev_to_cpu(page, offset, size, dir);
|
||||
}
|
||||
|
||||
@ -115,7 +121,6 @@ static void arm_dma_sync_single_for_device(struct device *dev,
|
||||
{
|
||||
unsigned int offset = handle & (PAGE_SIZE - 1);
|
||||
struct page *page = pfn_to_page(dma_to_pfn(dev, handle-offset));
|
||||
if (!arch_is_coherent())
|
||||
__dma_page_cpu_to_dev(page, offset, size, dir);
|
||||
}
|
||||
|
||||
@ -138,6 +143,22 @@ struct dma_map_ops arm_dma_ops = {
|
||||
};
|
||||
EXPORT_SYMBOL(arm_dma_ops);
|
||||
|
||||
static void *arm_coherent_dma_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs);
|
||||
static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t handle, struct dma_attrs *attrs);
|
||||
|
||||
struct dma_map_ops arm_coherent_dma_ops = {
|
||||
.alloc = arm_coherent_dma_alloc,
|
||||
.free = arm_coherent_dma_free,
|
||||
.mmap = arm_dma_mmap,
|
||||
.get_sgtable = arm_dma_get_sgtable,
|
||||
.map_page = arm_coherent_dma_map_page,
|
||||
.map_sg = arm_dma_map_sg,
|
||||
.set_dma_mask = arm_dma_set_mask,
|
||||
};
|
||||
EXPORT_SYMBOL(arm_coherent_dma_ops);
|
||||
|
||||
static u64 get_coherent_dma_mask(struct device *dev)
|
||||
{
|
||||
u64 mask = (u64)arm_dma_limit;
|
||||
@ -586,7 +607,7 @@ static void *__alloc_simple_buffer(struct device *dev, size_t size, gfp_t gfp,
|
||||
|
||||
|
||||
static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||
gfp_t gfp, pgprot_t prot, const void *caller)
|
||||
gfp_t gfp, pgprot_t prot, bool is_coherent, const void *caller)
|
||||
{
|
||||
u64 mask = get_coherent_dma_mask(dev);
|
||||
struct page *page;
|
||||
@ -619,7 +640,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||
*handle = DMA_ERROR_CODE;
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (arch_is_coherent() || nommu())
|
||||
if (is_coherent || nommu())
|
||||
addr = __alloc_simple_buffer(dev, size, gfp, &page);
|
||||
else if (gfp & GFP_ATOMIC)
|
||||
addr = __alloc_from_pool(size, &page);
|
||||
@ -647,7 +668,20 @@ void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
||||
if (dma_alloc_from_coherent(dev, size, handle, &memory))
|
||||
return memory;
|
||||
|
||||
return __dma_alloc(dev, size, handle, gfp, prot,
|
||||
return __dma_alloc(dev, size, handle, gfp, prot, false,
|
||||
__builtin_return_address(0));
|
||||
}
|
||||
|
||||
static void *arm_coherent_dma_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
|
||||
{
|
||||
pgprot_t prot = __get_dma_pgprot(attrs, pgprot_kernel);
|
||||
void *memory;
|
||||
|
||||
if (dma_alloc_from_coherent(dev, size, handle, &memory))
|
||||
return memory;
|
||||
|
||||
return __dma_alloc(dev, size, handle, gfp, prot, true,
|
||||
__builtin_return_address(0));
|
||||
}
|
||||
|
||||
@ -684,8 +718,9 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
/*
|
||||
* Free a buffer as defined by the above mapping.
|
||||
*/
|
||||
void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t handle, struct dma_attrs *attrs)
|
||||
static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t handle, struct dma_attrs *attrs,
|
||||
bool is_coherent)
|
||||
{
|
||||
struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
|
||||
|
||||
@ -694,7 +729,7 @@ void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (arch_is_coherent() || nommu()) {
|
||||
if (is_coherent || nommu()) {
|
||||
__dma_free_buffer(page, size);
|
||||
} else if (__free_from_pool(cpu_addr, size)) {
|
||||
return;
|
||||
@ -710,6 +745,18 @@ void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
}
|
||||
}
|
||||
|
||||
void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t handle, struct dma_attrs *attrs)
|
||||
{
|
||||
__arm_dma_free(dev, size, cpu_addr, handle, attrs, false);
|
||||
}
|
||||
|
||||
static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t handle, struct dma_attrs *attrs)
|
||||
{
|
||||
__arm_dma_free(dev, size, cpu_addr, handle, attrs, true);
|
||||
}
|
||||
|
||||
int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t handle, size_t size,
|
||||
struct dma_attrs *attrs)
|
||||
@ -1012,11 +1059,12 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size, gfp_t
|
||||
if (!pages[i])
|
||||
goto error;
|
||||
|
||||
if (order)
|
||||
if (order) {
|
||||
split_page(pages[i], order);
|
||||
j = 1 << order;
|
||||
while (--j)
|
||||
pages[i + j] = pages[i] + j;
|
||||
}
|
||||
|
||||
__dma_clear_buffer(pages[i], PAGE_SIZE << order);
|
||||
i += 1 << order;
|
||||
@ -1303,7 +1351,8 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
*/
|
||||
static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
|
||||
size_t size, dma_addr_t *handle,
|
||||
enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
enum dma_data_direction dir, struct dma_attrs *attrs,
|
||||
bool is_coherent)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
|
||||
dma_addr_t iova, iova_base;
|
||||
@ -1322,7 +1371,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
|
||||
phys_addr_t phys = page_to_phys(sg_page(s));
|
||||
unsigned int len = PAGE_ALIGN(s->offset + s->length);
|
||||
|
||||
if (!arch_is_coherent() &&
|
||||
if (!is_coherent &&
|
||||
!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
|
||||
|
||||
@ -1341,20 +1390,9 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
|
||||
* @dev: valid struct device pointer
|
||||
* @sg: list of buffers
|
||||
* @nents: number of buffers to map
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Map a set of buffers described by scatterlist in streaming mode for DMA.
|
||||
* The scatter gather list elements are merged together (if possible) and
|
||||
* tagged with the appropriate dma address and length. They are obtained via
|
||||
* sg_dma_{address,length}.
|
||||
*/
|
||||
int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir, struct dma_attrs *attrs,
|
||||
bool is_coherent)
|
||||
{
|
||||
struct scatterlist *s = sg, *dma = sg, *start = sg;
|
||||
int i, count = 0;
|
||||
@ -1370,7 +1408,7 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
|
||||
if (s->offset || (size & ~PAGE_MASK) || size + s->length > max) {
|
||||
if (__map_sg_chunk(dev, start, size, &dma->dma_address,
|
||||
dir, attrs) < 0)
|
||||
dir, attrs, is_coherent) < 0)
|
||||
goto bad_mapping;
|
||||
|
||||
dma->dma_address += offset;
|
||||
@ -1383,7 +1421,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
}
|
||||
size += s->length;
|
||||
}
|
||||
if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs) < 0)
|
||||
if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs,
|
||||
is_coherent) < 0)
|
||||
goto bad_mapping;
|
||||
|
||||
dma->dma_address += offset;
|
||||
@ -1397,6 +1436,76 @@ bad_mapping:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_coherent_iommu_map_sg - map a set of SG buffers for streaming mode DMA
|
||||
* @dev: valid struct device pointer
|
||||
* @sg: list of buffers
|
||||
* @nents: number of buffers to map
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Map a set of i/o coherent buffers described by scatterlist in streaming
|
||||
* mode for DMA. The scatter gather list elements are merged together (if
|
||||
* possible) and tagged with the appropriate dma address and length. They are
|
||||
* obtained via sg_dma_{address,length}.
|
||||
*/
|
||||
int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
{
|
||||
return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
|
||||
* @dev: valid struct device pointer
|
||||
* @sg: list of buffers
|
||||
* @nents: number of buffers to map
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Map a set of buffers described by scatterlist in streaming mode for DMA.
|
||||
* The scatter gather list elements are merged together (if possible) and
|
||||
* tagged with the appropriate dma address and length. They are obtained via
|
||||
* sg_dma_{address,length}.
|
||||
*/
|
||||
int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
{
|
||||
return __iommu_map_sg(dev, sg, nents, dir, attrs, false);
|
||||
}
|
||||
|
||||
static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction dir, struct dma_attrs *attrs,
|
||||
bool is_coherent)
|
||||
{
|
||||
struct scatterlist *s;
|
||||
int i;
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
if (sg_dma_len(s))
|
||||
__iommu_remove_mapping(dev, sg_dma_address(s),
|
||||
sg_dma_len(s));
|
||||
if (!is_coherent &&
|
||||
!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_dev_to_cpu(sg_page(s), s->offset,
|
||||
s->length, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_coherent_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
|
||||
* @dev: valid struct device pointer
|
||||
* @sg: list of buffers
|
||||
* @nents: number of buffers to unmap (same as was passed to dma_map_sg)
|
||||
* @dir: DMA transfer direction (same as was passed to dma_map_sg)
|
||||
*
|
||||
* Unmap a set of streaming mode DMA translations. Again, CPU access
|
||||
* rules concerning calls here are the same as for dma_unmap_single().
|
||||
*/
|
||||
void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
{
|
||||
__iommu_unmap_sg(dev, sg, nents, dir, attrs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
|
||||
* @dev: valid struct device pointer
|
||||
@ -1410,18 +1519,7 @@ bad_mapping:
|
||||
void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir, struct dma_attrs *attrs)
|
||||
{
|
||||
struct scatterlist *s;
|
||||
int i;
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
if (sg_dma_len(s))
|
||||
__iommu_remove_mapping(dev, sg_dma_address(s),
|
||||
sg_dma_len(s));
|
||||
if (!arch_is_coherent() &&
|
||||
!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_dev_to_cpu(sg_page(s), s->offset,
|
||||
s->length, dir);
|
||||
}
|
||||
__iommu_unmap_sg(dev, sg, nents, dir, attrs, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1438,7 +1536,6 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
int i;
|
||||
|
||||
for_each_sg(sg, s, nents, i)
|
||||
if (!arch_is_coherent())
|
||||
__dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
|
||||
|
||||
}
|
||||
@ -1457,11 +1554,42 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
int i;
|
||||
|
||||
for_each_sg(sg, s, nents, i)
|
||||
if (!arch_is_coherent())
|
||||
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* arm_coherent_iommu_map_page
|
||||
* @dev: valid struct device pointer
|
||||
* @page: page that buffer resides in
|
||||
* @offset: offset into page for start of buffer
|
||||
* @size: size of buffer to map
|
||||
* @dir: DMA transfer direction
|
||||
*
|
||||
* Coherent IOMMU aware version of arm_dma_map_page()
|
||||
*/
|
||||
static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
|
||||
dma_addr_t dma_addr;
|
||||
int ret, len = PAGE_ALIGN(size + offset);
|
||||
|
||||
dma_addr = __alloc_iova(mapping, len);
|
||||
if (dma_addr == DMA_ERROR_CODE)
|
||||
return dma_addr;
|
||||
|
||||
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, 0);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
return dma_addr + offset;
|
||||
fail:
|
||||
__free_iova(mapping, dma_addr, len);
|
||||
return DMA_ERROR_CODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_iommu_map_page
|
||||
* @dev: valid struct device pointer
|
||||
@ -1476,25 +1604,35 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
|
||||
dma_addr_t dma_addr;
|
||||
int ret, len = PAGE_ALIGN(size + offset);
|
||||
|
||||
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_cpu_to_dev(page, offset, size, dir);
|
||||
|
||||
dma_addr = __alloc_iova(mapping, len);
|
||||
if (dma_addr == DMA_ERROR_CODE)
|
||||
return dma_addr;
|
||||
return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
|
||||
}
|
||||
|
||||
ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, 0);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
/**
|
||||
* arm_coherent_iommu_unmap_page
|
||||
* @dev: valid struct device pointer
|
||||
* @handle: DMA address of buffer
|
||||
* @size: size of buffer (same as passed to dma_map_page)
|
||||
* @dir: DMA transfer direction (same as passed to dma_map_page)
|
||||
*
|
||||
* Coherent IOMMU aware version of arm_dma_unmap_page()
|
||||
*/
|
||||
static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
|
||||
dma_addr_t iova = handle & PAGE_MASK;
|
||||
int offset = handle & ~PAGE_MASK;
|
||||
int len = PAGE_ALIGN(size + offset);
|
||||
|
||||
return dma_addr + offset;
|
||||
fail:
|
||||
__free_iova(mapping, dma_addr, len);
|
||||
return DMA_ERROR_CODE;
|
||||
if (!iova)
|
||||
return;
|
||||
|
||||
iommu_unmap(mapping->domain, iova, len);
|
||||
__free_iova(mapping, iova, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1519,7 +1657,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
|
||||
if (!iova)
|
||||
return;
|
||||
|
||||
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
__dma_page_dev_to_cpu(page, offset, size, dir);
|
||||
|
||||
iommu_unmap(mapping->domain, iova, len);
|
||||
@ -1537,7 +1675,6 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev,
|
||||
if (!iova)
|
||||
return;
|
||||
|
||||
if (!arch_is_coherent())
|
||||
__dma_page_dev_to_cpu(page, offset, size, dir);
|
||||
}
|
||||
|
||||
@ -1572,6 +1709,19 @@ struct dma_map_ops iommu_ops = {
|
||||
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
|
||||
};
|
||||
|
||||
struct dma_map_ops iommu_coherent_ops = {
|
||||
.alloc = arm_iommu_alloc_attrs,
|
||||
.free = arm_iommu_free_attrs,
|
||||
.mmap = arm_iommu_mmap_attrs,
|
||||
.get_sgtable = arm_iommu_get_sgtable,
|
||||
|
||||
.map_page = arm_coherent_iommu_map_page,
|
||||
.unmap_page = arm_coherent_iommu_unmap_page,
|
||||
|
||||
.map_sg = arm_coherent_iommu_map_sg,
|
||||
.unmap_sg = arm_coherent_iommu_unmap_sg,
|
||||
};
|
||||
|
||||
/**
|
||||
* arm_iommu_create_mapping
|
||||
* @bus: pointer to the bus holding the client device (for IOMMU calls)
|
||||
@ -1665,7 +1815,7 @@ int arm_iommu_attach_device(struct device *dev,
|
||||
dev->archdata.mapping = mapping;
|
||||
set_dma_ops(dev, &iommu_ops);
|
||||
|
||||
pr_info("Attached IOMMU controller to %s device.\n", dev_name(dev));
|
||||
pr_debug("Attached IOMMU controller to %s device.\n", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -422,17 +422,6 @@ static void __init build_mem_type_table(void)
|
||||
cp = &cache_policies[cachepolicy];
|
||||
vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
|
||||
|
||||
/*
|
||||
* Enable CPU-specific coherency if supported.
|
||||
* (Only available on XSC3 at the moment.)
|
||||
*/
|
||||
if (arch_is_coherent() && cpu_is_xsc3()) {
|
||||
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S;
|
||||
mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED;
|
||||
mem_types[MT_MEMORY_DMA_READY].prot_pte |= L_PTE_SHARED;
|
||||
mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S;
|
||||
mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED;
|
||||
}
|
||||
/*
|
||||
* ARMv6 and above have extended page tables.
|
||||
*/
|
||||
|
@ -315,6 +315,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count,
|
||||
{
|
||||
unsigned long mask, pfn, pageno, start = 0;
|
||||
struct cma *cma = dev_get_cma_area(dev);
|
||||
struct page *page = NULL;
|
||||
int ret;
|
||||
|
||||
if (!cma || !cma->count)
|
||||
@ -336,18 +337,17 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count,
|
||||
for (;;) {
|
||||
pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
|
||||
start, count, mask);
|
||||
if (pageno >= cma->count) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
if (pageno >= cma->count)
|
||||
break;
|
||||
|
||||
pfn = cma->base_pfn + pageno;
|
||||
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
|
||||
if (ret == 0) {
|
||||
bitmap_set(cma->bitmap, pageno, count);
|
||||
page = pfn_to_page(pfn);
|
||||
break;
|
||||
} else if (ret != -EBUSY) {
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
pr_debug("%s(): memory range at %p is busy, retrying\n",
|
||||
__func__, pfn_to_page(pfn));
|
||||
@ -356,12 +356,8 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count,
|
||||
}
|
||||
|
||||
mutex_unlock(&cma_mutex);
|
||||
|
||||
pr_debug("%s(): returned %p\n", __func__, pfn_to_page(pfn));
|
||||
return pfn_to_page(pfn);
|
||||
error:
|
||||
mutex_unlock(&cma_mutex);
|
||||
return NULL;
|
||||
pr_debug("%s(): returned %p\n", __func__, page);
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user