Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[deliverable/linux.git] / drivers / staging / comedi / drivers / das16m1.c
CommitLineData
9afbebe4
FMH
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.
9afbebe4
FMH
20*/
21/*
22Driver: das16m1
23Description: CIO-DAS16/M1
24Author: Frank Mori Hess <fmhess@users.sourceforge.net>
34702518 25Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
9afbebe4
FMH
26Status: works
27
28This driver supports a single board - the CIO-DAS16/M1.
29As far as I know, there are no other boards that have
30the same register layout. Even the CIO-DAS16/M1/16 is
31significantly different.
32
33I was _barely_ able to reach the full 1 MHz capability
34of this board, using a hard real-time interrupt
ea6d0d4c 35(set the TRIG_RT flag in your struct comedi_cmd and use
9afbebe4
FMH
36rtlinux or RTAI). The board can't do dma, so the bottleneck is
37pulling the data across the ISA bus. I timed the interrupt
38handler, and it took my computer ~470 microseconds to pull 512
39samples from the board. So at 1 Mhz sampling rate,
40expect your CPU to be spending almost all of its
41time in the interrupt handler.
42
43This board has some unusual restrictions for its channel/gain list. If the
44list has 2 or more channels in it, then two conditions must be satisfied:
45(1) - even/odd channels must appear at even/odd indices in the list
46(2) - the list must have an even number of entries.
47
48Options:
49 [0] - base io address
50 [1] - irq (optional, but you probably want it)
51
52irq can be omitted, although the cmd interface will not work without it.
53*/
54
ce157f80 55#include <linux/module.h>
25436dc9 56#include <linux/interrupt.h>
9afbebe4
FMH
57#include "../comedidev.h"
58
59#include "8255.h"
60#include "8253.h"
61#include "comedi_fc.h"
62
9afbebe4
FMH
63#define DAS16M1_SIZE2 8
64
2696fb57 65#define FIFO_SIZE 1024 /* 1024 sample fifo */
9afbebe4
FMH
66
67/*
68 CIO-DAS16_M1.pdf
69
70 "cio-das16/m1"
71
72 0 a/d bits 0-3, mux start 12 bit
73 1 a/d bits 4-11 unused
74 2 status control
75 3 di 4 bit do 4 bit
76 4 unused clear interrupt
77 5 interrupt, pacer
78 6 channel/gain queue address
79 7 channel/gain queue data
80 89ab 8254
81 cdef 8254
82 400 8255
83 404-407 8254
84
85*/
86
2696fb57 87#define DAS16M1_AI 0 /* 16-bit wide register */
9afbebe4
FMH
88#define AI_CHAN(x) ((x) & 0xf)
89#define DAS16M1_CS 2
90#define EXT_TRIG_BIT 0x1
91#define OVRUN 0x20
92#define IRQDATA 0x80
93#define DAS16M1_DIO 3
94#define DAS16M1_CLEAR_INTR 4
95#define DAS16M1_INTR_CONTROL 5
96#define EXT_PACER 0x2
97#define INT_PACER 0x3
98#define PACER_MASK 0x3
99#define INTE 0x80
100#define DAS16M1_QUEUE_ADDR 6
101#define DAS16M1_QUEUE_DATA 7
102#define Q_CHAN(x) ((x) & 0x7)
103#define Q_RANGE(x) (((x) & 0xf) << 4)
104#define UNIPOLAR 0x40
105#define DAS16M1_8254_FIRST 0x8
106#define DAS16M1_8254_FIRST_CNTRL 0xb
107#define TOTAL_CLEAR 0x30
108#define DAS16M1_8254_SECOND 0xc
109#define DAS16M1_82C55 0x400
110#define DAS16M1_8254_THIRD 0x404
111
0b4c89d8
HS
112static const struct comedi_lrange range_das16m1 = {
113 9, {
114 BIP_RANGE(5),
115 BIP_RANGE(2.5),
116 BIP_RANGE(1.25),
117 BIP_RANGE(0.625),
118 UNI_RANGE(10),
119 UNI_RANGE(5),
120 UNI_RANGE(2.5),
121 UNI_RANGE(1.25),
122 BIP_RANGE(10)
123 }
9afbebe4
FMH
124};
125
9afbebe4
FMH
126struct das16m1_private_struct {
127 unsigned int control_state;
e3ebc55f 128 unsigned int adc_count; /* number of samples completed */
9afbebe4
FMH
129 /* initial value in lower half of hardware conversion counter,
130 * needed to keep track of whether new count has been loaded into
131 * counter yet (loaded by first sample conversion) */
132 u16 initial_hw_count;
eabbf3f4 133 unsigned short ai_buffer[FIFO_SIZE];
2696fb57
BP
134 unsigned int divisor1; /* divides master clock to obtain conversion speed */
135 unsigned int divisor2; /* divides master clock to obtain conversion speed */
a0b4bccc 136 unsigned long extra_iobase;
9afbebe4 137};
9afbebe4 138
eabbf3f4 139static inline unsigned short munge_sample(unsigned short data)
7114a280 140{
72022184 141 return (data >> 4) & 0xfff;
7114a280
AT
142}
143
eabbf3f4 144static void munge_sample_array(unsigned short *array, unsigned int num_elements)
7114a280 145{
72022184 146 unsigned int i;
9afbebe4 147
72022184
HS
148 for (i = 0; i < num_elements; i++)
149 array[i] = munge_sample(array[i]);
9afbebe4
FMH
150}
151
f71a2832
HS
152static int das16m1_ai_check_chanlist(struct comedi_device *dev,
153 struct comedi_subdevice *s,
154 struct comedi_cmd *cmd)
155{
156 int i;
157
158 if (cmd->chanlist_len == 1)
159 return 0;
160
161 if ((cmd->chanlist_len % 2) != 0) {
162 dev_dbg(dev->class_dev,
163 "chanlist must be of even length or length 1\n");
164 return -EINVAL;
165 }
166
167 for (i = 0; i < cmd->chanlist_len; i++) {
168 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
169
170 if ((i % 2) != (chan % 2)) {
171 dev_dbg(dev->class_dev,
172 "even/odd channels must go have even/odd chanlist indices\n");
173 return -EINVAL;
174 }
175 }
176
177 return 0;
178}
179
0a85b6f0
MT
180static int das16m1_cmd_test(struct comedi_device *dev,
181 struct comedi_subdevice *s, struct comedi_cmd *cmd)
9afbebe4 182{
9a1a6cf8 183 struct das16m1_private_struct *devpriv = dev->private;
e9ace43c
HS
184 int err = 0;
185 unsigned int arg;
9afbebe4 186
27020ffe 187 /* Step 1 : check if triggers are trivially valid */
9afbebe4 188
27020ffe
HS
189 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
190 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
191 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
192 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
193 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
9afbebe4
FMH
194
195 if (err)
196 return 1;
197
27020ffe
HS
198 /* Step 2a : make sure trigger sources are unique */
199
200 err |= cfc_check_trigger_is_unique(cmd->start_src);
201 err |= cfc_check_trigger_is_unique(cmd->convert_src);
202 err |= cfc_check_trigger_is_unique(cmd->stop_src);
203
204 /* Step 2b : and mutually compatible */
9afbebe4
FMH
205
206 if (err)
207 return 2;
208
050b3b18 209 /* Step 3: check if arguments are trivially valid */
9afbebe4 210
050b3b18 211 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
9afbebe4 212
050b3b18
HS
213 if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
214 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
9afbebe4 215
050b3b18
HS
216 if (cmd->convert_src == TRIG_TIMER)
217 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 1000);
218
219 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
9afbebe4 220
1a021d63
HS
221 if (cmd->stop_src == TRIG_COUNT)
222 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
223 else /* TRIG_NONE */
050b3b18 224 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
9afbebe4
FMH
225
226 if (err)
227 return 3;
228
229 /* step 4: fix up arguments */
230
231 if (cmd->convert_src == TRIG_TIMER) {
e9ace43c 232 arg = cmd->convert_arg;
cb9cfd7e
HS
233 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
234 &devpriv->divisor1,
235 &devpriv->divisor2,
e9ace43c
HS
236 &arg, cmd->flags);
237 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
9afbebe4
FMH
238 }
239
240 if (err)
241 return 4;
242
f71a2832
HS
243 /* Step 5: check channel list if it exists */
244 if (cmd->chanlist && cmd->chanlist_len > 0)
245 err |= das16m1_ai_check_chanlist(dev, s, cmd);
9afbebe4
FMH
246
247 if (err)
248 return 5;
249
250 return 0;
251}
252
863e07c9 253static void das16m1_set_pacer(struct comedi_device *dev)
72022184 254{
9a1a6cf8 255 struct das16m1_private_struct *devpriv = dev->private;
863e07c9 256 unsigned long timer_base = dev->iobase + DAS16M1_8254_SECOND;
9a1a6cf8 257
863e07c9
HS
258 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
259 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
72022184 260
863e07c9
HS
261 i8254_write(timer_base, 0, 1, devpriv->divisor1);
262 i8254_write(timer_base, 0, 2, devpriv->divisor2);
72022184
HS
263}
264
0a85b6f0
MT
265static int das16m1_cmd_exec(struct comedi_device *dev,
266 struct comedi_subdevice *s)
9afbebe4 267{
9a1a6cf8 268 struct das16m1_private_struct *devpriv = dev->private;
d163679c 269 struct comedi_async *async = s->async;
ea6d0d4c 270 struct comedi_cmd *cmd = &async->cmd;
bef74ba8 271 unsigned long timer_base = dev->iobase + DAS16M1_8254_FIRST;
9afbebe4
FMH
272 unsigned int byte, i;
273
9afbebe4
FMH
274 /* disable interrupts and internal pacer */
275 devpriv->control_state &= ~INTE & ~PACER_MASK;
276 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
277
2696fb57 278 /* set software count */
9afbebe4
FMH
279 devpriv->adc_count = 0;
280 /* Initialize lower half of hardware counter, used to determine how
281 * many samples are in fifo. Value doesn't actually load into counter
282 * until counter's next clock (the next a/d conversion) */
bef74ba8
HS
283 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
284 i8254_write(timer_base, 0, 1, 0);
9afbebe4
FMH
285 /* remember current reading of counter so we know when counter has
286 * actually been loaded */
bef74ba8 287 devpriv->initial_hw_count = i8254_read(timer_base, 0, 1);
9afbebe4
FMH
288 /* setup channel/gain queue */
289 for (i = 0; i < cmd->chanlist_len; i++) {
290 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
0a85b6f0
MT
291 byte =
292 Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
293 Q_RANGE(CR_RANGE(cmd->chanlist[i]));
9afbebe4
FMH
294 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
295 }
296
863e07c9
HS
297 /* enable interrupts and set internal pacer counter mode and counts */
298 devpriv->control_state &= ~PACER_MASK;
299 if (cmd->convert_src == TRIG_TIMER) {
300 das16m1_set_pacer(dev);
301 devpriv->control_state |= INT_PACER;
302 } else { /* TRIG_EXT */
303 devpriv->control_state |= EXT_PACER;
304 }
9afbebe4 305
2696fb57 306 /* set control & status register */
9afbebe4
FMH
307 byte = 0;
308 /* if we are using external start trigger (also board dislikes having
309 * both start and conversion triggers external simultaneously) */
4547251f 310 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
9afbebe4 311 byte |= EXT_TRIG_BIT;
4547251f 312
9afbebe4
FMH
313 outb(byte, dev->iobase + DAS16M1_CS);
314 /* clear interrupt bit */
315 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
316
9afbebe4
FMH
317 devpriv->control_state |= INTE;
318 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
319
320 return 0;
321}
322
da91b269 323static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
9afbebe4 324{
9a1a6cf8
HS
325 struct das16m1_private_struct *devpriv = dev->private;
326
9afbebe4
FMH
327 devpriv->control_state &= ~INTE & ~PACER_MASK;
328 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
329
330 return 0;
331}
332
ab984177
HS
333static int das16m1_ai_eoc(struct comedi_device *dev,
334 struct comedi_subdevice *s,
335 struct comedi_insn *insn,
336 unsigned long context)
337{
338 unsigned int status;
339
340 status = inb(dev->iobase + DAS16M1_CS);
341 if (status & IRQDATA)
342 return 0;
343 return -EBUSY;
344}
345
0a85b6f0
MT
346static int das16m1_ai_rinsn(struct comedi_device *dev,
347 struct comedi_subdevice *s,
348 struct comedi_insn *insn, unsigned int *data)
9afbebe4 349{
9a1a6cf8 350 struct das16m1_private_struct *devpriv = dev->private;
ab984177
HS
351 int ret;
352 int n;
9afbebe4 353 int byte;
9afbebe4
FMH
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 /* setup channel/gain queue */
360 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
0a85b6f0
MT
361 byte =
362 Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
9afbebe4
FMH
363 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
364
365 for (n = 0; n < insn->n; n++) {
366 /* clear IRQDATA bit */
367 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
368 /* trigger conversion */
369 outb(0, dev->iobase);
370
ab984177 371 ret = comedi_timeout(dev, s, insn, das16m1_ai_eoc, 0);
22ca19d9 372 if (ret)
ab984177 373 return ret;
ab984177 374
9afbebe4
FMH
375 data[n] = munge_sample(inw(dev->iobase));
376 }
377
378 return n;
379}
380
0a85b6f0
MT
381static int das16m1_di_rbits(struct comedi_device *dev,
382 struct comedi_subdevice *s,
383 struct comedi_insn *insn, unsigned int *data)
9afbebe4 384{
790c5541 385 unsigned int bits;
9afbebe4
FMH
386
387 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
388 data[1] = bits;
389 data[0] = 0;
390
a2714e3e 391 return insn->n;
9afbebe4
FMH
392}
393
0a85b6f0
MT
394static int das16m1_do_wbits(struct comedi_device *dev,
395 struct comedi_subdevice *s,
e49242a4
HS
396 struct comedi_insn *insn,
397 unsigned int *data)
9afbebe4 398{
e49242a4
HS
399 if (comedi_dio_update_state(s, data))
400 outb(s->state, dev->iobase + DAS16M1_DIO);
9afbebe4 401
e49242a4 402 data[1] = s->state;
9afbebe4 403
a2714e3e 404 return insn->n;
9afbebe4
FMH
405}
406
da91b269 407static void das16m1_handler(struct comedi_device *dev, unsigned int status)
9afbebe4 408{
9a1a6cf8 409 struct das16m1_private_struct *devpriv = dev->private;
34c43922 410 struct comedi_subdevice *s;
d163679c 411 struct comedi_async *async;
ea6d0d4c 412 struct comedi_cmd *cmd;
9afbebe4
FMH
413 u16 num_samples;
414 u16 hw_counter;
415
416 s = dev->read_subdev;
417 async = s->async;
9afbebe4
FMH
418 cmd = &async->cmd;
419
2696fb57 420 /* figure out how many samples are in fifo */
9afbebe4
FMH
421 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
422 /* make sure hardware counter reading is not bogus due to initial value
423 * not having been loaded yet */
424 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
425 num_samples = 0;
426 } else {
427 /* The calculation of num_samples looks odd, but it uses the following facts.
428 * 16 bit hardware counter is initialized with value of zero (which really
429 * means 0x1000). The counter decrements by one on each conversion
430 * (when the counter decrements from zero it goes to 0xffff). num_samples
431 * is a 16 bit variable, so it will roll over in a similar fashion to the
432 * hardware counter. Work it out, and this is what you get. */
433 num_samples = -hw_counter - devpriv->adc_count;
434 }
2696fb57 435 /* check if we only need some of the points */
9afbebe4
FMH
436 if (cmd->stop_src == TRIG_COUNT) {
437 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
438 num_samples = cmd->stop_arg * cmd->chanlist_len;
439 }
2696fb57 440 /* make sure we dont try to get too many points if fifo has overrun */
9afbebe4
FMH
441 if (num_samples > FIFO_SIZE)
442 num_samples = FIFO_SIZE;
443 insw(dev->iobase, devpriv->ai_buffer, num_samples);
444 munge_sample_array(devpriv->ai_buffer, num_samples);
445 cfc_write_array_to_buffer(s, devpriv->ai_buffer,
0a85b6f0 446 num_samples * sizeof(short));
9afbebe4
FMH
447 devpriv->adc_count += num_samples;
448
449 if (cmd->stop_src == TRIG_COUNT) {
6278c5d8
HS
450 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
451 /* end of acquisition */
9afbebe4
FMH
452 async->events |= COMEDI_CB_EOA;
453 }
454 }
455
456 /* this probably won't catch overruns since the card doesn't generate
457 * overrun interrupts, but we might as well try */
458 if (status & OVRUN) {
9afbebe4 459 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
b07262b0 460 dev_err(dev->class_dev, "fifo overflow\n");
9afbebe4
FMH
461 }
462
6278c5d8 463 cfc_handle_events(dev, s);
9afbebe4
FMH
464}
465
72022184 466static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
9afbebe4 467{
72022184
HS
468 unsigned long flags;
469 unsigned int status;
9afbebe4 470
72022184
HS
471 /* prevent race with interrupt handler */
472 spin_lock_irqsave(&dev->spinlock, flags);
473 status = inb(dev->iobase + DAS16M1_CS);
474 das16m1_handler(dev, status);
475 spin_unlock_irqrestore(&dev->spinlock, flags);
9afbebe4 476
f4f3f7cf 477 return comedi_buf_n_bytes_ready(s);
72022184
HS
478}
479
480static irqreturn_t das16m1_interrupt(int irq, void *d)
481{
482 int status;
483 struct comedi_device *dev = d;
484
a7401cdd 485 if (!dev->attached) {
b07262b0 486 dev_err(dev->class_dev, "premature interrupt\n");
72022184
HS
487 return IRQ_HANDLED;
488 }
489 /* prevent race with comedi_poll() */
490 spin_lock(&dev->spinlock);
491
492 status = inb(dev->iobase + DAS16M1_CS);
493
494 if ((status & (IRQDATA | OVRUN)) == 0) {
b07262b0 495 dev_err(dev->class_dev, "spurious interrupt\n");
72022184
HS
496 spin_unlock(&dev->spinlock);
497 return IRQ_NONE;
498 }
499
500 das16m1_handler(dev, status);
501
502 /* clear interrupt */
503 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
504
505 spin_unlock(&dev->spinlock);
506 return IRQ_HANDLED;
9afbebe4
FMH
507}
508
509static int das16m1_irq_bits(unsigned int irq)
510{
9afbebe4
FMH
511 switch (irq) {
512 case 10:
72998850 513 return 0x0;
9afbebe4 514 case 11:
72998850 515 return 0x1;
9afbebe4 516 case 12:
72998850 517 return 0x2;
9afbebe4 518 case 15:
72998850 519 return 0x3;
9afbebe4 520 case 2:
72998850 521 return 0x4;
9afbebe4 522 case 3:
72998850 523 return 0x5;
9afbebe4 524 case 5:
72998850 525 return 0x6;
9afbebe4 526 case 7:
72998850 527 return 0x7;
9afbebe4 528 default:
72998850 529 return 0x0;
9afbebe4 530 }
9afbebe4
FMH
531}
532
533/*
534 * Options list:
535 * 0 I/O base
536 * 1 IRQ
537 */
0a85b6f0
MT
538static int das16m1_attach(struct comedi_device *dev,
539 struct comedi_devconfig *it)
9afbebe4 540{
9a1a6cf8 541 struct das16m1_private_struct *devpriv;
34c43922 542 struct comedi_subdevice *s;
9afbebe4 543 int ret;
9afbebe4 544
0bdab509 545 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
546 if (!devpriv)
547 return -ENOMEM;
9afbebe4 548
862755ec 549 ret = comedi_request_region(dev, it->options[0], 0x10);
a983834e
HS
550 if (ret)
551 return ret;
552 /* Request an additional region for the 8255 */
5950ae51
HS
553 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
554 DAS16M1_SIZE2);
a0b4bccc
HS
555 if (ret)
556 return ret;
557 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
9afbebe4 558
72998850
HS
559 /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
560 if ((1 << it->options[1]) & 0xdcfc) {
561 ret = request_irq(it->options[1], das16m1_interrupt, 0,
562 dev->board_name, dev);
563 if (ret == 0)
564 dev->irq = it->options[1];
9afbebe4
FMH
565 }
566
2f0b9d08 567 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 568 if (ret)
9afbebe4
FMH
569 return ret;
570
9b7beb66 571 s = &dev->subdevices[0];
9afbebe4
FMH
572 /* ai */
573 s->type = COMEDI_SUBD_AI;
72998850 574 s->subdev_flags = SDF_READABLE | SDF_DIFF;
9afbebe4 575 s->n_chan = 8;
9afbebe4
FMH
576 s->maxdata = (1 << 12) - 1;
577 s->range_table = &range_das16m1;
578 s->insn_read = das16m1_ai_rinsn;
72998850
HS
579 if (dev->irq) {
580 dev->read_subdev = s;
581 s->subdev_flags |= SDF_CMD_READ;
582 s->len_chanlist = 256;
583 s->do_cmdtest = das16m1_cmd_test;
584 s->do_cmd = das16m1_cmd_exec;
585 s->cancel = das16m1_cancel;
586 s->poll = das16m1_poll;
587 }
9afbebe4 588
9b7beb66 589 s = &dev->subdevices[1];
9afbebe4
FMH
590 /* di */
591 s->type = COMEDI_SUBD_DI;
592 s->subdev_flags = SDF_READABLE;
593 s->n_chan = 4;
594 s->maxdata = 1;
595 s->range_table = &range_digital;
596 s->insn_bits = das16m1_di_rbits;
597
9b7beb66 598 s = &dev->subdevices[2];
9afbebe4
FMH
599 /* do */
600 s->type = COMEDI_SUBD_DO;
601 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
602 s->n_chan = 4;
603 s->maxdata = 1;
604 s->range_table = &range_digital;
605 s->insn_bits = das16m1_do_wbits;
606
9b7beb66 607 s = &dev->subdevices[3];
9afbebe4 608 /* 8255 */
4085e93b 609 ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
4b3fb0ff
HS
610 if (ret)
611 return ret;
9afbebe4 612
2696fb57 613 /* disable upper half of hardware conversion counter so it doesn't mess with us */
9afbebe4
FMH
614 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
615
2696fb57 616 /* initialize digital output lines */
e49242a4 617 outb(0, dev->iobase + DAS16M1_DIO);
9afbebe4
FMH
618
619 /* set the interrupt level */
72998850 620 devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
9afbebe4
FMH
621 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
622
623 return 0;
624}
625
484ecc95 626static void das16m1_detach(struct comedi_device *dev)
9afbebe4 627{
a0b4bccc
HS
628 struct das16m1_private_struct *devpriv = dev->private;
629
a0b4bccc
HS
630 if (devpriv && devpriv->extra_iobase)
631 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
632 comedi_legacy_detach(dev);
9afbebe4 633}
90f703d3 634
72022184
HS
635static struct comedi_driver das16m1_driver = {
636 .driver_name = "das16m1",
637 .module = THIS_MODULE,
638 .attach = das16m1_attach,
639 .detach = das16m1_detach,
72022184
HS
640};
641module_comedi_driver(das16m1_driver);
642
90f703d3
AT
643MODULE_AUTHOR("Comedi http://www.comedi.org");
644MODULE_DESCRIPTION("Comedi low-level driver");
645MODULE_LICENSE("GPL");
This page took 0.703419 seconds and 5 git commands to generate.