1/* pci-dma-nommu.c: Dynamic DMA mapping support for the FRV 2 * 3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. 4 * Written by David Woodhouse (dwmw2@infradead.org) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12#include <linux/types.h> 13#include <linux/slab.h> 14#include <linux/dma-mapping.h> 15#include <linux/list.h> 16#include <linux/pci.h> 17#include <asm/io.h> 18 19#define DMA_SRAM_START dma_coherent_mem_start 20#define DMA_SRAM_END dma_coherent_mem_end 21 22struct dma_alloc_record { 23 struct list_head list; 24 unsigned long ofs; 25 unsigned long len; 26}; 27 28static DEFINE_SPINLOCK(dma_alloc_lock); 29static LIST_HEAD(dma_alloc_list); 30 31void *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) 32{ 33 struct dma_alloc_record *new; 34 struct list_head *this = &dma_alloc_list; 35 unsigned long flags; 36 unsigned long start = DMA_SRAM_START; 37 unsigned long end; 38 39 if (!DMA_SRAM_START) { 40 printk("%s called without any DMA area reserved!\n", __func__); 41 return NULL; 42 } 43 44 new = kmalloc(sizeof (*new), GFP_ATOMIC); 45 if (!new) 46 return NULL; 47 48 /* Round up to a reasonable alignment */ 49 new->len = (size + 31) & ~31; 50 51 spin_lock_irqsave(&dma_alloc_lock, flags); 52 53 list_for_each (this, &dma_alloc_list) { 54 struct dma_alloc_record *this_r = list_entry(this, struct dma_alloc_record, list); 55 end = this_r->ofs; 56 57 if (end - start >= size) 58 goto gotone; 59 60 start = this_r->ofs + this_r->len; 61 } 62 /* Reached end of list. */ 63 end = DMA_SRAM_END; 64 this = &dma_alloc_list; 65 66 if (end - start >= size) { 67 gotone: 68 new->ofs = start; 69 list_add_tail(&new->list, this); 70 spin_unlock_irqrestore(&dma_alloc_lock, flags); 71 72 *dma_handle = start; 73 return (void *)start; 74 } 75 76 kfree(new); 77 spin_unlock_irqrestore(&dma_alloc_lock, flags); 78 return NULL; 79} 80 81EXPORT_SYMBOL(dma_alloc_coherent); 82 83void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) 84{ 85 struct dma_alloc_record *rec; 86 unsigned long flags; 87 88 spin_lock_irqsave(&dma_alloc_lock, flags); 89 90 list_for_each_entry(rec, &dma_alloc_list, list) { 91 if (rec->ofs == dma_handle) { 92 list_del(&rec->list); 93 kfree(rec); 94 spin_unlock_irqrestore(&dma_alloc_lock, flags); 95 return; 96 } 97 } 98 spin_unlock_irqrestore(&dma_alloc_lock, flags); 99 BUG(); 100} 101 102EXPORT_SYMBOL(dma_free_coherent); 103 104dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, 105 enum dma_data_direction direction) 106{ 107 BUG_ON(direction == DMA_NONE); 108 109 frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size); 110 111 return virt_to_bus(ptr); 112} 113 114EXPORT_SYMBOL(dma_map_single); 115 116int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, 117 enum dma_data_direction direction) 118{ 119 int i; 120 121 for (i=0; i<nents; i++) 122 frv_cache_wback_inv(sg_dma_address(&sg[i]), 123 sg_dma_address(&sg[i]) + sg_dma_len(&sg[i])); 124 125 BUG_ON(direction == DMA_NONE); 126 127 return nents; 128} 129 130EXPORT_SYMBOL(dma_map_sg); 131 132dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, 133 size_t size, enum dma_data_direction direction) 134{ 135 BUG_ON(direction == DMA_NONE); 136 flush_dcache_page(page); 137 return (dma_addr_t) page_to_phys(page) + offset; 138} 139 140EXPORT_SYMBOL(dma_map_page); 141