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