Commit | Line | Data |
---|---|---|
a211ea97 DS |
1 | /* |
2 | comedi/drivers/dt2814.c | |
3 | Hardware driver for Data Translation DT2814 | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 1998 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
a211ea97 DS |
17 | */ |
18 | /* | |
19 | Driver: dt2814 | |
20 | Description: Data Translation DT2814 | |
21 | Author: ds | |
22 | Status: complete | |
23 | Devices: [Data Translation] DT2814 (dt2814) | |
24 | ||
25 | Configuration options: | |
26 | [0] - I/O port base address | |
27 | [1] - IRQ | |
28 | ||
29 | This card has 16 analog inputs multiplexed onto a 12 bit ADC. There | |
30 | is a minimally useful onboard clock. The base frequency for the | |
31 | clock is selected by jumpers, and the clock divider can be selected | |
32 | via programmed I/O. Unfortunately, the clock divider can only be | |
33 | a power of 10, from 1 to 10^7, of which only 3 or 4 are useful. In | |
34 | addition, the clock does not seem to be very accurate. | |
35 | */ | |
36 | ||
ce157f80 | 37 | #include <linux/module.h> |
25436dc9 | 38 | #include <linux/interrupt.h> |
a211ea97 DS |
39 | #include "../comedidev.h" |
40 | ||
a211ea97 DS |
41 | #include <linux/delay.h> |
42 | ||
a211ea97 DS |
43 | #define DT2814_CSR 0 |
44 | #define DT2814_DATA 1 | |
45 | ||
46 | /* | |
47 | * flags | |
48 | */ | |
49 | ||
50 | #define DT2814_FINISH 0x80 | |
51 | #define DT2814_ERR 0x40 | |
52 | #define DT2814_BUSY 0x20 | |
53 | #define DT2814_ENB 0x10 | |
54 | #define DT2814_CHANMASK 0x0f | |
55 | ||
ca6f10ca | 56 | struct dt2814_private { |
a211ea97 DS |
57 | int ntrig; |
58 | int curadchan; | |
ca6f10ca BP |
59 | }; |
60 | ||
a211ea97 DS |
61 | #define DT2814_TIMEOUT 10 |
62 | #define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */ | |
63 | ||
82c7e864 HS |
64 | static int dt2814_ai_eoc(struct comedi_device *dev, |
65 | struct comedi_subdevice *s, | |
66 | struct comedi_insn *insn, | |
67 | unsigned long context) | |
68 | { | |
69 | unsigned int status; | |
70 | ||
71 | status = inb(dev->iobase + DT2814_CSR); | |
72 | if (status & DT2814_FINISH) | |
73 | return 0; | |
74 | return -EBUSY; | |
75 | } | |
76 | ||
0a85b6f0 MT |
77 | static int dt2814_ai_insn_read(struct comedi_device *dev, |
78 | struct comedi_subdevice *s, | |
79 | struct comedi_insn *insn, unsigned int *data) | |
a211ea97 | 80 | { |
82c7e864 | 81 | int n, hi, lo; |
a211ea97 | 82 | int chan; |
82c7e864 | 83 | int ret; |
a211ea97 DS |
84 | |
85 | for (n = 0; n < insn->n; n++) { | |
86 | chan = CR_CHAN(insn->chanspec); | |
87 | ||
88 | outb(chan, dev->iobase + DT2814_CSR); | |
82c7e864 HS |
89 | |
90 | ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0); | |
91 | if (ret) | |
92 | return ret; | |
a211ea97 DS |
93 | |
94 | hi = inb(dev->iobase + DT2814_DATA); | |
95 | lo = inb(dev->iobase + DT2814_DATA); | |
96 | ||
97 | data[n] = (hi << 4) | (lo >> 4); | |
98 | } | |
99 | ||
100 | return n; | |
101 | } | |
102 | ||
103 | static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags) | |
104 | { | |
105 | int i; | |
106 | unsigned int f; | |
107 | ||
108 | /* XXX ignores flags */ | |
109 | ||
110 | f = 10000; /* ns */ | |
111 | for (i = 0; i < 8; i++) { | |
112 | if ((2 * (*ns)) < (f * 11)) | |
113 | break; | |
114 | f *= 10; | |
115 | } | |
116 | ||
117 | *ns = f; | |
118 | ||
119 | return i; | |
120 | } | |
121 | ||
0a85b6f0 MT |
122 | static int dt2814_ai_cmdtest(struct comedi_device *dev, |
123 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
a211ea97 DS |
124 | { |
125 | int err = 0; | |
704b0050 | 126 | unsigned int arg; |
a211ea97 | 127 | |
27020ffe | 128 | /* Step 1 : check if triggers are trivially valid */ |
a211ea97 | 129 | |
71bb49d0 IA |
130 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); |
131 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER); | |
132 | err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
133 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
134 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
a211ea97 DS |
135 | |
136 | if (err) | |
137 | return 1; | |
138 | ||
27020ffe | 139 | /* Step 2a : make sure trigger sources are unique */ |
a211ea97 | 140 | |
71bb49d0 | 141 | err |= comedi_check_trigger_is_unique(cmd->stop_src); |
27020ffe HS |
142 | |
143 | /* Step 2b : and mutually compatible */ | |
a211ea97 DS |
144 | |
145 | if (err) | |
146 | return 2; | |
147 | ||
2345dc8b | 148 | /* Step 3: check if arguments are trivially valid */ |
a211ea97 | 149 | |
71bb49d0 | 150 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
2345dc8b | 151 | |
71bb49d0 IA |
152 | err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000); |
153 | err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, | |
154 | DT2814_MAX_SPEED); | |
2345dc8b | 155 | |
71bb49d0 IA |
156 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
157 | cmd->chanlist_len); | |
2345dc8b HS |
158 | |
159 | if (cmd->stop_src == TRIG_COUNT) | |
71bb49d0 | 160 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2); |
2345dc8b | 161 | else /* TRIG_NONE */ |
71bb49d0 | 162 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
a211ea97 DS |
163 | |
164 | if (err) | |
165 | return 3; | |
166 | ||
167 | /* step 4: fix up any arguments */ | |
168 | ||
704b0050 | 169 | arg = cmd->scan_begin_arg; |
a207c12f | 170 | dt2814_ns_to_timer(&arg, cmd->flags); |
71bb49d0 | 171 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); |
a211ea97 DS |
172 | |
173 | if (err) | |
174 | return 4; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
da91b269 | 179 | static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
a211ea97 | 180 | { |
9a1a6cf8 | 181 | struct dt2814_private *devpriv = dev->private; |
ea6d0d4c | 182 | struct comedi_cmd *cmd = &s->async->cmd; |
a211ea97 DS |
183 | int chan; |
184 | int trigvar; | |
185 | ||
a207c12f | 186 | trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags); |
a211ea97 DS |
187 | |
188 | chan = CR_CHAN(cmd->chanlist[0]); | |
189 | ||
190 | devpriv->ntrig = cmd->stop_arg; | |
191 | outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR); | |
192 | ||
193 | return 0; | |
a211ea97 DS |
194 | } |
195 | ||
7806012e HS |
196 | static irqreturn_t dt2814_interrupt(int irq, void *d) |
197 | { | |
198 | int lo, hi; | |
199 | struct comedi_device *dev = d; | |
9a1a6cf8 | 200 | struct dt2814_private *devpriv = dev->private; |
89d48385 | 201 | struct comedi_subdevice *s = dev->read_subdev; |
7806012e HS |
202 | int data; |
203 | ||
204 | if (!dev->attached) { | |
dffe87cd | 205 | dev_err(dev->class_dev, "spurious interrupt\n"); |
7806012e HS |
206 | return IRQ_HANDLED; |
207 | } | |
208 | ||
7806012e HS |
209 | hi = inb(dev->iobase + DT2814_DATA); |
210 | lo = inb(dev->iobase + DT2814_DATA); | |
211 | ||
212 | data = (hi << 4) | (lo >> 4); | |
213 | ||
214 | if (!(--devpriv->ntrig)) { | |
215 | int i; | |
216 | ||
217 | outb(0, dev->iobase + DT2814_CSR); | |
218 | /* note: turning off timed mode triggers another | |
219 | sample. */ | |
220 | ||
221 | for (i = 0; i < DT2814_TIMEOUT; i++) { | |
222 | if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH) | |
223 | break; | |
224 | } | |
225 | inb(dev->iobase + DT2814_DATA); | |
226 | inb(dev->iobase + DT2814_DATA); | |
227 | ||
228 | s->async->events |= COMEDI_CB_EOA; | |
229 | } | |
001cce48 | 230 | comedi_handle_events(dev, s); |
7806012e HS |
231 | return IRQ_HANDLED; |
232 | } | |
233 | ||
da91b269 | 234 | static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
a211ea97 | 235 | { |
9a1a6cf8 | 236 | struct dt2814_private *devpriv; |
34c43922 | 237 | struct comedi_subdevice *s; |
c5a9595b HS |
238 | int ret; |
239 | int i; | |
a211ea97 | 240 | |
862755ec | 241 | ret = comedi_request_region(dev, it->options[0], 0x2); |
c5c18cd3 HS |
242 | if (ret) |
243 | return ret; | |
a211ea97 DS |
244 | |
245 | outb(0, dev->iobase + DT2814_CSR); | |
5f74ea14 | 246 | udelay(100); |
a211ea97 | 247 | if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) { |
323cd355 | 248 | dev_err(dev->class_dev, "reset error (fatal)\n"); |
a211ea97 DS |
249 | return -EIO; |
250 | } | |
251 | i = inb(dev->iobase + DT2814_DATA); | |
252 | i = inb(dev->iobase + DT2814_DATA); | |
253 | ||
c5a9595b HS |
254 | if (it->options[1]) { |
255 | ret = request_irq(it->options[1], dt2814_interrupt, 0, | |
256 | dev->board_name, dev); | |
257 | if (ret == 0) | |
258 | dev->irq = it->options[1]; | |
a211ea97 DS |
259 | } |
260 | ||
2f0b9d08 | 261 | ret = comedi_alloc_subdevices(dev, 1); |
8b6c5694 | 262 | if (ret) |
a211ea97 | 263 | return ret; |
c3744138 | 264 | |
0bdab509 | 265 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
266 | if (!devpriv) |
267 | return -ENOMEM; | |
a211ea97 | 268 | |
92af10e1 | 269 | s = &dev->subdevices[0]; |
a211ea97 | 270 | s->type = COMEDI_SUBD_AI; |
c5a9595b | 271 | s->subdev_flags = SDF_READABLE | SDF_GROUND; |
a211ea97 | 272 | s->n_chan = 16; /* XXX */ |
a211ea97 | 273 | s->insn_read = dt2814_ai_insn_read; |
a211ea97 DS |
274 | s->maxdata = 0xfff; |
275 | s->range_table = &range_unknown; /* XXX */ | |
c5a9595b HS |
276 | if (dev->irq) { |
277 | dev->read_subdev = s; | |
278 | s->subdev_flags |= SDF_CMD_READ; | |
279 | s->len_chanlist = 1; | |
280 | s->do_cmd = dt2814_ai_cmd; | |
281 | s->do_cmdtest = dt2814_ai_cmdtest; | |
282 | } | |
a211ea97 DS |
283 | |
284 | return 0; | |
285 | } | |
286 | ||
7806012e HS |
287 | static struct comedi_driver dt2814_driver = { |
288 | .driver_name = "dt2814", | |
289 | .module = THIS_MODULE, | |
290 | .attach = dt2814_attach, | |
3d1fe3f7 | 291 | .detach = comedi_legacy_detach, |
7806012e HS |
292 | }; |
293 | module_comedi_driver(dt2814_driver); | |
90f703d3 AT |
294 | |
295 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
296 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
297 | MODULE_LICENSE("GPL"); |