]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/ni_labpc_isadma.c
Merge branch 'next' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[karo-tx-linux.git] / drivers / staging / comedi / drivers / ni_labpc_isadma.c
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 */
32 static const int dma_buffer_size = 0xff00;
33 /* 2 bytes per sample */
34 static const int sample_size = 2;
35
36 /* utility function that suggests a dma transfer size in bytes */
37 static 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
60 void 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 }
83 EXPORT_SYMBOL_GPL(labpc_setup_dma);
84
85 void 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         int status;
91         unsigned long flags;
92         unsigned int max_points, num_points, residue, leftover;
93         int i;
94
95         status = devpriv->stat1;
96
97         flags = claim_dma_lock();
98         disable_dma(devpriv->dma_chan);
99         /* clear flip-flop to make sure 2-byte registers for
100          * count and address get set correctly */
101         clear_dma_ff(devpriv->dma_chan);
102
103         /* figure out how many points to read */
104         max_points = devpriv->dma_transfer_size / sample_size;
105         /* residue is the number of points left to be done on the dma
106          * transfer.  It should always be zero at this point unless
107          * the stop_src is set to external triggering.
108          */
109         residue = get_dma_residue(devpriv->dma_chan) / sample_size;
110         num_points = max_points - residue;
111         if (devpriv->count < num_points && async->cmd.stop_src == TRIG_COUNT)
112                 num_points = devpriv->count;
113
114         /* figure out how many points will be stored next time */
115         leftover = 0;
116         if (async->cmd.stop_src != TRIG_COUNT) {
117                 leftover = devpriv->dma_transfer_size / sample_size;
118         } else if (devpriv->count > num_points) {
119                 leftover = devpriv->count - num_points;
120                 if (leftover > max_points)
121                         leftover = max_points;
122         }
123
124         /* write data to comedi buffer */
125         for (i = 0; i < num_points; i++)
126                 cfc_write_to_buffer(s, devpriv->dma_buffer[i]);
127
128         if (async->cmd.stop_src == TRIG_COUNT)
129                 devpriv->count -= num_points;
130
131         /* set address and count for next transfer */
132         set_dma_addr(devpriv->dma_chan, devpriv->dma_addr);
133         set_dma_count(devpriv->dma_chan, leftover * sample_size);
134         release_dma_lock(flags);
135
136         async->events |= COMEDI_CB_BLOCK;
137 }
138 EXPORT_SYMBOL_GPL(labpc_drain_dma);
139
140 static void handle_isa_dma(struct comedi_device *dev)
141 {
142         struct labpc_private *devpriv = dev->private;
143
144         labpc_drain_dma(dev);
145
146         enable_dma(devpriv->dma_chan);
147
148         /* clear dma tc interrupt */
149         devpriv->write_byte(0x1, dev->iobase + DMATC_CLEAR_REG);
150 }
151
152 void labpc_handle_dma_status(struct comedi_device *dev)
153 {
154         const struct labpc_boardinfo *board = comedi_board(dev);
155         struct labpc_private *devpriv = dev->private;
156
157         /*
158          * if a dma terminal count of external stop trigger
159          * has occurred
160          */
161         if (devpriv->stat1 & STAT1_GATA0 ||
162             (board->is_labpc1200 && devpriv->stat2 & STAT2_OUTA1))
163                 handle_isa_dma(dev);
164 }
165 EXPORT_SYMBOL_GPL(labpc_handle_dma_status);
166
167 int labpc_init_dma_chan(struct comedi_device *dev, unsigned int dma_chan)
168 {
169         struct labpc_private *devpriv = dev->private;
170         void *dma_buffer;
171         unsigned long dma_flags;
172         int ret;
173
174         if (dma_chan != 1 && dma_chan != 3)
175                 return -EINVAL;
176
177         dma_buffer = kmalloc(dma_buffer_size, GFP_KERNEL | GFP_DMA);
178         if (!dma_buffer)
179                 return -ENOMEM;
180
181         ret = request_dma(dma_chan, dev->board_name);
182         if (ret) {
183                 kfree(dma_buffer);
184                 return ret;
185         }
186
187         devpriv->dma_buffer = dma_buffer;
188         devpriv->dma_chan = dma_chan;
189         devpriv->dma_addr = virt_to_bus(devpriv->dma_buffer);
190
191         dma_flags = claim_dma_lock();
192         disable_dma(devpriv->dma_chan);
193         set_dma_mode(devpriv->dma_chan, DMA_MODE_READ);
194         release_dma_lock(dma_flags);
195
196         return 0;
197 }
198 EXPORT_SYMBOL_GPL(labpc_init_dma_chan);
199
200 void labpc_free_dma_chan(struct comedi_device *dev)
201 {
202         struct labpc_private *devpriv = dev->private;
203
204         kfree(devpriv->dma_buffer);
205         devpriv->dma_buffer = NULL;
206         if (devpriv->dma_chan) {
207                 free_dma(devpriv->dma_chan);
208                 devpriv->dma_chan = 0;
209         }
210 }
211 EXPORT_SYMBOL_GPL(labpc_free_dma_chan);
212
213 static int __init ni_labpc_isadma_init_module(void)
214 {
215         return 0;
216 }
217 module_init(ni_labpc_isadma_init_module);
218
219 static void __exit ni_labpc_isadma_cleanup_module(void)
220 {
221 }
222 module_exit(ni_labpc_isadma_cleanup_module);
223
224 MODULE_AUTHOR("Comedi http://www.comedi.org");
225 MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support");
226 MODULE_LICENSE("GPL");