summaryrefslogtreecommitdiff
path: root/arch/sh/mm/consistent.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/mm/consistent.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/sh/mm/consistent.c')
-rw-r--r--arch/sh/mm/consistent.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
new file mode 100644
index 000000000000..1f7af0c73cf4
--- /dev/null
+++ b/arch/sh/mm/consistent.c
@@ -0,0 +1,85 @@
+/*
+ * arch/sh/mm/consistent.c
+ *
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <asm/io.h>
+
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *handle)
+{
+ struct page *page, *end, *free;
+ void *ret;
+ int order;
+
+ size = PAGE_ALIGN(size);
+ order = get_order(size);
+
+ page = alloc_pages(gfp, order);
+ if (!page)
+ return NULL;
+
+ ret = page_address(page);
+ *handle = virt_to_phys(ret);
+
+ /*
+ * We must flush the cache before we pass it on to the device
+ */
+ dma_cache_wback_inv(ret, size);
+
+ page = virt_to_page(ret);
+ free = page + (size >> PAGE_SHIFT);
+ end = page + (1 << order);
+
+ while (++page < end) {
+ set_page_count(page, 1);
+
+ /* Free any unused pages */
+ if (page >= free) {
+ __free_page(page);
+ }
+ }
+
+ return P2SEGADDR(ret);
+}
+
+void consistent_free(void *vaddr, size_t size)
+{
+ unsigned long addr = P1SEGADDR((unsigned long)vaddr);
+ struct page *page=virt_to_page(addr);
+ int num_pages=(size+PAGE_SIZE-1) >> PAGE_SHIFT;
+ int i;
+
+ for(i=0;i<num_pages;i++) {
+ __free_page((page+i));
+ }
+}
+
+void consistent_sync(void *vaddr, size_t size, int direction)
+{
+ void * p1addr = (void*) P1SEGADDR((unsigned long)vaddr);
+
+ switch (direction) {
+ case DMA_FROM_DEVICE: /* invalidate only */
+ dma_cache_inv(p1addr, size);
+ break;
+ case DMA_TO_DEVICE: /* writeback only */
+ dma_cache_wback(p1addr, size);
+ break;
+ case DMA_BIDIRECTIONAL: /* writeback and invalidate */
+ dma_cache_wback_inv(p1addr, size);
+ break;
+ default:
+ BUG();
+ }
+}
+
+EXPORT_SYMBOL(consistent_alloc);
+EXPORT_SYMBOL(consistent_free);
+EXPORT_SYMBOL(consistent_sync);
+