Commit | Line | Data |
---|---|---|
e64374f8 DS |
1 | /* |
2 | comedi/drivers/das16.c | |
3 | DAS16 driver | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
7 | Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com> | |
8 | Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net> | |
9 | ||
10 | This program is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2 of the License, or | |
13 | (at your option) any later version. | |
14 | ||
15 | This program is distributed in the hope that it will be useful, | |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
19 | ||
20 | You should have received a copy of the GNU General Public License | |
21 | along with this program; if not, write to the Free Software | |
22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | ||
24 | ************************************************************************ | |
25 | */ | |
26 | /* | |
27 | Driver: das16 | |
28 | Description: DAS16 compatible boards | |
29 | Author: Sam Moore, Warren Jasper, ds, Chris Baugher, Frank Hess, Roman Fietze | |
30 | Devices: [Keithley Metrabyte] DAS-16 (das-16), DAS-16G (das-16g), | |
31 | DAS-16F (das-16f), DAS-1201 (das-1201), DAS-1202 (das-1202), | |
32 | DAS-1401 (das-1401), DAS-1402 (das-1402), DAS-1601 (das-1601), | |
33 | DAS-1602 (das-1602), | |
34 | [ComputerBoards] PC104-DAS16/JR (pc104-das16jr), | |
35 | PC104-DAS16JR/16 (pc104-das16jr/16), | |
36 | CIO-DAS16JR/16 (cio-das16jr/16), | |
37 | CIO-DAS16/JR (cio-das16/jr), CIO-DAS1401/12 (cio-das1401/12), | |
38 | CIO-DAS1402/12 (cio-das1402/12), CIO-DAS1402/16 (cio-das1402/16), | |
39 | CIO-DAS1601/12 (cio-das1601/12), CIO-DAS1602/12 (cio-das1602/12), | |
40 | CIO-DAS1602/16 (cio-das1602/16), CIO-DAS16/330 (cio-das16/330) | |
41 | Status: works | |
42 | Updated: 2003-10-12 | |
43 | ||
44 | A rewrite of the das16 and das1600 drivers. | |
45 | Options: | |
46 | [0] - base io address | |
47 | [1] - irq (does nothing, irq is not used anymore) | |
48 | [2] - dma (optional, required for comedi_command support) | |
49 | [3] - master clock speed in MHz (optional, 1 or 10, ignored if | |
50 | board can probe clock, defaults to 1) | |
51 | [4] - analog input range lowest voltage in microvolts (optional, | |
52 | only useful if your board does not have software | |
53 | programmable gain) | |
54 | [5] - analog input range highest voltage in microvolts (optional, | |
55 | only useful if board does not have software programmable | |
56 | gain) | |
57 | [6] - analog output range lowest voltage in microvolts (optional) | |
58 | [7] - analog output range highest voltage in microvolts (optional) | |
59 | [8] - use timer mode for DMA. Timer mode is needed e.g. for | |
60 | buggy DMA controllers in NS CS5530A (Geode Companion), and for | |
61 | 'jr' cards that lack a hardware fifo. This option is no | |
62 | longer needed, since timer mode is _always_ used. | |
63 | ||
64 | Passing a zero for an option is the same as leaving it unspecified. | |
65 | ||
66 | */ | |
67 | /* | |
68 | ||
69 | Testing and debugging help provided by Daniel Koch. | |
70 | ||
71 | Keithley Manuals: | |
72 | 2309.PDF (das16) | |
73 | 4919.PDF (das1400, 1600) | |
74 | 4922.PDF (das-1400) | |
75 | 4923.PDF (das1200, 1400, 1600) | |
76 | ||
3c0d681e GS |
77 | Computer boards manuals also available from their website |
78 | www.measurementcomputing.com | |
e64374f8 DS |
79 | |
80 | */ | |
81 | ||
82 | #include <linux/pci.h> | |
5a0e3ad6 | 83 | #include <linux/slab.h> |
25436dc9 | 84 | #include <linux/interrupt.h> |
e64374f8 DS |
85 | #include <asm/dma.h> |
86 | #include "../comedidev.h" | |
87 | ||
88 | #include "8253.h" | |
89 | #include "8255.h" | |
90 | #include "comedi_fc.h" | |
91 | ||
92 | #undef DEBUG | |
2696fb57 | 93 | /* #define DEBUG */ |
e64374f8 DS |
94 | |
95 | #ifdef DEBUG | |
3c0d681e GS |
96 | #define DEBUG_PRINT(format, args...) \ |
97 | printk(KERN_DEBUG "das16: " format, ## args) | |
e64374f8 DS |
98 | #else |
99 | #define DEBUG_PRINT(format, args...) | |
100 | #endif | |
101 | ||
2696fb57 BP |
102 | #define DAS16_SIZE 20 /* number of ioports */ |
103 | #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ | |
e64374f8 DS |
104 | |
105 | /* | |
106 | cio-das16.pdf | |
107 | ||
108 | "das16" | |
109 | "das16/f" | |
110 | ||
111 | 0 a/d bits 0-3 start 12 bit | |
112 | 1 a/d bits 4-11 unused | |
113 | 2 mux read mux set | |
114 | 3 di 4 bit do 4 bit | |
115 | 4 unused ao0_lsb | |
116 | 5 unused ao0_msb | |
117 | 6 unused ao1_lsb | |
118 | 7 unused ao1_msb | |
119 | 8 status eoc uni/bip interrupt reset | |
120 | 9 dma, int, trig ctrl set dma, int | |
121 | a pacer control unused | |
122 | b reserved reserved | |
123 | cdef 8254 | |
124 | 0123 8255 | |
125 | ||
126 | */ | |
127 | ||
128 | /* | |
129 | cio-das16jr.pdf | |
130 | ||
131 | "das16jr" | |
132 | ||
133 | 0 a/d bits 0-3 start 12 bit | |
134 | 1 a/d bits 4-11 unused | |
135 | 2 mux read mux set | |
136 | 3 di 4 bit do 4 bit | |
137 | 4567 unused unused | |
138 | 8 status eoc uni/bip interrupt reset | |
139 | 9 dma, int, trig ctrl set dma, int | |
140 | a pacer control unused | |
141 | b gain status gain control | |
142 | cdef 8254 | |
143 | ||
144 | */ | |
145 | ||
146 | /* | |
147 | cio-das16jr_16.pdf | |
148 | ||
149 | "das16jr_16" | |
150 | ||
151 | 0 a/d bits 0-7 start 16 bit | |
152 | 1 a/d bits 8-15 unused | |
153 | 2 mux read mux set | |
154 | 3 di 4 bit do 4 bit | |
155 | 4567 unused unused | |
156 | 8 status eoc uni/bip interrupt reset | |
157 | 9 dma, int, trig ctrl set dma, int | |
158 | a pacer control unused | |
159 | b gain status gain control | |
160 | cdef 8254 | |
161 | ||
162 | */ | |
163 | /* | |
164 | cio-das160x-1x.pdf | |
165 | ||
166 | "das1601/12" | |
167 | "das1602/12" | |
168 | "das1602/16" | |
169 | ||
170 | 0 a/d bits 0-3 start 12 bit | |
171 | 1 a/d bits 4-11 unused | |
172 | 2 mux read mux set | |
173 | 3 di 4 bit do 4 bit | |
174 | 4 unused ao0_lsb | |
175 | 5 unused ao0_msb | |
176 | 6 unused ao1_lsb | |
177 | 7 unused ao1_msb | |
178 | 8 status eoc uni/bip interrupt reset | |
179 | 9 dma, int, trig ctrl set dma, int | |
180 | a pacer control unused | |
181 | b gain status gain control | |
182 | cdef 8254 | |
183 | 400 8255 | |
184 | 404 unused conversion enable | |
185 | 405 unused burst enable | |
186 | 406 unused das1600 enable | |
187 | 407 status | |
188 | ||
189 | */ | |
190 | ||
3c0d681e GS |
191 | /* size in bytes of a sample from board */ |
192 | static const int sample_size = 2; | |
e64374f8 DS |
193 | |
194 | #define DAS16_TRIG 0 | |
195 | #define DAS16_AI_LSB 0 | |
196 | #define DAS16_AI_MSB 1 | |
197 | #define DAS16_MUX 2 | |
198 | #define DAS16_DIO 3 | |
3c0d681e GS |
199 | #define DAS16_AO_LSB(x) ((x) ? 6 : 4) |
200 | #define DAS16_AO_MSB(x) ((x) ? 7 : 5) | |
e64374f8 DS |
201 | #define DAS16_STATUS 8 |
202 | #define BUSY (1<<7) | |
203 | #define UNIPOLAR (1<<6) | |
204 | #define DAS16_MUXBIT (1<<5) | |
205 | #define DAS16_INT (1<<4) | |
206 | #define DAS16_CONTROL 9 | |
207 | #define DAS16_INTE (1<<7) | |
208 | #define DAS16_IRQ(x) (((x) & 0x7) << 4) | |
209 | #define DMA_ENABLE (1<<2) | |
210 | #define PACING_MASK 0x3 | |
211 | #define INT_PACER 0x03 | |
212 | #define EXT_PACER 0x02 | |
213 | #define DAS16_SOFT 0x00 | |
214 | #define DAS16_PACER 0x0A | |
215 | #define DAS16_CTR0 (1<<1) | |
216 | #define DAS16_TRIG0 (1<<0) | |
217 | #define BURST_LEN_BITS(x) (((x) & 0xf) << 4) | |
218 | #define DAS16_GAIN 0x0B | |
219 | #define DAS16_CNTR0_DATA 0x0C | |
220 | #define DAS16_CNTR1_DATA 0x0D | |
221 | #define DAS16_CNTR2_DATA 0x0E | |
222 | #define DAS16_CNTR_CONTROL 0x0F | |
223 | #define DAS16_TERM_CNT 0x00 | |
224 | #define DAS16_ONE_SHOT 0x02 | |
225 | #define DAS16_RATE_GEN 0x04 | |
226 | #define DAS16_CNTR_LSB_MSB 0x30 | |
227 | #define DAS16_CNTR0 0x00 | |
228 | #define DAS16_CNTR1 0x40 | |
229 | #define DAS16_CNTR2 0x80 | |
230 | ||
231 | #define DAS1600_CONV 0x404 | |
232 | #define DAS1600_CONV_DISABLE 0x40 | |
233 | #define DAS1600_BURST 0x405 | |
234 | #define DAS1600_BURST_VAL 0x40 | |
235 | #define DAS1600_ENABLE 0x406 | |
236 | #define DAS1600_ENABLE_VAL 0x40 | |
237 | #define DAS1600_STATUS_B 0x407 | |
238 | #define DAS1600_BME 0x40 | |
239 | #define DAS1600_ME 0x20 | |
240 | #define DAS1600_CD 0x10 | |
241 | #define DAS1600_WS 0x02 | |
242 | #define DAS1600_CLK_10MHZ 0x01 | |
243 | ||
9ced1de6 | 244 | static const struct comedi_lrange range_das1x01_bip = { 4, { |
0a85b6f0 MT |
245 | BIP_RANGE(10), |
246 | BIP_RANGE(1), | |
247 | BIP_RANGE(0.1), | |
248 | BIP_RANGE(0.01), | |
249 | } | |
e64374f8 | 250 | }; |
0a85b6f0 | 251 | |
9ced1de6 | 252 | static const struct comedi_lrange range_das1x01_unip = { 4, { |
0a85b6f0 MT |
253 | UNI_RANGE(10), |
254 | UNI_RANGE(1), | |
255 | UNI_RANGE(0.1), | |
256 | UNI_RANGE(0.01), | |
257 | } | |
e64374f8 | 258 | }; |
0a85b6f0 | 259 | |
9ced1de6 | 260 | static const struct comedi_lrange range_das1x02_bip = { 4, { |
0a85b6f0 MT |
261 | BIP_RANGE(10), |
262 | BIP_RANGE(5), | |
263 | BIP_RANGE(2.5), | |
264 | BIP_RANGE(1.25), | |
265 | } | |
e64374f8 | 266 | }; |
0a85b6f0 | 267 | |
9ced1de6 | 268 | static const struct comedi_lrange range_das1x02_unip = { 4, { |
0a85b6f0 MT |
269 | UNI_RANGE(10), |
270 | UNI_RANGE(5), | |
271 | UNI_RANGE(2.5), | |
272 | UNI_RANGE(1.25), | |
273 | } | |
e64374f8 | 274 | }; |
0a85b6f0 | 275 | |
9ced1de6 | 276 | static const struct comedi_lrange range_das16jr = { 9, { |
3c0d681e | 277 | /* also used by 16/330 */ |
0a85b6f0 MT |
278 | BIP_RANGE(10), |
279 | BIP_RANGE(5), | |
280 | BIP_RANGE(2.5), | |
281 | BIP_RANGE(1.25), | |
282 | BIP_RANGE(0.625), | |
283 | UNI_RANGE(10), | |
284 | UNI_RANGE(5), | |
285 | UNI_RANGE(2.5), | |
286 | UNI_RANGE(1.25), | |
287 | } | |
e64374f8 | 288 | }; |
0a85b6f0 | 289 | |
9ced1de6 | 290 | static const struct comedi_lrange range_das16jr_16 = { 8, { |
0a85b6f0 MT |
291 | BIP_RANGE(10), |
292 | BIP_RANGE(5), | |
293 | BIP_RANGE(2.5), | |
294 | BIP_RANGE(1.25), | |
295 | UNI_RANGE(10), | |
296 | UNI_RANGE(5), | |
297 | UNI_RANGE(2.5), | |
298 | UNI_RANGE(1.25), | |
299 | } | |
e64374f8 DS |
300 | }; |
301 | ||
302 | static const int das16jr_gainlist[] = { 8, 0, 1, 2, 3, 4, 5, 6, 7 }; | |
303 | static const int das16jr_16_gainlist[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; | |
304 | static const int das1600_gainlist[] = { 0, 1, 2, 3 }; | |
0a85b6f0 | 305 | |
e64374f8 DS |
306 | enum { |
307 | das16_pg_none = 0, | |
308 | das16_pg_16jr, | |
309 | das16_pg_16jr_16, | |
310 | das16_pg_1601, | |
311 | das16_pg_1602, | |
312 | }; | |
313 | static const int *const das16_gainlists[] = { | |
314 | NULL, | |
315 | das16jr_gainlist, | |
316 | das16jr_16_gainlist, | |
317 | das1600_gainlist, | |
318 | das1600_gainlist, | |
319 | }; | |
0a85b6f0 | 320 | |
9ced1de6 | 321 | static const struct comedi_lrange *const das16_ai_uni_lranges[] = { |
e64374f8 DS |
322 | &range_unknown, |
323 | &range_das16jr, | |
324 | &range_das16jr_16, | |
325 | &range_das1x01_unip, | |
326 | &range_das1x02_unip, | |
327 | }; | |
0a85b6f0 | 328 | |
9ced1de6 | 329 | static const struct comedi_lrange *const das16_ai_bip_lranges[] = { |
e64374f8 DS |
330 | &range_unknown, |
331 | &range_das16jr, | |
332 | &range_das16jr_16, | |
333 | &range_das1x01_bip, | |
334 | &range_das1x02_bip, | |
335 | }; | |
336 | ||
337 | struct munge_info { | |
338 | uint8_t byte; | |
339 | unsigned have_byte:1; | |
340 | }; | |
341 | ||
814900c9 | 342 | static int das16_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 343 | struct comedi_insn *insn, unsigned int *data); |
814900c9 | 344 | static int das16_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 345 | struct comedi_insn *insn, unsigned int *data); |
814900c9 | 346 | static int das16_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 347 | struct comedi_insn *insn, unsigned int *data); |
814900c9 | 348 | static int das16_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 349 | struct comedi_insn *insn, unsigned int *data); |
814900c9 BP |
350 | |
351 | static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, | |
0a85b6f0 MT |
352 | struct comedi_cmd *cmd); |
353 | static int das16_cmd_exec(struct comedi_device *dev, | |
354 | struct comedi_subdevice *s); | |
814900c9 | 355 | static int das16_cancel(struct comedi_device *dev, struct comedi_subdevice *s); |
0a85b6f0 MT |
356 | static void das16_ai_munge(struct comedi_device *dev, |
357 | struct comedi_subdevice *s, void *array, | |
358 | unsigned int num_bytes, | |
359 | unsigned int start_chan_index); | |
e64374f8 | 360 | |
814900c9 | 361 | static void das16_reset(struct comedi_device *dev); |
70265d24 | 362 | static irqreturn_t das16_dma_interrupt(int irq, void *d); |
e64374f8 | 363 | static void das16_timer_interrupt(unsigned long arg); |
814900c9 | 364 | static void das16_interrupt(struct comedi_device *dev); |
e64374f8 | 365 | |
814900c9 | 366 | static unsigned int das16_set_pacer(struct comedi_device *dev, unsigned int ns, |
0a85b6f0 | 367 | int flags); |
814900c9 BP |
368 | static int das1600_mode_detect(struct comedi_device *dev); |
369 | static unsigned int das16_suggest_transfer_size(struct comedi_device *dev, | |
0a85b6f0 | 370 | struct comedi_cmd cmd); |
e64374f8 | 371 | |
814900c9 | 372 | static void reg_dump(struct comedi_device *dev); |
e64374f8 | 373 | |
947a33fd | 374 | struct das16_board { |
e64374f8 DS |
375 | const char *name; |
376 | void *ai; | |
377 | unsigned int ai_nbits; | |
2696fb57 | 378 | unsigned int ai_speed; /* max conversion speed in nanosec */ |
e64374f8 DS |
379 | unsigned int ai_pg; |
380 | void *ao; | |
381 | unsigned int ao_nbits; | |
382 | void *di; | |
383 | void *do_; | |
384 | ||
385 | unsigned int i8255_offset; | |
386 | unsigned int i8254_offset; | |
387 | ||
388 | unsigned int size; | |
389 | unsigned int id; | |
947a33fd | 390 | }; |
e64374f8 | 391 | |
947a33fd | 392 | static const struct das16_board das16_boards[] = { |
e64374f8 | 393 | { |
0a85b6f0 MT |
394 | .name = "das-16", |
395 | .ai = das16_ai_rinsn, | |
396 | .ai_nbits = 12, | |
397 | .ai_speed = 15000, | |
398 | .ai_pg = das16_pg_none, | |
399 | .ao = das16_ao_winsn, | |
400 | .ao_nbits = 12, | |
401 | .di = das16_di_rbits, | |
402 | .do_ = das16_do_wbits, | |
403 | .i8255_offset = 0x10, | |
404 | .i8254_offset = 0x0c, | |
405 | .size = 0x14, | |
406 | .id = 0x00, | |
407 | }, | |
e64374f8 | 408 | { |
0a85b6f0 MT |
409 | .name = "das-16g", |
410 | .ai = das16_ai_rinsn, | |
411 | .ai_nbits = 12, | |
412 | .ai_speed = 15000, | |
413 | .ai_pg = das16_pg_none, | |
414 | .ao = das16_ao_winsn, | |
415 | .ao_nbits = 12, | |
416 | .di = das16_di_rbits, | |
417 | .do_ = das16_do_wbits, | |
418 | .i8255_offset = 0x10, | |
419 | .i8254_offset = 0x0c, | |
420 | .size = 0x14, | |
421 | .id = 0x00, | |
422 | }, | |
e64374f8 | 423 | { |
0a85b6f0 MT |
424 | .name = "das-16f", |
425 | .ai = das16_ai_rinsn, | |
426 | .ai_nbits = 12, | |
427 | .ai_speed = 8500, | |
428 | .ai_pg = das16_pg_none, | |
429 | .ao = das16_ao_winsn, | |
430 | .ao_nbits = 12, | |
431 | .di = das16_di_rbits, | |
432 | .do_ = das16_do_wbits, | |
433 | .i8255_offset = 0x10, | |
434 | .i8254_offset = 0x0c, | |
435 | .size = 0x14, | |
436 | .id = 0x00, | |
437 | }, | |
e64374f8 | 438 | { |
0a85b6f0 MT |
439 | .name = "cio-das16", /* cio-das16.pdf */ |
440 | .ai = das16_ai_rinsn, | |
441 | .ai_nbits = 12, | |
442 | .ai_speed = 20000, | |
443 | .ai_pg = das16_pg_none, | |
444 | .ao = das16_ao_winsn, | |
445 | .ao_nbits = 12, | |
446 | .di = das16_di_rbits, | |
447 | .do_ = das16_do_wbits, | |
448 | .i8255_offset = 0x10, | |
449 | .i8254_offset = 0x0c, | |
450 | .size = 0x14, | |
451 | .id = 0x80, | |
452 | }, | |
e64374f8 | 453 | { |
0a85b6f0 MT |
454 | .name = "cio-das16/f", /* das16.pdf */ |
455 | .ai = das16_ai_rinsn, | |
456 | .ai_nbits = 12, | |
457 | .ai_speed = 10000, | |
458 | .ai_pg = das16_pg_none, | |
459 | .ao = das16_ao_winsn, | |
460 | .ao_nbits = 12, | |
461 | .di = das16_di_rbits, | |
462 | .do_ = das16_do_wbits, | |
463 | .i8255_offset = 0x10, | |
464 | .i8254_offset = 0x0c, | |
465 | .size = 0x14, | |
466 | .id = 0x80, | |
467 | }, | |
e64374f8 | 468 | { |
0a85b6f0 MT |
469 | .name = "cio-das16/jr", /* cio-das16jr.pdf */ |
470 | .ai = das16_ai_rinsn, | |
471 | .ai_nbits = 12, | |
472 | .ai_speed = 7692, | |
473 | .ai_pg = das16_pg_16jr, | |
474 | .ao = NULL, | |
475 | .di = das16_di_rbits, | |
476 | .do_ = das16_do_wbits, | |
477 | .i8255_offset = 0, | |
478 | .i8254_offset = 0x0c, | |
479 | .size = 0x10, | |
480 | .id = 0x00, | |
481 | }, | |
e64374f8 | 482 | { |
0a85b6f0 MT |
483 | .name = "pc104-das16jr", /* pc104-das16jr_xx.pdf */ |
484 | .ai = das16_ai_rinsn, | |
485 | .ai_nbits = 12, | |
486 | .ai_speed = 3300, | |
487 | .ai_pg = das16_pg_16jr, | |
488 | .ao = NULL, | |
489 | .di = das16_di_rbits, | |
490 | .do_ = das16_do_wbits, | |
491 | .i8255_offset = 0, | |
492 | .i8254_offset = 0x0c, | |
493 | .size = 0x10, | |
494 | .id = 0x00, | |
495 | }, | |
e64374f8 | 496 | { |
0a85b6f0 MT |
497 | .name = "cio-das16jr/16", /* cio-das16jr_16.pdf */ |
498 | .ai = das16_ai_rinsn, | |
499 | .ai_nbits = 16, | |
500 | .ai_speed = 10000, | |
501 | .ai_pg = das16_pg_16jr_16, | |
502 | .ao = NULL, | |
503 | .di = das16_di_rbits, | |
504 | .do_ = das16_do_wbits, | |
505 | .i8255_offset = 0, | |
506 | .i8254_offset = 0x0c, | |
507 | .size = 0x10, | |
508 | .id = 0x00, | |
509 | }, | |
e64374f8 | 510 | { |
0a85b6f0 MT |
511 | .name = "pc104-das16jr/16", /* pc104-das16jr_xx.pdf */ |
512 | .ai = das16_ai_rinsn, | |
513 | .ai_nbits = 16, | |
514 | .ai_speed = 10000, | |
515 | .ai_pg = das16_pg_16jr_16, | |
516 | .ao = NULL, | |
517 | .di = das16_di_rbits, | |
518 | .do_ = das16_do_wbits, | |
519 | .i8255_offset = 0, | |
520 | .i8254_offset = 0x0c, | |
521 | .size = 0x10, | |
522 | .id = 0x00, | |
523 | }, | |
e64374f8 | 524 | { |
0a85b6f0 MT |
525 | .name = "das-1201", /* 4924.pdf (keithley user's manual) */ |
526 | .ai = das16_ai_rinsn, | |
527 | .ai_nbits = 12, | |
528 | .ai_speed = 20000, | |
529 | .ai_pg = das16_pg_none, | |
530 | .ao = NULL, | |
531 | .di = das16_di_rbits, | |
532 | .do_ = das16_do_wbits, | |
533 | .i8255_offset = 0x400, | |
534 | .i8254_offset = 0x0c, | |
535 | .size = 0x408, | |
536 | .id = 0x20, | |
537 | }, | |
e64374f8 | 538 | { |
0a85b6f0 MT |
539 | .name = "das-1202", /* 4924.pdf (keithley user's manual) */ |
540 | .ai = das16_ai_rinsn, | |
541 | .ai_nbits = 12, | |
542 | .ai_speed = 10000, | |
543 | .ai_pg = das16_pg_none, | |
544 | .ao = NULL, | |
545 | .di = das16_di_rbits, | |
546 | .do_ = das16_do_wbits, | |
547 | .i8255_offset = 0x400, | |
548 | .i8254_offset = 0x0c, | |
549 | .size = 0x408, | |
550 | .id = 0x20, | |
551 | }, | |
e64374f8 | 552 | { |
3c0d681e GS |
553 | /* 4919.pdf and 4922.pdf (keithley user's manual) */ |
554 | .name = "das-1401", | |
0a85b6f0 MT |
555 | .ai = das16_ai_rinsn, |
556 | .ai_nbits = 12, | |
557 | .ai_speed = 10000, | |
558 | .ai_pg = das16_pg_1601, | |
559 | .ao = NULL, | |
560 | .di = das16_di_rbits, | |
561 | .do_ = das16_do_wbits, | |
562 | .i8255_offset = 0x0, | |
563 | .i8254_offset = 0x0c, | |
564 | .size = 0x408, | |
3c0d681e | 565 | .id = 0xc0 /* 4919.pdf says id bits are 0xe0, 4922.pdf says 0xc0 */ |
0a85b6f0 | 566 | }, |
e64374f8 | 567 | { |
3c0d681e GS |
568 | /* 4919.pdf and 4922.pdf (keithley user's manual) */ |
569 | .name = "das-1402", | |
0a85b6f0 MT |
570 | .ai = das16_ai_rinsn, |
571 | .ai_nbits = 12, | |
572 | .ai_speed = 10000, | |
573 | .ai_pg = das16_pg_1602, | |
574 | .ao = NULL, | |
575 | .di = das16_di_rbits, | |
576 | .do_ = das16_do_wbits, | |
577 | .i8255_offset = 0x0, | |
578 | .i8254_offset = 0x0c, | |
579 | .size = 0x408, | |
3c0d681e | 580 | .id = 0xc0 /* 4919.pdf says id bits are 0xe0, 4922.pdf says 0xc0 */ |
0a85b6f0 | 581 | }, |
e64374f8 | 582 | { |
0a85b6f0 MT |
583 | .name = "das-1601", /* 4919.pdf */ |
584 | .ai = das16_ai_rinsn, | |
585 | .ai_nbits = 12, | |
586 | .ai_speed = 10000, | |
587 | .ai_pg = das16_pg_1601, | |
588 | .ao = das16_ao_winsn, | |
589 | .ao_nbits = 12, | |
590 | .di = das16_di_rbits, | |
591 | .do_ = das16_do_wbits, | |
592 | .i8255_offset = 0x400, | |
593 | .i8254_offset = 0x0c, | |
594 | .size = 0x408, | |
595 | .id = 0xc0}, | |
e64374f8 | 596 | { |
0a85b6f0 MT |
597 | .name = "das-1602", /* 4919.pdf */ |
598 | .ai = das16_ai_rinsn, | |
599 | .ai_nbits = 12, | |
600 | .ai_speed = 10000, | |
601 | .ai_pg = das16_pg_1602, | |
602 | .ao = das16_ao_winsn, | |
603 | .ao_nbits = 12, | |
604 | .di = das16_di_rbits, | |
605 | .do_ = das16_do_wbits, | |
606 | .i8255_offset = 0x400, | |
607 | .i8254_offset = 0x0c, | |
608 | .size = 0x408, | |
609 | .id = 0xc0}, | |
e64374f8 | 610 | { |
0a85b6f0 MT |
611 | .name = "cio-das1401/12", /* cio-das1400_series.pdf */ |
612 | .ai = das16_ai_rinsn, | |
613 | .ai_nbits = 12, | |
614 | .ai_speed = 6250, | |
615 | .ai_pg = das16_pg_1601, | |
616 | .ao = NULL, | |
617 | .di = das16_di_rbits, | |
618 | .do_ = das16_do_wbits, | |
619 | .i8255_offset = 0, | |
620 | .i8254_offset = 0x0c, | |
621 | .size = 0x408, | |
622 | .id = 0xc0}, | |
e64374f8 | 623 | { |
0a85b6f0 MT |
624 | .name = "cio-das1402/12", /* cio-das1400_series.pdf */ |
625 | .ai = das16_ai_rinsn, | |
626 | .ai_nbits = 12, | |
627 | .ai_speed = 6250, | |
628 | .ai_pg = das16_pg_1602, | |
629 | .ao = NULL, | |
630 | .di = das16_di_rbits, | |
631 | .do_ = das16_do_wbits, | |
632 | .i8255_offset = 0, | |
633 | .i8254_offset = 0x0c, | |
634 | .size = 0x408, | |
635 | .id = 0xc0}, | |
e64374f8 | 636 | { |
0a85b6f0 MT |
637 | .name = "cio-das1402/16", /* cio-das1400_series.pdf */ |
638 | .ai = das16_ai_rinsn, | |
639 | .ai_nbits = 16, | |
640 | .ai_speed = 10000, | |
641 | .ai_pg = das16_pg_1602, | |
642 | .ao = NULL, | |
643 | .di = das16_di_rbits, | |
644 | .do_ = das16_do_wbits, | |
645 | .i8255_offset = 0, | |
646 | .i8254_offset = 0x0c, | |
647 | .size = 0x408, | |
648 | .id = 0xc0}, | |
e64374f8 | 649 | { |
0a85b6f0 MT |
650 | .name = "cio-das1601/12", /* cio-das160x-1x.pdf */ |
651 | .ai = das16_ai_rinsn, | |
652 | .ai_nbits = 12, | |
653 | .ai_speed = 6250, | |
654 | .ai_pg = das16_pg_1601, | |
655 | .ao = das16_ao_winsn, | |
656 | .ao_nbits = 12, | |
657 | .di = das16_di_rbits, | |
658 | .do_ = das16_do_wbits, | |
659 | .i8255_offset = 0x400, | |
660 | .i8254_offset = 0x0c, | |
661 | .size = 0x408, | |
662 | .id = 0xc0}, | |
e64374f8 | 663 | { |
0a85b6f0 MT |
664 | .name = "cio-das1602/12", /* cio-das160x-1x.pdf */ |
665 | .ai = das16_ai_rinsn, | |
666 | .ai_nbits = 12, | |
667 | .ai_speed = 10000, | |
668 | .ai_pg = das16_pg_1602, | |
669 | .ao = das16_ao_winsn, | |
670 | .ao_nbits = 12, | |
671 | .di = das16_di_rbits, | |
672 | .do_ = das16_do_wbits, | |
673 | .i8255_offset = 0x400, | |
674 | .i8254_offset = 0x0c, | |
675 | .size = 0x408, | |
676 | .id = 0xc0}, | |
e64374f8 | 677 | { |
0a85b6f0 MT |
678 | .name = "cio-das1602/16", /* cio-das160x-1x.pdf */ |
679 | .ai = das16_ai_rinsn, | |
680 | .ai_nbits = 16, | |
681 | .ai_speed = 10000, | |
682 | .ai_pg = das16_pg_1602, | |
683 | .ao = das16_ao_winsn, | |
684 | .ao_nbits = 12, | |
685 | .di = das16_di_rbits, | |
686 | .do_ = das16_do_wbits, | |
687 | .i8255_offset = 0x400, | |
688 | .i8254_offset = 0x0c, | |
689 | .size = 0x408, | |
690 | .id = 0xc0}, | |
e64374f8 | 691 | { |
0a85b6f0 MT |
692 | .name = "cio-das16/330", /* ? */ |
693 | .ai = das16_ai_rinsn, | |
694 | .ai_nbits = 12, | |
695 | .ai_speed = 3030, | |
696 | .ai_pg = das16_pg_16jr, | |
697 | .ao = NULL, | |
698 | .di = das16_di_rbits, | |
699 | .do_ = das16_do_wbits, | |
700 | .i8255_offset = 0, | |
701 | .i8254_offset = 0x0c, | |
702 | .size = 0x14, | |
703 | .id = 0xf0}, | |
e64374f8 DS |
704 | #if 0 |
705 | { | |
0a85b6f0 MT |
706 | .name = "das16/330i", /* ? */ |
707 | }, | |
e64374f8 | 708 | { |
0a85b6f0 MT |
709 | .name = "das16/jr/ctr5", /* ? */ |
710 | }, | |
e64374f8 | 711 | { |
3c0d681e GS |
712 | /* cio-das16_m1_16.pdf, this board is a bit quirky, no dma */ |
713 | .name = "cio-das16/m1/16", | |
0a85b6f0 | 714 | }, |
e64374f8 DS |
715 | #endif |
716 | }; | |
717 | ||
da91b269 BP |
718 | static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
719 | static int das16_detach(struct comedi_device *dev); | |
139dfbdf | 720 | static struct comedi_driver driver_das16 = { |
68c3dbff BP |
721 | .driver_name = "das16", |
722 | .module = THIS_MODULE, | |
723 | .attach = das16_attach, | |
724 | .detach = das16_detach, | |
725 | .board_name = &das16_boards[0].name, | |
8629efa4 | 726 | .num_names = ARRAY_SIZE(das16_boards), |
68c3dbff | 727 | .offset = sizeof(das16_boards[0]), |
e64374f8 DS |
728 | }; |
729 | ||
730 | #define DAS16_TIMEOUT 1000 | |
731 | ||
732 | /* Period for timer interrupt in jiffies. It's a function | |
733 | * to deal with possibility of dynamic HZ patches */ | |
734 | static inline int timer_period(void) | |
735 | { | |
736 | return HZ / 20; | |
737 | } | |
0a85b6f0 | 738 | |
e64374f8 | 739 | struct das16_private_struct { |
2696fb57 BP |
740 | unsigned int ai_unipolar; /* unipolar flag */ |
741 | unsigned int ai_singleended; /* single ended flag */ | |
742 | unsigned int clockbase; /* master clock speed in ns */ | |
743 | volatile unsigned int control_state; /* dma, interrupt and trigger control bits */ | |
744 | volatile unsigned long adc_byte_count; /* number of bytes remaining */ | |
3c0d681e GS |
745 | /* divisor dividing master clock to get conversion frequency */ |
746 | unsigned int divisor1; | |
747 | /* divisor dividing master clock to get conversion frequency */ | |
748 | unsigned int divisor2; | |
2696fb57 | 749 | unsigned int dma_chan; /* dma channel */ |
e64374f8 DS |
750 | uint16_t *dma_buffer[2]; |
751 | dma_addr_t dma_buffer_addr[2]; | |
752 | unsigned int current_buffer; | |
2696fb57 | 753 | volatile unsigned int dma_transfer_size; /* target number of bytes to transfer per dma shot */ |
3c0d681e GS |
754 | /** |
755 | * user-defined analog input and output ranges | |
756 | * defined from config options | |
757 | */ | |
9ced1de6 BP |
758 | struct comedi_lrange *user_ai_range_table; |
759 | struct comedi_lrange *user_ao_range_table; | |
e64374f8 | 760 | |
2696fb57 | 761 | struct timer_list timer; /* for timed interrupt */ |
e64374f8 | 762 | volatile short timer_running; |
2696fb57 | 763 | volatile short timer_mode; /* true if using timer mode */ |
e64374f8 DS |
764 | }; |
765 | #define devpriv ((struct das16_private_struct *)(dev->private)) | |
947a33fd | 766 | #define thisboard ((struct das16_board *)(dev->board_ptr)) |
e64374f8 | 767 | |
da91b269 | 768 | static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 769 | struct comedi_cmd *cmd) |
e64374f8 DS |
770 | { |
771 | int err = 0, tmp; | |
772 | int gain, start_chan, i; | |
773 | int mask; | |
774 | ||
775 | /* make sure triggers are valid */ | |
776 | tmp = cmd->start_src; | |
777 | cmd->start_src &= TRIG_NOW; | |
778 | if (!cmd->start_src || tmp != cmd->start_src) | |
779 | err++; | |
780 | ||
781 | tmp = cmd->scan_begin_src; | |
782 | mask = TRIG_FOLLOW; | |
2696fb57 | 783 | /* if board supports burst mode */ |
e64374f8 DS |
784 | if (thisboard->size > 0x400) |
785 | mask |= TRIG_TIMER | TRIG_EXT; | |
786 | cmd->scan_begin_src &= mask; | |
787 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) | |
788 | err++; | |
789 | ||
790 | tmp = cmd->convert_src; | |
791 | mask = TRIG_TIMER | TRIG_EXT; | |
2696fb57 | 792 | /* if board supports burst mode */ |
e64374f8 DS |
793 | if (thisboard->size > 0x400) |
794 | mask |= TRIG_NOW; | |
795 | cmd->convert_src &= mask; | |
796 | if (!cmd->convert_src || tmp != cmd->convert_src) | |
797 | err++; | |
798 | ||
799 | tmp = cmd->scan_end_src; | |
800 | cmd->scan_end_src &= TRIG_COUNT; | |
801 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) | |
802 | err++; | |
803 | ||
804 | tmp = cmd->stop_src; | |
805 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE; | |
806 | if (!cmd->stop_src || tmp != cmd->stop_src) | |
807 | err++; | |
808 | ||
809 | if (err) | |
810 | return 1; | |
811 | ||
3c0d681e GS |
812 | /** |
813 | * step 2: make sure trigger sources are unique and | |
814 | * mutually compatible | |
815 | */ | |
e64374f8 | 816 | if (cmd->scan_begin_src != TRIG_TIMER && |
0a85b6f0 MT |
817 | cmd->scan_begin_src != TRIG_EXT && |
818 | cmd->scan_begin_src != TRIG_FOLLOW) | |
e64374f8 DS |
819 | err++; |
820 | if (cmd->convert_src != TRIG_TIMER && | |
0a85b6f0 | 821 | cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) |
e64374f8 DS |
822 | err++; |
823 | if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) | |
824 | err++; | |
825 | ||
2696fb57 | 826 | /* make sure scan_begin_src and convert_src dont conflict */ |
e64374f8 DS |
827 | if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW) |
828 | err++; | |
829 | if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW) | |
830 | err++; | |
831 | ||
832 | if (err) | |
833 | return 2; | |
834 | ||
835 | /* step 3: make sure arguments are trivially compatible */ | |
836 | if (cmd->start_arg != 0) { | |
837 | cmd->start_arg = 0; | |
838 | err++; | |
839 | } | |
840 | ||
841 | if (cmd->scan_begin_src == TRIG_FOLLOW) { | |
842 | /* internal trigger */ | |
843 | if (cmd->scan_begin_arg != 0) { | |
844 | cmd->scan_begin_arg = 0; | |
845 | err++; | |
846 | } | |
847 | } | |
848 | ||
849 | if (cmd->scan_end_arg != cmd->chanlist_len) { | |
850 | cmd->scan_end_arg = cmd->chanlist_len; | |
851 | err++; | |
852 | } | |
2696fb57 | 853 | /* check against maximum frequency */ |
e64374f8 DS |
854 | if (cmd->scan_begin_src == TRIG_TIMER) { |
855 | if (cmd->scan_begin_arg < | |
0a85b6f0 | 856 | thisboard->ai_speed * cmd->chanlist_len) { |
e64374f8 | 857 | cmd->scan_begin_arg = |
0a85b6f0 | 858 | thisboard->ai_speed * cmd->chanlist_len; |
e64374f8 DS |
859 | err++; |
860 | } | |
861 | } | |
862 | if (cmd->convert_src == TRIG_TIMER) { | |
863 | if (cmd->convert_arg < thisboard->ai_speed) { | |
864 | cmd->convert_arg = thisboard->ai_speed; | |
865 | err++; | |
866 | } | |
867 | } | |
868 | ||
869 | if (cmd->stop_src == TRIG_NONE) { | |
870 | if (cmd->stop_arg != 0) { | |
871 | cmd->stop_arg = 0; | |
872 | err++; | |
873 | } | |
874 | } | |
875 | if (err) | |
876 | return 3; | |
877 | ||
2696fb57 | 878 | /* step 4: fix up arguments */ |
e64374f8 DS |
879 | if (cmd->scan_begin_src == TRIG_TIMER) { |
880 | unsigned int tmp = cmd->scan_begin_arg; | |
2696fb57 | 881 | /* set divisors, correct timing arguments */ |
e64374f8 | 882 | i8253_cascade_ns_to_timer_2div(devpriv->clockbase, |
0a85b6f0 MT |
883 | &(devpriv->divisor1), |
884 | &(devpriv->divisor2), | |
885 | &(cmd->scan_begin_arg), | |
886 | cmd->flags & TRIG_ROUND_MASK); | |
e64374f8 DS |
887 | err += (tmp != cmd->scan_begin_arg); |
888 | } | |
889 | if (cmd->convert_src == TRIG_TIMER) { | |
890 | unsigned int tmp = cmd->convert_arg; | |
2696fb57 | 891 | /* set divisors, correct timing arguments */ |
e64374f8 | 892 | i8253_cascade_ns_to_timer_2div(devpriv->clockbase, |
0a85b6f0 MT |
893 | &(devpriv->divisor1), |
894 | &(devpriv->divisor2), | |
895 | &(cmd->convert_arg), | |
896 | cmd->flags & TRIG_ROUND_MASK); | |
e64374f8 DS |
897 | err += (tmp != cmd->convert_arg); |
898 | } | |
899 | if (err) | |
900 | return 4; | |
901 | ||
2696fb57 | 902 | /* check channel/gain list against card's limitations */ |
e64374f8 DS |
903 | if (cmd->chanlist) { |
904 | gain = CR_RANGE(cmd->chanlist[0]); | |
905 | start_chan = CR_CHAN(cmd->chanlist[0]); | |
906 | for (i = 1; i < cmd->chanlist_len; i++) { | |
907 | if (CR_CHAN(cmd->chanlist[i]) != | |
0a85b6f0 | 908 | (start_chan + i) % s->n_chan) { |
e64374f8 | 909 | comedi_error(dev, |
3c0d681e GS |
910 | "entries in chanlist must be " |
911 | "consecutive channels, " | |
912 | "counting upwards\n"); | |
e64374f8 DS |
913 | err++; |
914 | } | |
915 | if (CR_RANGE(cmd->chanlist[i]) != gain) { | |
916 | comedi_error(dev, | |
3c0d681e GS |
917 | "entries in chanlist must all " |
918 | "have the same gain\n"); | |
e64374f8 DS |
919 | err++; |
920 | } | |
921 | } | |
922 | } | |
923 | if (err) | |
924 | return 5; | |
925 | ||
926 | return 0; | |
927 | } | |
928 | ||
da91b269 | 929 | static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) |
e64374f8 | 930 | { |
d163679c | 931 | struct comedi_async *async = s->async; |
ea6d0d4c | 932 | struct comedi_cmd *cmd = &async->cmd; |
e64374f8 DS |
933 | unsigned int byte; |
934 | unsigned long flags; | |
935 | int range; | |
936 | ||
937 | if (devpriv->dma_chan == 0 || (dev->irq == 0 | |
0a85b6f0 | 938 | && devpriv->timer_mode == 0)) { |
e64374f8 | 939 | comedi_error(dev, |
3c0d681e GS |
940 | "irq (or use of 'timer mode') dma required to " |
941 | "execute comedi_cmd"); | |
e64374f8 DS |
942 | return -1; |
943 | } | |
944 | if (cmd->flags & TRIG_RT) { | |
3c0d681e GS |
945 | comedi_error(dev, "isa dma transfers cannot be performed with " |
946 | "TRIG_RT, aborting"); | |
e64374f8 DS |
947 | return -1; |
948 | } | |
949 | ||
950 | devpriv->adc_byte_count = | |
0a85b6f0 | 951 | cmd->stop_arg * cmd->chanlist_len * sizeof(uint16_t); |
e64374f8 | 952 | |
2696fb57 | 953 | /* disable conversions for das1600 mode */ |
3c0d681e | 954 | if (thisboard->size > 0x400) |
e64374f8 | 955 | outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV); |
3c0d681e | 956 | |
2696fb57 | 957 | /* set scan limits */ |
e64374f8 DS |
958 | byte = CR_CHAN(cmd->chanlist[0]); |
959 | byte |= CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]) << 4; | |
960 | outb(byte, dev->iobase + DAS16_MUX); | |
961 | ||
962 | /* set gain (this is also burst rate register but according to | |
3c0d681e GS |
963 | * computer boards manual, burst rate does nothing, even on |
964 | * keithley cards) */ | |
e64374f8 DS |
965 | if (thisboard->ai_pg != das16_pg_none) { |
966 | range = CR_RANGE(cmd->chanlist[0]); | |
967 | outb((das16_gainlists[thisboard->ai_pg])[range], | |
0a85b6f0 | 968 | dev->iobase + DAS16_GAIN); |
e64374f8 DS |
969 | } |
970 | ||
971 | /* set counter mode and counts */ | |
972 | cmd->convert_arg = | |
0a85b6f0 MT |
973 | das16_set_pacer(dev, cmd->convert_arg, |
974 | cmd->flags & TRIG_ROUND_MASK); | |
e64374f8 DS |
975 | DEBUG_PRINT("pacer period: %d ns\n", cmd->convert_arg); |
976 | ||
977 | /* enable counters */ | |
978 | byte = 0; | |
979 | /* Enable burst mode if appropriate. */ | |
980 | if (thisboard->size > 0x400) { | |
981 | if (cmd->convert_src == TRIG_NOW) { | |
982 | outb(DAS1600_BURST_VAL, dev->iobase + DAS1600_BURST); | |
2696fb57 | 983 | /* set burst length */ |
e64374f8 DS |
984 | byte |= BURST_LEN_BITS(cmd->chanlist_len - 1); |
985 | } else { | |
986 | outb(0, dev->iobase + DAS1600_BURST); | |
987 | } | |
988 | } | |
989 | outb(byte, dev->iobase + DAS16_PACER); | |
990 | ||
2696fb57 | 991 | /* set up dma transfer */ |
e64374f8 DS |
992 | flags = claim_dma_lock(); |
993 | disable_dma(devpriv->dma_chan); | |
994 | /* clear flip-flop to make sure 2-byte registers for | |
995 | * count and address get set correctly */ | |
996 | clear_dma_ff(devpriv->dma_chan); | |
997 | devpriv->current_buffer = 0; | |
998 | set_dma_addr(devpriv->dma_chan, | |
0a85b6f0 | 999 | devpriv->dma_buffer_addr[devpriv->current_buffer]); |
2696fb57 | 1000 | /* set appropriate size of transfer */ |
e64374f8 DS |
1001 | devpriv->dma_transfer_size = das16_suggest_transfer_size(dev, *cmd); |
1002 | set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); | |
1003 | enable_dma(devpriv->dma_chan); | |
1004 | release_dma_lock(flags); | |
1005 | ||
2696fb57 | 1006 | /* set up interrupt */ |
e64374f8 DS |
1007 | if (devpriv->timer_mode) { |
1008 | devpriv->timer_running = 1; | |
1009 | devpriv->timer.expires = jiffies + timer_period(); | |
1010 | add_timer(&devpriv->timer); | |
1011 | devpriv->control_state &= ~DAS16_INTE; | |
1012 | } else { | |
1013 | /* clear interrupt bit */ | |
1014 | outb(0x00, dev->iobase + DAS16_STATUS); | |
1015 | /* enable interrupts */ | |
1016 | devpriv->control_state |= DAS16_INTE; | |
1017 | } | |
1018 | devpriv->control_state |= DMA_ENABLE; | |
1019 | devpriv->control_state &= ~PACING_MASK; | |
1020 | if (cmd->convert_src == TRIG_EXT) | |
1021 | devpriv->control_state |= EXT_PACER; | |
1022 | else | |
1023 | devpriv->control_state |= INT_PACER; | |
1024 | outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); | |
1025 | ||
1026 | /* Enable conversions if using das1600 mode */ | |
3c0d681e | 1027 | if (thisboard->size > 0x400) |
e64374f8 | 1028 | outb(0, dev->iobase + DAS1600_CONV); |
3c0d681e | 1029 | |
e64374f8 DS |
1030 | |
1031 | return 0; | |
1032 | } | |
1033 | ||
da91b269 | 1034 | static int das16_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
e64374f8 DS |
1035 | { |
1036 | unsigned long flags; | |
1037 | ||
5f74ea14 | 1038 | spin_lock_irqsave(&dev->spinlock, flags); |
e64374f8 DS |
1039 | /* disable interrupts, dma and pacer clocked conversions */ |
1040 | devpriv->control_state &= ~DAS16_INTE & ~PACING_MASK & ~DMA_ENABLE; | |
1041 | outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); | |
1042 | if (devpriv->dma_chan) | |
1043 | disable_dma(devpriv->dma_chan); | |
1044 | ||
2696fb57 | 1045 | /* disable SW timer */ |
e64374f8 DS |
1046 | if (devpriv->timer_mode && devpriv->timer_running) { |
1047 | devpriv->timer_running = 0; | |
1048 | del_timer(&devpriv->timer); | |
1049 | } | |
1050 | ||
1051 | /* disable burst mode */ | |
3c0d681e | 1052 | if (thisboard->size > 0x400) |
e64374f8 | 1053 | outb(0, dev->iobase + DAS1600_BURST); |
3c0d681e | 1054 | |
e64374f8 | 1055 | |
5f74ea14 | 1056 | spin_unlock_irqrestore(&dev->spinlock, flags); |
e64374f8 DS |
1057 | |
1058 | return 0; | |
1059 | } | |
1060 | ||
da91b269 | 1061 | static void das16_reset(struct comedi_device *dev) |
e64374f8 DS |
1062 | { |
1063 | outb(0, dev->iobase + DAS16_STATUS); | |
1064 | outb(0, dev->iobase + DAS16_CONTROL); | |
1065 | outb(0, dev->iobase + DAS16_PACER); | |
1066 | outb(0, dev->iobase + DAS16_CNTR_CONTROL); | |
1067 | } | |
1068 | ||
da91b269 | 1069 | static int das16_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1070 | struct comedi_insn *insn, unsigned int *data) |
e64374f8 DS |
1071 | { |
1072 | int i, n; | |
1073 | int range; | |
1074 | int chan; | |
1075 | int msb, lsb; | |
1076 | ||
2696fb57 | 1077 | /* disable interrupts and pacing */ |
e64374f8 DS |
1078 | devpriv->control_state &= ~DAS16_INTE & ~DMA_ENABLE & ~PACING_MASK; |
1079 | outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); | |
1080 | ||
1081 | /* set multiplexer */ | |
1082 | chan = CR_CHAN(insn->chanspec); | |
1083 | chan |= CR_CHAN(insn->chanspec) << 4; | |
1084 | outb(chan, dev->iobase + DAS16_MUX); | |
1085 | ||
1086 | /* set gain */ | |
1087 | if (thisboard->ai_pg != das16_pg_none) { | |
1088 | range = CR_RANGE(insn->chanspec); | |
1089 | outb((das16_gainlists[thisboard->ai_pg])[range], | |
0a85b6f0 | 1090 | dev->iobase + DAS16_GAIN); |
e64374f8 DS |
1091 | } |
1092 | ||
1093 | for (n = 0; n < insn->n; n++) { | |
1094 | /* trigger conversion */ | |
1095 | outb_p(0, dev->iobase + DAS16_TRIG); | |
1096 | ||
1097 | for (i = 0; i < DAS16_TIMEOUT; i++) { | |
1098 | if (!(inb(dev->iobase + DAS16_STATUS) & BUSY)) | |
1099 | break; | |
1100 | } | |
1101 | if (i == DAS16_TIMEOUT) { | |
5f74ea14 | 1102 | printk("das16: timeout\n"); |
e64374f8 DS |
1103 | return -ETIME; |
1104 | } | |
1105 | msb = inb(dev->iobase + DAS16_AI_MSB); | |
1106 | lsb = inb(dev->iobase + DAS16_AI_LSB); | |
3c0d681e | 1107 | if (thisboard->ai_nbits == 12) |
e64374f8 | 1108 | data[n] = ((lsb >> 4) & 0xf) | (msb << 4); |
3c0d681e | 1109 | else |
e64374f8 | 1110 | data[n] = lsb | (msb << 8); |
3c0d681e | 1111 | |
e64374f8 DS |
1112 | } |
1113 | ||
1114 | return n; | |
1115 | } | |
1116 | ||
da91b269 | 1117 | static int das16_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1118 | struct comedi_insn *insn, unsigned int *data) |
e64374f8 | 1119 | { |
790c5541 | 1120 | unsigned int bits; |
e64374f8 DS |
1121 | |
1122 | bits = inb(dev->iobase + DAS16_DIO) & 0xf; | |
1123 | data[1] = bits; | |
1124 | data[0] = 0; | |
1125 | ||
1126 | return 2; | |
1127 | } | |
1128 | ||
da91b269 | 1129 | static int das16_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1130 | struct comedi_insn *insn, unsigned int *data) |
e64374f8 | 1131 | { |
790c5541 | 1132 | unsigned int wbits; |
e64374f8 | 1133 | |
2696fb57 | 1134 | /* only set bits that have been masked */ |
e64374f8 DS |
1135 | data[0] &= 0xf; |
1136 | wbits = s->state; | |
2696fb57 | 1137 | /* zero bits that have been masked */ |
e64374f8 | 1138 | wbits &= ~data[0]; |
2696fb57 | 1139 | /* set masked bits */ |
e64374f8 DS |
1140 | wbits |= data[0] & data[1]; |
1141 | s->state = wbits; | |
1142 | data[1] = wbits; | |
1143 | ||
1144 | outb(s->state, dev->iobase + DAS16_DIO); | |
1145 | ||
1146 | return 2; | |
1147 | } | |
1148 | ||
da91b269 | 1149 | static int das16_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 1150 | struct comedi_insn *insn, unsigned int *data) |
e64374f8 DS |
1151 | { |
1152 | int i; | |
1153 | int lsb, msb; | |
1154 | int chan; | |
1155 | ||
1156 | chan = CR_CHAN(insn->chanspec); | |
1157 | ||
1158 | for (i = 0; i < insn->n; i++) { | |
1159 | if (thisboard->ao_nbits == 12) { | |
1160 | lsb = (data[i] << 4) & 0xff; | |
1161 | msb = (data[i] >> 4) & 0xff; | |
1162 | } else { | |
1163 | lsb = data[i] & 0xff; | |
1164 | msb = (data[i] >> 8) & 0xff; | |
1165 | } | |
1166 | outb(lsb, dev->iobase + DAS16_AO_LSB(chan)); | |
1167 | outb(msb, dev->iobase + DAS16_AO_MSB(chan)); | |
1168 | } | |
1169 | ||
1170 | return i; | |
1171 | } | |
1172 | ||
70265d24 | 1173 | static irqreturn_t das16_dma_interrupt(int irq, void *d) |
e64374f8 DS |
1174 | { |
1175 | int status; | |
71b5f4f1 | 1176 | struct comedi_device *dev = d; |
e64374f8 DS |
1177 | |
1178 | status = inb(dev->iobase + DAS16_STATUS); | |
1179 | ||
1180 | if ((status & DAS16_INT) == 0) { | |
1181 | DEBUG_PRINT("spurious interrupt\n"); | |
1182 | return IRQ_NONE; | |
1183 | } | |
1184 | ||
1185 | /* clear interrupt */ | |
1186 | outb(0x00, dev->iobase + DAS16_STATUS); | |
1187 | das16_interrupt(dev); | |
1188 | return IRQ_HANDLED; | |
1189 | } | |
1190 | ||
1191 | static void das16_timer_interrupt(unsigned long arg) | |
1192 | { | |
0a85b6f0 | 1193 | struct comedi_device *dev = (struct comedi_device *)arg; |
e64374f8 DS |
1194 | |
1195 | das16_interrupt(dev); | |
1196 | ||
1197 | if (devpriv->timer_running) | |
1198 | mod_timer(&devpriv->timer, jiffies + timer_period()); | |
1199 | } | |
1200 | ||
1201 | /* the pc104-das16jr (at least) has problems if the dma | |
1202 | transfer is interrupted in the middle of transferring | |
1203 | a 16 bit sample, so this function takes care to get | |
1204 | an even transfer count after disabling dma | |
1205 | channel. | |
1206 | */ | |
da91b269 | 1207 | static int disable_dma_on_even(struct comedi_device *dev) |
e64374f8 DS |
1208 | { |
1209 | int residue; | |
1210 | int i; | |
1211 | static const int disable_limit = 100; | |
1212 | static const int enable_timeout = 100; | |
1213 | disable_dma(devpriv->dma_chan); | |
1214 | residue = get_dma_residue(devpriv->dma_chan); | |
1215 | for (i = 0; i < disable_limit && (residue % 2); ++i) { | |
1216 | int j; | |
1217 | enable_dma(devpriv->dma_chan); | |
1218 | for (j = 0; j < enable_timeout; ++j) { | |
1219 | int new_residue; | |
5f74ea14 | 1220 | udelay(2); |
e64374f8 DS |
1221 | new_residue = get_dma_residue(devpriv->dma_chan); |
1222 | if (new_residue != residue) | |
1223 | break; | |
1224 | } | |
1225 | disable_dma(devpriv->dma_chan); | |
1226 | residue = get_dma_residue(devpriv->dma_chan); | |
1227 | } | |
1228 | if (i == disable_limit) { | |
3c0d681e GS |
1229 | comedi_error(dev, "failed to get an even dma transfer, " |
1230 | "could be trouble."); | |
e64374f8 DS |
1231 | } |
1232 | return residue; | |
1233 | } | |
1234 | ||
da91b269 | 1235 | static void das16_interrupt(struct comedi_device *dev) |
e64374f8 DS |
1236 | { |
1237 | unsigned long dma_flags, spin_flags; | |
34c43922 | 1238 | struct comedi_subdevice *s = dev->read_subdev; |
d163679c | 1239 | struct comedi_async *async; |
ea6d0d4c | 1240 | struct comedi_cmd *cmd; |
e64374f8 DS |
1241 | int num_bytes, residue; |
1242 | int buffer_index; | |
1243 | ||
1244 | if (dev->attached == 0) { | |
1245 | comedi_error(dev, "premature interrupt"); | |
1246 | return; | |
1247 | } | |
2696fb57 | 1248 | /* initialize async here to make sure it is not NULL */ |
e64374f8 DS |
1249 | async = s->async; |
1250 | cmd = &async->cmd; | |
1251 | ||
1252 | if (devpriv->dma_chan == 0) { | |
1253 | comedi_error(dev, "interrupt with no dma channel?"); | |
1254 | return; | |
1255 | } | |
1256 | ||
5f74ea14 | 1257 | spin_lock_irqsave(&dev->spinlock, spin_flags); |
e64374f8 | 1258 | if ((devpriv->control_state & DMA_ENABLE) == 0) { |
5f74ea14 | 1259 | spin_unlock_irqrestore(&dev->spinlock, spin_flags); |
e64374f8 DS |
1260 | DEBUG_PRINT("interrupt while dma disabled?\n"); |
1261 | return; | |
1262 | } | |
1263 | ||
1264 | dma_flags = claim_dma_lock(); | |
1265 | clear_dma_ff(devpriv->dma_chan); | |
1266 | residue = disable_dma_on_even(dev); | |
1267 | ||
2696fb57 | 1268 | /* figure out how many points to read */ |
e64374f8 DS |
1269 | if (residue > devpriv->dma_transfer_size) { |
1270 | comedi_error(dev, "residue > transfer size!\n"); | |
1271 | async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
1272 | num_bytes = 0; | |
1273 | } else | |
1274 | num_bytes = devpriv->dma_transfer_size - residue; | |
1275 | ||
3c0d681e GS |
1276 | if (cmd->stop_src == TRIG_COUNT && |
1277 | num_bytes >= devpriv->adc_byte_count) { | |
e64374f8 DS |
1278 | num_bytes = devpriv->adc_byte_count; |
1279 | async->events |= COMEDI_CB_EOA; | |
1280 | } | |
1281 | ||
1282 | buffer_index = devpriv->current_buffer; | |
1283 | devpriv->current_buffer = (devpriv->current_buffer + 1) % 2; | |
1284 | devpriv->adc_byte_count -= num_bytes; | |
1285 | ||
2696fb57 | 1286 | /* figure out how many bytes for next transfer */ |
e64374f8 | 1287 | if (cmd->stop_src == TRIG_COUNT && devpriv->timer_mode == 0 && |
0a85b6f0 | 1288 | devpriv->dma_transfer_size > devpriv->adc_byte_count) |
e64374f8 DS |
1289 | devpriv->dma_transfer_size = devpriv->adc_byte_count; |
1290 | ||
2696fb57 | 1291 | /* re-enable dma */ |
e64374f8 DS |
1292 | if ((async->events & COMEDI_CB_EOA) == 0) { |
1293 | set_dma_addr(devpriv->dma_chan, | |
0a85b6f0 | 1294 | devpriv->dma_buffer_addr[devpriv->current_buffer]); |
e64374f8 DS |
1295 | set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); |
1296 | enable_dma(devpriv->dma_chan); | |
1297 | /* reenable conversions for das1600 mode, (stupid hardware) */ | |
3c0d681e | 1298 | if (thisboard->size > 0x400 && devpriv->timer_mode == 0) |
e64374f8 | 1299 | outb(0x00, dev->iobase + DAS1600_CONV); |
3c0d681e | 1300 | |
e64374f8 DS |
1301 | } |
1302 | release_dma_lock(dma_flags); | |
1303 | ||
5f74ea14 | 1304 | spin_unlock_irqrestore(&dev->spinlock, spin_flags); |
e64374f8 DS |
1305 | |
1306 | cfc_write_array_to_buffer(s, | |
0a85b6f0 | 1307 | devpriv->dma_buffer[buffer_index], num_bytes); |
e64374f8 DS |
1308 | |
1309 | cfc_handle_events(dev, s); | |
1310 | } | |
1311 | ||
da91b269 | 1312 | static unsigned int das16_set_pacer(struct comedi_device *dev, unsigned int ns, |
0a85b6f0 | 1313 | int rounding_flags) |
e64374f8 DS |
1314 | { |
1315 | i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1), | |
0a85b6f0 MT |
1316 | &(devpriv->divisor2), &ns, |
1317 | rounding_flags & TRIG_ROUND_MASK); | |
e64374f8 DS |
1318 | |
1319 | /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ | |
1320 | i8254_load(dev->iobase + DAS16_CNTR0_DATA, 0, 1, devpriv->divisor1, 2); | |
1321 | i8254_load(dev->iobase + DAS16_CNTR0_DATA, 0, 2, devpriv->divisor2, 2); | |
1322 | ||
1323 | return ns; | |
1324 | } | |
1325 | ||
da91b269 | 1326 | static void reg_dump(struct comedi_device *dev) |
e64374f8 DS |
1327 | { |
1328 | DEBUG_PRINT("********DAS1600 REGISTER DUMP********\n"); | |
1329 | DEBUG_PRINT("DAS16_MUX: %x\n", inb(dev->iobase + DAS16_MUX)); | |
1330 | DEBUG_PRINT("DAS16_DIO: %x\n", inb(dev->iobase + DAS16_DIO)); | |
1331 | DEBUG_PRINT("DAS16_STATUS: %x\n", inb(dev->iobase + DAS16_STATUS)); | |
1332 | DEBUG_PRINT("DAS16_CONTROL: %x\n", inb(dev->iobase + DAS16_CONTROL)); | |
1333 | DEBUG_PRINT("DAS16_PACER: %x\n", inb(dev->iobase + DAS16_PACER)); | |
1334 | DEBUG_PRINT("DAS16_GAIN: %x\n", inb(dev->iobase + DAS16_GAIN)); | |
1335 | DEBUG_PRINT("DAS16_CNTR_CONTROL: %x\n", | |
0a85b6f0 | 1336 | inb(dev->iobase + DAS16_CNTR_CONTROL)); |
e64374f8 DS |
1337 | DEBUG_PRINT("DAS1600_CONV: %x\n", inb(dev->iobase + DAS1600_CONV)); |
1338 | DEBUG_PRINT("DAS1600_BURST: %x\n", inb(dev->iobase + DAS1600_BURST)); | |
1339 | DEBUG_PRINT("DAS1600_ENABLE: %x\n", inb(dev->iobase + DAS1600_ENABLE)); | |
1340 | DEBUG_PRINT("DAS1600_STATUS_B: %x\n", | |
0a85b6f0 | 1341 | inb(dev->iobase + DAS1600_STATUS_B)); |
e64374f8 DS |
1342 | } |
1343 | ||
da91b269 | 1344 | static int das16_probe(struct comedi_device *dev, struct comedi_devconfig *it) |
e64374f8 DS |
1345 | { |
1346 | int status; | |
1347 | int diobits; | |
1348 | ||
1349 | /* status is available on all boards */ | |
1350 | ||
1351 | status = inb(dev->iobase + DAS16_STATUS); | |
1352 | ||
3c0d681e | 1353 | if ((status & UNIPOLAR)) |
e64374f8 | 1354 | devpriv->ai_unipolar = 1; |
3c0d681e | 1355 | else |
e64374f8 | 1356 | devpriv->ai_unipolar = 0; |
e64374f8 | 1357 | |
3c0d681e GS |
1358 | |
1359 | if ((status & DAS16_MUXBIT)) | |
e64374f8 | 1360 | devpriv->ai_singleended = 1; |
3c0d681e | 1361 | else |
e64374f8 | 1362 | devpriv->ai_singleended = 0; |
3c0d681e | 1363 | |
e64374f8 DS |
1364 | |
1365 | /* diobits indicates boards */ | |
1366 | ||
1367 | diobits = inb(dev->iobase + DAS16_DIO) & 0xf0; | |
1368 | ||
3c0d681e | 1369 | printk(KERN_INFO " id bits are 0x%02x\n", diobits); |
e64374f8 | 1370 | if (thisboard->id != diobits) { |
3c0d681e | 1371 | printk(KERN_INFO " requested board's id bits are 0x%x (ignore)\n", |
0a85b6f0 | 1372 | thisboard->id); |
e64374f8 DS |
1373 | } |
1374 | ||
1375 | return 0; | |
1376 | } | |
1377 | ||
da91b269 | 1378 | static int das1600_mode_detect(struct comedi_device *dev) |
e64374f8 DS |
1379 | { |
1380 | int status = 0; | |
1381 | ||
1382 | status = inb(dev->iobase + DAS1600_STATUS_B); | |
1383 | ||
1384 | if (status & DAS1600_CLK_10MHZ) { | |
1385 | devpriv->clockbase = 100; | |
3c0d681e | 1386 | printk(KERN_INFO " 10MHz pacer clock\n"); |
e64374f8 DS |
1387 | } else { |
1388 | devpriv->clockbase = 1000; | |
3c0d681e | 1389 | printk(KERN_INFO " 1MHz pacer clock\n"); |
e64374f8 DS |
1390 | } |
1391 | ||
1392 | reg_dump(dev); | |
1393 | ||
1394 | return 0; | |
1395 | } | |
1396 | ||
1397 | /* | |
1398 | * | |
1399 | * Options list: | |
1400 | * 0 I/O base | |
1401 | * 1 IRQ | |
1402 | * 2 DMA | |
1403 | * 3 Clock speed (in MHz) | |
1404 | */ | |
1405 | ||
da91b269 | 1406 | static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
e64374f8 | 1407 | { |
34c43922 | 1408 | struct comedi_subdevice *s; |
e64374f8 DS |
1409 | int ret; |
1410 | unsigned int irq; | |
1411 | unsigned long iobase; | |
1412 | unsigned int dma_chan; | |
1413 | int timer_mode; | |
1414 | unsigned long flags; | |
1f6325d6 | 1415 | struct comedi_krange *user_ai_range, *user_ao_range; |
e64374f8 DS |
1416 | |
1417 | iobase = it->options[0]; | |
1418 | #if 0 | |
1419 | irq = it->options[1]; | |
1420 | timer_mode = it->options[8]; | |
1421 | #endif | |
1422 | /* always use time_mode since using irq can drop samples while | |
1423 | * waiting for dma done interrupt (due to hardware limitations) */ | |
1424 | irq = 0; | |
1425 | timer_mode = 1; | |
1426 | if (timer_mode) | |
1427 | irq = 0; | |
1428 | ||
3c0d681e | 1429 | printk(KERN_INFO "comedi%d: das16:", dev->minor); |
e64374f8 | 1430 | |
2696fb57 | 1431 | /* check that clock setting is valid */ |
e64374f8 DS |
1432 | if (it->options[3]) { |
1433 | if (it->options[3] != 0 && | |
0a85b6f0 MT |
1434 | it->options[3] != 1 && it->options[3] != 10) { |
1435 | printk | |
3c0d681e GS |
1436 | ("\n Invalid option. Master clock must be set " |
1437 | "to 1 or 10 (MHz)\n"); | |
e64374f8 DS |
1438 | return -EINVAL; |
1439 | } | |
1440 | } | |
1441 | ||
c3744138 BP |
1442 | ret = alloc_private(dev, sizeof(struct das16_private_struct)); |
1443 | if (ret < 0) | |
e64374f8 DS |
1444 | return ret; |
1445 | ||
1446 | if (thisboard->size < 0x400) { | |
1447 | printk(" 0x%04lx-0x%04lx\n", iobase, iobase + thisboard->size); | |
1448 | if (!request_region(iobase, thisboard->size, "das16")) { | |
3c0d681e | 1449 | printk(KERN_ERR " I/O port conflict\n"); |
e64374f8 DS |
1450 | return -EIO; |
1451 | } | |
1452 | } else { | |
3c0d681e | 1453 | printk(KERN_INFO " 0x%04lx-0x%04lx 0x%04lx-0x%04lx\n", |
0a85b6f0 MT |
1454 | iobase, iobase + 0x0f, |
1455 | iobase + 0x400, | |
1456 | iobase + 0x400 + (thisboard->size & 0x3ff)); | |
e64374f8 | 1457 | if (!request_region(iobase, 0x10, "das16")) { |
3c0d681e | 1458 | printk(KERN_ERR " I/O port conflict: 0x%04lx-0x%04lx\n", |
0a85b6f0 | 1459 | iobase, iobase + 0x0f); |
e64374f8 DS |
1460 | return -EIO; |
1461 | } | |
1462 | if (!request_region(iobase + 0x400, thisboard->size & 0x3ff, | |
0a85b6f0 | 1463 | "das16")) { |
e64374f8 | 1464 | release_region(iobase, 0x10); |
3c0d681e | 1465 | printk(KERN_ERR " I/O port conflict: 0x%04lx-0x%04lx\n", |
0a85b6f0 MT |
1466 | iobase + 0x400, |
1467 | iobase + 0x400 + (thisboard->size & 0x3ff)); | |
e64374f8 DS |
1468 | return -EIO; |
1469 | } | |
1470 | } | |
1471 | ||
1472 | dev->iobase = iobase; | |
1473 | ||
2696fb57 | 1474 | /* probe id bits to make sure they are consistent */ |
e64374f8 | 1475 | if (das16_probe(dev, it)) { |
3c0d681e | 1476 | printk(KERN_ERR " id bits do not match selected board, aborting\n"); |
e64374f8 DS |
1477 | return -EINVAL; |
1478 | } | |
1479 | dev->board_name = thisboard->name; | |
1480 | ||
2696fb57 | 1481 | /* get master clock speed */ |
e64374f8 DS |
1482 | if (thisboard->size < 0x400) { |
1483 | if (it->options[3]) | |
1484 | devpriv->clockbase = 1000 / it->options[3]; | |
1485 | else | |
2696fb57 | 1486 | devpriv->clockbase = 1000; /* 1 MHz default */ |
e64374f8 DS |
1487 | } else { |
1488 | das1600_mode_detect(dev); | |
1489 | } | |
1490 | ||
1491 | /* now for the irq */ | |
1492 | if (irq > 1 && irq < 8) { | |
5f74ea14 | 1493 | ret = request_irq(irq, das16_dma_interrupt, 0, "das16", dev); |
c3744138 BP |
1494 | |
1495 | if (ret < 0) | |
e64374f8 DS |
1496 | return ret; |
1497 | dev->irq = irq; | |
3c0d681e | 1498 | printk(KERN_INFO " ( irq = %u )", irq); |
e64374f8 DS |
1499 | } else if (irq == 0) { |
1500 | printk(" ( no irq )"); | |
1501 | } else { | |
1502 | printk(" invalid irq\n"); | |
1503 | return -EINVAL; | |
1504 | } | |
1505 | ||
2696fb57 | 1506 | /* initialize dma */ |
e64374f8 DS |
1507 | dma_chan = it->options[2]; |
1508 | if (dma_chan == 1 || dma_chan == 3) { | |
2696fb57 | 1509 | /* allocate dma buffers */ |
e64374f8 DS |
1510 | int i; |
1511 | for (i = 0; i < 2; i++) { | |
3c0d681e GS |
1512 | devpriv->dma_buffer[i] = pci_alloc_consistent( |
1513 | NULL, DAS16_DMA_SIZE, | |
1514 | &devpriv->dma_buffer_addr[i]); | |
1515 | ||
e64374f8 DS |
1516 | if (devpriv->dma_buffer[i] == NULL) |
1517 | return -ENOMEM; | |
1518 | } | |
1519 | if (request_dma(dma_chan, "das16")) { | |
3c0d681e | 1520 | printk(KERN_ERR " failed to allocate dma channel %i\n", |
0a85b6f0 | 1521 | dma_chan); |
e64374f8 DS |
1522 | return -EINVAL; |
1523 | } | |
1524 | devpriv->dma_chan = dma_chan; | |
1525 | flags = claim_dma_lock(); | |
1526 | disable_dma(devpriv->dma_chan); | |
1527 | set_dma_mode(devpriv->dma_chan, DMA_MODE_READ); | |
1528 | release_dma_lock(flags); | |
3c0d681e | 1529 | printk(KERN_INFO " ( dma = %u)\n", dma_chan); |
e64374f8 | 1530 | } else if (dma_chan == 0) { |
3c0d681e | 1531 | printk(KERN_INFO " ( no dma )\n"); |
e64374f8 | 1532 | } else { |
3c0d681e | 1533 | printk(KERN_ERR " invalid dma channel\n"); |
e64374f8 DS |
1534 | return -EINVAL; |
1535 | } | |
1536 | ||
2696fb57 | 1537 | /* get any user-defined input range */ |
e64374f8 | 1538 | if (thisboard->ai_pg == das16_pg_none && |
0a85b6f0 | 1539 | (it->options[4] || it->options[5])) { |
2696fb57 | 1540 | /* allocate single-range range table */ |
e64374f8 | 1541 | devpriv->user_ai_range_table = |
0a85b6f0 MT |
1542 | kmalloc(sizeof(struct comedi_lrange) + |
1543 | sizeof(struct comedi_krange), GFP_KERNEL); | |
2696fb57 | 1544 | /* initialize ai range */ |
e64374f8 DS |
1545 | devpriv->user_ai_range_table->length = 1; |
1546 | user_ai_range = devpriv->user_ai_range_table->range; | |
1547 | user_ai_range->min = it->options[4]; | |
1548 | user_ai_range->max = it->options[5]; | |
1549 | user_ai_range->flags = UNIT_volt; | |
1550 | } | |
2696fb57 | 1551 | /* get any user-defined output range */ |
e64374f8 | 1552 | if (it->options[6] || it->options[7]) { |
2696fb57 | 1553 | /* allocate single-range range table */ |
e64374f8 | 1554 | devpriv->user_ao_range_table = |
0a85b6f0 MT |
1555 | kmalloc(sizeof(struct comedi_lrange) + |
1556 | sizeof(struct comedi_krange), GFP_KERNEL); | |
2696fb57 | 1557 | /* initialize ao range */ |
e64374f8 DS |
1558 | devpriv->user_ao_range_table->length = 1; |
1559 | user_ao_range = devpriv->user_ao_range_table->range; | |
1560 | user_ao_range->min = it->options[6]; | |
1561 | user_ao_range->max = it->options[7]; | |
1562 | user_ao_range->flags = UNIT_volt; | |
1563 | } | |
1564 | ||
1565 | if (timer_mode) { | |
1566 | init_timer(&(devpriv->timer)); | |
1567 | devpriv->timer.function = das16_timer_interrupt; | |
1568 | devpriv->timer.data = (unsigned long)dev; | |
1569 | } | |
1570 | devpriv->timer_mode = timer_mode ? 1 : 0; | |
1571 | ||
c3744138 BP |
1572 | ret = alloc_subdevices(dev, 5); |
1573 | if (ret < 0) | |
e64374f8 DS |
1574 | return ret; |
1575 | ||
1576 | s = dev->subdevices + 0; | |
1577 | dev->read_subdev = s; | |
1578 | /* ai */ | |
1579 | if (thisboard->ai) { | |
1580 | s->type = COMEDI_SUBD_AI; | |
1581 | s->subdev_flags = SDF_READABLE | SDF_CMD_READ; | |
1582 | if (devpriv->ai_singleended) { | |
1583 | s->n_chan = 16; | |
1584 | s->len_chanlist = 16; | |
1585 | s->subdev_flags |= SDF_GROUND; | |
1586 | } else { | |
1587 | s->n_chan = 8; | |
1588 | s->len_chanlist = 8; | |
1589 | s->subdev_flags |= SDF_DIFF; | |
1590 | } | |
1591 | s->maxdata = (1 << thisboard->ai_nbits) - 1; | |
3c0d681e | 1592 | if (devpriv->user_ai_range_table) { /* user defined ai range */ |
e64374f8 DS |
1593 | s->range_table = devpriv->user_ai_range_table; |
1594 | } else if (devpriv->ai_unipolar) { | |
1595 | s->range_table = das16_ai_uni_lranges[thisboard->ai_pg]; | |
1596 | } else { | |
1597 | s->range_table = das16_ai_bip_lranges[thisboard->ai_pg]; | |
1598 | } | |
1599 | s->insn_read = thisboard->ai; | |
1600 | s->do_cmdtest = das16_cmd_test; | |
1601 | s->do_cmd = das16_cmd_exec; | |
1602 | s->cancel = das16_cancel; | |
1603 | s->munge = das16_ai_munge; | |
1604 | } else { | |
1605 | s->type = COMEDI_SUBD_UNUSED; | |
1606 | } | |
1607 | ||
1608 | s = dev->subdevices + 1; | |
1609 | /* ao */ | |
1610 | if (thisboard->ao) { | |
1611 | s->type = COMEDI_SUBD_AO; | |
1612 | s->subdev_flags = SDF_WRITABLE; | |
1613 | s->n_chan = 2; | |
1614 | s->maxdata = (1 << thisboard->ao_nbits) - 1; | |
3c0d681e GS |
1615 | /* user defined ao range */ |
1616 | if (devpriv->user_ao_range_table) | |
e64374f8 | 1617 | s->range_table = devpriv->user_ao_range_table; |
3c0d681e | 1618 | else |
e64374f8 | 1619 | s->range_table = &range_unknown; |
3c0d681e | 1620 | |
e64374f8 DS |
1621 | s->insn_write = thisboard->ao; |
1622 | } else { | |
1623 | s->type = COMEDI_SUBD_UNUSED; | |
1624 | } | |
1625 | ||
1626 | s = dev->subdevices + 2; | |
1627 | /* di */ | |
1628 | if (thisboard->di) { | |
1629 | s->type = COMEDI_SUBD_DI; | |
1630 | s->subdev_flags = SDF_READABLE; | |
1631 | s->n_chan = 4; | |
1632 | s->maxdata = 1; | |
1633 | s->range_table = &range_digital; | |
1634 | s->insn_bits = thisboard->di; | |
1635 | } else { | |
1636 | s->type = COMEDI_SUBD_UNUSED; | |
1637 | } | |
1638 | ||
1639 | s = dev->subdevices + 3; | |
1640 | /* do */ | |
1641 | if (thisboard->do_) { | |
1642 | s->type = COMEDI_SUBD_DO; | |
1643 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; | |
1644 | s->n_chan = 4; | |
1645 | s->maxdata = 1; | |
1646 | s->range_table = &range_digital; | |
1647 | s->insn_bits = thisboard->do_; | |
2696fb57 | 1648 | /* initialize digital output lines */ |
e64374f8 DS |
1649 | outb(s->state, dev->iobase + DAS16_DIO); |
1650 | } else { | |
1651 | s->type = COMEDI_SUBD_UNUSED; | |
1652 | } | |
1653 | ||
1654 | s = dev->subdevices + 4; | |
1655 | /* 8255 */ | |
1656 | if (thisboard->i8255_offset != 0) { | |
1657 | subdev_8255_init(dev, s, NULL, (dev->iobase + | |
0a85b6f0 | 1658 | thisboard->i8255_offset)); |
e64374f8 DS |
1659 | } else { |
1660 | s->type = COMEDI_SUBD_UNUSED; | |
1661 | } | |
1662 | ||
1663 | das16_reset(dev); | |
1664 | /* set the interrupt level */ | |
1665 | devpriv->control_state = DAS16_IRQ(dev->irq); | |
1666 | outb(devpriv->control_state, dev->iobase + DAS16_CONTROL); | |
1667 | ||
2696fb57 | 1668 | /* turn on das1600 mode if available */ |
e64374f8 DS |
1669 | if (thisboard->size > 0x400) { |
1670 | outb(DAS1600_ENABLE_VAL, dev->iobase + DAS1600_ENABLE); | |
1671 | outb(0, dev->iobase + DAS1600_CONV); | |
1672 | outb(0, dev->iobase + DAS1600_BURST); | |
1673 | } | |
1674 | ||
1675 | return 0; | |
1676 | } | |
1677 | ||
da91b269 | 1678 | static int das16_detach(struct comedi_device *dev) |
e64374f8 | 1679 | { |
3c0d681e | 1680 | printk(KERN_INFO "comedi%d: das16: remove\n", dev->minor); |
e64374f8 DS |
1681 | |
1682 | das16_reset(dev); | |
1683 | ||
1684 | if (dev->subdevices) | |
1685 | subdev_8255_cleanup(dev, dev->subdevices + 4); | |
1686 | ||
1687 | if (devpriv) { | |
1688 | int i; | |
1689 | for (i = 0; i < 2; i++) { | |
1690 | if (devpriv->dma_buffer[i]) | |
1691 | pci_free_consistent(NULL, DAS16_DMA_SIZE, | |
0a85b6f0 MT |
1692 | devpriv->dma_buffer[i], |
1693 | devpriv-> | |
1694 | dma_buffer_addr[i]); | |
e64374f8 DS |
1695 | } |
1696 | if (devpriv->dma_chan) | |
1697 | free_dma(devpriv->dma_chan); | |
1698 | if (devpriv->user_ai_range_table) | |
1699 | kfree(devpriv->user_ai_range_table); | |
1700 | if (devpriv->user_ao_range_table) | |
1701 | kfree(devpriv->user_ao_range_table); | |
1702 | } | |
1703 | ||
1704 | if (dev->irq) | |
5f74ea14 | 1705 | free_irq(dev->irq, dev); |
e64374f8 DS |
1706 | |
1707 | if (dev->iobase) { | |
1708 | if (thisboard->size < 0x400) { | |
1709 | release_region(dev->iobase, thisboard->size); | |
1710 | } else { | |
1711 | release_region(dev->iobase, 0x10); | |
1712 | release_region(dev->iobase + 0x400, | |
0a85b6f0 | 1713 | thisboard->size & 0x3ff); |
e64374f8 DS |
1714 | } |
1715 | } | |
1716 | ||
1717 | return 0; | |
1718 | } | |
1719 | ||
7114a280 AT |
1720 | static int __init driver_das16_init_module(void) |
1721 | { | |
1722 | return comedi_driver_register(&driver_das16); | |
1723 | } | |
1724 | ||
1725 | static void __exit driver_das16_cleanup_module(void) | |
1726 | { | |
1727 | comedi_driver_unregister(&driver_das16); | |
1728 | } | |
1729 | ||
1730 | module_init(driver_das16_init_module); | |
1731 | module_exit(driver_das16_cleanup_module); | |
e64374f8 | 1732 | |
2696fb57 | 1733 | /* utility function that suggests a dma transfer size in bytes */ |
da91b269 | 1734 | static unsigned int das16_suggest_transfer_size(struct comedi_device *dev, |
0a85b6f0 | 1735 | struct comedi_cmd cmd) |
e64374f8 DS |
1736 | { |
1737 | unsigned int size; | |
1738 | unsigned int freq; | |
1739 | ||
1740 | /* if we are using timer interrupt, we don't care how long it | |
1741 | * will take to complete transfer since it will be interrupted | |
1742 | * by timer interrupt */ | |
1743 | if (devpriv->timer_mode) | |
1744 | return DAS16_DMA_SIZE; | |
1745 | ||
1746 | /* otherwise, we are relying on dma terminal count interrupt, | |
1747 | * so pick a reasonable size */ | |
1748 | if (cmd.convert_src == TRIG_TIMER) | |
1749 | freq = 1000000000 / cmd.convert_arg; | |
1750 | else if (cmd.scan_begin_src == TRIG_TIMER) | |
1751 | freq = (1000000000 / cmd.scan_begin_arg) * cmd.chanlist_len; | |
2696fb57 | 1752 | /* return some default value */ |
e64374f8 DS |
1753 | else |
1754 | freq = 0xffffffff; | |
1755 | ||
1756 | if (cmd.flags & TRIG_WAKE_EOS) { | |
1757 | size = sample_size * cmd.chanlist_len; | |
1758 | } else { | |
2696fb57 | 1759 | /* make buffer fill in no more than 1/3 second */ |
e64374f8 DS |
1760 | size = (freq / 3) * sample_size; |
1761 | } | |
1762 | ||
2696fb57 | 1763 | /* set a minimum and maximum size allowed */ |
e64374f8 DS |
1764 | if (size > DAS16_DMA_SIZE) |
1765 | size = DAS16_DMA_SIZE - DAS16_DMA_SIZE % sample_size; | |
1766 | else if (size < sample_size) | |
1767 | size = sample_size; | |
1768 | ||
1769 | if (cmd.stop_src == TRIG_COUNT && size > devpriv->adc_byte_count) | |
1770 | size = devpriv->adc_byte_count; | |
1771 | ||
1772 | return size; | |
1773 | } | |
1774 | ||
0a85b6f0 MT |
1775 | static void das16_ai_munge(struct comedi_device *dev, |
1776 | struct comedi_subdevice *s, void *array, | |
1777 | unsigned int num_bytes, | |
1778 | unsigned int start_chan_index) | |
e64374f8 | 1779 | { |
790c5541 BP |
1780 | unsigned int i, num_samples = num_bytes / sizeof(short); |
1781 | short *data = array; | |
e64374f8 DS |
1782 | |
1783 | for (i = 0; i < num_samples; i++) { | |
1784 | data[i] = le16_to_cpu(data[i]); | |
3c0d681e | 1785 | if (thisboard->ai_nbits == 12) |
e64374f8 | 1786 | data[i] = (data[i] >> 4) & 0xfff; |
3c0d681e | 1787 | |
e64374f8 DS |
1788 | } |
1789 | } | |
90f703d3 AT |
1790 | |
1791 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1792 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1793 | MODULE_LICENSE("GPL"); |