1 /* 2 * linux/arch/arm/kernel/dma.c 3 * 4 * Copyright (C) 1995-2000 Russell King 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Front-end to the DMA handling. This handles the allocation/freeing 11 * of DMA channels, and provides a unified interface to the machines 12 * DMA facilities. 13 */ 14 #include <linux/module.h> 15 #include <linux/init.h> 16 #include <linux/spinlock.h> 17 #include <linux/errno.h> 18 19 #include <asm/dma.h> 20 21 #include <asm/mach/dma.h> 22 23 DEFINE_SPINLOCK(dma_spin_lock); 24 EXPORT_SYMBOL(dma_spin_lock); 25 26 static dma_t dma_chan[MAX_DMA_CHANNELS]; 27 28 /* 29 * Request DMA channel 30 * 31 * On certain platforms, we have to allocate an interrupt as well... 32 */ 33 int request_dma(dmach_t channel, const char *device_id) 34 { 35 dma_t *dma = dma_chan + channel; 36 int ret; 37 38 if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) 39 goto bad_dma; 40 41 if (xchg(&dma->lock, 1) != 0) 42 goto busy; 43 44 dma->device_id = device_id; 45 dma->active = 0; 46 dma->invalid = 1; 47 48 ret = 0; 49 if (dma->d_ops->request) 50 ret = dma->d_ops->request(channel, dma); 51 52 if (ret) 53 xchg(&dma->lock, 0); 54 55 return ret; 56 57 bad_dma: 58 printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel); 59 return -EINVAL; 60 61 busy: 62 return -EBUSY; 63 } 64 EXPORT_SYMBOL(request_dma); 65 66 /* 67 * Free DMA channel 68 * 69 * On certain platforms, we have to free interrupt as well... 70 */ 71 void free_dma(dmach_t channel) 72 { 73 dma_t *dma = dma_chan + channel; 74 75 if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) 76 goto bad_dma; 77 78 if (dma->active) { 79 printk(KERN_ERR "dma%d: freeing active DMA\n", channel); 80 dma->d_ops->disable(channel, dma); 81 dma->active = 0; 82 } 83 84 if (xchg(&dma->lock, 0) != 0) { 85 if (dma->d_ops->free) 86 dma->d_ops->free(channel, dma); 87 return; 88 } 89 90 printk(KERN_ERR "dma%d: trying to free free DMA\n", channel); 91 return; 92 93 bad_dma: 94 printk(KERN_ERR "dma: trying to free DMA%d\n", channel); 95 } 96 EXPORT_SYMBOL(free_dma); 97 98 /* Set DMA Scatter-Gather list 99 */ 100 void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg) 101 { 102 dma_t *dma = dma_chan + channel; 103 104 if (dma->active) 105 printk(KERN_ERR "dma%d: altering DMA SG while " 106 "DMA active\n", channel); 107 108 dma->sg = sg; 109 dma->sgcount = nr_sg; 110 dma->invalid = 1; 111 } 112 EXPORT_SYMBOL(set_dma_sg); 113 114 /* Set DMA address 115 * 116 * Copy address to the structure, and set the invalid bit 117 */ 118 void __set_dma_addr (dmach_t channel, void *addr) 119 { 120 dma_t *dma = dma_chan + channel; 121 122 if (dma->active) 123 printk(KERN_ERR "dma%d: altering DMA address while " 124 "DMA active\n", channel); 125 126 dma->sg = NULL; 127 dma->addr = addr; 128 dma->invalid = 1; 129 } 130 EXPORT_SYMBOL(__set_dma_addr); 131 132 /* Set DMA byte count 133 * 134 * Copy address to the structure, and set the invalid bit 135 */ 136 void set_dma_count (dmach_t channel, unsigned long count) 137 { 138 dma_t *dma = dma_chan + channel; 139 140 if (dma->active) 141 printk(KERN_ERR "dma%d: altering DMA count while " 142 "DMA active\n", channel); 143 144 dma->sg = NULL; 145 dma->count = count; 146 dma->invalid = 1; 147 } 148 EXPORT_SYMBOL(set_dma_count); 149 150 /* Set DMA direction mode 151 */ 152 void set_dma_mode (dmach_t channel, dmamode_t mode) 153 { 154 dma_t *dma = dma_chan + channel; 155 156 if (dma->active) 157 printk(KERN_ERR "dma%d: altering DMA mode while " 158 "DMA active\n", channel); 159 160 dma->dma_mode = mode; 161 dma->invalid = 1; 162 } 163 EXPORT_SYMBOL(set_dma_mode); 164 165 /* Enable DMA channel 166 */ 167 void enable_dma (dmach_t channel) 168 { 169 dma_t *dma = dma_chan + channel; 170 171 if (!dma->lock) 172 goto free_dma; 173 174 if (dma->active == 0) { 175 dma->active = 1; 176 dma->d_ops->enable(channel, dma); 177 } 178 return; 179 180 free_dma: 181 printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel); 182 BUG(); 183 } 184 EXPORT_SYMBOL(enable_dma); 185 186 /* Disable DMA channel 187 */ 188 void disable_dma (dmach_t channel) 189 { 190 dma_t *dma = dma_chan + channel; 191 192 if (!dma->lock) 193 goto free_dma; 194 195 if (dma->active == 1) { 196 dma->active = 0; 197 dma->d_ops->disable(channel, dma); 198 } 199 return; 200 201 free_dma: 202 printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel); 203 BUG(); 204 } 205 EXPORT_SYMBOL(disable_dma); 206 207 /* 208 * Is the specified DMA channel active? 209 */ 210 int dma_channel_active(dmach_t channel) 211 { 212 return dma_chan[channel].active; 213 } 214 EXPORT_SYMBOL(dma_channel_active); 215 216 void set_dma_page(dmach_t channel, char pagenr) 217 { 218 printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel); 219 } 220 EXPORT_SYMBOL(set_dma_page); 221 222 void set_dma_speed(dmach_t channel, int cycle_ns) 223 { 224 dma_t *dma = dma_chan + channel; 225 int ret = 0; 226 227 if (dma->d_ops->setspeed) 228 ret = dma->d_ops->setspeed(channel, dma, cycle_ns); 229 dma->speed = ret; 230 } 231 EXPORT_SYMBOL(set_dma_speed); 232 233 int get_dma_residue(dmach_t channel) 234 { 235 dma_t *dma = dma_chan + channel; 236 int ret = 0; 237 238 if (dma->d_ops->residue) 239 ret = dma->d_ops->residue(channel, dma); 240 241 return ret; 242 } 243 EXPORT_SYMBOL(get_dma_residue); 244 245 static int __init init_dma(void) 246 { 247 arch_dma_init(dma_chan); 248 return 0; 249 } 250 251 core_initcall(init_dma); 252