Commit | Line | Data |
---|---|---|
e64374f8 | 1 | /* |
71c9e43b HS |
2 | * 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 | */ | |
e64374f8 | 20 | |
e64374f8 | 21 | /* |
71c9e43b HS |
22 | * Driver: das16 |
23 | * Description: DAS16 compatible boards | |
24 | * Author: Sam Moore, Warren Jasper, ds, Chris Baugher, Frank Hess, Roman Fietze | |
25 | * Devices: (Keithley Metrabyte) DAS-16 [das-16] | |
26 | * (Keithley Metrabyte) DAS-16G [das-16g] | |
27 | * (Keithley Metrabyte) DAS-16F [das-16f] | |
28 | * (Keithley Metrabyte) DAS-1201 [das-1201] | |
29 | * (Keithley Metrabyte) DAS-1202 [das-1202] | |
30 | * (Keithley Metrabyte) DAS-1401 [das-1401] | |
31 | * (Keithley Metrabyte) DAS-1402 [das-1402] | |
32 | * (Keithley Metrabyte) DAS-1601 [das-1601] | |
33 | * (Keithley Metrabyte) DAS-1602 [das-1602] | |
34 | * (ComputerBoards) PC104-DAS16/JR [pc104-das16jr] | |
35 | * (ComputerBoards) PC104-DAS16JR/16 [pc104-das16jr/16] | |
36 | * (ComputerBoards) CIO-DAS16 [cio-das16] | |
37 | * (ComputerBoards) CIO-DAS16F [cio-das16/f] | |
38 | * (ComputerBoards) CIO-DAS16/JR [cio-das16/jr] | |
39 | * (ComputerBoards) CIO-DAS16JR/16 [cio-das16jr/16] | |
40 | * (ComputerBoards) CIO-DAS1401/12 [cio-das1401/12] | |
41 | * (ComputerBoards) CIO-DAS1402/12 [cio-das1402/12] | |
42 | * (ComputerBoards) CIO-DAS1402/16 [cio-das1402/16] | |
43 | * (ComputerBoards) CIO-DAS1601/12 [cio-das1601/12] | |
44 | * (ComputerBoards) CIO-DAS1602/12 [cio-das1602/12] | |
45 | * (ComputerBoards) CIO-DAS1602/16 [cio-das1602/16] | |
46 | * (ComputerBoards) CIO-DAS16/330 [cio-das16/330] | |
47 | * Status: works | |
48 | * Updated: 2003-10-12 | |
49 | * | |
50 | * A rewrite of the das16 and das1600 drivers. | |
51 | * | |
52 | * Options: | |
53 | * [0] - base io address | |
54 | * [1] - irq (does nothing, irq is not used anymore) | |
55 | * [2] - dma channel (optional, required for comedi_command support) | |
56 | * [3] - master clock speed in MHz (optional, 1 or 10, ignored if | |
57 | * board can probe clock, defaults to 1) | |
58 | * [4] - analog input range lowest voltage in microvolts (optional, | |
59 | * only useful if your board does not have software | |
60 | * programmable gain) | |
61 | * [5] - analog input range highest voltage in microvolts (optional, | |
62 | * only useful if board does not have software programmable | |
63 | * gain) | |
64 | * [6] - analog output range lowest voltage in microvolts (optional) | |
65 | * [7] - analog output range highest voltage in microvolts (optional) | |
66 | * | |
67 | * Passing a zero for an option is the same as leaving it unspecified. | |
68 | */ | |
e64374f8 | 69 | |
e64374f8 | 70 | /* |
71c9e43b HS |
71 | * Testing and debugging help provided by Daniel Koch. |
72 | * | |
73 | * Keithley Manuals: | |
74 | * 2309.PDF (das16) | |
75 | * 4919.PDF (das1400, 1600) | |
76 | * 4922.PDF (das-1400) | |
77 | * 4923.PDF (das1200, 1400, 1600) | |
78 | * | |
79 | * Computer boards manuals also available from their website | |
80 | * www.measurementcomputing.com | |
81 | */ | |
e64374f8 | 82 | |
ce157f80 | 83 | #include <linux/module.h> |
41506a9f | 84 | #include <linux/slab.h> |
ce157f80 | 85 | #include <linux/delay.h> |
e64374f8 | 86 | #include <linux/pci.h> |
25436dc9 | 87 | #include <linux/interrupt.h> |
33782dd5 | 88 | |
e64374f8 | 89 | #include <asm/dma.h> |
33782dd5 | 90 | |
e64374f8 DS |
91 | #include "../comedidev.h" |
92 | ||
93 | #include "8253.h" | |
94 | #include "8255.h" | |
95 | #include "comedi_fc.h" | |
96 | ||
2696fb57 | 97 | #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ |
e64374f8 DS |
98 | |
99 | /* | |
9336140c HS |
100 | * Register I/O map |
101 | */ | |
0f13a8d0 HS |
102 | #define DAS16_TRIG_REG 0x00 |
103 | #define DAS16_AI_LSB_REG 0x00 | |
104 | #define DAS16_AI_MSB_REG 0x01 | |
105 | #define DAS16_MUX_REG 0x02 | |
106 | #define DAS16_DIO_REG 0x03 | |
107 | #define DAS16_AO_LSB_REG(x) ((x) ? 0x06 : 0x04) | |
108 | #define DAS16_AO_MSB_REG(x) ((x) ? 0x07 : 0x05) | |
109 | #define DAS16_STATUS_REG 0x08 | |
110 | #define DAS16_STATUS_BUSY (1 << 7) | |
111 | #define DAS16_STATUS_UNIPOLAR (1 << 6) | |
112 | #define DAS16_STATUS_MUXBIT (1 << 5) | |
113 | #define DAS16_STATUS_INT (1 << 4) | |
114 | #define DAS16_CTRL_REG 0x09 | |
115 | #define DAS16_CTRL_INTE (1 << 7) | |
116 | #define DAS16_CTRL_IRQ(x) (((x) & 0x7) << 4) | |
117 | #define DAS16_CTRL_DMAE (1 << 2) | |
118 | #define DAS16_CTRL_PACING_MASK (3 << 0) | |
119 | #define DAS16_CTRL_INT_PACER (3 << 0) | |
120 | #define DAS16_CTRL_EXT_PACER (2 << 0) | |
121 | #define DAS16_CTRL_SOFT_PACER (0 << 0) | |
122 | #define DAS16_PACER_REG 0x0a | |
123 | #define DAS16_PACER_BURST_LEN(x) (((x) & 0xf) << 4) | |
124 | #define DAS16_PACER_CTR0 (1 << 1) | |
125 | #define DAS16_PACER_TRIG0 (1 << 0) | |
126 | #define DAS16_GAIN_REG 0x0b | |
127 | #define DAS16_TIMER_BASE_REG 0x0c /* to 0x0f */ | |
128 | ||
129 | #define DAS1600_CONV_REG 0x404 | |
130 | #define DAS1600_CONV_DISABLE (1 << 6) | |
131 | #define DAS1600_BURST_REG 0x405 | |
132 | #define DAS1600_BURST_VAL (1 << 6) | |
133 | #define DAS1600_ENABLE_REG 0x406 | |
134 | #define DAS1600_ENABLE_VAL (1 << 6) | |
135 | #define DAS1600_STATUS_REG 0x407 | |
136 | #define DAS1600_STATUS_BME (1 << 6) | |
137 | #define DAS1600_STATUS_ME (1 << 5) | |
138 | #define DAS1600_STATUS_CD (1 << 4) | |
139 | #define DAS1600_STATUS_WS (1 << 1) | |
140 | #define DAS1600_STATUS_CLK_10MHZ (1 << 0) | |
e64374f8 | 141 | |
6d33b860 HS |
142 | static const struct comedi_lrange range_das1x01_bip = { |
143 | 4, { | |
144 | BIP_RANGE(10), | |
145 | BIP_RANGE(1), | |
146 | BIP_RANGE(0.1), | |
147 | BIP_RANGE(0.01) | |
148 | } | |
e64374f8 | 149 | }; |
0a85b6f0 | 150 | |
6d33b860 HS |
151 | static const struct comedi_lrange range_das1x01_unip = { |
152 | 4, { | |
153 | UNI_RANGE(10), | |
154 | UNI_RANGE(1), | |
155 | UNI_RANGE(0.1), | |
156 | UNI_RANGE(0.01) | |
157 | } | |
e64374f8 | 158 | }; |
0a85b6f0 | 159 | |
6d33b860 HS |
160 | static const struct comedi_lrange range_das1x02_bip = { |
161 | 4, { | |
162 | BIP_RANGE(10), | |
163 | BIP_RANGE(5), | |
164 | BIP_RANGE(2.5), | |
165 | BIP_RANGE(1.25) | |
166 | } | |
e64374f8 | 167 | }; |
0a85b6f0 | 168 | |
6d33b860 HS |
169 | static const struct comedi_lrange range_das1x02_unip = { |
170 | 4, { | |
171 | UNI_RANGE(10), | |
172 | UNI_RANGE(5), | |
173 | UNI_RANGE(2.5), | |
174 | UNI_RANGE(1.25) | |
175 | } | |
e64374f8 | 176 | }; |
0a85b6f0 | 177 | |
6d33b860 HS |
178 | static const struct comedi_lrange range_das16jr = { |
179 | 9, { | |
180 | BIP_RANGE(10), | |
181 | BIP_RANGE(5), | |
182 | BIP_RANGE(2.5), | |
183 | BIP_RANGE(1.25), | |
184 | BIP_RANGE(0.625), | |
185 | UNI_RANGE(10), | |
186 | UNI_RANGE(5), | |
187 | UNI_RANGE(2.5), | |
188 | UNI_RANGE(1.25) | |
189 | } | |
e64374f8 | 190 | }; |
0a85b6f0 | 191 | |
6d33b860 HS |
192 | static const struct comedi_lrange range_das16jr_16 = { |
193 | 8, { | |
194 | BIP_RANGE(10), | |
195 | BIP_RANGE(5), | |
196 | BIP_RANGE(2.5), | |
197 | BIP_RANGE(1.25), | |
198 | UNI_RANGE(10), | |
199 | UNI_RANGE(5), | |
200 | UNI_RANGE(2.5), | |
201 | UNI_RANGE(1.25) | |
202 | } | |
e64374f8 DS |
203 | }; |
204 | ||
205 | static const int das16jr_gainlist[] = { 8, 0, 1, 2, 3, 4, 5, 6, 7 }; | |
206 | static const int das16jr_16_gainlist[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; | |
207 | static const int das1600_gainlist[] = { 0, 1, 2, 3 }; | |
0a85b6f0 | 208 | |
e64374f8 DS |
209 | enum { |
210 | das16_pg_none = 0, | |
211 | das16_pg_16jr, | |
212 | das16_pg_16jr_16, | |
213 | das16_pg_1601, | |
214 | das16_pg_1602, | |
215 | }; | |
216 | static const int *const das16_gainlists[] = { | |
217 | NULL, | |
218 | das16jr_gainlist, | |
219 | das16jr_16_gainlist, | |
220 | das1600_gainlist, | |
221 | das1600_gainlist, | |
222 | }; | |
0a85b6f0 | 223 | |
9ced1de6 | 224 | static const struct comedi_lrange *const das16_ai_uni_lranges[] = { |
e64374f8 DS |
225 | &range_unknown, |
226 | &range_das16jr, | |
227 | &range_das16jr_16, | |
228 | &range_das1x01_unip, | |
229 | &range_das1x02_unip, | |
230 | }; | |
0a85b6f0 | 231 | |
9ced1de6 | 232 | static const struct comedi_lrange *const das16_ai_bip_lranges[] = { |
e64374f8 DS |
233 | &range_unknown, |
234 | &range_das16jr, | |
235 | &range_das16jr_16, | |
236 | &range_das1x01_bip, | |
237 | &range_das1x02_bip, | |
238 | }; | |
239 | ||
947a33fd | 240 | struct das16_board { |
e64374f8 | 241 | const char *name; |
0dc7c5db | 242 | unsigned int ai_maxdata; |
2696fb57 | 243 | unsigned int ai_speed; /* max conversion speed in nanosec */ |
e64374f8 | 244 | unsigned int ai_pg; |
6d063851 | 245 | unsigned int has_ao:1; |
21b8f248 | 246 | unsigned int has_8255:1; |
e64374f8 DS |
247 | |
248 | unsigned int i8255_offset; | |
e64374f8 DS |
249 | |
250 | unsigned int size; | |
251 | unsigned int id; | |
947a33fd | 252 | }; |
e64374f8 | 253 | |
46399a84 HS |
254 | static const struct das16_board das16_boards[] = { |
255 | { | |
256 | .name = "das-16", | |
257 | .ai_maxdata = 0x0fff, | |
258 | .ai_speed = 15000, | |
259 | .ai_pg = das16_pg_none, | |
260 | .has_ao = 1, | |
21b8f248 | 261 | .has_8255 = 1, |
46399a84 | 262 | .i8255_offset = 0x10, |
46399a84 HS |
263 | .size = 0x14, |
264 | .id = 0x00, | |
265 | }, { | |
266 | .name = "das-16g", | |
267 | .ai_maxdata = 0x0fff, | |
268 | .ai_speed = 15000, | |
269 | .ai_pg = das16_pg_none, | |
270 | .has_ao = 1, | |
21b8f248 | 271 | .has_8255 = 1, |
46399a84 | 272 | .i8255_offset = 0x10, |
46399a84 HS |
273 | .size = 0x14, |
274 | .id = 0x00, | |
275 | }, { | |
276 | .name = "das-16f", | |
277 | .ai_maxdata = 0x0fff, | |
278 | .ai_speed = 8500, | |
279 | .ai_pg = das16_pg_none, | |
280 | .has_ao = 1, | |
21b8f248 | 281 | .has_8255 = 1, |
46399a84 | 282 | .i8255_offset = 0x10, |
46399a84 HS |
283 | .size = 0x14, |
284 | .id = 0x00, | |
285 | }, { | |
286 | .name = "cio-das16", | |
287 | .ai_maxdata = 0x0fff, | |
288 | .ai_speed = 20000, | |
289 | .ai_pg = das16_pg_none, | |
290 | .has_ao = 1, | |
21b8f248 | 291 | .has_8255 = 1, |
46399a84 | 292 | .i8255_offset = 0x10, |
46399a84 HS |
293 | .size = 0x14, |
294 | .id = 0x80, | |
295 | }, { | |
296 | .name = "cio-das16/f", | |
297 | .ai_maxdata = 0x0fff, | |
298 | .ai_speed = 10000, | |
299 | .ai_pg = das16_pg_none, | |
300 | .has_ao = 1, | |
21b8f248 | 301 | .has_8255 = 1, |
46399a84 | 302 | .i8255_offset = 0x10, |
46399a84 HS |
303 | .size = 0x14, |
304 | .id = 0x80, | |
305 | }, { | |
306 | .name = "cio-das16/jr", | |
307 | .ai_maxdata = 0x0fff, | |
308 | .ai_speed = 7692, | |
309 | .ai_pg = das16_pg_16jr, | |
46399a84 HS |
310 | .size = 0x10, |
311 | .id = 0x00, | |
312 | }, { | |
313 | .name = "pc104-das16jr", | |
314 | .ai_maxdata = 0x0fff, | |
315 | .ai_speed = 3300, | |
316 | .ai_pg = das16_pg_16jr, | |
46399a84 HS |
317 | .size = 0x10, |
318 | .id = 0x00, | |
319 | }, { | |
320 | .name = "cio-das16jr/16", | |
321 | .ai_maxdata = 0xffff, | |
322 | .ai_speed = 10000, | |
323 | .ai_pg = das16_pg_16jr_16, | |
46399a84 HS |
324 | .size = 0x10, |
325 | .id = 0x00, | |
326 | }, { | |
327 | .name = "pc104-das16jr/16", | |
328 | .ai_maxdata = 0xffff, | |
329 | .ai_speed = 10000, | |
330 | .ai_pg = das16_pg_16jr_16, | |
46399a84 HS |
331 | .size = 0x10, |
332 | .id = 0x00, | |
333 | }, { | |
334 | .name = "das-1201", | |
335 | .ai_maxdata = 0x0fff, | |
336 | .ai_speed = 20000, | |
337 | .ai_pg = das16_pg_none, | |
21b8f248 | 338 | .has_8255 = 1, |
46399a84 | 339 | .i8255_offset = 0x400, |
46399a84 HS |
340 | .size = 0x408, |
341 | .id = 0x20, | |
342 | }, { | |
343 | .name = "das-1202", | |
344 | .ai_maxdata = 0x0fff, | |
345 | .ai_speed = 10000, | |
346 | .ai_pg = das16_pg_none, | |
21b8f248 | 347 | .has_8255 = 1, |
46399a84 | 348 | .i8255_offset = 0x400, |
46399a84 HS |
349 | .size = 0x408, |
350 | .id = 0x20, | |
351 | }, { | |
352 | .name = "das-1401", | |
353 | .ai_maxdata = 0x0fff, | |
354 | .ai_speed = 10000, | |
355 | .ai_pg = das16_pg_1601, | |
46399a84 HS |
356 | .size = 0x408, |
357 | .id = 0xc0, | |
358 | }, { | |
359 | .name = "das-1402", | |
360 | .ai_maxdata = 0x0fff, | |
361 | .ai_speed = 10000, | |
362 | .ai_pg = das16_pg_1602, | |
46399a84 HS |
363 | .size = 0x408, |
364 | .id = 0xc0, | |
365 | }, { | |
366 | .name = "das-1601", | |
367 | .ai_maxdata = 0x0fff, | |
368 | .ai_speed = 10000, | |
369 | .ai_pg = das16_pg_1601, | |
370 | .has_ao = 1, | |
21b8f248 | 371 | .has_8255 = 1, |
46399a84 | 372 | .i8255_offset = 0x400, |
46399a84 HS |
373 | .size = 0x408, |
374 | .id = 0xc0, | |
375 | }, { | |
376 | .name = "das-1602", | |
377 | .ai_maxdata = 0x0fff, | |
378 | .ai_speed = 10000, | |
379 | .ai_pg = das16_pg_1602, | |
380 | .has_ao = 1, | |
21b8f248 | 381 | .has_8255 = 1, |
46399a84 | 382 | .i8255_offset = 0x400, |
46399a84 HS |
383 | .size = 0x408, |
384 | .id = 0xc0, | |
385 | }, { | |
386 | .name = "cio-das1401/12", | |
387 | .ai_maxdata = 0x0fff, | |
388 | .ai_speed = 6250, | |
389 | .ai_pg = das16_pg_1601, | |
46399a84 HS |
390 | .size = 0x408, |
391 | .id = 0xc0, | |
392 | }, { | |
393 | .name = "cio-das1402/12", | |
394 | .ai_maxdata = 0x0fff, | |
395 | .ai_speed = 6250, | |
396 | .ai_pg = das16_pg_1602, | |
46399a84 HS |
397 | .size = 0x408, |
398 | .id = 0xc0, | |
399 | }, { | |
400 | .name = "cio-das1402/16", | |
401 | .ai_maxdata = 0xffff, | |
402 | .ai_speed = 10000, | |
403 | .ai_pg = das16_pg_1602, | |
46399a84 HS |
404 | .size = 0x408, |
405 | .id = 0xc0, | |
406 | }, { | |
407 | .name = "cio-das1601/12", | |
408 | .ai_maxdata = 0x0fff, | |
409 | .ai_speed = 6250, | |
410 | .ai_pg = das16_pg_1601, | |
411 | .has_ao = 1, | |
21b8f248 | 412 | .has_8255 = 1, |
46399a84 | 413 | .i8255_offset = 0x400, |
46399a84 HS |
414 | .size = 0x408, |
415 | .id = 0xc0, | |
416 | }, { | |
417 | .name = "cio-das1602/12", | |
418 | .ai_maxdata = 0x0fff, | |
419 | .ai_speed = 10000, | |
420 | .ai_pg = das16_pg_1602, | |
421 | .has_ao = 1, | |
21b8f248 | 422 | .has_8255 = 1, |
46399a84 | 423 | .i8255_offset = 0x400, |
46399a84 HS |
424 | .size = 0x408, |
425 | .id = 0xc0, | |
426 | }, { | |
427 | .name = "cio-das1602/16", | |
428 | .ai_maxdata = 0xffff, | |
429 | .ai_speed = 10000, | |
430 | .ai_pg = das16_pg_1602, | |
431 | .has_ao = 1, | |
21b8f248 | 432 | .has_8255 = 1, |
46399a84 | 433 | .i8255_offset = 0x400, |
46399a84 HS |
434 | .size = 0x408, |
435 | .id = 0xc0, | |
436 | }, { | |
437 | .name = "cio-das16/330", | |
438 | .ai_maxdata = 0x0fff, | |
439 | .ai_speed = 3030, | |
440 | .ai_pg = das16_pg_16jr, | |
46399a84 HS |
441 | .size = 0x14, |
442 | .id = 0xf0, | |
443 | }, | |
444 | }; | |
445 | ||
e64374f8 DS |
446 | /* Period for timer interrupt in jiffies. It's a function |
447 | * to deal with possibility of dynamic HZ patches */ | |
448 | static inline int timer_period(void) | |
449 | { | |
450 | return HZ / 20; | |
451 | } | |
0a85b6f0 | 452 | |
e64374f8 | 453 | struct das16_private_struct { |
10206ef6 HS |
454 | unsigned int clockbase; |
455 | unsigned int ctrl_reg; | |
456 | unsigned long adc_byte_count; | |
457 | unsigned int divisor1; | |
458 | unsigned int divisor2; | |
459 | unsigned int dma_chan; | |
460 | uint16_t *dma_buffer[2]; | |
461 | dma_addr_t dma_buffer_addr[2]; | |
462 | unsigned int current_buffer; | |
463 | unsigned int dma_transfer_size; | |
464 | struct comedi_lrange *user_ai_range_table; | |
465 | struct comedi_lrange *user_ao_range_table; | |
466 | struct timer_list timer; | |
467 | short timer_running; | |
468 | unsigned long extra_iobase; | |
469 | unsigned int can_burst:1; | |
e64374f8 | 470 | }; |
e64374f8 | 471 | |
b53ceaf7 HS |
472 | static void das16_ai_enable(struct comedi_device *dev, |
473 | unsigned int mode, unsigned int src) | |
474 | { | |
475 | struct das16_private_struct *devpriv = dev->private; | |
476 | ||
0f13a8d0 HS |
477 | devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | |
478 | DAS16_CTRL_DMAE | | |
479 | DAS16_CTRL_PACING_MASK); | |
b53ceaf7 HS |
480 | devpriv->ctrl_reg |= mode; |
481 | ||
482 | if (src == TRIG_EXT) | |
0f13a8d0 | 483 | devpriv->ctrl_reg |= DAS16_CTRL_EXT_PACER; |
b53ceaf7 | 484 | else |
0f13a8d0 HS |
485 | devpriv->ctrl_reg |= DAS16_CTRL_INT_PACER; |
486 | outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); | |
b53ceaf7 HS |
487 | } |
488 | ||
489 | static void das16_ai_disable(struct comedi_device *dev) | |
490 | { | |
491 | struct das16_private_struct *devpriv = dev->private; | |
492 | ||
493 | /* disable interrupts, dma and pacer clocked conversions */ | |
0f13a8d0 HS |
494 | devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | |
495 | DAS16_CTRL_DMAE | | |
496 | DAS16_CTRL_PACING_MASK); | |
497 | outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); | |
b53ceaf7 HS |
498 | } |
499 | ||
372f3bb9 HS |
500 | /* the pc104-das16jr (at least) has problems if the dma |
501 | transfer is interrupted in the middle of transferring | |
502 | a 16 bit sample, so this function takes care to get | |
503 | an even transfer count after disabling dma | |
504 | channel. | |
505 | */ | |
506 | static int disable_dma_on_even(struct comedi_device *dev) | |
507 | { | |
508 | struct das16_private_struct *devpriv = dev->private; | |
372f3bb9 HS |
509 | static const int disable_limit = 100; |
510 | static const int enable_timeout = 100; | |
d7e31e56 HS |
511 | int residue; |
512 | int new_residue; | |
513 | int i; | |
514 | int j; | |
372f3bb9 HS |
515 | |
516 | disable_dma(devpriv->dma_chan); | |
517 | residue = get_dma_residue(devpriv->dma_chan); | |
518 | for (i = 0; i < disable_limit && (residue % 2); ++i) { | |
372f3bb9 HS |
519 | enable_dma(devpriv->dma_chan); |
520 | for (j = 0; j < enable_timeout; ++j) { | |
372f3bb9 HS |
521 | udelay(2); |
522 | new_residue = get_dma_residue(devpriv->dma_chan); | |
523 | if (new_residue != residue) | |
524 | break; | |
525 | } | |
526 | disable_dma(devpriv->dma_chan); | |
527 | residue = get_dma_residue(devpriv->dma_chan); | |
528 | } | |
529 | if (i == disable_limit) { | |
c04fc1a5 HS |
530 | dev_err(dev->class_dev, |
531 | "failed to get an even dma transfer, could be trouble\n"); | |
372f3bb9 HS |
532 | } |
533 | return residue; | |
534 | } | |
535 | ||
536 | static void das16_interrupt(struct comedi_device *dev) | |
537 | { | |
538 | struct das16_private_struct *devpriv = dev->private; | |
372f3bb9 | 539 | struct comedi_subdevice *s = dev->read_subdev; |
b65daeef HS |
540 | struct comedi_async *async = s->async; |
541 | struct comedi_cmd *cmd = &async->cmd; | |
542 | unsigned long spin_flags; | |
543 | unsigned long dma_flags; | |
372f3bb9 HS |
544 | int num_bytes, residue; |
545 | int buffer_index; | |
546 | ||
372f3bb9 | 547 | spin_lock_irqsave(&dev->spinlock, spin_flags); |
0f13a8d0 | 548 | if (!(devpriv->ctrl_reg & DAS16_CTRL_DMAE)) { |
372f3bb9 HS |
549 | spin_unlock_irqrestore(&dev->spinlock, spin_flags); |
550 | return; | |
551 | } | |
552 | ||
553 | dma_flags = claim_dma_lock(); | |
554 | clear_dma_ff(devpriv->dma_chan); | |
555 | residue = disable_dma_on_even(dev); | |
556 | ||
557 | /* figure out how many points to read */ | |
558 | if (residue > devpriv->dma_transfer_size) { | |
c04fc1a5 | 559 | dev_err(dev->class_dev, "residue > transfer size!\n"); |
372f3bb9 HS |
560 | async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; |
561 | num_bytes = 0; | |
562 | } else | |
563 | num_bytes = devpriv->dma_transfer_size - residue; | |
564 | ||
565 | if (cmd->stop_src == TRIG_COUNT && | |
566 | num_bytes >= devpriv->adc_byte_count) { | |
567 | num_bytes = devpriv->adc_byte_count; | |
568 | async->events |= COMEDI_CB_EOA; | |
569 | } | |
570 | ||
571 | buffer_index = devpriv->current_buffer; | |
572 | devpriv->current_buffer = (devpriv->current_buffer + 1) % 2; | |
573 | devpriv->adc_byte_count -= num_bytes; | |
574 | ||
575 | /* re-enable dma */ | |
576 | if ((async->events & COMEDI_CB_EOA) == 0) { | |
577 | set_dma_addr(devpriv->dma_chan, | |
578 | devpriv->dma_buffer_addr[devpriv->current_buffer]); | |
579 | set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); | |
580 | enable_dma(devpriv->dma_chan); | |
581 | } | |
582 | release_dma_lock(dma_flags); | |
583 | ||
584 | spin_unlock_irqrestore(&dev->spinlock, spin_flags); | |
585 | ||
586 | cfc_write_array_to_buffer(s, | |
587 | devpriv->dma_buffer[buffer_index], num_bytes); | |
588 | ||
589 | cfc_handle_events(dev, s); | |
590 | } | |
591 | ||
592 | static void das16_timer_interrupt(unsigned long arg) | |
593 | { | |
594 | struct comedi_device *dev = (struct comedi_device *)arg; | |
595 | struct das16_private_struct *devpriv = dev->private; | |
596 | ||
597 | das16_interrupt(dev); | |
598 | ||
599 | if (devpriv->timer_running) | |
600 | mod_timer(&devpriv->timer, jiffies + timer_period()); | |
601 | } | |
602 | ||
f207eda1 HS |
603 | static int das16_ai_check_chanlist(struct comedi_device *dev, |
604 | struct comedi_subdevice *s, | |
605 | struct comedi_cmd *cmd) | |
606 | { | |
607 | unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); | |
608 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); | |
609 | int i; | |
610 | ||
611 | for (i = 1; i < cmd->chanlist_len; i++) { | |
612 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); | |
613 | unsigned int range = CR_RANGE(cmd->chanlist[i]); | |
614 | ||
615 | if (chan != ((chan0 + i) % s->n_chan)) { | |
616 | dev_dbg(dev->class_dev, | |
617 | "entries in chanlist must be consecutive channels, counting upwards\n"); | |
618 | return -EINVAL; | |
619 | } | |
620 | ||
621 | if (range != range0) { | |
622 | dev_dbg(dev->class_dev, | |
623 | "entries in chanlist must all have the same gain\n"); | |
624 | return -EINVAL; | |
625 | } | |
626 | } | |
627 | ||
628 | return 0; | |
629 | } | |
630 | ||
da91b269 | 631 | static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 632 | struct comedi_cmd *cmd) |
e64374f8 | 633 | { |
7a5bd23d | 634 | const struct das16_board *board = dev->board_ptr; |
9a1a6cf8 | 635 | struct das16_private_struct *devpriv = dev->private; |
0e217211 HS |
636 | int err = 0; |
637 | unsigned int trig_mask; | |
638 | unsigned int arg; | |
e64374f8 | 639 | |
27020ffe HS |
640 | /* Step 1 : check if triggers are trivially valid */ |
641 | ||
642 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); | |
e64374f8 | 643 | |
0e217211 | 644 | trig_mask = TRIG_FOLLOW; |
96334bfe | 645 | if (devpriv->can_burst) |
0e217211 HS |
646 | trig_mask |= TRIG_TIMER | TRIG_EXT; |
647 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, trig_mask); | |
e64374f8 | 648 | |
0e217211 | 649 | trig_mask = TRIG_TIMER | TRIG_EXT; |
96334bfe | 650 | if (devpriv->can_burst) |
0e217211 HS |
651 | trig_mask |= TRIG_NOW; |
652 | err |= cfc_check_trigger_src(&cmd->convert_src, trig_mask); | |
e64374f8 | 653 | |
27020ffe HS |
654 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
655 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
e64374f8 DS |
656 | |
657 | if (err) | |
658 | return 1; | |
659 | ||
27020ffe HS |
660 | /* Step 2a : make sure trigger sources are unique */ |
661 | ||
662 | err |= cfc_check_trigger_is_unique(cmd->scan_begin_src); | |
663 | err |= cfc_check_trigger_is_unique(cmd->convert_src); | |
664 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
665 | ||
666 | /* Step 2b : and mutually compatible */ | |
e64374f8 | 667 | |
2696fb57 | 668 | /* make sure scan_begin_src and convert_src dont conflict */ |
e64374f8 | 669 | if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW) |
27020ffe | 670 | err |= -EINVAL; |
e64374f8 | 671 | if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW) |
27020ffe | 672 | err |= -EINVAL; |
e64374f8 DS |
673 | |
674 | if (err) | |
675 | return 2; | |
676 | ||
a48e1258 | 677 | /* Step 3: check if arguments are trivially valid */ |
e64374f8 | 678 | |
a48e1258 | 679 | err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); |
e64374f8 | 680 | |
a48e1258 HS |
681 | if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */ |
682 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
683 | ||
684 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); | |
685 | ||
686 | /* check against maximum frequency */ | |
687 | if (cmd->scan_begin_src == TRIG_TIMER) | |
688 | err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, | |
689 | board->ai_speed * cmd->chanlist_len); | |
690 | ||
691 | if (cmd->convert_src == TRIG_TIMER) | |
692 | err |= cfc_check_trigger_arg_min(&cmd->convert_arg, | |
693 | board->ai_speed); | |
694 | ||
2c8af779 HS |
695 | if (cmd->stop_src == TRIG_COUNT) |
696 | err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); | |
697 | else /* TRIG_NONE */ | |
a48e1258 | 698 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); |
e64374f8 | 699 | |
e64374f8 DS |
700 | if (err) |
701 | return 3; | |
702 | ||
2696fb57 | 703 | /* step 4: fix up arguments */ |
e64374f8 | 704 | if (cmd->scan_begin_src == TRIG_TIMER) { |
0e217211 | 705 | arg = cmd->scan_begin_arg; |
cb9cfd7e HS |
706 | i8253_cascade_ns_to_timer(devpriv->clockbase, |
707 | &devpriv->divisor1, | |
708 | &devpriv->divisor2, | |
0e217211 HS |
709 | &arg, cmd->flags); |
710 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg); | |
e64374f8 DS |
711 | } |
712 | if (cmd->convert_src == TRIG_TIMER) { | |
0e217211 | 713 | arg = cmd->convert_arg; |
cb9cfd7e HS |
714 | i8253_cascade_ns_to_timer(devpriv->clockbase, |
715 | &devpriv->divisor1, | |
716 | &devpriv->divisor2, | |
0e217211 HS |
717 | &arg, cmd->flags); |
718 | err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg); | |
e64374f8 DS |
719 | } |
720 | if (err) | |
721 | return 4; | |
722 | ||
f207eda1 HS |
723 | /* Step 5: check channel list if it exists */ |
724 | if (cmd->chanlist && cmd->chanlist_len > 0) | |
725 | err |= das16_ai_check_chanlist(dev, s, cmd); | |
726 | ||
e64374f8 DS |
727 | if (err) |
728 | return 5; | |
729 | ||
730 | return 0; | |
731 | } | |
732 | ||
5cdd9b75 | 733 | static unsigned int das16_set_pacer(struct comedi_device *dev, unsigned int ns, |
a207c12f | 734 | unsigned int flags) |
5cdd9b75 | 735 | { |
9a1a6cf8 | 736 | struct das16_private_struct *devpriv = dev->private; |
0f13a8d0 | 737 | unsigned long timer_base = dev->iobase + DAS16_TIMER_BASE_REG; |
9a1a6cf8 | 738 | |
cb9cfd7e HS |
739 | i8253_cascade_ns_to_timer(devpriv->clockbase, |
740 | &devpriv->divisor1, &devpriv->divisor2, | |
a207c12f | 741 | &ns, flags); |
5cdd9b75 | 742 | |
8383b8c4 HS |
743 | i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY); |
744 | i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY); | |
745 | i8254_write(timer_base, 0, 1, devpriv->divisor1); | |
746 | i8254_write(timer_base, 0, 2, devpriv->divisor2); | |
5cdd9b75 HS |
747 | |
748 | return ns; | |
749 | } | |
750 | ||
da91b269 | 751 | static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) |
e64374f8 | 752 | { |
7a5bd23d | 753 | const struct das16_board *board = dev->board_ptr; |
9a1a6cf8 | 754 | struct das16_private_struct *devpriv = dev->private; |
d163679c | 755 | struct comedi_async *async = s->async; |
ea6d0d4c | 756 | struct comedi_cmd *cmd = &async->cmd; |
e64374f8 DS |
757 | unsigned int byte; |
758 | unsigned long flags; | |
759 | int range; | |
760 | ||
0dbcf9c1 | 761 | if (cmd->flags & CMDF_PRIORITY) { |
c04fc1a5 | 762 | dev_err(dev->class_dev, |
0dbcf9c1 | 763 | "isa dma transfers cannot be performed with CMDF_PRIORITY, aborting\n"); |
e64374f8 DS |
764 | return -1; |
765 | } | |
766 | ||
4bc03044 | 767 | devpriv->adc_byte_count = cmd->stop_arg * cfc_bytes_per_scan(s); |
e64374f8 | 768 | |
96334bfe | 769 | if (devpriv->can_burst) |
0f13a8d0 | 770 | outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV_REG); |
3c0d681e | 771 | |
2696fb57 | 772 | /* set scan limits */ |
e64374f8 DS |
773 | byte = CR_CHAN(cmd->chanlist[0]); |
774 | byte |= CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]) << 4; | |
0f13a8d0 | 775 | outb(byte, dev->iobase + DAS16_MUX_REG); |
e64374f8 DS |
776 | |
777 | /* set gain (this is also burst rate register but according to | |
3c0d681e GS |
778 | * computer boards manual, burst rate does nothing, even on |
779 | * keithley cards) */ | |
d493f213 | 780 | if (board->ai_pg != das16_pg_none) { |
e64374f8 | 781 | range = CR_RANGE(cmd->chanlist[0]); |
d493f213 | 782 | outb((das16_gainlists[board->ai_pg])[range], |
0f13a8d0 | 783 | dev->iobase + DAS16_GAIN_REG); |
e64374f8 DS |
784 | } |
785 | ||
786 | /* set counter mode and counts */ | |
a207c12f | 787 | cmd->convert_arg = das16_set_pacer(dev, cmd->convert_arg, cmd->flags); |
e64374f8 DS |
788 | |
789 | /* enable counters */ | |
790 | byte = 0; | |
96334bfe | 791 | if (devpriv->can_burst) { |
e64374f8 | 792 | if (cmd->convert_src == TRIG_NOW) { |
0541144e HS |
793 | outb(DAS1600_BURST_VAL, |
794 | dev->iobase + DAS1600_BURST_REG); | |
2696fb57 | 795 | /* set burst length */ |
0f13a8d0 | 796 | byte |= DAS16_PACER_BURST_LEN(cmd->chanlist_len - 1); |
e64374f8 | 797 | } else { |
0f13a8d0 | 798 | outb(0, dev->iobase + DAS1600_BURST_REG); |
e64374f8 DS |
799 | } |
800 | } | |
0f13a8d0 | 801 | outb(byte, dev->iobase + DAS16_PACER_REG); |
e64374f8 | 802 | |
2696fb57 | 803 | /* set up dma transfer */ |
e64374f8 DS |
804 | flags = claim_dma_lock(); |
805 | disable_dma(devpriv->dma_chan); | |
806 | /* clear flip-flop to make sure 2-byte registers for | |
807 | * count and address get set correctly */ | |
808 | clear_dma_ff(devpriv->dma_chan); | |
809 | devpriv->current_buffer = 0; | |
810 | set_dma_addr(devpriv->dma_chan, | |
0a85b6f0 | 811 | devpriv->dma_buffer_addr[devpriv->current_buffer]); |
23cdad33 | 812 | devpriv->dma_transfer_size = DAS16_DMA_SIZE; |
e64374f8 DS |
813 | set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size); |
814 | enable_dma(devpriv->dma_chan); | |
815 | release_dma_lock(flags); | |
816 | ||
2696fb57 | 817 | /* set up interrupt */ |
23cdad33 HS |
818 | devpriv->timer_running = 1; |
819 | devpriv->timer.expires = jiffies + timer_period(); | |
820 | add_timer(&devpriv->timer); | |
b53ceaf7 | 821 | |
0f13a8d0 | 822 | das16_ai_enable(dev, DAS16_CTRL_DMAE, cmd->convert_src); |
e64374f8 | 823 | |
96334bfe | 824 | if (devpriv->can_burst) |
0f13a8d0 | 825 | outb(0, dev->iobase + DAS1600_CONV_REG); |
3c0d681e | 826 | |
e64374f8 DS |
827 | return 0; |
828 | } | |
829 | ||
da91b269 | 830 | static int das16_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
e64374f8 | 831 | { |
9a1a6cf8 | 832 | struct das16_private_struct *devpriv = dev->private; |
e64374f8 DS |
833 | unsigned long flags; |
834 | ||
5f74ea14 | 835 | spin_lock_irqsave(&dev->spinlock, flags); |
b53ceaf7 HS |
836 | |
837 | das16_ai_disable(dev); | |
b0926a7a | 838 | disable_dma(devpriv->dma_chan); |
e64374f8 | 839 | |
2696fb57 | 840 | /* disable SW timer */ |
23cdad33 | 841 | if (devpriv->timer_running) { |
e64374f8 DS |
842 | devpriv->timer_running = 0; |
843 | del_timer(&devpriv->timer); | |
844 | } | |
845 | ||
96334bfe | 846 | if (devpriv->can_burst) |
0f13a8d0 | 847 | outb(0, dev->iobase + DAS1600_BURST_REG); |
3c0d681e | 848 | |
5f74ea14 | 849 | spin_unlock_irqrestore(&dev->spinlock, flags); |
e64374f8 DS |
850 | |
851 | return 0; | |
852 | } | |
853 | ||
372f3bb9 HS |
854 | static void das16_ai_munge(struct comedi_device *dev, |
855 | struct comedi_subdevice *s, void *array, | |
856 | unsigned int num_bytes, | |
857 | unsigned int start_chan_index) | |
e64374f8 | 858 | { |
372f3bb9 | 859 | unsigned int i, num_samples = num_bytes / sizeof(short); |
92098345 | 860 | unsigned short *data = array; |
372f3bb9 HS |
861 | |
862 | for (i = 0; i < num_samples; i++) { | |
863 | data[i] = le16_to_cpu(data[i]); | |
864 | if (s->maxdata == 0x0fff) | |
865 | data[i] >>= 4; | |
866 | data[i] &= s->maxdata; | |
867 | } | |
e64374f8 DS |
868 | } |
869 | ||
fe45e153 HS |
870 | static int das16_ai_eoc(struct comedi_device *dev, |
871 | struct comedi_subdevice *s, | |
872 | struct comedi_insn *insn, | |
873 | unsigned long context) | |
73ef2b53 HS |
874 | { |
875 | unsigned int status; | |
73ef2b53 | 876 | |
fe45e153 HS |
877 | status = inb(dev->iobase + DAS16_STATUS_REG); |
878 | if ((status & DAS16_STATUS_BUSY) == 0) | |
879 | return 0; | |
880 | return -EBUSY; | |
73ef2b53 HS |
881 | } |
882 | ||
465e25b5 HS |
883 | static int das16_ai_insn_read(struct comedi_device *dev, |
884 | struct comedi_subdevice *s, | |
885 | struct comedi_insn *insn, | |
886 | unsigned int *data) | |
e64374f8 | 887 | { |
7a5bd23d | 888 | const struct das16_board *board = dev->board_ptr; |
73ef2b53 HS |
889 | unsigned int chan = CR_CHAN(insn->chanspec); |
890 | unsigned int range = CR_RANGE(insn->chanspec); | |
891 | unsigned int val; | |
892 | int ret; | |
893 | int i; | |
e64374f8 | 894 | |
b53ceaf7 | 895 | das16_ai_disable(dev); |
e64374f8 DS |
896 | |
897 | /* set multiplexer */ | |
0f13a8d0 | 898 | outb(chan | (chan << 4), dev->iobase + DAS16_MUX_REG); |
e64374f8 DS |
899 | |
900 | /* set gain */ | |
d493f213 | 901 | if (board->ai_pg != das16_pg_none) { |
d493f213 | 902 | outb((das16_gainlists[board->ai_pg])[range], |
0f13a8d0 | 903 | dev->iobase + DAS16_GAIN_REG); |
e64374f8 DS |
904 | } |
905 | ||
73ef2b53 | 906 | for (i = 0; i < insn->n; i++) { |
e64374f8 | 907 | /* trigger conversion */ |
0f13a8d0 | 908 | outb_p(0, dev->iobase + DAS16_TRIG_REG); |
e64374f8 | 909 | |
fe45e153 | 910 | ret = comedi_timeout(dev, s, insn, das16_ai_eoc, 0); |
73ef2b53 HS |
911 | if (ret) |
912 | return ret; | |
3c0d681e | 913 | |
0f13a8d0 HS |
914 | val = inb(dev->iobase + DAS16_AI_MSB_REG) << 8; |
915 | val |= inb(dev->iobase + DAS16_AI_LSB_REG); | |
0dc7c5db | 916 | if (s->maxdata == 0x0fff) |
73ef2b53 | 917 | val >>= 4; |
0dc7c5db HS |
918 | val &= s->maxdata; |
919 | ||
73ef2b53 | 920 | data[i] = val; |
e64374f8 DS |
921 | } |
922 | ||
73ef2b53 | 923 | return insn->n; |
e64374f8 DS |
924 | } |
925 | ||
6d063851 HS |
926 | static int das16_ao_insn_write(struct comedi_device *dev, |
927 | struct comedi_subdevice *s, | |
928 | struct comedi_insn *insn, | |
929 | unsigned int *data) | |
e64374f8 | 930 | { |
6d063851 | 931 | unsigned int chan = CR_CHAN(insn->chanspec); |
e64374f8 | 932 | int i; |
e64374f8 DS |
933 | |
934 | for (i = 0; i < insn->n; i++) { | |
551739b0 HS |
935 | unsigned int val = data[i]; |
936 | ||
937 | s->readback[chan] = val; | |
938 | ||
6d063851 HS |
939 | val <<= 4; |
940 | ||
0f13a8d0 HS |
941 | outb(val & 0xff, dev->iobase + DAS16_AO_LSB_REG(chan)); |
942 | outb((val >> 8) & 0xff, dev->iobase + DAS16_AO_MSB_REG(chan)); | |
e64374f8 DS |
943 | } |
944 | ||
6d063851 | 945 | return insn->n; |
e64374f8 DS |
946 | } |
947 | ||
372f3bb9 HS |
948 | static int das16_di_insn_bits(struct comedi_device *dev, |
949 | struct comedi_subdevice *s, | |
950 | struct comedi_insn *insn, | |
951 | unsigned int *data) | |
e64374f8 | 952 | { |
0f13a8d0 | 953 | data[1] = inb(dev->iobase + DAS16_DIO_REG) & 0xf; |
9a1a6cf8 | 954 | |
372f3bb9 | 955 | return insn->n; |
e64374f8 DS |
956 | } |
957 | ||
372f3bb9 HS |
958 | static int das16_do_insn_bits(struct comedi_device *dev, |
959 | struct comedi_subdevice *s, | |
960 | struct comedi_insn *insn, | |
961 | unsigned int *data) | |
e64374f8 | 962 | { |
97f4289a | 963 | if (comedi_dio_update_state(s, data)) |
0f13a8d0 | 964 | outb(s->state, dev->iobase + DAS16_DIO_REG); |
e64374f8 | 965 | |
372f3bb9 | 966 | data[1] = s->state; |
5cdd9b75 | 967 | |
372f3bb9 | 968 | return insn->n; |
e64374f8 DS |
969 | } |
970 | ||
da91b269 | 971 | static int das16_probe(struct comedi_device *dev, struct comedi_devconfig *it) |
e64374f8 | 972 | { |
7a5bd23d | 973 | const struct das16_board *board = dev->board_ptr; |
e64374f8 DS |
974 | int diobits; |
975 | ||
e64374f8 | 976 | /* diobits indicates boards */ |
0f13a8d0 | 977 | diobits = inb(dev->iobase + DAS16_DIO_REG) & 0xf0; |
d493f213 | 978 | if (board->id != diobits) { |
b08c4015 HS |
979 | dev_err(dev->class_dev, |
980 | "requested board's id bits are incorrect (0x%x != 0x%x)\n", | |
981 | board->id, diobits); | |
982 | return -EINVAL; | |
e64374f8 DS |
983 | } |
984 | ||
985 | return 0; | |
986 | } | |
987 | ||
372f3bb9 | 988 | static void das16_reset(struct comedi_device *dev) |
5cdd9b75 | 989 | { |
0f13a8d0 HS |
990 | outb(0, dev->iobase + DAS16_STATUS_REG); |
991 | outb(0, dev->iobase + DAS16_CTRL_REG); | |
992 | outb(0, dev->iobase + DAS16_PACER_REG); | |
993 | outb(0, dev->iobase + DAS16_TIMER_BASE_REG + i8254_control_reg); | |
5cdd9b75 HS |
994 | } |
995 | ||
da91b269 | 996 | static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
e64374f8 | 997 | { |
7a5bd23d | 998 | const struct das16_board *board = dev->board_ptr; |
9a1a6cf8 | 999 | struct das16_private_struct *devpriv; |
34c43922 | 1000 | struct comedi_subdevice *s; |
05591eaf HS |
1001 | struct comedi_lrange *lrange; |
1002 | struct comedi_krange *krange; | |
b0926a7a | 1003 | unsigned int dma_chan = it->options[2]; |
1aa4694f | 1004 | unsigned int status; |
e64374f8 | 1005 | int ret; |
e64374f8 | 1006 | |
2696fb57 | 1007 | /* check that clock setting is valid */ |
e64374f8 DS |
1008 | if (it->options[3]) { |
1009 | if (it->options[3] != 0 && | |
0a85b6f0 | 1010 | it->options[3] != 1 && it->options[3] != 10) { |
ccb26aa7 HS |
1011 | dev_err(dev->class_dev, |
1012 | "Invalid option. Master clock must be set to 1 or 10 (MHz)\n"); | |
e64374f8 DS |
1013 | return -EINVAL; |
1014 | } | |
1015 | } | |
1016 | ||
0bdab509 | 1017 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
1018 | if (!devpriv) |
1019 | return -ENOMEM; | |
e64374f8 | 1020 | |
d493f213 | 1021 | if (board->size < 0x400) { |
7c0bad23 HS |
1022 | ret = comedi_request_region(dev, it->options[0], board->size); |
1023 | if (ret) | |
1024 | return ret; | |
e64374f8 | 1025 | } else { |
7c0bad23 HS |
1026 | ret = comedi_request_region(dev, it->options[0], 0x10); |
1027 | if (ret) | |
1028 | return ret; | |
1029 | /* Request an additional region for the 8255 */ | |
962e5a36 HS |
1030 | ret = __comedi_request_region(dev, dev->iobase + 0x400, |
1031 | board->size & 0x3ff); | |
adfaa207 HS |
1032 | if (ret) |
1033 | return ret; | |
1034 | devpriv->extra_iobase = dev->iobase + 0x400; | |
96334bfe | 1035 | devpriv->can_burst = 1; |
e64374f8 DS |
1036 | } |
1037 | ||
2696fb57 | 1038 | /* probe id bits to make sure they are consistent */ |
b08c4015 | 1039 | if (das16_probe(dev, it)) |
e64374f8 | 1040 | return -EINVAL; |
e64374f8 | 1041 | |
2696fb57 | 1042 | /* get master clock speed */ |
96334bfe | 1043 | if (devpriv->can_burst) { |
f2e92ee1 HS |
1044 | status = inb(dev->iobase + DAS1600_STATUS_REG); |
1045 | ||
1046 | if (status & DAS1600_STATUS_CLK_10MHZ) | |
cb9cfd7e | 1047 | devpriv->clockbase = I8254_OSC_BASE_10MHZ; |
f2e92ee1 | 1048 | else |
cb9cfd7e | 1049 | devpriv->clockbase = I8254_OSC_BASE_1MHZ; |
96334bfe | 1050 | } else { |
e64374f8 | 1051 | if (it->options[3]) |
cb9cfd7e HS |
1052 | devpriv->clockbase = I8254_OSC_BASE_1MHZ / |
1053 | it->options[3]; | |
e64374f8 | 1054 | else |
cb9cfd7e | 1055 | devpriv->clockbase = I8254_OSC_BASE_1MHZ; |
e64374f8 DS |
1056 | } |
1057 | ||
b0926a7a | 1058 | /* initialize dma */ |
e64374f8 | 1059 | if (dma_chan == 1 || dma_chan == 3) { |
b0926a7a | 1060 | unsigned long flags; |
e64374f8 | 1061 | int i; |
3c0d681e | 1062 | |
8234d51a | 1063 | if (request_dma(dma_chan, dev->board_name)) { |
b0926a7a HS |
1064 | dev_err(dev->class_dev, |
1065 | "failed to request dma channel %i\n", | |
1066 | dma_chan); | |
e64374f8 DS |
1067 | return -EINVAL; |
1068 | } | |
1069 | devpriv->dma_chan = dma_chan; | |
b0926a7a HS |
1070 | |
1071 | /* allocate dma buffers */ | |
1072 | for (i = 0; i < 2; i++) { | |
1073 | void *p; | |
1074 | ||
1075 | p = pci_alloc_consistent(NULL, DAS16_DMA_SIZE, | |
1076 | &devpriv->dma_buffer_addr[i]); | |
1077 | if (!p) | |
1078 | return -ENOMEM; | |
1079 | devpriv->dma_buffer[i] = p; | |
1080 | } | |
1081 | ||
e64374f8 DS |
1082 | flags = claim_dma_lock(); |
1083 | disable_dma(devpriv->dma_chan); | |
1084 | set_dma_mode(devpriv->dma_chan, DMA_MODE_READ); | |
1085 | release_dma_lock(flags); | |
b0926a7a | 1086 | |
27b579be | 1087 | init_timer(&devpriv->timer); |
b0926a7a HS |
1088 | devpriv->timer.function = das16_timer_interrupt; |
1089 | devpriv->timer.data = (unsigned long)dev; | |
e64374f8 DS |
1090 | } |
1091 | ||
05591eaf | 1092 | /* get any user-defined input range */ |
d493f213 | 1093 | if (board->ai_pg == das16_pg_none && |
0a85b6f0 | 1094 | (it->options[4] || it->options[5])) { |
05591eaf HS |
1095 | /* allocate single-range range table */ |
1096 | lrange = kzalloc(sizeof(*lrange) + sizeof(*krange), GFP_KERNEL); | |
1097 | if (!lrange) | |
1098 | return -ENOMEM; | |
1099 | ||
1100 | /* initialize ai range */ | |
1101 | devpriv->user_ai_range_table = lrange; | |
1102 | lrange->length = 1; | |
1103 | krange = devpriv->user_ai_range_table->range; | |
1104 | krange->min = it->options[4]; | |
1105 | krange->max = it->options[5]; | |
1106 | krange->flags = UNIT_volt; | |
e64374f8 | 1107 | } |
05591eaf HS |
1108 | |
1109 | /* get any user-defined output range */ | |
e64374f8 | 1110 | if (it->options[6] || it->options[7]) { |
05591eaf HS |
1111 | /* allocate single-range range table */ |
1112 | lrange = kzalloc(sizeof(*lrange) + sizeof(*krange), GFP_KERNEL); | |
1113 | if (!lrange) | |
1114 | return -ENOMEM; | |
1115 | ||
1116 | /* initialize ao range */ | |
1117 | devpriv->user_ao_range_table = lrange; | |
1118 | lrange->length = 1; | |
1119 | krange = devpriv->user_ao_range_table->range; | |
1120 | krange->min = it->options[6]; | |
1121 | krange->max = it->options[7]; | |
1122 | krange->flags = UNIT_volt; | |
e64374f8 DS |
1123 | } |
1124 | ||
21b8f248 | 1125 | ret = comedi_alloc_subdevices(dev, 4 + board->has_8255); |
8b6c5694 | 1126 | if (ret) |
e64374f8 DS |
1127 | return ret; |
1128 | ||
0f13a8d0 | 1129 | status = inb(dev->iobase + DAS16_STATUS_REG); |
1aa4694f | 1130 | |
465e25b5 | 1131 | /* Analog Input subdevice */ |
35d10629 | 1132 | s = &dev->subdevices[0]; |
465e25b5 HS |
1133 | s->type = COMEDI_SUBD_AI; |
1134 | s->subdev_flags = SDF_READABLE; | |
0f13a8d0 | 1135 | if (status & DAS16_STATUS_MUXBIT) { |
465e25b5 HS |
1136 | s->subdev_flags |= SDF_GROUND; |
1137 | s->n_chan = 16; | |
e64374f8 | 1138 | } else { |
465e25b5 HS |
1139 | s->subdev_flags |= SDF_DIFF; |
1140 | s->n_chan = 8; | |
1141 | } | |
1142 | s->len_chanlist = s->n_chan; | |
0dc7c5db | 1143 | s->maxdata = board->ai_maxdata; |
465e25b5 HS |
1144 | if (devpriv->user_ai_range_table) { /* user defined ai range */ |
1145 | s->range_table = devpriv->user_ai_range_table; | |
0f13a8d0 | 1146 | } else if (status & DAS16_STATUS_UNIPOLAR) { |
465e25b5 HS |
1147 | s->range_table = das16_ai_uni_lranges[board->ai_pg]; |
1148 | } else { | |
1149 | s->range_table = das16_ai_bip_lranges[board->ai_pg]; | |
1150 | } | |
1151 | s->insn_read = das16_ai_insn_read; | |
1152 | if (devpriv->dma_chan) { | |
1153 | dev->read_subdev = s; | |
1154 | s->subdev_flags |= SDF_CMD_READ; | |
1155 | s->do_cmdtest = das16_cmd_test; | |
1156 | s->do_cmd = das16_cmd_exec; | |
1157 | s->cancel = das16_cancel; | |
1158 | s->munge = das16_ai_munge; | |
e64374f8 DS |
1159 | } |
1160 | ||
6d063851 | 1161 | /* Analog Output subdevice */ |
35d10629 | 1162 | s = &dev->subdevices[1]; |
6d063851 HS |
1163 | if (board->has_ao) { |
1164 | s->type = COMEDI_SUBD_AO; | |
1165 | s->subdev_flags = SDF_WRITABLE; | |
1166 | s->n_chan = 2; | |
1167 | s->maxdata = 0x0fff; | |
3ffb03e6 | 1168 | s->range_table = devpriv->user_ao_range_table; |
6d063851 | 1169 | s->insn_write = das16_ao_insn_write; |
551739b0 HS |
1170 | s->insn_read = comedi_readback_insn_read; |
1171 | ||
1172 | ret = comedi_alloc_subdev_readback(s); | |
1173 | if (ret) | |
1174 | return ret; | |
e64374f8 | 1175 | } else { |
6d063851 | 1176 | s->type = COMEDI_SUBD_UNUSED; |
e64374f8 DS |
1177 | } |
1178 | ||
fb1864ba | 1179 | /* Digital Input subdevice */ |
35d10629 | 1180 | s = &dev->subdevices[2]; |
fb1864ba HS |
1181 | s->type = COMEDI_SUBD_DI; |
1182 | s->subdev_flags = SDF_READABLE; | |
1183 | s->n_chan = 4; | |
1184 | s->maxdata = 1; | |
1185 | s->range_table = &range_digital; | |
1186 | s->insn_bits = das16_di_insn_bits; | |
e64374f8 | 1187 | |
6050cc91 | 1188 | /* Digital Output subdevice */ |
35d10629 | 1189 | s = &dev->subdevices[3]; |
6050cc91 HS |
1190 | s->type = COMEDI_SUBD_DO; |
1191 | s->subdev_flags = SDF_WRITABLE; | |
1192 | s->n_chan = 4; | |
1193 | s->maxdata = 1; | |
1194 | s->range_table = &range_digital; | |
1195 | s->insn_bits = das16_do_insn_bits; | |
1196 | ||
1197 | /* initialize digital output lines */ | |
0f13a8d0 | 1198 | outb(s->state, dev->iobase + DAS16_DIO_REG); |
e64374f8 | 1199 | |
21b8f248 HS |
1200 | /* 8255 Digital I/O subdevice */ |
1201 | if (board->has_8255) { | |
1202 | s = &dev->subdevices[4]; | |
4085e93b | 1203 | ret = subdev_8255_init(dev, s, NULL, board->i8255_offset); |
21b8f248 HS |
1204 | if (ret) |
1205 | return ret; | |
e64374f8 DS |
1206 | } |
1207 | ||
1208 | das16_reset(dev); | |
1209 | /* set the interrupt level */ | |
0f13a8d0 HS |
1210 | devpriv->ctrl_reg = DAS16_CTRL_IRQ(dev->irq); |
1211 | outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG); | |
e64374f8 | 1212 | |
96334bfe | 1213 | if (devpriv->can_burst) { |
0f13a8d0 HS |
1214 | outb(DAS1600_ENABLE_VAL, dev->iobase + DAS1600_ENABLE_REG); |
1215 | outb(0, dev->iobase + DAS1600_CONV_REG); | |
1216 | outb(0, dev->iobase + DAS1600_BURST_REG); | |
e64374f8 DS |
1217 | } |
1218 | ||
1219 | return 0; | |
1220 | } | |
1221 | ||
484ecc95 | 1222 | static void das16_detach(struct comedi_device *dev) |
e64374f8 | 1223 | { |
7a5bd23d | 1224 | const struct das16_board *board = dev->board_ptr; |
9a1a6cf8 | 1225 | struct das16_private_struct *devpriv = dev->private; |
cb09d912 | 1226 | int i; |
d493f213 | 1227 | |
e64374f8 | 1228 | if (devpriv) { |
cb09d912 HS |
1229 | if (dev->iobase) |
1230 | das16_reset(dev); | |
1231 | ||
e64374f8 DS |
1232 | for (i = 0; i < 2; i++) { |
1233 | if (devpriv->dma_buffer[i]) | |
1234 | pci_free_consistent(NULL, DAS16_DMA_SIZE, | |
0a85b6f0 MT |
1235 | devpriv->dma_buffer[i], |
1236 | devpriv-> | |
1237 | dma_buffer_addr[i]); | |
e64374f8 DS |
1238 | } |
1239 | if (devpriv->dma_chan) | |
1240 | free_dma(devpriv->dma_chan); | |
e4e1f289 IM |
1241 | kfree(devpriv->user_ai_range_table); |
1242 | kfree(devpriv->user_ao_range_table); | |
cb09d912 HS |
1243 | |
1244 | if (devpriv->extra_iobase) | |
1245 | release_region(devpriv->extra_iobase, | |
1246 | board->size & 0x3ff); | |
e64374f8 | 1247 | } |
cb09d912 | 1248 | |
adfaa207 | 1249 | comedi_legacy_detach(dev); |
e64374f8 DS |
1250 | } |
1251 | ||
5cdd9b75 HS |
1252 | static struct comedi_driver das16_driver = { |
1253 | .driver_name = "das16", | |
1254 | .module = THIS_MODULE, | |
1255 | .attach = das16_attach, | |
1256 | .detach = das16_detach, | |
1257 | .board_name = &das16_boards[0].name, | |
1258 | .num_names = ARRAY_SIZE(das16_boards), | |
1259 | .offset = sizeof(das16_boards[0]), | |
1260 | }; | |
1261 | module_comedi_driver(das16_driver); | |
90f703d3 AT |
1262 | |
1263 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
efb62316 | 1264 | MODULE_DESCRIPTION("Comedi driver for DAS16 compatible boards"); |
90f703d3 | 1265 | MODULE_LICENSE("GPL"); |