1/* 2 * File: arch/blackfin/kernel/dma-mapping.c 3 * Based on: 4 * Author: 5 * 6 * Created: 7 * Description: Dynamic DMA mapping support. 8 * 9 * Modified: 10 * Copyright 2004-2006 Analog Devices Inc. 11 * 12 * Bugs: Enter bugs at http://blackfin.uclinux.org/ 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation; either version 2 of the License, or 17 * (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, see the file COPYING, or write 26 * to the Free Software Foundation, Inc., 27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 28 */ 29 30#include <linux/types.h> 31#include <linux/mm.h> 32#include <linux/string.h> 33#include <linux/bootmem.h> 34#include <linux/spinlock.h> 35#include <linux/device.h> 36#include <linux/dma-mapping.h> 37#include <asm/cacheflush.h> 38#include <asm/io.h> 39#include <asm/bfin-global.h> 40 41static spinlock_t dma_page_lock; 42static unsigned int *dma_page; 43static unsigned int dma_pages; 44static unsigned long dma_base; 45static unsigned long dma_size; 46static unsigned int dma_initialized; 47 48void dma_alloc_init(unsigned long start, unsigned long end) 49{ 50 spin_lock_init(&dma_page_lock); 51 dma_initialized = 0; 52 53 dma_page = (unsigned int *)__get_free_page(GFP_KERNEL); 54 memset(dma_page, 0, PAGE_SIZE); 55 dma_base = PAGE_ALIGN(start); 56 dma_size = PAGE_ALIGN(end) - PAGE_ALIGN(start); 57 dma_pages = dma_size >> PAGE_SHIFT; 58 memset((void *)dma_base, 0, DMA_UNCACHED_REGION); 59 dma_initialized = 1; 60 61 printk(KERN_INFO "%s: dma_page @ 0x%p - %d pages at 0x%08lx\n", __FUNCTION__, 62 dma_page, dma_pages, dma_base); 63} 64 65static inline unsigned int get_pages(size_t size) 66{ 67 return ((size - 1) >> PAGE_SHIFT) + 1; 68} 69 70static unsigned long __alloc_dma_pages(unsigned int pages) 71{ 72 unsigned long ret = 0, flags; 73 int i, count = 0; 74 75 if (dma_initialized == 0) 76 dma_alloc_init(_ramend - DMA_UNCACHED_REGION, _ramend); 77 78 spin_lock_irqsave(&dma_page_lock, flags); 79 80 for (i = 0; i < dma_pages;) { 81 if (dma_page[i++] == 0) { 82 if (++count == pages) { 83 while (count--) 84 dma_page[--i] = 1; 85 ret = dma_base + (i << PAGE_SHIFT); 86 break; 87 } 88 } else 89 count = 0; 90 } 91 spin_unlock_irqrestore(&dma_page_lock, flags); 92 return ret; 93} 94 95static void __free_dma_pages(unsigned long addr, unsigned int pages) 96{ 97 unsigned long page = (addr - dma_base) >> PAGE_SHIFT; 98 unsigned long flags; 99 int i; 100 101 if ((page + pages) > dma_pages) { 102 printk(KERN_ERR "%s: freeing outside range.\n", __FUNCTION__); 103 BUG(); 104 } 105 106 spin_lock_irqsave(&dma_page_lock, flags); 107 for (i = page; i < page + pages; i++) { 108 dma_page[i] = 0; 109 } 110 spin_unlock_irqrestore(&dma_page_lock, flags); 111} 112 113void *dma_alloc_coherent(struct device *dev, size_t size, 114 dma_addr_t * dma_handle, gfp_t gfp) 115{ 116 void *ret; 117 118 ret = (void *)__alloc_dma_pages(get_pages(size)); 119 120 if (ret) { 121 memset(ret, 0, size); 122 *dma_handle = virt_to_phys(ret); 123 } 124 125 return ret; 126} 127EXPORT_SYMBOL(dma_alloc_coherent); 128 129void 130dma_free_coherent(struct device *dev, size_t size, void *vaddr, 131 dma_addr_t dma_handle) 132{ 133 __free_dma_pages((unsigned long)vaddr, get_pages(size)); 134} 135EXPORT_SYMBOL(dma_free_coherent); 136 137/* 138 * Dummy functions defined for some existing drivers 139 */ 140 141dma_addr_t 142dma_map_single(struct device *dev, void *ptr, size_t size, 143 enum dma_data_direction direction) 144{ 145 BUG_ON(direction == DMA_NONE); 146 147 invalidate_dcache_range((unsigned long)ptr, 148 (unsigned long)ptr + size); 149 150 return (dma_addr_t) ptr; 151} 152EXPORT_SYMBOL(dma_map_single); 153 154int 155dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, 156 enum dma_data_direction direction) 157{ 158 int i; 159 160 BUG_ON(direction == DMA_NONE); 161 162 for (i = 0; i < nents; i++) 163 invalidate_dcache_range(sg_dma_address(&sg[i]), 164 sg_dma_address(&sg[i]) + 165 sg_dma_len(&sg[i])); 166 167 return nents; 168} 169EXPORT_SYMBOL(dma_map_sg); 170 171void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, 172 enum dma_data_direction direction) 173{ 174 BUG_ON(direction == DMA_NONE); 175} 176EXPORT_SYMBOL(dma_unmap_single); 177 178void dma_unmap_sg(struct device *dev, struct scatterlist *sg, 179 int nhwentries, enum dma_data_direction direction) 180{ 181 BUG_ON(direction == DMA_NONE); 182} 183EXPORT_SYMBOL(dma_unmap_sg); 184