consistent.c 1.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11
/*
 * 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>
12 13
#include <asm/cacheflush.h>
#include <asm/addrspace.h>
L
Linus Torvalds 已提交
14 15
#include <asm/io.h>

A
Al Viro 已提交
16
void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *handle)
L
Linus Torvalds 已提交
17 18 19 20 21 22 23 24 25 26 27
{
	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;
N
Nick Piggin 已提交
28
	split_page(page, order);
L
Linus Torvalds 已提交
29 30

	ret = page_address(page);
31
	memset(ret, 0, size);
L
Linus Torvalds 已提交
32 33 34 35 36
	*handle = virt_to_phys(ret);

	/*
	 * We must flush the cache before we pass it on to the device
	 */
37
	__flush_purge_region(ret, size);
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

	page = virt_to_page(ret);
	free = page + (size >> PAGE_SHIFT);
	end  = page + (1 << order);

	while (++page < end) {
		/* 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 */
71
		__flush_invalidate_region(p1addr, size);
L
Linus Torvalds 已提交
72 73
		break;
	case DMA_TO_DEVICE:		/* writeback only */
74
		__flush_wback_region(p1addr, size);
L
Linus Torvalds 已提交
75 76
		break;
	case DMA_BIDIRECTIONAL:		/* writeback and invalidate */
77
		__flush_purge_region(p1addr, size);
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85 86 87
		break;
	default:
		BUG();
	}
}

EXPORT_SYMBOL(consistent_alloc);
EXPORT_SYMBOL(consistent_free);
EXPORT_SYMBOL(consistent_sync);