summaryrefslogtreecommitdiff
path: root/arch/xtensa/kernel/pci-dma.c
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2015-05-25 06:55:05 +0300
committerMax Filippov <jcmvbkbc@gmail.com>2015-08-17 07:31:00 +0300
commitc75959a6da19df2700556dc76861442e9716317d (patch)
tree81ad55a5431fd510c25d518983c5ffb388ca2864 /arch/xtensa/kernel/pci-dma.c
parent4229fb12a03e5da5882b420b0aa4a02e77447b86 (diff)
xtensa: reimplement DMA API using common helpers
- keep existing functionality: don't handle attributes, don't support high memory; - implement scatterlist primitives (map/unmap/sync); - enable DMA API debug. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'arch/xtensa/kernel/pci-dma.c')
-rw-r--r--arch/xtensa/kernel/pci-dma.c203
1 files changed, 173 insertions, 30 deletions
diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
index e8b76b8e4b29..fb75ebf1463a 100644
--- a/arch/xtensa/kernel/pci-dma.c
+++ b/arch/xtensa/kernel/pci-dma.c
@@ -1,6 +1,4 @@
/*
- * arch/xtensa/kernel/pci-dma.c
- *
* DMA coherent memory allocation.
*
* This program is free software; you can redistribute it and/or modify it
@@ -9,6 +7,7 @@
* option) any later version.
*
* Copyright (C) 2002 - 2005 Tensilica Inc.
+ * Copyright (C) 2015 Cadence Design Systems Inc.
*
* Based on version for i386.
*
@@ -25,13 +24,107 @@
#include <asm/io.h>
#include <asm/cacheflush.h>
+void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+ enum dma_data_direction dir)
+{
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ __flush_invalidate_dcache_range((unsigned long)vaddr, size);
+ break;
+
+ case DMA_FROM_DEVICE:
+ __invalidate_dcache_range((unsigned long)vaddr, size);
+ break;
+
+ case DMA_TO_DEVICE:
+ __flush_dcache_range((unsigned long)vaddr, size);
+ break;
+
+ case DMA_NONE:
+ BUG();
+ break;
+ }
+}
+EXPORT_SYMBOL(dma_cache_sync);
+
+static void xtensa_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction dir)
+{
+ void *vaddr;
+
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_FROM_DEVICE:
+ vaddr = bus_to_virt(dma_handle);
+ __invalidate_dcache_range((unsigned long)vaddr, size);
+ break;
+
+ case DMA_NONE:
+ BUG();
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void xtensa_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction dir)
+{
+ void *vaddr;
+
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_TO_DEVICE:
+ vaddr = bus_to_virt(dma_handle);
+ __flush_dcache_range((unsigned long)vaddr, size);
+ break;
+
+ case DMA_NONE:
+ BUG();
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void xtensa_sync_sg_for_cpu(struct device *dev,
+ struct scatterlist *sg, int nents,
+ enum dma_data_direction dir)
+{
+ struct scatterlist *s;
+ int i;
+
+ for_each_sg(sg, s, nents, i) {
+ xtensa_sync_single_for_cpu(dev, sg_dma_address(s),
+ sg_dma_len(s), dir);
+ }
+}
+
+static void xtensa_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sg, int nents,
+ enum dma_data_direction dir)
+{
+ struct scatterlist *s;
+ int i;
+
+ for_each_sg(sg, s, nents, i) {
+ xtensa_sync_single_for_device(dev, sg_dma_address(s),
+ sg_dma_len(s), dir);
+ }
+}
+
/*
* Note: We assume that the full memory space is always mapped to 'kseg'
* Otherwise we have to use page attributes (not implemented).
*/
-void *
-dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *handle,gfp_t flag)
+static void *xtensa_dma_alloc(struct device *dev, size_t size,
+ dma_addr_t *handle, gfp_t flag,
+ struct dma_attrs *attrs)
{
unsigned long ret;
unsigned long uncached = 0;
@@ -52,20 +145,15 @@ dma_alloc_coherent(struct device *dev,size_t size,dma_addr_t *handle,gfp_t flag)
BUG_ON(ret < XCHAL_KSEG_CACHED_VADDR ||
ret > XCHAL_KSEG_CACHED_VADDR + XCHAL_KSEG_SIZE - 1);
+ uncached = ret + XCHAL_KSEG_BYPASS_VADDR - XCHAL_KSEG_CACHED_VADDR;
+ *handle = virt_to_bus((void *)ret);
+ __invalidate_dcache_range(ret, size);
- if (ret != 0) {
- memset((void*) ret, 0, size);
- uncached = ret+XCHAL_KSEG_BYPASS_VADDR-XCHAL_KSEG_CACHED_VADDR;
- *handle = virt_to_bus((void*)ret);
- __flush_invalidate_dcache_range(ret, size);
- }
-
- return (void*)uncached;
+ return (void *)uncached;
}
-EXPORT_SYMBOL(dma_alloc_coherent);
-void dma_free_coherent(struct device *hwdev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
+static void xtensa_dma_free(struct device *hwdev, size_t size, void *vaddr,
+ dma_addr_t dma_handle, struct dma_attrs *attrs)
{
unsigned long addr = (unsigned long)vaddr +
XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
@@ -75,24 +163,79 @@ void dma_free_coherent(struct device *hwdev, size_t size,
free_pages(addr, get_order(size));
}
-EXPORT_SYMBOL(dma_free_coherent);
+static dma_addr_t xtensa_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 dma_handle = page_to_phys(page) + offset;
+
+ BUG_ON(PageHighMem(page));
+ xtensa_sync_single_for_device(dev, dma_handle, size, dir);
+ return dma_handle;
+}
-void consistent_sync(void *vaddr, size_t size, int direction)
+static void xtensa_unmap_page(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
{
- switch (direction) {
- case PCI_DMA_NONE:
- BUG();
- case PCI_DMA_FROMDEVICE: /* invalidate only */
- __invalidate_dcache_range((unsigned long)vaddr,
- (unsigned long)size);
- break;
+ xtensa_sync_single_for_cpu(dev, dma_handle, size, dir);
+}
- case PCI_DMA_TODEVICE: /* writeback only */
- case PCI_DMA_BIDIRECTIONAL: /* writeback and invalidate */
- __flush_invalidate_dcache_range((unsigned long)vaddr,
- (unsigned long)size);
- break;
+static int xtensa_map_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) {
+ s->dma_address = xtensa_map_page(dev, sg_page(s), s->offset,
+ s->length, dir, attrs);
+ }
+ return nents;
+}
+
+static void xtensa_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) {
+ xtensa_unmap_page(dev, sg_dma_address(s),
+ sg_dma_len(s), dir, attrs);
}
}
-EXPORT_SYMBOL(consistent_sync);
+
+int xtensa_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+ return 0;
+}
+
+struct dma_map_ops xtensa_dma_map_ops = {
+ .alloc = xtensa_dma_alloc,
+ .free = xtensa_dma_free,
+ .map_page = xtensa_map_page,
+ .unmap_page = xtensa_unmap_page,
+ .map_sg = xtensa_map_sg,
+ .unmap_sg = xtensa_unmap_sg,
+ .sync_single_for_cpu = xtensa_sync_single_for_cpu,
+ .sync_single_for_device = xtensa_sync_single_for_device,
+ .sync_sg_for_cpu = xtensa_sync_sg_for_cpu,
+ .sync_sg_for_device = xtensa_sync_sg_for_device,
+ .mapping_error = xtensa_dma_mapping_error,
+};
+EXPORT_SYMBOL(xtensa_dma_map_ops);
+
+#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
+
+static int __init xtensa_dma_init(void)
+{
+ dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
+ return 0;
+}
+fs_initcall(xtensa_dma_init);