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