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