Merge branch 'for-linus' of git://github.com/schandinat/linux-2.6
[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 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");