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 */ 19 void 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 } 31 EXPORT_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 */ 39 unsigned 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 } 51 EXPORT_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 */ 60 unsigned 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 } 90 EXPORT_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 */ 98 unsigned 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 } 126 EXPORT_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 */ 133 void 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 } 138 EXPORT_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 */ 151 struct 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 216 no_dma: 217 comedi_isadma_free(dma); 218 return NULL; 219 } 220 EXPORT_SYMBOL_GPL(comedi_isadma_alloc); 221 222 /** 223 * comedi_isadma_free - free the ISA DMA 224 * @dma: the ISA DMA to free 225 */ 226 void 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 } 250 EXPORT_SYMBOL_GPL(comedi_isadma_free); 251 252 static int __init comedi_isadma_init(void) 253 { 254 return 0; 255 } 256 module_init(comedi_isadma_init); 257 258 static void __exit comedi_isadma_exit(void) 259 { 260 } 261 module_exit(comedi_isadma_exit); 262 263 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 264 MODULE_DESCRIPTION("Comedi ISA DMA support"); 265 MODULE_LICENSE("GPL"); 266