| /* |
| * 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); |
| |