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@redhat.com) 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 104/* 105 * Map a single buffer of the indicated size for DMA in streaming mode. 106 * The 32-bit bus address to use is returned. 107 * 108 * Once the device is given the dma address, the device owns this memory 109 * until either pci_unmap_single or pci_dma_sync_single is performed. 110 */ 111dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, 112 enum dma_data_direction direction) 113{ 114 if (direction == DMA_NONE) 115 BUG(); 116 117 frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size); 118 119 return virt_to_bus(ptr); 120} 121 122EXPORT_SYMBOL(dma_map_single); 123 124/* 125 * Map a set of buffers described by scatterlist in streaming 126 * mode for DMA. This is the scather-gather version of the 127 * above pci_map_single interface. Here the scatter gather list 128 * elements are each tagged with the appropriate dma address 129 * and length. They are obtained via sg_dma_{address,length}(SG). 130 * 131 * NOTE: An implementation may be able to use a smaller number of 132 * DMA address/length pairs than there are SG table elements. 133 * (for example via virtual mapping capabilities) 134 * The routine returns the number of addr/length pairs actually 135 * used, at most nents. 136 * 137 * Device ownership issues as mentioned above for pci_map_single are 138 * the same here. 139 */ 140int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, 141 enum dma_data_direction direction) 142{ 143 int i; 144 145 for (i=0; i<nents; i++) 146 frv_cache_wback_inv(sg_dma_address(&sg[i]), 147 sg_dma_address(&sg[i]) + sg_dma_len(&sg[i])); 148 149 if (direction == DMA_NONE) 150 BUG(); 151 152 return nents; 153} 154 155EXPORT_SYMBOL(dma_map_sg); 156