1 /*
2 comedi/drivers/das800.c
3 Driver for Keitley das800 series boards and compatibles
4 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 */
19 /*
20 Driver: das800
21 Description: Keithley Metrabyte DAS800 (& compatibles)
22 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
24 DAS-802 (das-802),
25 [Measurement Computing] CIO-DAS800 (cio-das800),
26 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
27 CIO-DAS802/16 (cio-das802/16)
28 Status: works, cio-das802/16 untested - email me if you have tested it
29
30 Configuration options:
31 [0] - I/O port base address
32 [1] - IRQ (optional, required for timed or externally triggered conversions)
33
34 Notes:
35 IRQ can be omitted, although the cmd interface will not work without it.
36
37 All entries in the channel/gain list must use the same gain and be
38 consecutive channels counting upwards in channel number (these are
39 hardware limitations.)
40
41 I've never tested the gain setting stuff since I only have a
42 DAS-800 board with fixed gain.
43
44 The cio-das802/16 does not have a fifo-empty status bit! Therefore
45 only fifo-half-full transfers are possible with this card.
46
47 cmd triggers supported:
48 start_src: TRIG_NOW | TRIG_EXT
49 scan_begin_src: TRIG_FOLLOW
50 scan_end_src: TRIG_COUNT
51 convert_src: TRIG_TIMER | TRIG_EXT
52 stop_src: TRIG_NONE | TRIG_COUNT
53 */
54
55 #include <linux/module.h>
56 #include <linux/interrupt.h>
57 #include <linux/delay.h>
58
59 #include "../comedidev.h"
60
61 #include "comedi_8254.h"
62
63 #define N_CHAN_AI 8 /* number of analog input channels */
64
65 /* Registers for the das800 */
66
67 #define DAS800_LSB 0
68 #define FIFO_EMPTY 0x1
69 #define FIFO_OVF 0x2
70 #define DAS800_MSB 1
71 #define DAS800_CONTROL1 2
72 #define CONTROL1_INTE 0x8
73 #define DAS800_CONV_CONTROL 2
74 #define ITE 0x1
75 #define CASC 0x2
76 #define DTEN 0x4
77 #define IEOC 0x8
78 #define EACS 0x10
79 #define CONV_HCEN 0x80
80 #define DAS800_SCAN_LIMITS 2
81 #define DAS800_STATUS 2
82 #define IRQ 0x8
83 #define BUSY 0x80
84 #define DAS800_GAIN 3
85 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
86 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
87 #define CONTROL1 0x80
88 #define CONV_CONTROL 0xa0
89 #define SCAN_LIMITS 0xc0
90 #define ID 0xe0
91 #define DAS800_8254 4
92 #define DAS800_STATUS2 7
93 #define STATUS2_HCEN 0x80
94 #define STATUS2_INTE 0X20
95 #define DAS800_ID 7
96
97 #define DAS802_16_HALF_FIFO_SZ 128
98
99 struct das800_board {
100 const char *name;
101 int ai_speed;
102 const struct comedi_lrange *ai_range;
103 int resolution;
104 };
105
106 static const struct comedi_lrange range_das801_ai = {
107 9, {
108 BIP_RANGE(5),
109 BIP_RANGE(10),
110 UNI_RANGE(10),
111 BIP_RANGE(0.5),
112 UNI_RANGE(1),
113 BIP_RANGE(0.05),
114 UNI_RANGE(0.1),
115 BIP_RANGE(0.01),
116 UNI_RANGE(0.02)
117 }
118 };
119
120 static const struct comedi_lrange range_cio_das801_ai = {
121 9, {
122 BIP_RANGE(5),
123 BIP_RANGE(10),
124 UNI_RANGE(10),
125 BIP_RANGE(0.5),
126 UNI_RANGE(1),
127 BIP_RANGE(0.05),
128 UNI_RANGE(0.1),
129 BIP_RANGE(0.005),
130 UNI_RANGE(0.01)
131 }
132 };
133
134 static const struct comedi_lrange range_das802_ai = {
135 9, {
136 BIP_RANGE(5),
137 BIP_RANGE(10),
138 UNI_RANGE(10),
139 BIP_RANGE(2.5),
140 UNI_RANGE(5),
141 BIP_RANGE(1.25),
142 UNI_RANGE(2.5),
143 BIP_RANGE(0.625),
144 UNI_RANGE(1.25)
145 }
146 };
147
148 static const struct comedi_lrange range_das80216_ai = {
149 8, {
150 BIP_RANGE(10),
151 UNI_RANGE(10),
152 BIP_RANGE(5),
153 UNI_RANGE(5),
154 BIP_RANGE(2.5),
155 UNI_RANGE(2.5),
156 BIP_RANGE(1.25),
157 UNI_RANGE(1.25)
158 }
159 };
160
161 enum das800_boardinfo {
162 BOARD_DAS800,
163 BOARD_CIODAS800,
164 BOARD_DAS801,
165 BOARD_CIODAS801,
166 BOARD_DAS802,
167 BOARD_CIODAS802,
168 BOARD_CIODAS80216,
169 };
170
171 static const struct das800_board das800_boards[] = {
172 [BOARD_DAS800] = {
173 .name = "das-800",
174 .ai_speed = 25000,
175 .ai_range = &range_bipolar5,
176 .resolution = 12,
177 },
178 [BOARD_CIODAS800] = {
179 .name = "cio-das800",
180 .ai_speed = 20000,
181 .ai_range = &range_bipolar5,
182 .resolution = 12,
183 },
184 [BOARD_DAS801] = {
185 .name = "das-801",
186 .ai_speed = 25000,
187 .ai_range = &range_das801_ai,
188 .resolution = 12,
189 },
190 [BOARD_CIODAS801] = {
191 .name = "cio-das801",
192 .ai_speed = 20000,
193 .ai_range = &range_cio_das801_ai,
194 .resolution = 12,
195 },
196 [BOARD_DAS802] = {
197 .name = "das-802",
198 .ai_speed = 25000,
199 .ai_range = &range_das802_ai,
200 .resolution = 12,
201 },
202 [BOARD_CIODAS802] = {
203 .name = "cio-das802",
204 .ai_speed = 20000,
205 .ai_range = &range_das802_ai,
206 .resolution = 12,
207 },
208 [BOARD_CIODAS80216] = {
209 .name = "cio-das802/16",
210 .ai_speed = 10000,
211 .ai_range = &range_das80216_ai,
212 .resolution = 16,
213 },
214 };
215
216 struct das800_private {
217 unsigned int do_bits; /* digital output bits */
218 };
219
das800_ind_write(struct comedi_device * dev,unsigned val,unsigned reg)220 static void das800_ind_write(struct comedi_device *dev,
221 unsigned val, unsigned reg)
222 {
223 /*
224 * Select dev->iobase + 2 to be desired register
225 * then write to that register.
226 */
227 outb(reg, dev->iobase + DAS800_GAIN);
228 outb(val, dev->iobase + 2);
229 }
230
das800_ind_read(struct comedi_device * dev,unsigned reg)231 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
232 {
233 /*
234 * Select dev->iobase + 7 to be desired register
235 * then read from that register.
236 */
237 outb(reg, dev->iobase + DAS800_GAIN);
238 return inb(dev->iobase + 7);
239 }
240
das800_enable(struct comedi_device * dev)241 static void das800_enable(struct comedi_device *dev)
242 {
243 const struct das800_board *board = dev->board_ptr;
244 struct das800_private *devpriv = dev->private;
245 unsigned long irq_flags;
246
247 spin_lock_irqsave(&dev->spinlock, irq_flags);
248 /* enable fifo-half full interrupts for cio-das802/16 */
249 if (board->resolution == 16)
250 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
251 /* enable hardware triggering */
252 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
253 /* enable card's interrupt */
254 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
255 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
256 }
257
das800_disable(struct comedi_device * dev)258 static void das800_disable(struct comedi_device *dev)
259 {
260 unsigned long irq_flags;
261
262 spin_lock_irqsave(&dev->spinlock, irq_flags);
263 /* disable hardware triggering of conversions */
264 das800_ind_write(dev, 0x0, CONV_CONTROL);
265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
266 }
267
das800_cancel(struct comedi_device * dev,struct comedi_subdevice * s)268 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
269 {
270 das800_disable(dev);
271 return 0;
272 }
273
das800_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)274 static int das800_ai_check_chanlist(struct comedi_device *dev,
275 struct comedi_subdevice *s,
276 struct comedi_cmd *cmd)
277 {
278 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
279 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
280 int i;
281
282 for (i = 1; i < cmd->chanlist_len; i++) {
283 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
284 unsigned int range = CR_RANGE(cmd->chanlist[i]);
285
286 if (chan != (chan0 + i) % s->n_chan) {
287 dev_dbg(dev->class_dev,
288 "chanlist must be consecutive, counting upwards\n");
289 return -EINVAL;
290 }
291
292 if (range != range0) {
293 dev_dbg(dev->class_dev,
294 "chanlist must all have the same gain\n");
295 return -EINVAL;
296 }
297 }
298
299 return 0;
300 }
301
das800_ai_do_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)302 static int das800_ai_do_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 struct comedi_cmd *cmd)
305 {
306 const struct das800_board *board = dev->board_ptr;
307 int err = 0;
308
309 /* Step 1 : check if triggers are trivially valid */
310
311 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
312 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
313 err |= comedi_check_trigger_src(&cmd->convert_src,
314 TRIG_TIMER | TRIG_EXT);
315 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
317
318 if (err)
319 return 1;
320
321 /* Step 2a : make sure trigger sources are unique */
322
323 err |= comedi_check_trigger_is_unique(cmd->start_src);
324 err |= comedi_check_trigger_is_unique(cmd->convert_src);
325 err |= comedi_check_trigger_is_unique(cmd->stop_src);
326
327 /* Step 2b : and mutually compatible */
328
329 if (err)
330 return 2;
331
332 /* Step 3: check if arguments are trivially valid */
333
334 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
335
336 if (cmd->convert_src == TRIG_TIMER) {
337 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
338 board->ai_speed);
339 }
340
341 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
343 cmd->chanlist_len);
344
345 if (cmd->stop_src == TRIG_COUNT)
346 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
347 else /* TRIG_NONE */
348 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
349
350 if (err)
351 return 3;
352
353 /* step 4: fix up any arguments */
354
355 if (cmd->convert_src == TRIG_TIMER) {
356 unsigned int arg = cmd->convert_arg;
357
358 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
359 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
360 }
361
362 if (err)
363 return 4;
364
365 /* Step 5: check channel list if it exists */
366 if (cmd->chanlist && cmd->chanlist_len > 0)
367 err |= das800_ai_check_chanlist(dev, s, cmd);
368
369 if (err)
370 return 5;
371
372 return 0;
373 }
374
das800_ai_do_cmd(struct comedi_device * dev,struct comedi_subdevice * s)375 static int das800_ai_do_cmd(struct comedi_device *dev,
376 struct comedi_subdevice *s)
377 {
378 const struct das800_board *board = dev->board_ptr;
379 struct comedi_async *async = s->async;
380 struct comedi_cmd *cmd = &async->cmd;
381 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
382 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
383 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
384 unsigned int scan_chans = (end_chan << 3) | start_chan;
385 int conv_bits;
386 unsigned long irq_flags;
387
388 das800_disable(dev);
389
390 spin_lock_irqsave(&dev->spinlock, irq_flags);
391 /* set scan limits */
392 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
393 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
394
395 /* set gain */
396 if (board->resolution == 12 && gain > 0)
397 gain += 0x7;
398 gain &= 0xf;
399 outb(gain, dev->iobase + DAS800_GAIN);
400
401 /* enable auto channel scan, send interrupts on end of conversion
402 * and set clock source to internal or external
403 */
404 conv_bits = 0;
405 conv_bits |= EACS | IEOC;
406 if (cmd->start_src == TRIG_EXT)
407 conv_bits |= DTEN;
408 if (cmd->convert_src == TRIG_TIMER) {
409 conv_bits |= CASC | ITE;
410 comedi_8254_update_divisors(dev->pacer);
411 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
412 }
413
414 spin_lock_irqsave(&dev->spinlock, irq_flags);
415 das800_ind_write(dev, conv_bits, CONV_CONTROL);
416 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
417
418 das800_enable(dev);
419 return 0;
420 }
421
das800_ai_get_sample(struct comedi_device * dev)422 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
423 {
424 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
425 unsigned int msb = inb(dev->iobase + DAS800_MSB);
426
427 return (msb << 8) | lsb;
428 }
429
das800_interrupt(int irq,void * d)430 static irqreturn_t das800_interrupt(int irq, void *d)
431 {
432 struct comedi_device *dev = d;
433 struct das800_private *devpriv = dev->private;
434 struct comedi_subdevice *s = dev->read_subdev;
435 struct comedi_async *async;
436 struct comedi_cmd *cmd;
437 unsigned long irq_flags;
438 unsigned int status;
439 unsigned int val;
440 bool fifo_empty;
441 bool fifo_overflow;
442 int i;
443
444 status = inb(dev->iobase + DAS800_STATUS);
445 if (!(status & IRQ))
446 return IRQ_NONE;
447 if (!dev->attached)
448 return IRQ_HANDLED;
449
450 async = s->async;
451 cmd = &async->cmd;
452
453 spin_lock_irqsave(&dev->spinlock, irq_flags);
454 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
455 /*
456 * Don't release spinlock yet since we want to make sure
457 * no one else disables hardware conversions.
458 */
459
460 /* if hardware conversions are not enabled, then quit */
461 if (status == 0) {
462 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
463 return IRQ_HANDLED;
464 }
465
466 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
467 val = das800_ai_get_sample(dev);
468 if (s->maxdata == 0x0fff) {
469 fifo_empty = !!(val & FIFO_EMPTY);
470 fifo_overflow = !!(val & FIFO_OVF);
471 } else {
472 /* cio-das802/16 has no fifo empty status bit */
473 fifo_empty = false;
474 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
475 CIO_FFOV);
476 }
477 if (fifo_empty || fifo_overflow)
478 break;
479
480 if (s->maxdata == 0x0fff)
481 val >>= 4; /* 12-bit sample */
482
483 val &= s->maxdata;
484 comedi_buf_write_samples(s, &val, 1);
485
486 if (cmd->stop_src == TRIG_COUNT &&
487 async->scans_done >= cmd->stop_arg) {
488 async->events |= COMEDI_CB_EOA;
489 break;
490 }
491 }
492
493 if (fifo_overflow) {
494 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
495 async->events |= COMEDI_CB_ERROR;
496 comedi_handle_events(dev, s);
497 return IRQ_HANDLED;
498 }
499
500 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
501 /*
502 * Re-enable card's interrupt.
503 * We already have spinlock, so indirect addressing is safe
504 */
505 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
506 CONTROL1);
507 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
508 } else {
509 /* otherwise, stop taking data */
510 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
511 das800_disable(dev);
512 }
513 comedi_handle_events(dev, s);
514 return IRQ_HANDLED;
515 }
516
das800_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)517 static int das800_ai_eoc(struct comedi_device *dev,
518 struct comedi_subdevice *s,
519 struct comedi_insn *insn,
520 unsigned long context)
521 {
522 unsigned int status;
523
524 status = inb(dev->iobase + DAS800_STATUS);
525 if ((status & BUSY) == 0)
526 return 0;
527 return -EBUSY;
528 }
529
das800_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)530 static int das800_ai_insn_read(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn,
533 unsigned int *data)
534 {
535 struct das800_private *devpriv = dev->private;
536 unsigned int chan = CR_CHAN(insn->chanspec);
537 unsigned int range = CR_RANGE(insn->chanspec);
538 unsigned long irq_flags;
539 unsigned int val;
540 int ret;
541 int i;
542
543 das800_disable(dev);
544
545 /* set multiplexer */
546 spin_lock_irqsave(&dev->spinlock, irq_flags);
547 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
548 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
549
550 /* set gain / range */
551 if (s->maxdata == 0x0fff && range)
552 range += 0x7;
553 range &= 0xf;
554 outb(range, dev->iobase + DAS800_GAIN);
555
556 udelay(5);
557
558 for (i = 0; i < insn->n; i++) {
559 /* trigger conversion */
560 outb_p(0, dev->iobase + DAS800_MSB);
561
562 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
563 if (ret)
564 return ret;
565
566 val = das800_ai_get_sample(dev);
567 if (s->maxdata == 0x0fff)
568 val >>= 4; /* 12-bit sample */
569 data[i] = val & s->maxdata;
570 }
571
572 return insn->n;
573 }
574
das800_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)575 static int das800_di_insn_bits(struct comedi_device *dev,
576 struct comedi_subdevice *s,
577 struct comedi_insn *insn,
578 unsigned int *data)
579 {
580 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
581
582 return insn->n;
583 }
584
das800_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)585 static int das800_do_insn_bits(struct comedi_device *dev,
586 struct comedi_subdevice *s,
587 struct comedi_insn *insn,
588 unsigned int *data)
589 {
590 struct das800_private *devpriv = dev->private;
591 unsigned long irq_flags;
592
593 if (comedi_dio_update_state(s, data)) {
594 devpriv->do_bits = s->state << 4;
595
596 spin_lock_irqsave(&dev->spinlock, irq_flags);
597 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
598 CONTROL1);
599 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
600 }
601
602 data[1] = s->state;
603
604 return insn->n;
605 }
606
das800_probe(struct comedi_device * dev)607 static const struct das800_board *das800_probe(struct comedi_device *dev)
608 {
609 const struct das800_board *board = dev->board_ptr;
610 int index = board ? board - das800_boards : -EINVAL;
611 int id_bits;
612 unsigned long irq_flags;
613
614 /*
615 * The dev->board_ptr will be set by comedi_device_attach() if the
616 * board name provided by the user matches a board->name in this
617 * driver. If so, this function sanity checks the id_bits to verify
618 * that the board is correct.
619 *
620 * If the dev->board_ptr is not set, the user is trying to attach
621 * an unspecified board to this driver. In this case the id_bits
622 * are used to 'probe' for the correct dev->board_ptr.
623 */
624 spin_lock_irqsave(&dev->spinlock, irq_flags);
625 id_bits = das800_ind_read(dev, ID) & 0x3;
626 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
627
628 switch (id_bits) {
629 case 0x0:
630 if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
631 return board;
632 index = BOARD_DAS800;
633 break;
634 case 0x2:
635 if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
636 return board;
637 index = BOARD_DAS801;
638 break;
639 case 0x3:
640 if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
641 index == BOARD_CIODAS80216)
642 return board;
643 index = BOARD_DAS802;
644 break;
645 default:
646 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
647 id_bits);
648 return NULL;
649 }
650 dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
651 das800_boards[index].name);
652
653 return &das800_boards[index];
654 }
655
das800_attach(struct comedi_device * dev,struct comedi_devconfig * it)656 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
657 {
658 const struct das800_board *board;
659 struct das800_private *devpriv;
660 struct comedi_subdevice *s;
661 unsigned int irq = it->options[1];
662 unsigned long irq_flags;
663 int ret;
664
665 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
666 if (!devpriv)
667 return -ENOMEM;
668
669 ret = comedi_request_region(dev, it->options[0], 0x8);
670 if (ret)
671 return ret;
672
673 board = das800_probe(dev);
674 if (!board)
675 return -ENODEV;
676 dev->board_ptr = board;
677 dev->board_name = board->name;
678
679 if (irq > 1 && irq <= 7) {
680 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
681 dev);
682 if (ret == 0)
683 dev->irq = irq;
684 }
685
686 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
687 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
688 if (!dev->pacer)
689 return -ENOMEM;
690
691 ret = comedi_alloc_subdevices(dev, 3);
692 if (ret)
693 return ret;
694
695 /* Analog Input subdevice */
696 s = &dev->subdevices[0];
697 dev->read_subdev = s;
698 s->type = COMEDI_SUBD_AI;
699 s->subdev_flags = SDF_READABLE | SDF_GROUND;
700 s->n_chan = 8;
701 s->maxdata = (1 << board->resolution) - 1;
702 s->range_table = board->ai_range;
703 s->insn_read = das800_ai_insn_read;
704 if (dev->irq) {
705 s->subdev_flags |= SDF_CMD_READ;
706 s->len_chanlist = 8;
707 s->do_cmdtest = das800_ai_do_cmdtest;
708 s->do_cmd = das800_ai_do_cmd;
709 s->cancel = das800_cancel;
710 }
711
712 /* Digital Input subdevice */
713 s = &dev->subdevices[1];
714 s->type = COMEDI_SUBD_DI;
715 s->subdev_flags = SDF_READABLE;
716 s->n_chan = 3;
717 s->maxdata = 1;
718 s->range_table = &range_digital;
719 s->insn_bits = das800_di_insn_bits;
720
721 /* Digital Output subdevice */
722 s = &dev->subdevices[2];
723 s->type = COMEDI_SUBD_DO;
724 s->subdev_flags = SDF_WRITABLE;
725 s->n_chan = 4;
726 s->maxdata = 1;
727 s->range_table = &range_digital;
728 s->insn_bits = das800_do_insn_bits;
729
730 das800_disable(dev);
731
732 /* initialize digital out channels */
733 spin_lock_irqsave(&dev->spinlock, irq_flags);
734 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
735 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
736
737 return 0;
738 };
739
740 static struct comedi_driver driver_das800 = {
741 .driver_name = "das800",
742 .module = THIS_MODULE,
743 .attach = das800_attach,
744 .detach = comedi_legacy_detach,
745 .num_names = ARRAY_SIZE(das800_boards),
746 .board_name = &das800_boards[0].name,
747 .offset = sizeof(struct das800_board),
748 };
749 module_comedi_driver(driver_das800);
750
751 MODULE_AUTHOR("Comedi http://www.comedi.org");
752 MODULE_DESCRIPTION("Comedi low-level driver");
753 MODULE_LICENSE("GPL");
754