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