1/* 2 * comedi/drivers/ni_labpc_isadma.c 3 * ISA DMA support for National Instruments Lab-PC series boards and 4 * compatibles. 5 * 6 * Extracted from ni_labpc.c: 7 * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 */ 19 20#include <linux/module.h> 21#include <linux/slab.h> 22#include "../comedidev.h" 23 24#include <asm/dma.h> 25 26#include "comedi_fc.h" 27#include "ni_labpc.h" 28#include "ni_labpc_regs.h" 29#include "ni_labpc_isadma.h" 30 31/* size in bytes of dma buffer */ 32static const int dma_buffer_size = 0xff00; 33/* 2 bytes per sample */ 34static const int sample_size = 2; 35 36/* utility function that suggests a dma transfer size in bytes */ 37static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd) 38{ 39 unsigned int size; 40 unsigned int freq; 41 42 if (cmd->convert_src == TRIG_TIMER) 43 freq = 1000000000 / cmd->convert_arg; 44 else 45 /* return some default value */ 46 freq = 0xffffffff; 47 48 /* make buffer fill in no more than 1/3 second */ 49 size = (freq / 3) * sample_size; 50 51 /* set a minimum and maximum size allowed */ 52 if (size > dma_buffer_size) 53 size = dma_buffer_size - dma_buffer_size % sample_size; 54 else if (size < sample_size) 55 size = sample_size; 56 57 return size; 58} 59 60void labpc_setup_dma(struct comedi_device *dev, struct comedi_subdevice *s) 61{ 62 struct labpc_private *devpriv = dev->private; 63 struct comedi_cmd *cmd = &s->async->cmd; 64 unsigned long irq_flags; 65 66 irq_flags = claim_dma_lock(); 67 disable_dma(devpriv->dma_chan); 68 /* clear flip-flop to make sure 2-byte registers for 69 * count and address get set correctly */ 70 clear_dma_ff(devpriv->dma_chan); 71 set_dma_addr(devpriv->dma_chan, devpriv->dma_addr); 72 /* set appropriate size of transfer */ 73 devpriv->dma_transfer_size = labpc_suggest_transfer_size(cmd); 74 if (cmd->stop_src == TRIG_COUNT && 75 devpriv->count * sample_size < devpriv->dma_transfer_size) 76 devpriv->dma_transfer_size = devpriv->count * sample_size; 77 set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); 78 enable_dma(devpriv->dma_chan); 79 release_dma_lock(irq_flags); 80 /* set CMD3 bits for caller to enable DMA and interrupt */ 81 devpriv->cmd3 |= (CMD3_DMAEN | CMD3_DMATCINTEN); 82} 83EXPORT_SYMBOL_GPL(labpc_setup_dma); 84 85void labpc_drain_dma(struct comedi_device *dev) 86{ 87 struct labpc_private *devpriv = dev->private; 88 struct comedi_subdevice *s = dev->read_subdev; 89 struct comedi_async *async = s->async; 90 struct comedi_cmd *cmd = &async->cmd; 91 int status; 92 unsigned long flags; 93 unsigned int max_points, num_points, residue, leftover; 94 int i; 95 96 status = devpriv->stat1; 97 98 flags = claim_dma_lock(); 99 disable_dma(devpriv->dma_chan); 100 /* clear flip-flop to make sure 2-byte registers for 101 * count and address get set correctly */ 102 clear_dma_ff(devpriv->dma_chan); 103 104 /* figure out how many points to read */ 105 max_points = devpriv->dma_transfer_size / sample_size; 106 /* residue is the number of points left to be done on the dma 107 * transfer. It should always be zero at this point unless 108 * the stop_src is set to external triggering. 109 */ 110 residue = get_dma_residue(devpriv->dma_chan) / sample_size; 111 num_points = max_points - residue; 112 if (cmd->stop_src == TRIG_COUNT && devpriv->count < num_points) 113 num_points = devpriv->count; 114 115 /* figure out how many points will be stored next time */ 116 leftover = 0; 117 if (cmd->stop_src != TRIG_COUNT) { 118 leftover = devpriv->dma_transfer_size / sample_size; 119 } else if (devpriv->count > num_points) { 120 leftover = devpriv->count - num_points; 121 if (leftover > max_points) 122 leftover = max_points; 123 } 124 125 /* write data to comedi buffer */ 126 for (i = 0; i < num_points; i++) 127 cfc_write_to_buffer(s, devpriv->dma_buffer[i]); 128 129 if (cmd->stop_src == TRIG_COUNT) 130 devpriv->count -= num_points; 131 132 /* set address and count for next transfer */ 133 set_dma_addr(devpriv->dma_chan, devpriv->dma_addr); 134 set_dma_count(devpriv->dma_chan, leftover * sample_size); 135 release_dma_lock(flags); 136 137 async->events |= COMEDI_CB_BLOCK; 138} 139EXPORT_SYMBOL_GPL(labpc_drain_dma); 140 141static void handle_isa_dma(struct comedi_device *dev) 142{ 143 struct labpc_private *devpriv = dev->private; 144 145 labpc_drain_dma(dev); 146 147 enable_dma(devpriv->dma_chan); 148 149 /* clear dma tc interrupt */ 150 devpriv->write_byte(dev, 0x1, DMATC_CLEAR_REG); 151} 152 153void labpc_handle_dma_status(struct comedi_device *dev) 154{ 155 const struct labpc_boardinfo *board = dev->board_ptr; 156 struct labpc_private *devpriv = dev->private; 157 158 /* 159 * if a dma terminal count of external stop trigger 160 * has occurred 161 */ 162 if (devpriv->stat1 & STAT1_GATA0 || 163 (board->is_labpc1200 && devpriv->stat2 & STAT2_OUTA1)) 164 handle_isa_dma(dev); 165} 166EXPORT_SYMBOL_GPL(labpc_handle_dma_status); 167 168int labpc_init_dma_chan(struct comedi_device *dev, unsigned int dma_chan) 169{ 170 struct labpc_private *devpriv = dev->private; 171 void *dma_buffer; 172 unsigned long dma_flags; 173 int ret; 174 175 if (dma_chan != 1 && dma_chan != 3) 176 return -EINVAL; 177 178 dma_buffer = kmalloc(dma_buffer_size, GFP_KERNEL | GFP_DMA); 179 if (!dma_buffer) 180 return -ENOMEM; 181 182 ret = request_dma(dma_chan, dev->board_name); 183 if (ret) { 184 kfree(dma_buffer); 185 return ret; 186 } 187 188 devpriv->dma_buffer = dma_buffer; 189 devpriv->dma_chan = dma_chan; 190 devpriv->dma_addr = virt_to_bus(devpriv->dma_buffer); 191 192 dma_flags = claim_dma_lock(); 193 disable_dma(devpriv->dma_chan); 194 set_dma_mode(devpriv->dma_chan, DMA_MODE_READ); 195 release_dma_lock(dma_flags); 196 197 return 0; 198} 199EXPORT_SYMBOL_GPL(labpc_init_dma_chan); 200 201void labpc_free_dma_chan(struct comedi_device *dev) 202{ 203 struct labpc_private *devpriv = dev->private; 204 205 kfree(devpriv->dma_buffer); 206 devpriv->dma_buffer = NULL; 207 if (devpriv->dma_chan) { 208 free_dma(devpriv->dma_chan); 209 devpriv->dma_chan = 0; 210 } 211} 212EXPORT_SYMBOL_GPL(labpc_free_dma_chan); 213 214static int __init ni_labpc_isadma_init_module(void) 215{ 216 return 0; 217} 218module_init(ni_labpc_isadma_init_module); 219 220static void __exit ni_labpc_isadma_cleanup_module(void) 221{ 222} 223module_exit(ni_labpc_isadma_cleanup_module); 224 225MODULE_AUTHOR("Comedi http://www.comedi.org"); 226MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support"); 227MODULE_LICENSE("GPL"); 228