linux/arch/arm/kernel/dma.c
<<
>>
Prefs
   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
  23DEFINE_SPINLOCK(dma_spin_lock);
  24EXPORT_SYMBOL(dma_spin_lock);
  25
  26static dma_t dma_chan[MAX_DMA_CHANNELS];
  27
  28/*
  29 * Get dma list for /proc/dma
  30 */
  31int 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 */
  50int 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
  74bad_dma:
  75        printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel);
  76        return -EINVAL;
  77
  78busy:
  79        return -EBUSY;
  80}
  81EXPORT_SYMBOL(request_dma);
  82
  83/*
  84 * Free DMA channel
  85 *
  86 * On certain platforms, we have to free interrupt as well...
  87 */
  88void 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
 110bad_dma:
 111        printk(KERN_ERR "dma: trying to free DMA%d\n", channel);
 112}
 113EXPORT_SYMBOL(free_dma);
 114
 115/* Set DMA Scatter-Gather list
 116 */
 117void 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}
 129EXPORT_SYMBOL(set_dma_sg);
 130
 131/* Set DMA address
 132 *
 133 * Copy address to the structure, and set the invalid bit
 134 */
 135void __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}
 147EXPORT_SYMBOL(__set_dma_addr);
 148
 149/* Set DMA byte count
 150 *
 151 * Copy address to the structure, and set the invalid bit
 152 */
 153void 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}
 165EXPORT_SYMBOL(set_dma_count);
 166
 167/* Set DMA direction mode
 168 */
 169void 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}
 180EXPORT_SYMBOL(set_dma_mode);
 181
 182/* Enable DMA channel
 183 */
 184void 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
 197free_dma:
 198        printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel);
 199        BUG();
 200}
 201EXPORT_SYMBOL(enable_dma);
 202
 203/* Disable DMA channel
 204 */
 205void 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
 218free_dma:
 219        printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel);
 220        BUG();
 221}
 222EXPORT_SYMBOL(disable_dma);
 223
 224/*
 225 * Is the specified DMA channel active?
 226 */
 227int dma_channel_active(dmach_t channel)
 228{
 229        return dma_chan[channel].active;
 230}
 231EXPORT_SYMBOL(dma_channel_active);
 232
 233void set_dma_page(dmach_t channel, char pagenr)
 234{
 235        printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel);
 236}
 237EXPORT_SYMBOL(set_dma_page);
 238
 239void 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}
 248EXPORT_SYMBOL(set_dma_speed);
 249
 250int 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}
 260EXPORT_SYMBOL(get_dma_residue);
 261
 262static int __init init_dma(void)
 263{
 264        arch_dma_init(dma_chan);
 265        return 0;
 266}
 267
 268core_initcall(init_dma);
 269