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