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