]>
Commit | Line | Data |
---|---|---|
01b0a258 FMH |
1 | /* |
2 | comedi/drivers/ni_at_a2150.c | |
3 | Driver for National Instruments AT-A2150 boards | |
4 | Copyright (C) 2001, 2002 Frank Mori Hess <fmhess@users.sourceforge.net> | |
5 | ||
6 | COMEDI - Linux Control and Measurement Device Interface | |
7 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
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. | |
01b0a258 FMH |
18 | */ |
19 | /* | |
20 | Driver: ni_at_a2150 | |
21 | Description: National Instruments AT-A2150 | |
22 | Author: Frank Mori Hess | |
23 | Status: works | |
24 | Devices: [National Instruments] AT-A2150C (at_a2150c), AT-2150S (at_a2150s) | |
25 | ||
26 | If you want to ac couple the board's inputs, use AREF_OTHER. | |
27 | ||
28 | Configuration options: | |
29 | [0] - I/O port base address | |
30 | [1] - IRQ (optional, required for timed conversions) | |
31 | [2] - DMA (optional, required for timed conversions) | |
32 | ||
33 | */ | |
34 | /* | |
35 | Yet another driver for obsolete hardware brought to you by Frank Hess. | |
36 | Testing and debugging help provided by Dave Andruczyk. | |
37 | ||
38 | This driver supports the boards: | |
39 | ||
40 | AT-A2150C | |
41 | AT-A2150S | |
42 | ||
43 | The only difference is their master clock frequencies. | |
44 | ||
45 | Options: | |
46 | [0] - base io address | |
47 | [1] - irq | |
48 | [2] - dma channel | |
49 | ||
50 | References (from ftp://ftp.natinst.com/support/manuals): | |
51 | ||
52 | 320360.pdf AT-A2150 User Manual | |
53 | ||
54 | TODO: | |
55 | ||
56 | analog level triggering | |
57 | TRIG_WAKE_EOS | |
58 | ||
59 | */ | |
60 | ||
ce157f80 HS |
61 | #include <linux/module.h> |
62 | #include <linux/delay.h> | |
25436dc9 | 63 | #include <linux/interrupt.h> |
5a0e3ad6 | 64 | #include <linux/slab.h> |
845d131e | 65 | #include <linux/io.h> |
ce157f80 | 66 | |
1a97f144 | 67 | #include "../comedidev.h" |
01b0a258 | 68 | |
1a97f144 | 69 | #include "comedi_isadma.h" |
e875132a | 70 | #include "comedi_8254.h" |
01b0a258 | 71 | |
30c687c1 | 72 | #define A2150_DMA_BUFFER_SIZE 0xff00 /* size in bytes of dma buffer */ |
01b0a258 | 73 | |
01b0a258 FMH |
74 | /* Registers and bits */ |
75 | #define CONFIG_REG 0x0 | |
76 | #define CHANNEL_BITS(x) ((x) & 0x7) | |
77 | #define CHANNEL_MASK 0x7 | |
78 | #define CLOCK_SELECT_BITS(x) (((x) & 0x3) << 3) | |
79 | #define CLOCK_DIVISOR_BITS(x) (((x) & 0x3) << 5) | |
80 | #define CLOCK_MASK (0xf << 3) | |
30c687c1 BP |
81 | #define ENABLE0_BIT 0x80 /* enable (don't internally ground) channels 0 and 1 */ |
82 | #define ENABLE1_BIT 0x100 /* enable (don't internally ground) channels 2 and 3 */ | |
83 | #define AC0_BIT 0x200 /* ac couple channels 0,1 */ | |
84 | #define AC1_BIT 0x400 /* ac couple channels 2,3 */ | |
85 | #define APD_BIT 0x800 /* analog power down */ | |
86 | #define DPD_BIT 0x1000 /* digital power down */ | |
87 | #define TRIGGER_REG 0x2 /* trigger config register */ | |
01b0a258 FMH |
88 | #define POST_TRIGGER_BITS 0x2 |
89 | #define DELAY_TRIGGER_BITS 0x3 | |
30c687c1 BP |
90 | #define HW_TRIG_EN 0x10 /* enable hardware trigger */ |
91 | #define FIFO_START_REG 0x6 /* software start aquistion trigger */ | |
92 | #define FIFO_RESET_REG 0x8 /* clears fifo + fifo flags */ | |
93 | #define FIFO_DATA_REG 0xa /* read data */ | |
94 | #define DMA_TC_CLEAR_REG 0xe /* clear dma terminal count interrupt */ | |
95 | #define STATUS_REG 0x12 /* read only */ | |
96 | #define FNE_BIT 0x1 /* fifo not empty */ | |
97 | #define OVFL_BIT 0x8 /* fifo overflow */ | |
25985edc | 98 | #define EDAQ_BIT 0x10 /* end of acquisition interrupt */ |
30c687c1 | 99 | #define DCAL_BIT 0x20 /* offset calibration in progress */ |
25985edc LDM |
100 | #define INTR_BIT 0x40 /* interrupt has occurred */ |
101 | #define DMA_TC_BIT 0x80 /* dma terminal count interrupt has occurred */ | |
01b0a258 | 102 | #define ID_BITS(x) (((x) >> 8) & 0x3) |
30c687c1 BP |
103 | #define IRQ_DMA_CNTRL_REG 0x12 /* write only */ |
104 | #define DMA_CHAN_BITS(x) ((x) & 0x7) /* sets dma channel */ | |
105 | #define DMA_EN_BIT 0x8 /* enables dma */ | |
106 | #define IRQ_LVL_BITS(x) (((x) & 0xf) << 4) /* sets irq level */ | |
107 | #define FIFO_INTR_EN_BIT 0x100 /* enable fifo interrupts */ | |
108 | #define FIFO_INTR_FHF_BIT 0x200 /* interrupt fifo half full */ | |
109 | #define DMA_INTR_EN_BIT 0x800 /* enable interrupt on dma terminal count */ | |
110 | #define DMA_DEM_EN_BIT 0x1000 /* enables demand mode dma */ | |
01b0a258 | 111 | #define I8253_BASE_REG 0x14 |
01b0a258 | 112 | |
92b635c5 | 113 | struct a2150_board { |
01b0a258 | 114 | const char *name; |
30c687c1 BP |
115 | int clock[4]; /* master clock periods, in nanoseconds */ |
116 | int num_clocks; /* number of available master clock speeds */ | |
117 | int ai_speed; /* maximum conversion rate in nanoseconds */ | |
92b635c5 | 118 | }; |
01b0a258 | 119 | |
30c687c1 | 120 | /* analog input range */ |
9ced1de6 | 121 | static const struct comedi_lrange range_a2150 = { |
168a2103 HS |
122 | 1, { |
123 | BIP_RANGE(2.828) | |
124 | } | |
01b0a258 FMH |
125 | }; |
126 | ||
30c687c1 | 127 | /* enum must match board indices */ |
01b0a258 | 128 | enum { a2150_c, a2150_s }; |
92b635c5 | 129 | static const struct a2150_board a2150_boards[] = { |
01b0a258 | 130 | { |
0a85b6f0 MT |
131 | .name = "at-a2150c", |
132 | .clock = {31250, 22676, 20833, 19531}, | |
133 | .num_clocks = 4, | |
134 | .ai_speed = 19531, | |
135 | }, | |
01b0a258 | 136 | { |
0a85b6f0 MT |
137 | .name = "at-a2150s", |
138 | .clock = {62500, 50000, 41667, 0}, | |
139 | .num_clocks = 3, | |
140 | .ai_speed = 41667, | |
141 | }, | |
01b0a258 FMH |
142 | }; |
143 | ||
3cc3872b | 144 | struct a2150_private { |
1a97f144 | 145 | struct comedi_isadma *dma; |
45478548 | 146 | unsigned int count; /* number of data points left to be taken */ |
30c687c1 BP |
147 | int irq_dma_bits; /* irq/dma register bits */ |
148 | int config_bits; /* config register bits */ | |
3cc3872b BP |
149 | }; |
150 | ||
01b0a258 | 151 | /* interrupt service routine */ |
70265d24 | 152 | static irqreturn_t a2150_interrupt(int irq, void *d) |
01b0a258 | 153 | { |
71b5f4f1 | 154 | struct comedi_device *dev = d; |
9a1a6cf8 | 155 | struct a2150_private *devpriv = dev->private; |
1a97f144 HS |
156 | struct comedi_isadma *dma = devpriv->dma; |
157 | struct comedi_isadma_desc *desc = &dma->desc[0]; | |
34c43922 | 158 | struct comedi_subdevice *s = dev->read_subdev; |
1a97f144 HS |
159 | struct comedi_async *async = s->async; |
160 | struct comedi_cmd *cmd = &async->cmd; | |
161 | unsigned short *buf = desc->virt_addr; | |
01b0a258 | 162 | unsigned int max_points, num_points, residue, leftover; |
2fb5cd38 | 163 | unsigned short dpnt; |
1a97f144 HS |
164 | int status; |
165 | int i; | |
01b0a258 | 166 | |
1a97f144 | 167 | if (!dev->attached) |
01b0a258 | 168 | return IRQ_HANDLED; |
01b0a258 FMH |
169 | |
170 | status = inw(dev->iobase + STATUS_REG); | |
1a97f144 | 171 | if ((status & INTR_BIT) == 0) |
01b0a258 | 172 | return IRQ_NONE; |
01b0a258 FMH |
173 | |
174 | if (status & OVFL_BIT) { | |
3e6cb74f | 175 | async->events |= COMEDI_CB_ERROR; |
4a706e2e | 176 | comedi_handle_events(dev, s); |
01b0a258 FMH |
177 | } |
178 | ||
179 | if ((status & DMA_TC_BIT) == 0) { | |
3e6cb74f | 180 | async->events |= COMEDI_CB_ERROR; |
4a706e2e | 181 | comedi_handle_events(dev, s); |
01b0a258 FMH |
182 | return IRQ_HANDLED; |
183 | } | |
184 | ||
10f3a2dc HS |
185 | /* |
186 | * residue is the number of bytes left to be done on the dma | |
01b0a258 FMH |
187 | * transfer. It should always be zero at this point unless |
188 | * the stop_src is set to external triggering. | |
189 | */ | |
1a97f144 | 190 | residue = comedi_isadma_disable(desc->chan); |
10f3a2dc HS |
191 | |
192 | /* figure out how many points to read */ | |
1a97f144 | 193 | max_points = comedi_bytes_to_samples(s, desc->size); |
10f3a2dc | 194 | num_points = max_points - comedi_bytes_to_samples(s, residue); |
01b0a258 FMH |
195 | if (devpriv->count < num_points && cmd->stop_src == TRIG_COUNT) |
196 | num_points = devpriv->count; | |
197 | ||
30c687c1 | 198 | /* figure out how many points will be stored next time */ |
01b0a258 FMH |
199 | leftover = 0; |
200 | if (cmd->stop_src == TRIG_NONE) { | |
1a97f144 | 201 | leftover = comedi_bytes_to_samples(s, desc->size); |
01b0a258 FMH |
202 | } else if (devpriv->count > max_points) { |
203 | leftover = devpriv->count - max_points; | |
204 | if (leftover > max_points) | |
205 | leftover = max_points; | |
206 | } | |
207 | /* there should only be a residue if collection was stopped by having | |
208 | * the stop_src set to an external trigger, in which case there | |
209 | * will be no more data | |
210 | */ | |
211 | if (residue) | |
212 | leftover = 0; | |
213 | ||
214 | for (i = 0; i < num_points; i++) { | |
215 | /* write data point to comedi buffer */ | |
6d262751 | 216 | dpnt = buf[i]; |
30c687c1 | 217 | /* convert from 2's complement to unsigned coding */ |
01b0a258 | 218 | dpnt ^= 0x8000; |
7138e892 | 219 | comedi_buf_write_samples(s, &dpnt, 1); |
01b0a258 FMH |
220 | if (cmd->stop_src == TRIG_COUNT) { |
221 | if (--devpriv->count == 0) { /* end of acquisition */ | |
01b0a258 FMH |
222 | async->events |= COMEDI_CB_EOA; |
223 | break; | |
224 | } | |
225 | } | |
226 | } | |
c92b0b29 | 227 | /* re-enable dma */ |
01b0a258 | 228 | if (leftover) { |
1a97f144 HS |
229 | desc->size = comedi_samples_to_bytes(s, leftover); |
230 | comedi_isadma_program(desc); | |
01b0a258 | 231 | } |
01b0a258 | 232 | |
4a706e2e | 233 | comedi_handle_events(dev, s); |
01b0a258 FMH |
234 | |
235 | /* clear interrupt */ | |
236 | outw(0x00, dev->iobase + DMA_TC_CLEAR_REG); | |
237 | ||
238 | return IRQ_HANDLED; | |
239 | } | |
240 | ||
da91b269 | 241 | static int a2150_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
01b0a258 | 242 | { |
9a1a6cf8 | 243 | struct a2150_private *devpriv = dev->private; |
1a97f144 HS |
244 | struct comedi_isadma *dma = devpriv->dma; |
245 | struct comedi_isadma_desc *desc = &dma->desc[0]; | |
9a1a6cf8 | 246 | |
30c687c1 | 247 | /* disable dma on card */ |
01b0a258 FMH |
248 | devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT; |
249 | outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG); | |
250 | ||
30c687c1 | 251 | /* disable computer's dma */ |
1a97f144 | 252 | comedi_isadma_disable(desc->chan); |
01b0a258 | 253 | |
30c687c1 | 254 | /* clear fifo and reset triggering circuitry */ |
01b0a258 FMH |
255 | outw(0, dev->iobase + FIFO_RESET_REG); |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
c9bef032 HS |
260 | /* |
261 | * sets bits in devpriv->clock_bits to nearest approximation of requested | |
262 | * period, adjusts requested period to actual timing. | |
263 | */ | |
264 | static int a2150_get_timing(struct comedi_device *dev, unsigned int *period, | |
265 | unsigned int flags) | |
266 | { | |
02cf9703 | 267 | const struct a2150_board *thisboard = dev->board_ptr; |
c9bef032 HS |
268 | struct a2150_private *devpriv = dev->private; |
269 | int lub, glb, temp; | |
270 | int lub_divisor_shift, lub_index, glb_divisor_shift, glb_index; | |
271 | int i, j; | |
272 | ||
273 | /* initialize greatest lower and least upper bounds */ | |
274 | lub_divisor_shift = 3; | |
275 | lub_index = 0; | |
276 | lub = thisboard->clock[lub_index] * (1 << lub_divisor_shift); | |
277 | glb_divisor_shift = 0; | |
278 | glb_index = thisboard->num_clocks - 1; | |
279 | glb = thisboard->clock[glb_index] * (1 << glb_divisor_shift); | |
280 | ||
281 | /* make sure period is in available range */ | |
282 | if (*period < glb) | |
283 | *period = glb; | |
284 | if (*period > lub) | |
285 | *period = lub; | |
286 | ||
287 | /* we can multiply period by 1, 2, 4, or 8, using (1 << i) */ | |
288 | for (i = 0; i < 4; i++) { | |
289 | /* there are a maximum of 4 master clocks */ | |
290 | for (j = 0; j < thisboard->num_clocks; j++) { | |
291 | /* temp is the period in nanosec we are evaluating */ | |
292 | temp = thisboard->clock[j] * (1 << i); | |
293 | /* if it is the best match yet */ | |
294 | if (temp < lub && temp >= *period) { | |
295 | lub_divisor_shift = i; | |
296 | lub_index = j; | |
297 | lub = temp; | |
298 | } | |
299 | if (temp > glb && temp <= *period) { | |
300 | glb_divisor_shift = i; | |
301 | glb_index = j; | |
302 | glb = temp; | |
303 | } | |
304 | } | |
305 | } | |
b544bd69 IA |
306 | switch (flags & CMDF_ROUND_MASK) { |
307 | case CMDF_ROUND_NEAREST: | |
c9bef032 HS |
308 | default: |
309 | /* if least upper bound is better approximation */ | |
310 | if (lub - *period < *period - glb) | |
311 | *period = lub; | |
312 | else | |
313 | *period = glb; | |
314 | break; | |
b544bd69 | 315 | case CMDF_ROUND_UP: |
c9bef032 HS |
316 | *period = lub; |
317 | break; | |
b544bd69 | 318 | case CMDF_ROUND_DOWN: |
c9bef032 HS |
319 | *period = glb; |
320 | break; | |
321 | } | |
322 | ||
323 | /* set clock bits for config register appropriately */ | |
324 | devpriv->config_bits &= ~CLOCK_MASK; | |
325 | if (*period == lub) { | |
326 | devpriv->config_bits |= | |
327 | CLOCK_SELECT_BITS(lub_index) | | |
328 | CLOCK_DIVISOR_BITS(lub_divisor_shift); | |
329 | } else { | |
330 | devpriv->config_bits |= | |
331 | CLOCK_SELECT_BITS(glb_index) | | |
332 | CLOCK_DIVISOR_BITS(glb_divisor_shift); | |
333 | } | |
334 | ||
335 | return 0; | |
336 | } | |
337 | ||
338 | static int a2150_set_chanlist(struct comedi_device *dev, | |
339 | unsigned int start_channel, | |
340 | unsigned int num_channels) | |
341 | { | |
342 | struct a2150_private *devpriv = dev->private; | |
343 | ||
344 | if (start_channel + num_channels > 4) | |
345 | return -1; | |
346 | ||
347 | devpriv->config_bits &= ~CHANNEL_MASK; | |
348 | ||
349 | switch (num_channels) { | |
350 | case 1: | |
351 | devpriv->config_bits |= CHANNEL_BITS(0x4 | start_channel); | |
352 | break; | |
353 | case 2: | |
354 | if (start_channel == 0) | |
355 | devpriv->config_bits |= CHANNEL_BITS(0x2); | |
356 | else if (start_channel == 2) | |
357 | devpriv->config_bits |= CHANNEL_BITS(0x3); | |
358 | else | |
359 | return -1; | |
360 | break; | |
361 | case 4: | |
362 | devpriv->config_bits |= CHANNEL_BITS(0x1); | |
363 | break; | |
364 | default: | |
365 | return -1; | |
366 | } | |
367 | ||
368 | return 0; | |
369 | } | |
370 | ||
8f61419f HS |
371 | static int a2150_ai_check_chanlist(struct comedi_device *dev, |
372 | struct comedi_subdevice *s, | |
373 | struct comedi_cmd *cmd) | |
374 | { | |
375 | unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); | |
376 | unsigned int aref0 = CR_AREF(cmd->chanlist[0]); | |
377 | int i; | |
378 | ||
379 | if (cmd->chanlist_len == 2 && (chan0 == 1 || chan0 == 3)) { | |
380 | dev_dbg(dev->class_dev, | |
381 | "length 2 chanlist must be channels 0,1 or channels 2,3\n"); | |
382 | return -EINVAL; | |
383 | } | |
384 | ||
385 | if (cmd->chanlist_len == 3) { | |
386 | dev_dbg(dev->class_dev, | |
387 | "chanlist must have 1,2 or 4 channels\n"); | |
388 | return -EINVAL; | |
389 | } | |
390 | ||
391 | for (i = 1; i < cmd->chanlist_len; i++) { | |
392 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); | |
393 | unsigned int aref = CR_AREF(cmd->chanlist[i]); | |
394 | ||
395 | if (chan != (chan0 + i)) { | |
396 | dev_dbg(dev->class_dev, | |
397 | "entries in chanlist must be consecutive channels, counting upwards\n"); | |
398 | return -EINVAL; | |
399 | } | |
400 | ||
401 | if (chan == 2) | |
402 | aref0 = aref; | |
403 | if (aref != aref0) { | |
404 | dev_dbg(dev->class_dev, | |
405 | "channels 0/1 and 2/3 must have the same analog reference\n"); | |
406 | return -EINVAL; | |
407 | } | |
408 | } | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
0a85b6f0 MT |
413 | static int a2150_ai_cmdtest(struct comedi_device *dev, |
414 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
01b0a258 | 415 | { |
02cf9703 | 416 | const struct a2150_board *thisboard = dev->board_ptr; |
01b0a258 | 417 | int err = 0; |
75cff543 | 418 | unsigned int arg; |
01b0a258 | 419 | |
27020ffe | 420 | /* Step 1 : check if triggers are trivially valid */ |
01b0a258 | 421 | |
ded24683 IA |
422 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); |
423 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER); | |
424 | err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
425 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
426 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
01b0a258 FMH |
427 | |
428 | if (err) | |
429 | return 1; | |
430 | ||
27020ffe | 431 | /* Step 2a : make sure trigger sources are unique */ |
01b0a258 | 432 | |
ded24683 IA |
433 | err |= comedi_check_trigger_is_unique(cmd->start_src); |
434 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
27020ffe HS |
435 | |
436 | /* Step 2b : and mutually compatible */ | |
01b0a258 FMH |
437 | |
438 | if (err) | |
439 | return 2; | |
440 | ||
dd254844 | 441 | /* Step 3: check if arguments are trivially valid */ |
01b0a258 | 442 | |
ded24683 | 443 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
dd254844 | 444 | |
ded24683 IA |
445 | if (cmd->convert_src == TRIG_TIMER) { |
446 | err |= comedi_check_trigger_arg_min(&cmd->convert_arg, | |
447 | thisboard->ai_speed); | |
448 | } | |
dd254844 | 449 | |
ded24683 IA |
450 | err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); |
451 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, | |
452 | cmd->chanlist_len); | |
dd254844 HS |
453 | |
454 | if (cmd->stop_src == TRIG_COUNT) | |
ded24683 | 455 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); |
dd254844 | 456 | else /* TRIG_NONE */ |
ded24683 | 457 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
01b0a258 FMH |
458 | |
459 | if (err) | |
460 | return 3; | |
461 | ||
462 | /* step 4: fix up any arguments */ | |
463 | ||
464 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
75cff543 HS |
465 | arg = cmd->scan_begin_arg; |
466 | a2150_get_timing(dev, &arg, cmd->flags); | |
ded24683 | 467 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); |
01b0a258 FMH |
468 | } |
469 | ||
470 | if (err) | |
471 | return 4; | |
472 | ||
8f61419f HS |
473 | /* Step 5: check channel list if it exists */ |
474 | if (cmd->chanlist && cmd->chanlist_len > 0) | |
475 | err |= a2150_ai_check_chanlist(dev, s, cmd); | |
01b0a258 FMH |
476 | |
477 | if (err) | |
478 | return 5; | |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
da91b269 | 483 | static int a2150_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
01b0a258 | 484 | { |
9a1a6cf8 | 485 | struct a2150_private *devpriv = dev->private; |
1a97f144 HS |
486 | struct comedi_isadma *dma = devpriv->dma; |
487 | struct comedi_isadma_desc *desc = &dma->desc[0]; | |
d163679c | 488 | struct comedi_async *async = s->async; |
ea6d0d4c | 489 | struct comedi_cmd *cmd = &async->cmd; |
01b0a258 FMH |
490 | unsigned int old_config_bits = devpriv->config_bits; |
491 | unsigned int trigger_bits; | |
492 | ||
34ae4160 | 493 | if (cmd->flags & CMDF_PRIORITY) { |
770bc73d | 494 | dev_err(dev->class_dev, |
34ae4160 | 495 | "dma incompatible with hard real-time interrupt (CMDF_PRIORITY), aborting\n"); |
01b0a258 FMH |
496 | return -1; |
497 | } | |
30c687c1 | 498 | /* clear fifo and reset triggering circuitry */ |
01b0a258 FMH |
499 | outw(0, dev->iobase + FIFO_RESET_REG); |
500 | ||
501 | /* setup chanlist */ | |
502 | if (a2150_set_chanlist(dev, CR_CHAN(cmd->chanlist[0]), | |
0a85b6f0 | 503 | cmd->chanlist_len) < 0) |
01b0a258 FMH |
504 | return -1; |
505 | ||
30c687c1 | 506 | /* setup ac/dc coupling */ |
01b0a258 FMH |
507 | if (CR_AREF(cmd->chanlist[0]) == AREF_OTHER) |
508 | devpriv->config_bits |= AC0_BIT; | |
509 | else | |
510 | devpriv->config_bits &= ~AC0_BIT; | |
511 | if (CR_AREF(cmd->chanlist[2]) == AREF_OTHER) | |
512 | devpriv->config_bits |= AC1_BIT; | |
513 | else | |
514 | devpriv->config_bits &= ~AC1_BIT; | |
515 | ||
30c687c1 | 516 | /* setup timing */ |
01b0a258 FMH |
517 | a2150_get_timing(dev, &cmd->scan_begin_arg, cmd->flags); |
518 | ||
30c687c1 | 519 | /* send timing, channel, config bits */ |
01b0a258 FMH |
520 | outw(devpriv->config_bits, dev->iobase + CONFIG_REG); |
521 | ||
30c687c1 | 522 | /* initialize number of samples remaining */ |
01b0a258 FMH |
523 | devpriv->count = cmd->stop_arg * cmd->chanlist_len; |
524 | ||
1a97f144 | 525 | comedi_isadma_disable(desc->chan); |
c92b0b29 | 526 | |
30c687c1 | 527 | /* set size of transfer to fill in 1/3 second */ |
01b0a258 | 528 | #define ONE_THIRD_SECOND 333333333 |
1a97f144 | 529 | desc->size = comedi_bytes_per_sample(s) * cmd->chanlist_len * |
5bf7d295 | 530 | ONE_THIRD_SECOND / cmd->scan_begin_arg; |
1a97f144 HS |
531 | if (desc->size > desc->maxsize) |
532 | desc->size = desc->maxsize; | |
533 | if (desc->size < comedi_bytes_per_sample(s)) | |
534 | desc->size = comedi_bytes_per_sample(s); | |
535 | desc->size -= desc->size % comedi_bytes_per_sample(s); | |
c92b0b29 | 536 | |
1a97f144 | 537 | comedi_isadma_program(desc); |
01b0a258 FMH |
538 | |
539 | /* clear dma interrupt before enabling it, to try and get rid of that | |
540 | * one spurious interrupt that has been happening */ | |
541 | outw(0x00, dev->iobase + DMA_TC_CLEAR_REG); | |
542 | ||
30c687c1 | 543 | /* enable dma on card */ |
01b0a258 FMH |
544 | devpriv->irq_dma_bits |= DMA_INTR_EN_BIT | DMA_EN_BIT; |
545 | outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG); | |
546 | ||
30c687c1 | 547 | /* may need to wait 72 sampling periods if timing was changed */ |
e875132a | 548 | comedi_8254_load(dev->pacer, 2, 72, I8254_MODE0 | I8254_BINARY); |
01b0a258 | 549 | |
30c687c1 | 550 | /* setup start triggering */ |
01b0a258 | 551 | trigger_bits = 0; |
30c687c1 | 552 | /* decide if we need to wait 72 periods for valid data */ |
01b0a258 | 553 | if (cmd->start_src == TRIG_NOW && |
0a85b6f0 MT |
554 | (old_config_bits & CLOCK_MASK) != |
555 | (devpriv->config_bits & CLOCK_MASK)) { | |
30c687c1 | 556 | /* set trigger source to delay trigger */ |
01b0a258 FMH |
557 | trigger_bits |= DELAY_TRIGGER_BITS; |
558 | } else { | |
30c687c1 | 559 | /* otherwise no delay */ |
01b0a258 FMH |
560 | trigger_bits |= POST_TRIGGER_BITS; |
561 | } | |
30c687c1 | 562 | /* enable external hardware trigger */ |
01b0a258 FMH |
563 | if (cmd->start_src == TRIG_EXT) { |
564 | trigger_bits |= HW_TRIG_EN; | |
565 | } else if (cmd->start_src == TRIG_OTHER) { | |
30c687c1 | 566 | /* XXX add support for level/slope start trigger using TRIG_OTHER */ |
770bc73d | 567 | dev_err(dev->class_dev, "you shouldn't see this?\n"); |
01b0a258 | 568 | } |
30c687c1 | 569 | /* send trigger config bits */ |
01b0a258 FMH |
570 | outw(trigger_bits, dev->iobase + TRIGGER_REG); |
571 | ||
25985edc | 572 | /* start acquisition for soft trigger */ |
a96b98f2 | 573 | if (cmd->start_src == TRIG_NOW) |
01b0a258 | 574 | outw(0, dev->iobase + FIFO_START_REG); |
01b0a258 FMH |
575 | |
576 | return 0; | |
577 | } | |
578 | ||
33a6d44b HS |
579 | static int a2150_ai_eoc(struct comedi_device *dev, |
580 | struct comedi_subdevice *s, | |
581 | struct comedi_insn *insn, | |
582 | unsigned long context) | |
583 | { | |
584 | unsigned int status; | |
585 | ||
586 | status = inw(dev->iobase + STATUS_REG); | |
587 | if (status & FNE_BIT) | |
588 | return 0; | |
589 | return -EBUSY; | |
590 | } | |
591 | ||
da91b269 | 592 | static int a2150_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 593 | struct comedi_insn *insn, unsigned int *data) |
01b0a258 | 594 | { |
9a1a6cf8 | 595 | struct a2150_private *devpriv = dev->private; |
33a6d44b HS |
596 | unsigned int n; |
597 | int ret; | |
01b0a258 | 598 | |
30c687c1 | 599 | /* clear fifo and reset triggering circuitry */ |
01b0a258 FMH |
600 | outw(0, dev->iobase + FIFO_RESET_REG); |
601 | ||
602 | /* setup chanlist */ | |
603 | if (a2150_set_chanlist(dev, CR_CHAN(insn->chanspec), 1) < 0) | |
604 | return -1; | |
605 | ||
30c687c1 | 606 | /* set dc coupling */ |
01b0a258 FMH |
607 | devpriv->config_bits &= ~AC0_BIT; |
608 | devpriv->config_bits &= ~AC1_BIT; | |
609 | ||
30c687c1 | 610 | /* send timing, channel, config bits */ |
01b0a258 FMH |
611 | outw(devpriv->config_bits, dev->iobase + CONFIG_REG); |
612 | ||
30c687c1 | 613 | /* disable dma on card */ |
01b0a258 FMH |
614 | devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT; |
615 | outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG); | |
616 | ||
30c687c1 | 617 | /* setup start triggering */ |
01b0a258 FMH |
618 | outw(0, dev->iobase + TRIGGER_REG); |
619 | ||
25985edc | 620 | /* start acquisition for soft trigger */ |
01b0a258 FMH |
621 | outw(0, dev->iobase + FIFO_START_REG); |
622 | ||
949fd38c RKM |
623 | /* |
624 | * there is a 35.6 sample delay for data to get through the | |
625 | * antialias filter | |
626 | */ | |
33a6d44b HS |
627 | for (n = 0; n < 36; n++) { |
628 | ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0); | |
22ca19d9 | 629 | if (ret) |
33a6d44b | 630 | return ret; |
33a6d44b | 631 | |
01b0a258 FMH |
632 | inw(dev->iobase + FIFO_DATA_REG); |
633 | } | |
634 | ||
30c687c1 | 635 | /* read data */ |
01b0a258 | 636 | for (n = 0; n < insn->n; n++) { |
33a6d44b | 637 | ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0); |
22ca19d9 | 638 | if (ret) |
33a6d44b | 639 | return ret; |
33a6d44b | 640 | |
01b0a258 | 641 | data[n] = inw(dev->iobase + FIFO_DATA_REG); |
01b0a258 FMH |
642 | data[n] ^= 0x8000; |
643 | } | |
644 | ||
30c687c1 | 645 | /* clear fifo and reset triggering circuitry */ |
01b0a258 FMH |
646 | outw(0, dev->iobase + FIFO_RESET_REG); |
647 | ||
648 | return n; | |
649 | } | |
650 | ||
1a97f144 HS |
651 | static void a2150_alloc_irq_and_dma(struct comedi_device *dev, |
652 | struct comedi_devconfig *it) | |
7cbb0ef9 HS |
653 | { |
654 | struct a2150_private *devpriv = dev->private; | |
655 | unsigned int irq_num = it->options[1]; | |
656 | unsigned int dma_chan = it->options[2]; | |
657 | ||
658 | /* | |
659 | * Only IRQs 15, 14, 12-9, and 7-3 are valid. | |
660 | * Only DMA channels 7-5 and 3-0 are valid. | |
7cbb0ef9 HS |
661 | */ |
662 | if (irq_num > 15 || dma_chan > 7 || | |
663 | !((1 << irq_num) & 0xdef8) || !((1 << dma_chan) & 0xef)) | |
664 | return; | |
665 | ||
7cbb0ef9 HS |
666 | if (request_irq(irq_num, a2150_interrupt, 0, dev->board_name, dev)) |
667 | return; | |
1a97f144 HS |
668 | |
669 | /* DMA uses 1 buffer */ | |
670 | devpriv->dma = comedi_isadma_alloc(dev, 1, dma_chan, dma_chan, | |
671 | A2150_DMA_BUFFER_SIZE, | |
672 | COMEDI_ISADMA_READ); | |
673 | if (!devpriv->dma) { | |
7cbb0ef9 | 674 | free_irq(irq_num, dev); |
1a97f144 HS |
675 | } else { |
676 | dev->irq = irq_num; | |
677 | devpriv->irq_dma_bits = IRQ_LVL_BITS(irq_num) | | |
678 | DMA_CHAN_BITS(dma_chan); | |
7cbb0ef9 | 679 | } |
7cbb0ef9 HS |
680 | } |
681 | ||
b1478901 HS |
682 | static void a2150_free_dma(struct comedi_device *dev) |
683 | { | |
684 | struct a2150_private *devpriv = dev->private; | |
b1478901 | 685 | |
1a97f144 HS |
686 | if (devpriv) |
687 | comedi_isadma_free(devpriv->dma); | |
b1478901 HS |
688 | } |
689 | ||
fe14fa2b HS |
690 | /* probes board type, returns offset */ |
691 | static int a2150_probe(struct comedi_device *dev) | |
692 | { | |
693 | int status = inw(dev->iobase + STATUS_REG); | |
a95b7ccf | 694 | |
fe14fa2b HS |
695 | return ID_BITS(status); |
696 | } | |
697 | ||
698 | static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it) | |
699 | { | |
1d57261c | 700 | const struct a2150_board *thisboard; |
9a1a6cf8 | 701 | struct a2150_private *devpriv; |
fe14fa2b | 702 | struct comedi_subdevice *s; |
fe14fa2b HS |
703 | static const int timeout = 2000; |
704 | int i; | |
8b6c5694 | 705 | int ret; |
fe14fa2b | 706 | |
0bdab509 | 707 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
708 | if (!devpriv) |
709 | return -ENOMEM; | |
fe14fa2b | 710 | |
862755ec | 711 | ret = comedi_request_region(dev, it->options[0], 0x1c); |
3671cae1 HS |
712 | if (ret) |
713 | return ret; | |
fe14fa2b | 714 | |
e988e1f3 IA |
715 | i = a2150_probe(dev); |
716 | if (i >= ARRAY_SIZE(a2150_boards)) | |
717 | return -ENODEV; | |
718 | ||
719 | dev->board_ptr = a2150_boards + i; | |
02cf9703 | 720 | thisboard = dev->board_ptr; |
6cb8e1a2 HS |
721 | dev->board_name = thisboard->name; |
722 | ||
1a97f144 HS |
723 | /* an IRQ and DMA are required to support async commands */ |
724 | a2150_alloc_irq_and_dma(dev, it); | |
fe14fa2b | 725 | |
e875132a HS |
726 | dev->pacer = comedi_8254_init(dev->iobase + I8253_BASE_REG, |
727 | 0, I8254_IO8, 0); | |
728 | if (!dev->pacer) | |
729 | return -ENOMEM; | |
730 | ||
8b6c5694 HS |
731 | ret = comedi_alloc_subdevices(dev, 1); |
732 | if (ret) | |
733 | return ret; | |
fe14fa2b HS |
734 | |
735 | /* analog input subdevice */ | |
ca3caabb | 736 | s = &dev->subdevices[0]; |
fe14fa2b | 737 | s->type = COMEDI_SUBD_AI; |
6cb8e1a2 | 738 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER; |
fe14fa2b | 739 | s->n_chan = 4; |
fe14fa2b HS |
740 | s->maxdata = 0xffff; |
741 | s->range_table = &range_a2150; | |
fe14fa2b | 742 | s->insn_read = a2150_ai_rinsn; |
7cbb0ef9 | 743 | if (dev->irq) { |
6cb8e1a2 HS |
744 | dev->read_subdev = s; |
745 | s->subdev_flags |= SDF_CMD_READ; | |
746 | s->len_chanlist = s->n_chan; | |
747 | s->do_cmd = a2150_ai_cmd; | |
748 | s->do_cmdtest = a2150_ai_cmdtest; | |
749 | s->cancel = a2150_cancel; | |
750 | } | |
fe14fa2b | 751 | |
fe14fa2b HS |
752 | /* set card's irq and dma levels */ |
753 | outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG); | |
754 | ||
755 | /* reset and sync adc clock circuitry */ | |
756 | outw_p(DPD_BIT | APD_BIT, dev->iobase + CONFIG_REG); | |
757 | outw_p(DPD_BIT, dev->iobase + CONFIG_REG); | |
758 | /* initialize configuration register */ | |
759 | devpriv->config_bits = 0; | |
760 | outw(devpriv->config_bits, dev->iobase + CONFIG_REG); | |
761 | /* wait until offset calibration is done, then enable analog inputs */ | |
762 | for (i = 0; i < timeout; i++) { | |
763 | if ((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0) | |
764 | break; | |
765 | udelay(1000); | |
766 | } | |
767 | if (i == timeout) { | |
bc0640a7 HS |
768 | dev_err(dev->class_dev, |
769 | "timed out waiting for offset calibration to complete\n"); | |
fe14fa2b HS |
770 | return -ETIME; |
771 | } | |
772 | devpriv->config_bits |= ENABLE0_BIT | ENABLE1_BIT; | |
773 | outw(devpriv->config_bits, dev->iobase + CONFIG_REG); | |
774 | ||
775 | return 0; | |
776 | }; | |
777 | ||
484ecc95 | 778 | static void a2150_detach(struct comedi_device *dev) |
fe14fa2b | 779 | { |
a32c6d00 | 780 | if (dev->iobase) |
fe14fa2b | 781 | outw(APD_BIT | DPD_BIT, dev->iobase + CONFIG_REG); |
b1478901 | 782 | a2150_free_dma(dev); |
a32c6d00 | 783 | comedi_legacy_detach(dev); |
fe14fa2b HS |
784 | }; |
785 | ||
786 | static struct comedi_driver ni_at_a2150_driver = { | |
787 | .driver_name = "ni_at_a2150", | |
788 | .module = THIS_MODULE, | |
789 | .attach = a2150_attach, | |
790 | .detach = a2150_detach, | |
791 | }; | |
792 | module_comedi_driver(ni_at_a2150_driver); | |
793 | ||
90f703d3 AT |
794 | MODULE_AUTHOR("Comedi http://www.comedi.org"); |
795 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
796 | MODULE_LICENSE("GPL"); |