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