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 * Get dma list for /proc/dma 30 */ 31 int get_dma_list(char *buf) 32 { 33 dma_t *dma; 34 char *p = buf; 35 int i; 36 37 for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++) 38 if (dma->lock) 39 p += sprintf(p, "%2d: %14s %s\n", i, 40 dma->d_ops->type, dma->device_id); 41 42 return p - buf; 43 } 44 45 /* 46 * Request DMA channel 47 * 48 * On certain platforms, we have to allocate an interrupt as well... 49 */ 50 int request_dma(dmach_t channel, const char *device_id) 51 { 52 dma_t *dma = dma_chan + channel; 53 int ret; 54 55 if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) 56 goto bad_dma; 57 58 if (xchg(&dma->lock, 1) != 0) 59 goto busy; 60 61 dma->device_id = device_id; 62 dma->active = 0; 63 dma->invalid = 1; 64 65 ret = 0; 66 if (dma->d_ops->request) 67 ret = dma->d_ops->request(channel, dma); 68 69 if (ret) 70 xchg(&dma->lock, 0); 71 72 return ret; 73 74 bad_dma: 75 printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel); 76 return -EINVAL; 77 78 busy: 79 return -EBUSY; 80 } 81 EXPORT_SYMBOL(request_dma); 82 83 /* 84 * Free DMA channel 85 * 86 * On certain platforms, we have to free interrupt as well... 87 */ 88 void free_dma(dmach_t channel) 89 { 90 dma_t *dma = dma_chan + channel; 91 92 if (channel >= MAX_DMA_CHANNELS || !dma->d_ops) 93 goto bad_dma; 94 95 if (dma->active) { 96 printk(KERN_ERR "dma%d: freeing active DMA\n", channel); 97 dma->d_ops->disable(channel, dma); 98 dma->active = 0; 99 } 100 101 if (xchg(&dma->lock, 0) != 0) { 102 if (dma->d_ops->free) 103 dma->d_ops->free(channel, dma); 104 return; 105 } 106 107 printk(KERN_ERR "dma%d: trying to free free DMA\n", channel); 108 return; 109 110 bad_dma: 111 printk(KERN_ERR "dma: trying to free DMA%d\n", channel); 112 } 113 EXPORT_SYMBOL(free_dma); 114 115 /* Set DMA Scatter-Gather list 116 */ 117 void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg) 118 { 119 dma_t *dma = dma_chan + channel; 120 121 if (dma->active) 122 printk(KERN_ERR "dma%d: altering DMA SG while " 123 "DMA active\n", channel); 124 125 dma->sg = sg; 126 dma->sgcount = nr_sg; 127 dma->invalid = 1; 128 } 129 EXPORT_SYMBOL(set_dma_sg); 130 131 /* Set DMA address 132 * 133 * Copy address to the structure, and set the invalid bit 134 */ 135 void __set_dma_addr (dmach_t channel, void *addr) 136 { 137 dma_t *dma = dma_chan + channel; 138 139 if (dma->active) 140 printk(KERN_ERR "dma%d: altering DMA address while " 141 "DMA active\n", channel); 142 143 dma->sg = NULL; 144 dma->addr = addr; 145 dma->invalid = 1; 146 } 147 EXPORT_SYMBOL(__set_dma_addr); 148 149 /* Set DMA byte count 150 * 151 * Copy address to the structure, and set the invalid bit 152 */ 153 void set_dma_count (dmach_t channel, unsigned long count) 154 { 155 dma_t *dma = dma_chan + channel; 156 157 if (dma->active) 158 printk(KERN_ERR "dma%d: altering DMA count while " 159 "DMA active\n", channel); 160 161 dma->sg = NULL; 162 dma->count = count; 163 dma->invalid = 1; 164 } 165 EXPORT_SYMBOL(set_dma_count); 166 167 /* Set DMA direction mode 168 */ 169 void set_dma_mode (dmach_t channel, dmamode_t mode) 170 { 171 dma_t *dma = dma_chan + channel; 172 173 if (dma->active) 174 printk(KERN_ERR "dma%d: altering DMA mode while " 175 "DMA active\n", channel); 176 177 dma->dma_mode = mode; 178 dma->invalid = 1; 179 } 180 EXPORT_SYMBOL(set_dma_mode); 181 182 /* Enable DMA channel 183 */ 184 void enable_dma (dmach_t channel) 185 { 186 dma_t *dma = dma_chan + channel; 187 188 if (!dma->lock) 189 goto free_dma; 190 191 if (dma->active == 0) { 192 dma->active = 1; 193 dma->d_ops->enable(channel, dma); 194 } 195 return; 196 197 free_dma: 198 printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel); 199 BUG(); 200 } 201 EXPORT_SYMBOL(enable_dma); 202 203 /* Disable DMA channel 204 */ 205 void disable_dma (dmach_t channel) 206 { 207 dma_t *dma = dma_chan + channel; 208 209 if (!dma->lock) 210 goto free_dma; 211 212 if (dma->active == 1) { 213 dma->active = 0; 214 dma->d_ops->disable(channel, dma); 215 } 216 return; 217 218 free_dma: 219 printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel); 220 BUG(); 221 } 222 EXPORT_SYMBOL(disable_dma); 223 224 /* 225 * Is the specified DMA channel active? 226 */ 227 int dma_channel_active(dmach_t channel) 228 { 229 return dma_chan[channel].active; 230 } 231 EXPORT_SYMBOL(dma_channel_active); 232 233 void set_dma_page(dmach_t channel, char pagenr) 234 { 235 printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel); 236 } 237 EXPORT_SYMBOL(set_dma_page); 238 239 void set_dma_speed(dmach_t channel, int cycle_ns) 240 { 241 dma_t *dma = dma_chan + channel; 242 int ret = 0; 243 244 if (dma->d_ops->setspeed) 245 ret = dma->d_ops->setspeed(channel, dma, cycle_ns); 246 dma->speed = ret; 247 } 248 EXPORT_SYMBOL(set_dma_speed); 249 250 int get_dma_residue(dmach_t channel) 251 { 252 dma_t *dma = dma_chan + channel; 253 int ret = 0; 254 255 if (dma->d_ops->residue) 256 ret = dma->d_ops->residue(channel, dma); 257 258 return ret; 259 } 260 EXPORT_SYMBOL(get_dma_residue); 261 262 static int __init init_dma(void) 263 { 264 arch_dma_init(dma_chan); 265 return 0; 266 } 267 268 core_initcall(init_dma); 269