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