linux/arch/arm/kernel/dma.c
<<
>>
Prefs
   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
 284