]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/staging/comedi/drivers/das16m1.c
Staging: comedi: remove comedi-specific wrappers
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / das16m1.c
1 /*
2 comedi/drivers/das16m1.c
3 CIO-DAS16/M1 driver
4 Author: Frank Mori Hess, based on code from the das16
5 driver.
6 Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 ************************************************************************
26 */
27 /*
28 Driver: das16m1
29 Description: CIO-DAS16/M1
30 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
31 Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
32 Status: works
33
34 This driver supports a single board - the CIO-DAS16/M1.
35 As far as I know, there are no other boards that have
36 the same register layout. Even the CIO-DAS16/M1/16 is
37 significantly different.
38
39 I was _barely_ able to reach the full 1 MHz capability
40 of this board, using a hard real-time interrupt
41 (set the TRIG_RT flag in your struct comedi_cmd and use
42 rtlinux or RTAI). The board can't do dma, so the bottleneck is
43 pulling the data across the ISA bus. I timed the interrupt
44 handler, and it took my computer ~470 microseconds to pull 512
45 samples from the board. So at 1 Mhz sampling rate,
46 expect your CPU to be spending almost all of its
47 time in the interrupt handler.
48
49 This board has some unusual restrictions for its channel/gain list. If the
50 list has 2 or more channels in it, then two conditions must be satisfied:
51 (1) - even/odd channels must appear at even/odd indices in the list
52 (2) - the list must have an even number of entries.
53
54 Options:
55 [0] - base io address
56 [1] - irq (optional, but you probably want it)
57
58 irq can be omitted, although the cmd interface will not work without it.
59 */
60
61 #include <linux/ioport.h>
62 #include "../comedidev.h"
63
64 #include "8255.h"
65 #include "8253.h"
66 #include "comedi_fc.h"
67
68 #define DAS16M1_SIZE 16
69 #define DAS16M1_SIZE2 8
70
71 #define DAS16M1_XTAL 100 /* 10 MHz master clock */
72
73 #define FIFO_SIZE 1024 /* 1024 sample fifo */
74
75 /*
76 CIO-DAS16_M1.pdf
77
78 "cio-das16/m1"
79
80 0 a/d bits 0-3, mux start 12 bit
81 1 a/d bits 4-11 unused
82 2 status control
83 3 di 4 bit do 4 bit
84 4 unused clear interrupt
85 5 interrupt, pacer
86 6 channel/gain queue address
87 7 channel/gain queue data
88 89ab 8254
89 cdef 8254
90 400 8255
91 404-407 8254
92
93 */
94
95 #define DAS16M1_AI 0 /* 16-bit wide register */
96 #define AI_CHAN(x) ((x) & 0xf)
97 #define DAS16M1_CS 2
98 #define EXT_TRIG_BIT 0x1
99 #define OVRUN 0x20
100 #define IRQDATA 0x80
101 #define DAS16M1_DIO 3
102 #define DAS16M1_CLEAR_INTR 4
103 #define DAS16M1_INTR_CONTROL 5
104 #define EXT_PACER 0x2
105 #define INT_PACER 0x3
106 #define PACER_MASK 0x3
107 #define INTE 0x80
108 #define DAS16M1_QUEUE_ADDR 6
109 #define DAS16M1_QUEUE_DATA 7
110 #define Q_CHAN(x) ((x) & 0x7)
111 #define Q_RANGE(x) (((x) & 0xf) << 4)
112 #define UNIPOLAR 0x40
113 #define DAS16M1_8254_FIRST 0x8
114 #define DAS16M1_8254_FIRST_CNTRL 0xb
115 #define TOTAL_CLEAR 0x30
116 #define DAS16M1_8254_SECOND 0xc
117 #define DAS16M1_82C55 0x400
118 #define DAS16M1_8254_THIRD 0x404
119
120 static const struct comedi_lrange range_das16m1 = { 9,
121 {
122 BIP_RANGE(5),
123 BIP_RANGE(2.5),
124 BIP_RANGE(1.25),
125 BIP_RANGE(0.625),
126 UNI_RANGE(10),
127 UNI_RANGE(5),
128 UNI_RANGE(2.5),
129 UNI_RANGE(1.25),
130 BIP_RANGE(10),
131 }
132 };
133
134 static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
135 struct comedi_insn *insn, unsigned int *data);
136 static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
137 struct comedi_insn *insn, unsigned int *data);
138 static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
139 struct comedi_insn *insn, unsigned int *data);
140
141 static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
142 struct comedi_cmd *cmd);
143 static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s);
144 static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
145
146 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s);
147 static irqreturn_t das16m1_interrupt(int irq, void *d);
148 static void das16m1_handler(struct comedi_device *dev, unsigned int status);
149
150 static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns,
151 int round_flag);
152
153 static int das16m1_irq_bits(unsigned int irq);
154
155 struct das16m1_board {
156 const char *name;
157 unsigned int ai_speed;
158 };
159
160 static const struct das16m1_board das16m1_boards[] = {
161 {
162 .name = "cio-das16/m1", /* CIO-DAS16_M1.pdf */
163 .ai_speed = 1000, /* 1MHz max speed */
164 },
165 };
166
167 static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it);
168 static int das16m1_detach(struct comedi_device *dev);
169 static struct comedi_driver driver_das16m1 = {
170 .driver_name = "das16m1",
171 .module = THIS_MODULE,
172 .attach = das16m1_attach,
173 .detach = das16m1_detach,
174 .board_name = &das16m1_boards[0].name,
175 .num_names = ARRAY_SIZE(das16m1_boards),
176 .offset = sizeof(das16m1_boards[0]),
177 };
178
179 struct das16m1_private_struct {
180 unsigned int control_state;
181 volatile unsigned int adc_count; /* number of samples completed */
182 /* initial value in lower half of hardware conversion counter,
183 * needed to keep track of whether new count has been loaded into
184 * counter yet (loaded by first sample conversion) */
185 u16 initial_hw_count;
186 short ai_buffer[FIFO_SIZE];
187 unsigned int do_bits; /* saves status of digital output bits */
188 unsigned int divisor1; /* divides master clock to obtain conversion speed */
189 unsigned int divisor2; /* divides master clock to obtain conversion speed */
190 };
191 #define devpriv ((struct das16m1_private_struct *)(dev->private))
192 #define thisboard ((const struct das16m1_board *)(dev->board_ptr))
193
194 COMEDI_INITCLEANUP(driver_das16m1);
195
196 static inline short munge_sample(short data)
197 {
198 return (data >> 4) & 0xfff;
199 }
200
201 static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
202 struct comedi_cmd *cmd)
203 {
204 unsigned int err = 0, tmp, i;
205
206 /* make sure triggers are valid */
207 tmp = cmd->start_src;
208 cmd->start_src &= TRIG_NOW | TRIG_EXT;
209 if (!cmd->start_src || tmp != cmd->start_src)
210 err++;
211
212 tmp = cmd->scan_begin_src;
213 cmd->scan_begin_src &= TRIG_FOLLOW;
214 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
215 err++;
216
217 tmp = cmd->convert_src;
218 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
219 if (!cmd->convert_src || tmp != cmd->convert_src)
220 err++;
221
222 tmp = cmd->scan_end_src;
223 cmd->scan_end_src &= TRIG_COUNT;
224 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
225 err++;
226
227 tmp = cmd->stop_src;
228 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
229 if (!cmd->stop_src || tmp != cmd->stop_src)
230 err++;
231
232 if (err)
233 return 1;
234
235 /* step 2: make sure trigger sources are unique and mutually compatible */
236 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
237 err++;
238 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
239 err++;
240 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
241 err++;
242
243 if (err)
244 return 2;
245
246 /* step 3: make sure arguments are trivially compatible */
247 if (cmd->start_arg != 0) {
248 cmd->start_arg = 0;
249 err++;
250 }
251
252 if (cmd->scan_begin_src == TRIG_FOLLOW) {
253 /* internal trigger */
254 if (cmd->scan_begin_arg != 0) {
255 cmd->scan_begin_arg = 0;
256 err++;
257 }
258 }
259
260 if (cmd->convert_src == TRIG_TIMER) {
261 if (cmd->convert_arg < thisboard->ai_speed) {
262 cmd->convert_arg = thisboard->ai_speed;
263 err++;
264 }
265 }
266
267 if (cmd->scan_end_arg != cmd->chanlist_len) {
268 cmd->scan_end_arg = cmd->chanlist_len;
269 err++;
270 }
271
272 if (cmd->stop_src == TRIG_COUNT) {
273 /* any count is allowed */
274 } else {
275 /* TRIG_NONE */
276 if (cmd->stop_arg != 0) {
277 cmd->stop_arg = 0;
278 err++;
279 }
280 }
281
282 if (err)
283 return 3;
284
285 /* step 4: fix up arguments */
286
287 if (cmd->convert_src == TRIG_TIMER) {
288 tmp = cmd->convert_arg;
289 /* calculate counter values that give desired timing */
290 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
291 &(devpriv->divisor1), &(devpriv->divisor2),
292 &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
293 if (tmp != cmd->convert_arg)
294 err++;
295 }
296
297 if (err)
298 return 4;
299
300 /* check chanlist against board's peculiarities */
301 if (cmd->chanlist && cmd->chanlist_len > 1) {
302 for (i = 0; i < cmd->chanlist_len; i++) {
303 /* even/odd channels must go into even/odd queue addresses */
304 if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
305 comedi_error(dev, "bad chanlist:\n"
306 " even/odd channels must go have even/odd chanlist indices");
307 err++;
308 }
309 }
310 if ((cmd->chanlist_len % 2) != 0) {
311 comedi_error(dev,
312 "chanlist must be of even length or length 1");
313 err++;
314 }
315 }
316
317 if (err)
318 return 5;
319
320 return 0;
321 }
322
323 static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s)
324 {
325 struct comedi_async *async = s->async;
326 struct comedi_cmd *cmd = &async->cmd;
327 unsigned int byte, i;
328
329 if (dev->irq == 0) {
330 comedi_error(dev, "irq required to execute comedi_cmd");
331 return -1;
332 }
333
334 /* disable interrupts and internal pacer */
335 devpriv->control_state &= ~INTE & ~PACER_MASK;
336 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
337
338 /* set software count */
339 devpriv->adc_count = 0;
340 /* Initialize lower half of hardware counter, used to determine how
341 * many samples are in fifo. Value doesn't actually load into counter
342 * until counter's next clock (the next a/d conversion) */
343 i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
344 /* remember current reading of counter so we know when counter has
345 * actually been loaded */
346 devpriv->initial_hw_count =
347 i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
348 /* setup channel/gain queue */
349 for (i = 0; i < cmd->chanlist_len; i++) {
350 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
351 byte = Q_CHAN(CR_CHAN(cmd->
352 chanlist[i])) | Q_RANGE(CR_RANGE(cmd->
353 chanlist[i]));
354 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
355 }
356
357 /* set counter mode and counts */
358 cmd->convert_arg =
359 das16m1_set_pacer(dev, cmd->convert_arg,
360 cmd->flags & TRIG_ROUND_MASK);
361
362 /* set control & status register */
363 byte = 0;
364 /* if we are using external start trigger (also board dislikes having
365 * both start and conversion triggers external simultaneously) */
366 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
367 byte |= EXT_TRIG_BIT;
368 }
369 outb(byte, dev->iobase + DAS16M1_CS);
370 /* clear interrupt bit */
371 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
372
373 /* enable interrupts and internal pacer */
374 devpriv->control_state &= ~PACER_MASK;
375 if (cmd->convert_src == TRIG_TIMER) {
376 devpriv->control_state |= INT_PACER;
377 } else {
378 devpriv->control_state |= EXT_PACER;
379 }
380 devpriv->control_state |= INTE;
381 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
382
383 return 0;
384 }
385
386 static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
387 {
388 devpriv->control_state &= ~INTE & ~PACER_MASK;
389 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
390
391 return 0;
392 }
393
394 static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
395 struct comedi_insn *insn, unsigned int *data)
396 {
397 int i, n;
398 int byte;
399 const int timeout = 1000;
400
401 /* disable interrupts and internal pacer */
402 devpriv->control_state &= ~INTE & ~PACER_MASK;
403 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
404
405 /* setup channel/gain queue */
406 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
407 byte = Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->
408 chanspec));
409 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
410
411 for (n = 0; n < insn->n; n++) {
412 /* clear IRQDATA bit */
413 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
414 /* trigger conversion */
415 outb(0, dev->iobase);
416
417 for (i = 0; i < timeout; i++) {
418 if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
419 break;
420 }
421 if (i == timeout) {
422 comedi_error(dev, "timeout");
423 return -ETIME;
424 }
425 data[n] = munge_sample(inw(dev->iobase));
426 }
427
428 return n;
429 }
430
431 static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
432 struct comedi_insn *insn, unsigned int *data)
433 {
434 unsigned int bits;
435
436 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
437 data[1] = bits;
438 data[0] = 0;
439
440 return 2;
441 }
442
443 static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
444 struct comedi_insn *insn, unsigned int *data)
445 {
446 unsigned int wbits;
447
448 /* only set bits that have been masked */
449 data[0] &= 0xf;
450 wbits = devpriv->do_bits;
451 /* zero bits that have been masked */
452 wbits &= ~data[0];
453 /* set masked bits */
454 wbits |= data[0] & data[1];
455 devpriv->do_bits = wbits;
456 data[1] = wbits;
457
458 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
459
460 return 2;
461 }
462
463 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
464 {
465 unsigned long flags;
466 unsigned int status;
467
468 /* prevent race with interrupt handler */
469 spin_lock_irqsave(&dev->spinlock, flags);
470 status = inb(dev->iobase + DAS16M1_CS);
471 das16m1_handler(dev, status);
472 spin_unlock_irqrestore(&dev->spinlock, flags);
473
474 return s->async->buf_write_count - s->async->buf_read_count;
475 }
476
477 static irqreturn_t das16m1_interrupt(int irq, void *d)
478 {
479 int status;
480 struct comedi_device *dev = d;
481
482 if (dev->attached == 0) {
483 comedi_error(dev, "premature interrupt");
484 return IRQ_HANDLED;
485 }
486 /* prevent race with comedi_poll() */
487 spin_lock(&dev->spinlock);
488
489 status = inb(dev->iobase + DAS16M1_CS);
490
491 if ((status & (IRQDATA | OVRUN)) == 0) {
492 comedi_error(dev, "spurious interrupt");
493 spin_unlock(&dev->spinlock);
494 return IRQ_NONE;
495 }
496
497 das16m1_handler(dev, status);
498
499 /* clear interrupt */
500 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
501
502 spin_unlock(&dev->spinlock);
503 return IRQ_HANDLED;
504 }
505
506 static void munge_sample_array(short *array, unsigned int num_elements)
507 {
508 unsigned int i;
509
510 for (i = 0; i < num_elements; i++) {
511 array[i] = munge_sample(array[i]);
512 }
513 }
514
515 static void das16m1_handler(struct comedi_device *dev, unsigned int status)
516 {
517 struct comedi_subdevice *s;
518 struct comedi_async *async;
519 struct comedi_cmd *cmd;
520 u16 num_samples;
521 u16 hw_counter;
522
523 s = dev->read_subdev;
524 async = s->async;
525 async->events = 0;
526 cmd = &async->cmd;
527
528 /* figure out how many samples are in fifo */
529 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
530 /* make sure hardware counter reading is not bogus due to initial value
531 * not having been loaded yet */
532 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
533 num_samples = 0;
534 } else {
535 /* The calculation of num_samples looks odd, but it uses the following facts.
536 * 16 bit hardware counter is initialized with value of zero (which really
537 * means 0x1000). The counter decrements by one on each conversion
538 * (when the counter decrements from zero it goes to 0xffff). num_samples
539 * is a 16 bit variable, so it will roll over in a similar fashion to the
540 * hardware counter. Work it out, and this is what you get. */
541 num_samples = -hw_counter - devpriv->adc_count;
542 }
543 /* check if we only need some of the points */
544 if (cmd->stop_src == TRIG_COUNT) {
545 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
546 num_samples = cmd->stop_arg * cmd->chanlist_len;
547 }
548 /* make sure we dont try to get too many points if fifo has overrun */
549 if (num_samples > FIFO_SIZE)
550 num_samples = FIFO_SIZE;
551 insw(dev->iobase, devpriv->ai_buffer, num_samples);
552 munge_sample_array(devpriv->ai_buffer, num_samples);
553 cfc_write_array_to_buffer(s, devpriv->ai_buffer,
554 num_samples * sizeof(short));
555 devpriv->adc_count += num_samples;
556
557 if (cmd->stop_src == TRIG_COUNT) {
558 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
559 das16m1_cancel(dev, s);
560 async->events |= COMEDI_CB_EOA;
561 }
562 }
563
564 /* this probably won't catch overruns since the card doesn't generate
565 * overrun interrupts, but we might as well try */
566 if (status & OVRUN) {
567 das16m1_cancel(dev, s);
568 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
569 comedi_error(dev, "fifo overflow");
570 }
571
572 comedi_event(dev, s);
573
574 }
575
576 /* This function takes a time in nanoseconds and sets the *
577 * 2 pacer clocks to the closest frequency possible. It also *
578 * returns the actual sampling period. */
579 static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns,
580 int rounding_flags)
581 {
582 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
583 &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
584
585 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
586 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
587 2);
588 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
589 2);
590
591 return ns;
592 }
593
594 static int das16m1_irq_bits(unsigned int irq)
595 {
596 int ret;
597
598 switch (irq) {
599 case 10:
600 ret = 0x0;
601 break;
602 case 11:
603 ret = 0x1;
604 break;
605 case 12:
606 ret = 0x2;
607 break;
608 case 15:
609 ret = 0x3;
610 break;
611 case 2:
612 ret = 0x4;
613 break;
614 case 3:
615 ret = 0x5;
616 break;
617 case 5:
618 ret = 0x6;
619 break;
620 case 7:
621 ret = 0x7;
622 break;
623 default:
624 return -1;
625 break;
626 }
627 return ret << 4;
628 }
629
630 /*
631 * Options list:
632 * 0 I/O base
633 * 1 IRQ
634 */
635
636 static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it)
637 {
638 struct comedi_subdevice *s;
639 int ret;
640 unsigned int irq;
641 unsigned long iobase;
642
643 iobase = it->options[0];
644
645 printk("comedi%d: das16m1:", dev->minor);
646
647 ret = alloc_private(dev, sizeof(struct das16m1_private_struct));
648 if (ret < 0)
649 return ret;
650
651 dev->board_name = thisboard->name;
652
653 printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
654 iobase, iobase + DAS16M1_SIZE,
655 iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
656 if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
657 printk(" I/O port conflict\n");
658 return -EIO;
659 }
660 if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
661 driver_das16m1.driver_name)) {
662 release_region(iobase, DAS16M1_SIZE);
663 printk(" I/O port conflict\n");
664 return -EIO;
665 }
666 dev->iobase = iobase;
667
668 /* now for the irq */
669 irq = it->options[1];
670 /* make sure it is valid */
671 if (das16m1_irq_bits(irq) >= 0) {
672 ret = request_irq(irq, das16m1_interrupt, 0,
673 driver_das16m1.driver_name, dev);
674 if (ret < 0) {
675 printk(", irq unavailable\n");
676 return ret;
677 }
678 dev->irq = irq;
679 printk(", irq %u\n", irq);
680 } else if (irq == 0) {
681 printk(", no irq\n");
682 } else {
683 printk(", invalid irq\n"
684 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
685 return -EINVAL;
686 }
687
688 ret = alloc_subdevices(dev, 4);
689 if (ret < 0)
690 return ret;
691
692 s = dev->subdevices + 0;
693 dev->read_subdev = s;
694 /* ai */
695 s->type = COMEDI_SUBD_AI;
696 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
697 s->n_chan = 8;
698 s->subdev_flags = SDF_DIFF;
699 s->len_chanlist = 256;
700 s->maxdata = (1 << 12) - 1;
701 s->range_table = &range_das16m1;
702 s->insn_read = das16m1_ai_rinsn;
703 s->do_cmdtest = das16m1_cmd_test;
704 s->do_cmd = das16m1_cmd_exec;
705 s->cancel = das16m1_cancel;
706 s->poll = das16m1_poll;
707
708 s = dev->subdevices + 1;
709 /* di */
710 s->type = COMEDI_SUBD_DI;
711 s->subdev_flags = SDF_READABLE;
712 s->n_chan = 4;
713 s->maxdata = 1;
714 s->range_table = &range_digital;
715 s->insn_bits = das16m1_di_rbits;
716
717 s = dev->subdevices + 2;
718 /* do */
719 s->type = COMEDI_SUBD_DO;
720 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
721 s->n_chan = 4;
722 s->maxdata = 1;
723 s->range_table = &range_digital;
724 s->insn_bits = das16m1_do_wbits;
725
726 s = dev->subdevices + 3;
727 /* 8255 */
728 subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
729
730 /* disable upper half of hardware conversion counter so it doesn't mess with us */
731 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
732
733 /* initialize digital output lines */
734 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
735
736 /* set the interrupt level */
737 if (dev->irq)
738 devpriv->control_state = das16m1_irq_bits(dev->irq);
739 else
740 devpriv->control_state = 0;
741 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
742
743 return 0;
744 }
745
746 static int das16m1_detach(struct comedi_device *dev)
747 {
748 printk("comedi%d: das16m1: remove\n", dev->minor);
749
750 /* das16m1_reset(dev); */
751
752 if (dev->subdevices)
753 subdev_8255_cleanup(dev, dev->subdevices + 3);
754
755 if (dev->irq)
756 free_irq(dev->irq, dev);
757
758 if (dev->iobase) {
759 release_region(dev->iobase, DAS16M1_SIZE);
760 release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
761 }
762
763 return 0;
764 }