|  | /* | 
|  | * arch/sh/mm/pg-dma.c | 
|  | * | 
|  | * Fast clear_page()/copy_page() implementation using the SH DMAC | 
|  | * | 
|  | * Copyright (C) 2003  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/init.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/module.h> | 
|  | #include <asm/semaphore.h> | 
|  | #include <asm/mmu_context.h> | 
|  | #include <asm/addrspace.h> | 
|  | #include <asm/atomic.h> | 
|  | #include <asm/page.h> | 
|  | #include <asm/dma.h> | 
|  | #include <asm/io.h> | 
|  |  | 
|  | /* Channel to use for page ops, must be dual-address mode capable. */ | 
|  | static int dma_channel = CONFIG_DMA_PAGE_OPS_CHANNEL; | 
|  |  | 
|  | static void copy_page_dma(void *to, void *from) | 
|  | { | 
|  | /* | 
|  | * This doesn't seem to get triggered until further along in the | 
|  | * boot process, at which point the DMAC is already initialized. | 
|  | * Fix this in the same fashion as clear_page_dma() in the event | 
|  | * that this crashes due to the DMAC not being initialized. | 
|  | */ | 
|  |  | 
|  | flush_icache_range((unsigned long)from, PAGE_SIZE); | 
|  | dma_write_page(dma_channel, (unsigned long)from, (unsigned long)to); | 
|  | dma_wait_for_completion(dma_channel); | 
|  | } | 
|  |  | 
|  | static void clear_page_dma(void *to) | 
|  | { | 
|  | /* | 
|  | * We get invoked quite early on, if the DMAC hasn't been initialized | 
|  | * yet, fall back on the slow manual implementation. | 
|  | */ | 
|  | if (dma_info[dma_channel].chan != dma_channel) { | 
|  | clear_page_slow(to); | 
|  | return; | 
|  | } | 
|  |  | 
|  | dma_write_page(dma_channel, (unsigned long)empty_zero_page, | 
|  | (unsigned long)to); | 
|  |  | 
|  | /* | 
|  | * FIXME: Something is a bit racy here, if we poll the counter right | 
|  | * away, we seem to lock. flushing the page from the dcache doesn't | 
|  | * seem to make a difference one way or the other, though either a full | 
|  | * icache or dcache flush does. | 
|  | * | 
|  | * The location of this is important as well, and must happen prior to | 
|  | * the completion loop but after the transfer was initiated. | 
|  | * | 
|  | * Oddly enough, this doesn't appear to be an issue for copy_page().. | 
|  | */ | 
|  | flush_icache_range((unsigned long)to, PAGE_SIZE); | 
|  |  | 
|  | dma_wait_for_completion(dma_channel); | 
|  | } | 
|  |  | 
|  | static int __init pg_dma_init(void) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = request_dma(dma_channel, "page ops"); | 
|  | if (ret != 0) | 
|  | return ret; | 
|  |  | 
|  | copy_page = copy_page_dma; | 
|  | clear_page = clear_page_dma; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void __exit pg_dma_exit(void) | 
|  | { | 
|  | free_dma(dma_channel); | 
|  | } | 
|  |  | 
|  | module_init(pg_dma_init); | 
|  | module_exit(pg_dma_exit); | 
|  |  | 
|  | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | 
|  | MODULE_DESCRIPTION("Optimized page copy/clear routines using a dual-address mode capable DMAC channel"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  |