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