| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * arch/sh/mm/pg-dma.c | 
|  | 3 | * | 
|  | 4 | * Fast clear_page()/copy_page() implementation using the SH DMAC | 
|  | 5 | * | 
|  | 6 | * Copyright (C) 2003  Paul Mundt | 
|  | 7 | * | 
|  | 8 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 9 | * License.  See the file "COPYING" in the main directory of this archive | 
|  | 10 | * for more details. | 
|  | 11 | */ | 
|  | 12 | #include <linux/init.h> | 
|  | 13 | #include <linux/kernel.h> | 
|  | 14 | #include <linux/module.h> | 
|  | 15 | #include <asm/semaphore.h> | 
|  | 16 | #include <asm/mmu_context.h> | 
|  | 17 | #include <asm/addrspace.h> | 
|  | 18 | #include <asm/atomic.h> | 
|  | 19 | #include <asm/page.h> | 
|  | 20 | #include <asm/dma.h> | 
|  | 21 | #include <asm/io.h> | 
|  | 22 |  | 
|  | 23 | /* Channel to use for page ops, must be dual-address mode capable. */ | 
|  | 24 | static int dma_channel = CONFIG_DMA_PAGE_OPS_CHANNEL; | 
|  | 25 |  | 
|  | 26 | static void copy_page_dma(void *to, void *from) | 
|  | 27 | { | 
|  | 28 | /* | 
|  | 29 | * This doesn't seem to get triggered until further along in the | 
|  | 30 | * boot process, at which point the DMAC is already initialized. | 
|  | 31 | * Fix this in the same fashion as clear_page_dma() in the event | 
|  | 32 | * that this crashes due to the DMAC not being initialized. | 
|  | 33 | */ | 
|  | 34 |  | 
|  | 35 | flush_icache_range((unsigned long)from, PAGE_SIZE); | 
|  | 36 | dma_write_page(dma_channel, (unsigned long)from, (unsigned long)to); | 
|  | 37 | dma_wait_for_completion(dma_channel); | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | static void clear_page_dma(void *to) | 
|  | 41 | { | 
|  | 42 | extern unsigned long empty_zero_page[1024]; | 
|  | 43 |  | 
|  | 44 | /* | 
|  | 45 | * We get invoked quite early on, if the DMAC hasn't been initialized | 
|  | 46 | * yet, fall back on the slow manual implementation. | 
|  | 47 | */ | 
|  | 48 | if (dma_info[dma_channel].chan != dma_channel) { | 
|  | 49 | clear_page_slow(to); | 
|  | 50 | return; | 
|  | 51 | } | 
|  | 52 |  | 
|  | 53 | dma_write_page(dma_channel, (unsigned long)empty_zero_page, | 
|  | 54 | (unsigned long)to); | 
|  | 55 |  | 
|  | 56 | /* | 
|  | 57 | * FIXME: Something is a bit racy here, if we poll the counter right | 
|  | 58 | * away, we seem to lock. flushing the page from the dcache doesn't | 
|  | 59 | * seem to make a difference one way or the other, though either a full | 
|  | 60 | * icache or dcache flush does. | 
|  | 61 | * | 
|  | 62 | * The location of this is important as well, and must happen prior to | 
|  | 63 | * the completion loop but after the transfer was initiated. | 
|  | 64 | * | 
|  | 65 | * Oddly enough, this doesn't appear to be an issue for copy_page().. | 
|  | 66 | */ | 
|  | 67 | flush_icache_range((unsigned long)to, PAGE_SIZE); | 
|  | 68 |  | 
|  | 69 | dma_wait_for_completion(dma_channel); | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | static int __init pg_dma_init(void) | 
|  | 73 | { | 
|  | 74 | int ret; | 
|  | 75 |  | 
|  | 76 | ret = request_dma(dma_channel, "page ops"); | 
|  | 77 | if (ret != 0) | 
|  | 78 | return ret; | 
|  | 79 |  | 
|  | 80 | copy_page = copy_page_dma; | 
|  | 81 | clear_page = clear_page_dma; | 
|  | 82 |  | 
|  | 83 | return ret; | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | static void __exit pg_dma_exit(void) | 
|  | 87 | { | 
|  | 88 | free_dma(dma_channel); | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 | module_init(pg_dma_init); | 
|  | 92 | module_exit(pg_dma_exit); | 
|  | 93 |  | 
|  | 94 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | 
|  | 95 | MODULE_DESCRIPTION("Optimized page copy/clear routines using a dual-address mode capable DMAC channel"); | 
|  | 96 | MODULE_LICENSE("GPL"); | 
|  | 97 |  |