Merge branch 'timers-for-linus-hpet' of git://git.kernel.org/pub/scm/linux/kernel...
[GitHub/mt8127/android_kernel_alcatel_ttab.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 }