root/drivers/staging/comedi/drivers/comedi_isadma.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. comedi_isadma_program
  2. comedi_isadma_disable
  3. comedi_isadma_disable_on_sample
  4. comedi_isadma_poll
  5. comedi_isadma_set_mode
  6. comedi_isadma_alloc
  7. comedi_isadma_free
  8. comedi_isadma_init
  9. comedi_isadma_exit

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * COMEDI ISA DMA support functions
   4  * Copyright (c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
   5  */
   6 
   7 #include <linux/module.h>
   8 #include <linux/slab.h>
   9 #include <linux/delay.h>
  10 #include <linux/dma-mapping.h>
  11 #include <asm/dma.h>
  12 
  13 #include "../comedidev.h"
  14 
  15 #include "comedi_isadma.h"
  16 
  17 /**
  18  * comedi_isadma_program - program and enable an ISA DMA transfer
  19  * @desc:       the ISA DMA cookie to program and enable
  20  */
  21 void comedi_isadma_program(struct comedi_isadma_desc *desc)
  22 {
  23         unsigned long flags;
  24 
  25         flags = claim_dma_lock();
  26         clear_dma_ff(desc->chan);
  27         set_dma_mode(desc->chan, desc->mode);
  28         set_dma_addr(desc->chan, desc->hw_addr);
  29         set_dma_count(desc->chan, desc->size);
  30         enable_dma(desc->chan);
  31         release_dma_lock(flags);
  32 }
  33 EXPORT_SYMBOL_GPL(comedi_isadma_program);
  34 
  35 /**
  36  * comedi_isadma_disable - disable the ISA DMA channel
  37  * @dma_chan:   the DMA channel to disable
  38  *
  39  * Returns the residue (remaining bytes) left in the DMA transfer.
  40  */
  41 unsigned int comedi_isadma_disable(unsigned int dma_chan)
  42 {
  43         unsigned long flags;
  44         unsigned int residue;
  45 
  46         flags = claim_dma_lock();
  47         disable_dma(dma_chan);
  48         residue = get_dma_residue(dma_chan);
  49         release_dma_lock(flags);
  50 
  51         return residue;
  52 }
  53 EXPORT_SYMBOL_GPL(comedi_isadma_disable);
  54 
  55 /**
  56  * comedi_isadma_disable_on_sample - disable the ISA DMA channel
  57  * @dma_chan:   the DMA channel to disable
  58  * @size:       the sample size (in bytes)
  59  *
  60  * Returns the residue (remaining bytes) left in the DMA transfer.
  61  */
  62 unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan,
  63                                              unsigned int size)
  64 {
  65         int stalled = 0;
  66         unsigned long flags;
  67         unsigned int residue;
  68         unsigned int new_residue;
  69 
  70         residue = comedi_isadma_disable(dma_chan);
  71         while (residue % size) {
  72                 /* residue is a partial sample, enable DMA to allow more data */
  73                 flags = claim_dma_lock();
  74                 enable_dma(dma_chan);
  75                 release_dma_lock(flags);
  76 
  77                 udelay(2);
  78                 new_residue = comedi_isadma_disable(dma_chan);
  79 
  80                 /* is DMA stalled? */
  81                 if (new_residue == residue) {
  82                         stalled++;
  83                         if (stalled > 10)
  84                                 break;
  85                 } else {
  86                         residue = new_residue;
  87                         stalled = 0;
  88                 }
  89         }
  90         return residue;
  91 }
  92 EXPORT_SYMBOL_GPL(comedi_isadma_disable_on_sample);
  93 
  94 /**
  95  * comedi_isadma_poll - poll the current DMA transfer
  96  * @dma:        the ISA DMA to poll
  97  *
  98  * Returns the position (in bytes) of the current DMA transfer.
  99  */
 100 unsigned int comedi_isadma_poll(struct comedi_isadma *dma)
 101 {
 102         struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 103         unsigned long flags;
 104         unsigned int result;
 105         unsigned int result1;
 106 
 107         flags = claim_dma_lock();
 108         clear_dma_ff(desc->chan);
 109         if (!isa_dma_bridge_buggy)
 110                 disable_dma(desc->chan);
 111         result = get_dma_residue(desc->chan);
 112         /*
 113          * Read the counter again and choose higher value in order to
 114          * avoid reading during counter lower byte roll over if the
 115          * isa_dma_bridge_buggy is set.
 116          */
 117         result1 = get_dma_residue(desc->chan);
 118         if (!isa_dma_bridge_buggy)
 119                 enable_dma(desc->chan);
 120         release_dma_lock(flags);
 121 
 122         if (result < result1)
 123                 result = result1;
 124         if (result >= desc->size || result == 0)
 125                 return 0;
 126         return desc->size - result;
 127 }
 128 EXPORT_SYMBOL_GPL(comedi_isadma_poll);
 129 
 130 /**
 131  * comedi_isadma_set_mode - set the ISA DMA transfer direction
 132  * @desc:       the ISA DMA cookie to set
 133  * @dma_dir:    the DMA direction
 134  */
 135 void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, char dma_dir)
 136 {
 137         desc->mode = (dma_dir == COMEDI_ISADMA_READ) ? DMA_MODE_READ
 138                                                      : DMA_MODE_WRITE;
 139 }
 140 EXPORT_SYMBOL_GPL(comedi_isadma_set_mode);
 141 
 142 /**
 143  * comedi_isadma_alloc - allocate and initialize the ISA DMA
 144  * @dev:        comedi_device struct
 145  * @n_desc:     the number of cookies to allocate
 146  * @dma_chan:   DMA channel for the first cookie
 147  * @dma_chan2:  DMA channel for the second cookie
 148  * @maxsize:    the size of the buffer to allocate for each cookie
 149  * @dma_dir:    the DMA direction
 150  *
 151  * Returns the allocated and initialized ISA DMA or NULL if anything fails.
 152  */
 153 struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev,
 154                                           int n_desc, unsigned int dma_chan1,
 155                                           unsigned int dma_chan2,
 156                                           unsigned int maxsize, char dma_dir)
 157 {
 158         struct comedi_isadma *dma = NULL;
 159         struct comedi_isadma_desc *desc;
 160         unsigned int dma_chans[2];
 161         int i;
 162 
 163         if (n_desc < 1 || n_desc > 2)
 164                 goto no_dma;
 165 
 166         dma = kzalloc(sizeof(*dma), GFP_KERNEL);
 167         if (!dma)
 168                 goto no_dma;
 169 
 170         desc = kcalloc(n_desc, sizeof(*desc), GFP_KERNEL);
 171         if (!desc)
 172                 goto no_dma;
 173         dma->desc = desc;
 174         dma->n_desc = n_desc;
 175         if (dev->hw_dev) {
 176                 dma->dev = dev->hw_dev;
 177         } else {
 178                 /* Fall back to using the "class" device. */
 179                 if (!dev->class_dev)
 180                         goto no_dma;
 181                 /* Need 24-bit mask for ISA DMA. */
 182                 if (dma_coerce_mask_and_coherent(dev->class_dev,
 183                                                  DMA_BIT_MASK(24))) {
 184                         goto no_dma;
 185                 }
 186                 dma->dev = dev->class_dev;
 187         }
 188 
 189         dma_chans[0] = dma_chan1;
 190         if (dma_chan2 == 0 || dma_chan2 == dma_chan1)
 191                 dma_chans[1] = dma_chan1;
 192         else
 193                 dma_chans[1] = dma_chan2;
 194 
 195         if (request_dma(dma_chans[0], dev->board_name))
 196                 goto no_dma;
 197         dma->chan = dma_chans[0];
 198         if (dma_chans[1] != dma_chans[0]) {
 199                 if (request_dma(dma_chans[1], dev->board_name))
 200                         goto no_dma;
 201         }
 202         dma->chan2 = dma_chans[1];
 203 
 204         for (i = 0; i < n_desc; i++) {
 205                 desc = &dma->desc[i];
 206                 desc->chan = dma_chans[i];
 207                 desc->maxsize = maxsize;
 208                 desc->virt_addr = dma_alloc_coherent(dma->dev, desc->maxsize,
 209                                                      &desc->hw_addr,
 210                                                      GFP_KERNEL);
 211                 if (!desc->virt_addr)
 212                         goto no_dma;
 213                 comedi_isadma_set_mode(desc, dma_dir);
 214         }
 215 
 216         return dma;
 217 
 218 no_dma:
 219         comedi_isadma_free(dma);
 220         return NULL;
 221 }
 222 EXPORT_SYMBOL_GPL(comedi_isadma_alloc);
 223 
 224 /**
 225  * comedi_isadma_free - free the ISA DMA
 226  * @dma:        the ISA DMA to free
 227  */
 228 void comedi_isadma_free(struct comedi_isadma *dma)
 229 {
 230         struct comedi_isadma_desc *desc;
 231         int i;
 232 
 233         if (!dma)
 234                 return;
 235 
 236         if (dma->desc) {
 237                 for (i = 0; i < dma->n_desc; i++) {
 238                         desc = &dma->desc[i];
 239                         if (desc->virt_addr)
 240                                 dma_free_coherent(dma->dev, desc->maxsize,
 241                                                   desc->virt_addr,
 242                                                   desc->hw_addr);
 243                 }
 244                 kfree(dma->desc);
 245         }
 246         if (dma->chan2 && dma->chan2 != dma->chan)
 247                 free_dma(dma->chan2);
 248         if (dma->chan)
 249                 free_dma(dma->chan);
 250         kfree(dma);
 251 }
 252 EXPORT_SYMBOL_GPL(comedi_isadma_free);
 253 
 254 static int __init comedi_isadma_init(void)
 255 {
 256         return 0;
 257 }
 258 module_init(comedi_isadma_init);
 259 
 260 static void __exit comedi_isadma_exit(void)
 261 {
 262 }
 263 module_exit(comedi_isadma_exit);
 264 
 265 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
 266 MODULE_DESCRIPTION("Comedi ISA DMA support");
 267 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */