Commit | Line | Data |
---|---|---|
0882eaa6 DS |
1 | /* |
2 | comedi/drivers/das08.c | |
3 | DAS08 driver | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
7 | Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net> | |
8 | Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org> | |
9 | ||
10 | This program is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2 of the License, or | |
13 | (at your option) any later version. | |
14 | ||
15 | This program is distributed in the hope that it will be useful, | |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
19 | ||
20 | You should have received a copy of the GNU General Public License | |
21 | along with this program; if not, write to the Free Software | |
22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | ||
24 | ***************************************************************** | |
25 | ||
26 | */ | |
27 | /* | |
28 | Driver: das08 | |
29 | Description: DAS-08 compatible boards | |
30 | Author: Warren Jasper, ds, Frank Hess | |
31 | Devices: [Keithley Metrabyte] DAS08 (isa-das08), [ComputerBoards] DAS08 (isa-das08), | |
32 | DAS08-PGM (das08-pgm), | |
33 | DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh), | |
34 | DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao), | |
35 | DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (das08), | |
36 | PC104-DAS08 (pc104-das08), DAS08/JR/16 (das08jr/16) | |
37 | Status: works | |
38 | ||
39 | This is a rewrite of the das08 and das08jr drivers. | |
40 | ||
41 | Options (for ISA cards): | |
42 | [0] - base io address | |
43 | ||
44 | Options (for pci-das08): | |
45 | [0] - bus (optional) | |
46 | [1] = slot (optional) | |
47 | ||
48 | The das08 driver doesn't support asynchronous commands, since | |
49 | the cheap das08 hardware doesn't really support them. The | |
50 | comedi_rt_timer driver can be used to emulate commands for this | |
51 | driver. | |
52 | */ | |
53 | ||
54 | #include "../comedidev.h" | |
55 | ||
56 | #include <linux/delay.h> | |
57 | ||
58 | #include "comedi_pci.h" | |
59 | #include "8255.h" | |
60 | #include "das08.h" | |
61 | ||
62 | #define DRV_NAME "das08" | |
63 | ||
64 | #define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307 | |
65 | #define PCI_DEVICE_ID_PCIDAS08 0x29 | |
66 | #define PCIDAS08_SIZE 0x54 | |
67 | ||
68 | // pci configuration registers | |
69 | #define INTCSR 0x4c | |
70 | #define INTR1_ENABLE 0x1 | |
71 | #define INTR1_HIGH_POLARITY 0x2 | |
72 | #define PCI_INTR_ENABLE 0x40 | |
73 | #define INTR1_EDGE_TRIG 0x100 // requires high polarity | |
74 | #define CNTRL 0x50 | |
75 | #define CNTRL_DIR 0x2 | |
76 | #define CNTRL_INTR 0x4 | |
77 | ||
78 | /* | |
79 | cio-das08.pdf | |
80 | ||
81 | "isa-das08" | |
82 | ||
83 | 0 a/d bits 0-3 start 8 bit | |
84 | 1 a/d bits 4-11 start 12 bit | |
85 | 2 eoc, ip1-3, irq, mux op1-4, inte, mux | |
86 | 3 unused unused | |
87 | 4567 8254 | |
88 | 89ab 8255 | |
89 | ||
90 | requires hard-wiring for async ai | |
91 | ||
92 | */ | |
93 | ||
94 | #define DAS08_LSB 0 | |
95 | #define DAS08_MSB 1 | |
96 | #define DAS08_TRIG_12BIT 1 | |
97 | #define DAS08_STATUS 2 | |
98 | #define DAS08_EOC (1<<7) | |
99 | #define DAS08_IRQ (1<<3) | |
100 | #define DAS08_IP(x) (((x)>>4)&0x7) | |
101 | #define DAS08_CONTROL 2 | |
102 | #define DAS08_MUX_MASK 0x7 | |
103 | #define DAS08_MUX(x) ((x) & DAS08_MUX_MASK) | |
104 | #define DAS08_INTE (1<<3) | |
105 | #define DAS08_DO_MASK 0xf0 | |
106 | #define DAS08_OP(x) (((x) << 4) & DAS08_DO_MASK) | |
107 | ||
108 | /* | |
109 | cio-das08jr.pdf | |
110 | ||
111 | "das08/jr-ao" | |
112 | ||
113 | 0 a/d bits 0-3 unused | |
114 | 1 a/d bits 4-11 start 12 bit | |
115 | 2 eoc, mux mux | |
116 | 3 di do | |
117 | 4 unused ao0_lsb | |
118 | 5 unused ao0_msb | |
119 | 6 unused ao1_lsb | |
120 | 7 unused ao1_msb | |
121 | ||
122 | */ | |
123 | ||
124 | #define DAS08JR_DIO 3 | |
125 | #define DAS08JR_AO_LSB(x) ((x)?6:4) | |
126 | #define DAS08JR_AO_MSB(x) ((x)?7:5) | |
127 | ||
128 | /* | |
129 | cio-das08_aox.pdf | |
130 | ||
131 | "das08-aoh" | |
132 | "das08-aol" | |
133 | "das08-aom" | |
134 | ||
135 | 0 a/d bits 0-3 start 8 bit | |
136 | 1 a/d bits 4-11 start 12 bit | |
137 | 2 eoc, ip1-3, irq, mux op1-4, inte, mux | |
138 | 3 mux, gain status gain control | |
139 | 4567 8254 | |
140 | 8 unused ao0_lsb | |
141 | 9 unused ao0_msb | |
142 | a unused ao1_lsb | |
143 | b unused ao1_msb | |
144 | 89ab | |
145 | cdef 8255 | |
146 | */ | |
147 | ||
148 | #define DAS08AO_GAIN_CONTROL 3 | |
149 | #define DAS08AO_GAIN_STATUS 3 | |
150 | ||
151 | #define DAS08AO_AO_LSB(x) ((x)?0xa:8) | |
152 | #define DAS08AO_AO_MSB(x) ((x)?0xb:9) | |
153 | #define DAS08AO_AO_UPDATE 8 | |
154 | ||
155 | /* gainlist same as _pgx_ below */ | |
156 | ||
34c43922 | 157 | static int das08_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 158 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 159 | static int das08_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 160 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 161 | static int das08_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 162 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 163 | static int das08jr_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 164 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 165 | static int das08jr_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 166 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 167 | static int das08jr_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 168 | struct comedi_insn * insn, unsigned int * data); |
34c43922 | 169 | static int das08ao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 170 | struct comedi_insn * insn, unsigned int * data); |
0882eaa6 DS |
171 | static void i8254_set_mode_low(unsigned int base, int channel, |
172 | unsigned int mode); | |
173 | ||
9ced1de6 | 174 | static const struct comedi_lrange range_das08_pgl = { 9, { |
0882eaa6 DS |
175 | BIP_RANGE(10), |
176 | BIP_RANGE(5), | |
177 | BIP_RANGE(2.5), | |
178 | BIP_RANGE(1.25), | |
179 | BIP_RANGE(0.625), | |
180 | UNI_RANGE(10), | |
181 | UNI_RANGE(5), | |
182 | UNI_RANGE(2.5), | |
183 | UNI_RANGE(1.25) | |
184 | } | |
185 | }; | |
9ced1de6 | 186 | static const struct comedi_lrange range_das08_pgh = { 12, { |
0882eaa6 DS |
187 | BIP_RANGE(10), |
188 | BIP_RANGE(5), | |
189 | BIP_RANGE(1), | |
190 | BIP_RANGE(0.5), | |
191 | BIP_RANGE(0.1), | |
192 | BIP_RANGE(0.05), | |
193 | BIP_RANGE(0.01), | |
194 | BIP_RANGE(0.005), | |
195 | UNI_RANGE(10), | |
196 | UNI_RANGE(1), | |
197 | UNI_RANGE(0.1), | |
198 | UNI_RANGE(0.01), | |
199 | } | |
200 | }; | |
9ced1de6 | 201 | static const struct comedi_lrange range_das08_pgm = { 9, { |
0882eaa6 DS |
202 | BIP_RANGE(10), |
203 | BIP_RANGE(5), | |
204 | BIP_RANGE(0.5), | |
205 | BIP_RANGE(0.05), | |
206 | BIP_RANGE(0.01), | |
207 | UNI_RANGE(10), | |
208 | UNI_RANGE(1), | |
209 | UNI_RANGE(0.1), | |
210 | UNI_RANGE(0.01) | |
211 | } | |
212 | }; /* | |
213 | cio-das08jr.pdf | |
214 | ||
215 | "das08/jr-ao" | |
216 | ||
217 | 0 a/d bits 0-3 unused | |
218 | 1 a/d bits 4-11 start 12 bit | |
219 | 2 eoc, mux mux | |
220 | 3 di do | |
221 | 4 unused ao0_lsb | |
222 | 5 unused ao0_msb | |
223 | 6 unused ao1_lsb | |
224 | 7 unused ao1_msb | |
225 | ||
226 | */ | |
227 | ||
9ced1de6 | 228 | static const struct comedi_lrange *const das08_ai_lranges[] = { |
0882eaa6 DS |
229 | &range_unknown, |
230 | &range_bipolar5, | |
231 | &range_das08_pgh, | |
232 | &range_das08_pgl, | |
233 | &range_das08_pgm, | |
234 | }; | |
235 | ||
236 | static const int das08_pgh_gainlist[] = | |
237 | { 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7 }; | |
238 | static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 }; | |
239 | static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 }; | |
240 | ||
241 | static const int *const das08_gainlists[] = { | |
242 | NULL, | |
243 | NULL, | |
244 | das08_pgh_gainlist, | |
245 | das08_pgl_gainlist, | |
246 | das08_pgm_gainlist, | |
247 | }; | |
248 | ||
249 | static const struct das08_board_struct das08_boards[] = { | |
250 | { | |
251 | name: "isa-das08", // cio-das08.pdf | |
252 | bustype: isa, | |
253 | ai: das08_ai_rinsn, | |
254 | ai_nbits:12, | |
255 | ai_pg: das08_pg_none, | |
256 | ai_encoding:das08_encode12, | |
257 | ao: NULL, | |
258 | ao_nbits:12, | |
259 | di: das08_di_rbits, | |
260 | do_: das08_do_wbits, | |
261 | do_nchan:4, | |
262 | i8255_offset:8, | |
263 | i8254_offset:4, | |
264 | iosize: 16, // unchecked | |
265 | }, | |
266 | { | |
267 | name: "das08-pgm", // cio-das08pgx.pdf | |
268 | bustype: isa, | |
269 | ai: das08_ai_rinsn, | |
270 | ai_nbits:12, | |
271 | ai_pg: das08_pgm, | |
272 | ai_encoding:das08_encode12, | |
273 | ao: NULL, | |
274 | di: das08_di_rbits, | |
275 | do_: das08_do_wbits, | |
276 | do_nchan:4, | |
277 | i8255_offset:0, | |
278 | i8254_offset:0x04, | |
279 | iosize: 16, // unchecked | |
280 | }, | |
281 | { | |
282 | name: "das08-pgh", // cio-das08pgx.pdf | |
283 | bustype: isa, | |
284 | ai: das08_ai_rinsn, | |
285 | ai_nbits:12, | |
286 | ai_pg: das08_pgh, | |
287 | ai_encoding:das08_encode12, | |
288 | ao: NULL, | |
289 | di: das08_di_rbits, | |
290 | do_: das08_do_wbits, | |
291 | do_nchan:4, | |
292 | i8255_offset:0, | |
293 | i8254_offset:0x04, | |
294 | iosize: 16, // unchecked | |
295 | }, | |
296 | { | |
297 | name: "das08-pgl", // cio-das08pgx.pdf | |
298 | bustype: isa, | |
299 | ai: das08_ai_rinsn, | |
300 | ai_nbits:12, | |
301 | ai_pg: das08_pgl, | |
302 | ai_encoding:das08_encode12, | |
303 | ao: NULL, | |
304 | di: das08_di_rbits, | |
305 | do_: das08_do_wbits, | |
306 | do_nchan:4, | |
307 | i8255_offset:0, | |
308 | i8254_offset:0x04, | |
309 | iosize: 16, // unchecked | |
310 | }, | |
311 | { | |
312 | name: "das08-aoh", // cio-das08_aox.pdf | |
313 | bustype: isa, | |
314 | ai: das08_ai_rinsn, | |
315 | ai_nbits:12, | |
316 | ai_pg: das08_pgh, | |
317 | ai_encoding:das08_encode12, | |
318 | ao: das08ao_ao_winsn, // 8 | |
319 | ao_nbits:12, | |
320 | di: das08_di_rbits, | |
321 | do_: das08_do_wbits, | |
322 | do_nchan:4, | |
323 | i8255_offset:0x0c, | |
324 | i8254_offset:0x04, | |
325 | iosize: 16, // unchecked | |
326 | }, | |
327 | { | |
328 | name: "das08-aol", // cio-das08_aox.pdf | |
329 | bustype: isa, | |
330 | ai: das08_ai_rinsn, | |
331 | ai_nbits:12, | |
332 | ai_pg: das08_pgl, | |
333 | ai_encoding:das08_encode12, | |
334 | ao: das08ao_ao_winsn, // 8 | |
335 | ao_nbits:12, | |
336 | di: das08_di_rbits, | |
337 | do_: das08_do_wbits, | |
338 | do_nchan:4, | |
339 | i8255_offset:0x0c, | |
340 | i8254_offset:0x04, | |
341 | iosize: 16, // unchecked | |
342 | }, | |
343 | { | |
344 | name: "das08-aom", // cio-das08_aox.pdf | |
345 | bustype: isa, | |
346 | ai: das08_ai_rinsn, | |
347 | ai_nbits:12, | |
348 | ai_pg: das08_pgm, | |
349 | ai_encoding:das08_encode12, | |
350 | ao: das08ao_ao_winsn, // 8 | |
351 | ao_nbits:12, | |
352 | di: das08_di_rbits, | |
353 | do_: das08_do_wbits, | |
354 | do_nchan:4, | |
355 | i8255_offset:0x0c, | |
356 | i8254_offset:0x04, | |
357 | iosize: 16, // unchecked | |
358 | }, | |
359 | { | |
360 | name: "das08/jr-ao", // cio-das08-jr-ao.pdf | |
361 | bustype: isa, | |
362 | ai: das08_ai_rinsn, | |
363 | ai_nbits:12, | |
364 | ai_pg: das08_pg_none, | |
365 | ai_encoding:das08_encode12, | |
366 | ao: das08jr_ao_winsn, | |
367 | ao_nbits:12, | |
368 | di: das08jr_di_rbits, | |
369 | do_: das08jr_do_wbits, | |
370 | do_nchan:8, | |
371 | i8255_offset:0, | |
372 | i8254_offset:0, | |
373 | iosize: 16, // unchecked | |
374 | }, | |
375 | { | |
376 | name: "das08jr-16-ao", // cio-das08jr-16-ao.pdf | |
377 | bustype: isa, | |
378 | ai: das08_ai_rinsn, | |
379 | ai_nbits:16, | |
380 | ai_pg: das08_pg_none, | |
381 | ai_encoding:das08_encode12, | |
382 | ao: das08jr_ao_winsn, | |
383 | ao_nbits:16, | |
384 | di: das08jr_di_rbits, | |
385 | do_: das08jr_do_wbits, | |
386 | do_nchan:8, | |
387 | i8255_offset:0, | |
388 | i8254_offset:0x04, | |
389 | iosize: 16, // unchecked | |
390 | }, | |
391 | #ifdef CONFIG_COMEDI_PCI | |
392 | { | |
393 | name: "das08", // pci-das08 | |
394 | id: PCI_DEVICE_ID_PCIDAS08, | |
395 | bustype: pci, | |
396 | ai: das08_ai_rinsn, | |
397 | ai_nbits:12, | |
398 | ai_pg: das08_bipolar5, | |
399 | ai_encoding:das08_encode12, | |
400 | ao: NULL, | |
401 | ao_nbits:0, | |
402 | di: das08_di_rbits, | |
403 | do_: das08_do_wbits, | |
404 | do_nchan:4, | |
405 | i8255_offset:0, | |
406 | i8254_offset:4, | |
407 | iosize: 8, | |
408 | }, | |
409 | #endif | |
410 | { | |
411 | name: "pc104-das08", | |
412 | bustype: pc104, | |
413 | ai: das08_ai_rinsn, | |
414 | ai_nbits:12, | |
415 | ai_pg: das08_pg_none, | |
416 | ai_encoding:das08_encode12, | |
417 | ao: NULL, | |
418 | ao_nbits:0, | |
419 | di: das08_di_rbits, | |
420 | do_: das08_do_wbits, | |
421 | do_nchan:4, | |
422 | i8255_offset:0, | |
423 | i8254_offset:4, | |
424 | iosize: 16, // unchecked | |
425 | }, | |
426 | #if 0 | |
427 | { | |
428 | name: "das08/f", | |
429 | }, | |
430 | { | |
431 | name: "das08jr", | |
432 | }, | |
433 | #endif | |
434 | { | |
435 | name: "das08jr/16", | |
436 | bustype: isa, | |
437 | ai: das08_ai_rinsn, | |
438 | ai_nbits:16, | |
439 | ai_pg: das08_pg_none, | |
440 | ai_encoding:das08_encode16, | |
441 | ao: NULL, | |
442 | ao_nbits:0, | |
443 | di: das08jr_di_rbits, | |
444 | do_: das08jr_do_wbits, | |
445 | do_nchan:8, | |
446 | i8255_offset:0, | |
447 | i8254_offset:0, | |
448 | iosize: 16, // unchecked | |
449 | }, | |
450 | #if 0 | |
451 | { | |
452 | name: "das48-pga", // cio-das48-pga.pdf | |
453 | }, | |
454 | { | |
455 | name: "das08-pga-g2", // a KM board | |
456 | }, | |
457 | #endif | |
458 | }; | |
459 | ||
460 | #ifdef CONFIG_COMEDI_PCMCIA | |
461 | struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = { | |
462 | { | |
463 | name: "pcm-das08", | |
464 | id: 0x0, // XXX | |
465 | bustype: pcmcia, | |
466 | ai: das08_ai_rinsn, | |
467 | ai_nbits:12, | |
468 | ai_pg: das08_bipolar5, | |
469 | ai_encoding:das08_pcm_encode12, | |
470 | ao: NULL, | |
471 | ao_nbits:0, | |
472 | di: das08_di_rbits, | |
473 | do_: das08_do_wbits, | |
474 | do_nchan:3, | |
475 | i8255_offset:0, | |
476 | i8254_offset:0, | |
477 | iosize: 16, | |
478 | }, | |
479 | // duplicate so driver name can be used also | |
480 | { | |
481 | name: "das08_cs", | |
482 | id: 0x0, // XXX | |
483 | bustype: pcmcia, | |
484 | ai: das08_ai_rinsn, | |
485 | ai_nbits:12, | |
486 | ai_pg: das08_bipolar5, | |
487 | ai_encoding:das08_pcm_encode12, | |
488 | ao: NULL, | |
489 | ao_nbits:0, | |
490 | di: das08_di_rbits, | |
491 | do_: das08_do_wbits, | |
492 | do_nchan:3, | |
493 | i8255_offset:0, | |
494 | i8254_offset:0, | |
495 | iosize: 16, | |
496 | }, | |
497 | }; | |
498 | #endif | |
499 | ||
500 | #ifdef CONFIG_COMEDI_PCI | |
501 | static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = { | |
502 | {PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08, PCI_ANY_ID, | |
503 | PCI_ANY_ID, 0, 0, 0}, | |
504 | {0} | |
505 | }; | |
506 | ||
507 | MODULE_DEVICE_TABLE(pci, das08_pci_table); | |
508 | #endif | |
509 | ||
510 | #define devpriv ((struct das08_private_struct *)dev->private) | |
511 | #define thisboard ((const struct das08_board_struct *)dev->board_ptr) | |
512 | ||
513 | #define TIMEOUT 100000 | |
514 | ||
34c43922 | 515 | static int das08_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 516 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
517 | { |
518 | int i, n; | |
519 | int chan; | |
520 | int range; | |
521 | int lsb, msb; | |
522 | ||
523 | chan = CR_CHAN(insn->chanspec); | |
524 | range = CR_RANGE(insn->chanspec); | |
525 | ||
526 | /* clear crap */ | |
527 | inb(dev->iobase + DAS08_LSB); | |
528 | inb(dev->iobase + DAS08_MSB); | |
529 | ||
530 | /* set multiplexer */ | |
531 | spin_lock(&dev->spinlock); // lock to prevent race with digital output | |
532 | devpriv->do_mux_bits &= ~DAS08_MUX_MASK; | |
533 | devpriv->do_mux_bits |= DAS08_MUX(chan); | |
534 | outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); | |
535 | spin_unlock(&dev->spinlock); | |
536 | ||
537 | if (s->range_table->length > 1) { | |
538 | /* set gain/range */ | |
539 | range = CR_RANGE(insn->chanspec); | |
540 | outb(devpriv->pg_gainlist[range], | |
541 | dev->iobase + DAS08AO_GAIN_CONTROL); | |
542 | } | |
543 | ||
544 | for (n = 0; n < insn->n; n++) { | |
545 | /* clear over-range bits for 16-bit boards */ | |
546 | if (thisboard->ai_nbits == 16) | |
547 | if (inb(dev->iobase + DAS08_MSB) & 0x80) | |
548 | rt_printk("das08: over-range\n"); | |
549 | ||
550 | /* trigger conversion */ | |
551 | outb_p(0, dev->iobase + DAS08_TRIG_12BIT); | |
552 | ||
553 | for (i = 0; i < TIMEOUT; i++) { | |
554 | if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC)) | |
555 | break; | |
556 | } | |
557 | if (i == TIMEOUT) { | |
558 | rt_printk("das08: timeout\n"); | |
559 | return -ETIME; | |
560 | } | |
561 | msb = inb(dev->iobase + DAS08_MSB); | |
562 | lsb = inb(dev->iobase + DAS08_LSB); | |
563 | if (thisboard->ai_encoding == das08_encode12) { | |
564 | data[n] = (lsb >> 4) | (msb << 4); | |
565 | } else if (thisboard->ai_encoding == das08_pcm_encode12) { | |
566 | data[n] = (msb << 8) + lsb; | |
567 | } else if (thisboard->ai_encoding == das08_encode16) { | |
568 | /* FPOS 16-bit boards are sign-magnitude */ | |
569 | if (msb & 0x80) | |
570 | data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8); | |
571 | else | |
572 | data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8); | |
573 | } else { | |
574 | comedi_error(dev, "bug! unknown ai encoding"); | |
575 | return -1; | |
576 | } | |
577 | } | |
578 | ||
579 | return n; | |
580 | } | |
581 | ||
34c43922 | 582 | static int das08_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 583 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
584 | { |
585 | data[0] = 0; | |
586 | data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS)); | |
587 | ||
588 | return 2; | |
589 | } | |
590 | ||
34c43922 | 591 | static int das08_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 592 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
593 | { |
594 | int wbits; | |
595 | ||
596 | // get current settings of digital output lines | |
597 | wbits = (devpriv->do_mux_bits >> 4) & 0xf; | |
598 | // null bits we are going to set | |
599 | wbits &= ~data[0]; | |
600 | // set new bit values | |
601 | wbits |= data[0] & data[1]; | |
602 | // remember digital output bits | |
603 | spin_lock(&dev->spinlock); // prevent race with setting of analog input mux | |
604 | devpriv->do_mux_bits &= ~DAS08_DO_MASK; | |
605 | devpriv->do_mux_bits |= DAS08_OP(wbits); | |
606 | outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL); | |
607 | spin_unlock(&dev->spinlock); | |
608 | ||
609 | data[1] = wbits; | |
610 | ||
611 | return 2; | |
612 | } | |
613 | ||
34c43922 | 614 | static int das08jr_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 615 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
616 | { |
617 | data[0] = 0; | |
618 | data[1] = inb(dev->iobase + DAS08JR_DIO); | |
619 | ||
620 | return 2; | |
621 | } | |
622 | ||
34c43922 | 623 | static int das08jr_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 624 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
625 | { |
626 | // null bits we are going to set | |
627 | devpriv->do_bits &= ~data[0]; | |
628 | // set new bit values | |
629 | devpriv->do_bits |= data[0] & data[1]; | |
630 | outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO); | |
631 | ||
632 | data[1] = devpriv->do_bits; | |
633 | ||
634 | return 2; | |
635 | } | |
636 | ||
34c43922 | 637 | static int das08jr_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 638 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
639 | { |
640 | int n; | |
641 | int lsb, msb; | |
642 | int chan; | |
643 | ||
644 | lsb = data[0] & 0xff; | |
645 | msb = (data[0] >> 8) & 0xf; | |
646 | ||
647 | chan = CR_CHAN(insn->chanspec); | |
648 | ||
649 | for (n = 0; n < insn->n; n++) { | |
650 | #if 0 | |
651 | outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); | |
652 | outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); | |
653 | #else | |
654 | outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan)); | |
655 | outb(msb, dev->iobase + DAS08JR_AO_MSB(chan)); | |
656 | #endif | |
657 | ||
658 | /* load DACs */ | |
659 | inb(dev->iobase + DAS08JR_DIO); | |
660 | } | |
661 | ||
662 | return n; | |
663 | } | |
664 | ||
665 | /* | |
666 | * | |
667 | * The -aox boards have the DACs at a different offset and use | |
668 | * a different method to force an update. | |
669 | * | |
670 | */ | |
34c43922 | 671 | static int das08ao_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 672 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
673 | { |
674 | int n; | |
675 | int lsb, msb; | |
676 | int chan; | |
677 | ||
678 | lsb = data[0] & 0xff; | |
679 | msb = (data[0] >> 8) & 0xf; | |
680 | ||
681 | chan = CR_CHAN(insn->chanspec); | |
682 | ||
683 | for (n = 0; n < insn->n; n++) { | |
684 | #if 0 | |
685 | outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]); | |
686 | outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]); | |
687 | #else | |
688 | outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan)); | |
689 | outb(msb, dev->iobase + DAS08AO_AO_MSB(chan)); | |
690 | #endif | |
691 | ||
692 | /* load DACs */ | |
693 | inb(dev->iobase + DAS08AO_AO_UPDATE); | |
694 | } | |
695 | ||
696 | return n; | |
697 | } | |
698 | ||
699 | static unsigned int i8254_read_channel_low(unsigned int base, int chan) | |
700 | { | |
701 | unsigned int msb, lsb; | |
702 | ||
703 | /* The following instructions must be in order. | |
704 | We must avoid other process reading the counter's value in the | |
705 | middle. | |
706 | The spin_lock isn't needed since ioctl calls grab the big kernel | |
707 | lock automatically */ | |
708 | /*spin_lock(sp); */ | |
709 | outb(chan << 6, base + I8254_CTRL); | |
710 | base += chan; | |
711 | lsb = inb(base); | |
712 | msb = inb(base); | |
713 | /*spin_unlock(sp); */ | |
714 | ||
715 | return lsb | (msb << 8); | |
716 | } | |
717 | ||
718 | static void i8254_write_channel_low(unsigned int base, int chan, | |
719 | unsigned int value) | |
720 | { | |
721 | unsigned int msb, lsb; | |
722 | ||
723 | lsb = value & 0xFF; | |
724 | msb = value >> 8; | |
725 | ||
726 | /* write lsb, then msb */ | |
727 | base += chan; | |
728 | /* See comments in i8254_read_channel_low */ | |
729 | /*spin_lock(sp); */ | |
730 | outb(lsb, base); | |
731 | outb(msb, base); | |
732 | /*spin_unlock(sp); */ | |
733 | } | |
734 | ||
735 | static unsigned int i8254_read_channel(struct i8254_struct *st, int channel) | |
736 | { | |
737 | int chan = st->logic2phys[channel]; | |
738 | ||
739 | return i8254_read_channel_low(st->iobase, chan); | |
740 | } | |
741 | ||
742 | static void i8254_write_channel(struct i8254_struct *st, int channel, | |
743 | unsigned int value) | |
744 | { | |
745 | int chan = st->logic2phys[channel]; | |
746 | ||
747 | i8254_write_channel_low(st->iobase, chan, value); | |
748 | } | |
749 | ||
750 | static void i8254_initialize(struct i8254_struct *st) | |
751 | { | |
752 | int i; | |
753 | for (i = 0; i < 3; ++i) | |
754 | i8254_set_mode_low(st->iobase, i, st->mode[i]); | |
755 | } | |
756 | ||
757 | static void i8254_set_mode_low(unsigned int base, int channel, | |
758 | unsigned int mode) | |
759 | { | |
760 | outb((channel << 6) | 0x30 | (mode & 0x0F), base + I8254_CTRL); | |
761 | } | |
762 | ||
763 | static void i8254_set_mode(struct i8254_struct *st, int channel, | |
764 | unsigned int mode) | |
765 | { | |
766 | int chan = st->logic2phys[channel]; | |
767 | ||
768 | st->mode[chan] = mode; | |
769 | return i8254_set_mode_low(st->iobase, chan, mode); | |
770 | } | |
771 | ||
772 | static unsigned int i8254_read_status_low(unsigned int base, int channel) | |
773 | { | |
774 | outb(0xE0 | (2 << channel), base + I8254_CTRL); | |
775 | return inb(base + channel); | |
776 | } | |
777 | ||
778 | static unsigned int i8254_read_status(struct i8254_struct *st, int channel) | |
779 | { | |
780 | int chan = st->logic2phys[channel]; | |
781 | ||
782 | return i8254_read_status_low(st->iobase, chan); | |
783 | } | |
784 | ||
34c43922 | 785 | static int das08_counter_read(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 786 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
787 | { |
788 | int chan = insn->chanspec; | |
789 | ||
790 | //printk("Reading counter channel %d ",chan); | |
791 | data[0] = i8254_read_channel(&devpriv->i8254, chan); | |
792 | //printk("=> 0x%08X\n",data[0]); | |
793 | ||
794 | return 1; | |
795 | } | |
796 | ||
34c43922 | 797 | static int das08_counter_write(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 798 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
799 | { |
800 | int chan = insn->chanspec; | |
801 | ||
802 | //printk("Writing counter channel %d with 0x%04X\n",chan,data[0]); | |
803 | i8254_write_channel(&devpriv->i8254, chan, data[0]); | |
804 | ||
805 | return 1; | |
806 | } | |
807 | ||
34c43922 | 808 | static int das08_counter_config(struct comedi_device * dev, struct comedi_subdevice * s, |
90035c08 | 809 | struct comedi_insn * insn, unsigned int * data) |
0882eaa6 DS |
810 | { |
811 | int chan = insn->chanspec; | |
812 | ||
813 | if (insn->n != 2) | |
814 | return -EINVAL; | |
815 | ||
816 | switch (data[0]) { | |
817 | case INSN_CONFIG_SET_COUNTER_MODE: | |
818 | i8254_set_mode(&devpriv->i8254, chan, data[1]); | |
819 | break; | |
820 | case INSN_CONFIG_8254_READ_STATUS: | |
821 | data[1] = i8254_read_status(&devpriv->i8254, chan); | |
822 | break; | |
823 | default: | |
824 | return -EINVAL; | |
825 | break; | |
826 | } | |
827 | return 2; | |
828 | } | |
829 | ||
0707bb04 | 830 | static int das08_attach(struct comedi_device * dev, struct comedi_devconfig * it); |
0882eaa6 | 831 | |
139dfbdf | 832 | static struct comedi_driver driver_das08 = { |
0882eaa6 DS |
833 | driver_name: DRV_NAME, |
834 | module:THIS_MODULE, | |
835 | attach:das08_attach, | |
836 | detach:das08_common_detach, | |
837 | board_name:&das08_boards[0].name, | |
838 | num_names:sizeof(das08_boards) / | |
839 | sizeof(struct das08_board_struct), | |
840 | offset:sizeof(struct das08_board_struct), | |
841 | }; | |
842 | ||
71b5f4f1 | 843 | int das08_common_attach(struct comedi_device * dev, unsigned long iobase) |
0882eaa6 | 844 | { |
34c43922 | 845 | struct comedi_subdevice *s; |
0882eaa6 DS |
846 | int ret; |
847 | ||
848 | // allocate ioports for non-pcmcia, non-pci boards | |
849 | if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { | |
850 | printk(" iobase 0x%lx\n", iobase); | |
851 | if (!request_region(iobase, thisboard->iosize, DRV_NAME)) { | |
852 | printk(" I/O port conflict\n"); | |
853 | return -EIO; | |
854 | } | |
855 | } | |
856 | dev->iobase = iobase; | |
857 | ||
858 | dev->board_name = thisboard->name; | |
859 | ||
860 | if ((ret = alloc_subdevices(dev, 6)) < 0) | |
861 | return ret; | |
862 | ||
863 | s = dev->subdevices + 0; | |
864 | /* ai */ | |
865 | if (thisboard->ai) { | |
866 | s->type = COMEDI_SUBD_AI; | |
867 | /* XXX some boards actually have differential inputs instead of single ended. | |
868 | * The driver does nothing with arefs though, so it's no big deal. */ | |
869 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
870 | s->n_chan = 8; | |
871 | s->maxdata = (1 << thisboard->ai_nbits) - 1; | |
872 | s->range_table = das08_ai_lranges[thisboard->ai_pg]; | |
873 | s->insn_read = thisboard->ai; | |
874 | devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg]; | |
875 | } else { | |
876 | s->type = COMEDI_SUBD_UNUSED; | |
877 | } | |
878 | ||
879 | s = dev->subdevices + 1; | |
880 | /* ao */ | |
881 | if (thisboard->ao) { | |
882 | s->type = COMEDI_SUBD_AO; | |
883 | // XXX lacks read-back insn | |
884 | s->subdev_flags = SDF_WRITABLE; | |
885 | s->n_chan = 2; | |
886 | s->maxdata = (1 << thisboard->ao_nbits) - 1; | |
887 | s->range_table = &range_bipolar5; | |
888 | s->insn_write = thisboard->ao; | |
889 | } else { | |
890 | s->type = COMEDI_SUBD_UNUSED; | |
891 | } | |
892 | ||
893 | s = dev->subdevices + 2; | |
894 | /* di */ | |
895 | if (thisboard->di) { | |
896 | s->type = COMEDI_SUBD_DI; | |
897 | s->subdev_flags = SDF_READABLE; | |
898 | s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8; | |
899 | s->maxdata = 1; | |
900 | s->range_table = &range_digital; | |
901 | s->insn_bits = thisboard->di; | |
902 | } else { | |
903 | s->type = COMEDI_SUBD_UNUSED; | |
904 | } | |
905 | ||
906 | s = dev->subdevices + 3; | |
907 | /* do */ | |
908 | if (thisboard->do_) { | |
909 | s->type = COMEDI_SUBD_DO; | |
910 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; | |
911 | s->n_chan = thisboard->do_nchan; | |
912 | s->maxdata = 1; | |
913 | s->range_table = &range_digital; | |
914 | s->insn_bits = thisboard->do_; | |
915 | } else { | |
916 | s->type = COMEDI_SUBD_UNUSED; | |
917 | } | |
918 | ||
919 | s = dev->subdevices + 4; | |
920 | /* 8255 */ | |
921 | if (thisboard->i8255_offset != 0) { | |
922 | subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase + | |
923 | thisboard->i8255_offset)); | |
924 | } else { | |
925 | s->type = COMEDI_SUBD_UNUSED; | |
926 | } | |
927 | ||
928 | s = dev->subdevices + 5; | |
929 | /* 8254 */ | |
930 | if (thisboard->i8254_offset != 0) { | |
931 | s->type = COMEDI_SUBD_COUNTER; | |
932 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; | |
933 | s->n_chan = 3; | |
934 | s->maxdata = 0xFFFF; | |
935 | s->insn_read = das08_counter_read; | |
936 | s->insn_write = das08_counter_write; | |
937 | s->insn_config = das08_counter_config; | |
938 | /* Set-up the 8254 structure */ | |
939 | devpriv->i8254.channels = 3; | |
940 | devpriv->i8254.logic2phys[0] = 0; | |
941 | devpriv->i8254.logic2phys[1] = 1; | |
942 | devpriv->i8254.logic2phys[2] = 2; | |
943 | devpriv->i8254.iobase = iobase + thisboard->i8254_offset; | |
944 | devpriv->i8254.mode[0] = | |
945 | devpriv->i8254.mode[1] = | |
946 | devpriv->i8254.mode[2] = I8254_MODE0 | I8254_BINARY; | |
947 | i8254_initialize(&devpriv->i8254); | |
948 | } else { | |
949 | s->type = COMEDI_SUBD_UNUSED; | |
950 | } | |
951 | ||
952 | return 0; | |
953 | } | |
954 | ||
0707bb04 | 955 | static int das08_attach(struct comedi_device * dev, struct comedi_devconfig * it) |
0882eaa6 DS |
956 | { |
957 | int ret; | |
958 | unsigned long iobase; | |
959 | #ifdef CONFIG_COMEDI_PCI | |
960 | unsigned long pci_iobase = 0; | |
961 | struct pci_dev *pdev; | |
962 | #endif | |
963 | ||
964 | if ((ret = alloc_private(dev, sizeof(struct das08_private_struct))) < 0) | |
965 | return ret; | |
966 | ||
967 | printk("comedi%d: das08: ", dev->minor); | |
968 | // deal with a pci board | |
969 | if (thisboard->bustype == pci) { | |
970 | #ifdef CONFIG_COMEDI_PCI | |
971 | if (it->options[0] || it->options[1]) { | |
972 | printk("bus %i slot %i ", | |
973 | it->options[0], it->options[1]); | |
974 | } | |
975 | printk("\n"); | |
976 | // find card | |
977 | for (pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
978 | pdev != NULL; | |
979 | pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) { | |
980 | if (pdev->vendor == PCI_VENDOR_ID_COMPUTERBOARDS | |
981 | && pdev->device == PCI_DEVICE_ID_PCIDAS08) { | |
982 | if (it->options[0] || it->options[1]) { | |
983 | if (pdev->bus->number == it->options[0] | |
984 | && PCI_SLOT(pdev->devfn) == | |
985 | it->options[1]) { | |
986 | break; | |
987 | } | |
988 | } else { | |
989 | break; | |
990 | } | |
991 | } | |
992 | } | |
993 | if (!pdev) { | |
994 | printk("No pci das08 cards found\n"); | |
995 | return -EIO; | |
996 | } | |
997 | devpriv->pdev = pdev; | |
998 | // enable PCI device and reserve I/O spaces | |
999 | if (comedi_pci_enable(pdev, DRV_NAME)) { | |
1000 | printk(" Error enabling PCI device and requesting regions\n"); | |
1001 | return -EIO; | |
1002 | } | |
1003 | // read base addresses | |
1004 | pci_iobase = pci_resource_start(pdev, 1); | |
1005 | iobase = pci_resource_start(pdev, 2); | |
1006 | printk("pcibase 0x%lx iobase 0x%lx\n", pci_iobase, iobase); | |
1007 | devpriv->pci_iobase = pci_iobase; | |
1008 | #if 0 | |
1009 | /* We could enable to pci-das08's interrupt here to make it possible | |
1010 | * to do timed input in this driver, but there is little point since | |
1011 | * conversions would have to be started by the interrupt handler | |
1012 | * so you might as well use comedi_rt_timer to emulate commands | |
1013 | */ | |
1014 | /* set source of interrupt trigger to counter2 output */ | |
1015 | outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL); | |
1016 | /* Enable local interrupt 1 and pci interrupt */ | |
1017 | outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR); | |
1018 | #endif | |
1019 | #else /* CONFIG_COMEDI_PCI */ | |
1020 | printk("this driver has not been built with PCI support.\n"); | |
1021 | return -EINVAL; | |
1022 | #endif /* CONFIG_COMEDI_PCI */ | |
1023 | } else { | |
1024 | iobase = it->options[0]; | |
1025 | } | |
1026 | printk("\n"); | |
1027 | ||
1028 | return das08_common_attach(dev, iobase); | |
1029 | } | |
1030 | ||
71b5f4f1 | 1031 | int das08_common_detach(struct comedi_device * dev) |
0882eaa6 DS |
1032 | { |
1033 | printk(KERN_INFO "comedi%d: das08: remove\n", dev->minor); | |
1034 | ||
1035 | if (dev->subdevices) | |
1036 | subdev_8255_cleanup(dev, dev->subdevices + 4); | |
1037 | ||
1038 | // deallocate ioports for non-pcmcia, non-pci boards | |
1039 | if ((thisboard->bustype != pcmcia) && (thisboard->bustype != pci)) { | |
1040 | if (dev->iobase) | |
1041 | release_region(dev->iobase, thisboard->iosize); | |
1042 | } | |
1043 | ||
1044 | #ifdef CONFIG_COMEDI_PCI | |
1045 | if (devpriv) { | |
1046 | if (devpriv->pdev) { | |
1047 | if (devpriv->pci_iobase) { | |
1048 | comedi_pci_disable(devpriv->pdev); | |
1049 | } | |
1050 | pci_dev_put(devpriv->pdev); | |
1051 | } | |
1052 | } | |
1053 | #endif | |
1054 | ||
1055 | return 0; | |
1056 | } | |
1057 | ||
1058 | #ifdef CONFIG_COMEDI_PCI | |
1059 | COMEDI_PCI_INITCLEANUP(driver_das08, das08_pci_table); | |
1060 | #else | |
1061 | COMEDI_INITCLEANUP(driver_das08); | |
1062 | #endif | |
1063 | ||
1064 | EXPORT_SYMBOL_GPL(das08_common_attach); | |
1065 | EXPORT_SYMBOL_GPL(das08_common_detach); | |
1066 | #ifdef CONFIG_COMEDI_PCMCIA | |
1067 | EXPORT_SYMBOL_GPL(das08_cs_boards); | |
1068 | #endif |