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