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