Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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