staging: comedi: das1800: factor out chanlist checking from (*do_cmdtest)
[linux-2.6-block.git] / drivers / staging / comedi / drivers / das800.c
CommitLineData
3726e56b
FMH
1/*
2 comedi/drivers/das800.c
3 Driver for Keitley das800 series boards and compatibles
4 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
3726e56b
FMH
18*/
19/*
20Driver: das800
21Description: Keithley Metrabyte DAS800 (& compatibles)
22Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
24 DAS-802 (das-802),
25 [Measurement Computing] CIO-DAS800 (cio-das800),
26 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
27 CIO-DAS802/16 (cio-das802/16)
28Status: works, cio-das802/16 untested - email me if you have tested it
29
30Configuration options:
31 [0] - I/O port base address
32 [1] - IRQ (optional, required for timed or externally triggered conversions)
33
34Notes:
35 IRQ can be omitted, although the cmd interface will not work without it.
36
37 All entries in the channel/gain list must use the same gain and be
38 consecutive channels counting upwards in channel number (these are
39 hardware limitations.)
40
41 I've never tested the gain setting stuff since I only have a
42 DAS-800 board with fixed gain.
43
44 The cio-das802/16 does not have a fifo-empty status bit! Therefore
45 only fifo-half-full transfers are possible with this card.
46*/
47/*
48
49cmd triggers supported:
50 start_src: TRIG_NOW | TRIG_EXT
51 scan_begin_src: TRIG_FOLLOW
52 scan_end_src: TRIG_COUNT
53 convert_src: TRIG_TIMER | TRIG_EXT
54 stop_src: TRIG_NONE | TRIG_COUNT
55
56
57*/
58
ce157f80 59#include <linux/module.h>
25436dc9 60#include <linux/interrupt.h>
3726e56b
FMH
61#include "../comedidev.h"
62
3726e56b
FMH
63#include <linux/delay.h>
64
65#include "8253.h"
66#include "comedi_fc.h"
67
68#define DAS800_SIZE 8
2696fb57 69#define N_CHAN_AI 8 /* number of analog input channels */
3726e56b
FMH
70
71/* Registers for the das800 */
72
73#define DAS800_LSB 0
74#define FIFO_EMPTY 0x1
75#define FIFO_OVF 0x2
76#define DAS800_MSB 1
77#define DAS800_CONTROL1 2
78#define CONTROL1_INTE 0x8
79#define DAS800_CONV_CONTROL 2
80#define ITE 0x1
81#define CASC 0x2
82#define DTEN 0x4
83#define IEOC 0x8
84#define EACS 0x10
85#define CONV_HCEN 0x80
86#define DAS800_SCAN_LIMITS 2
87#define DAS800_STATUS 2
88#define IRQ 0x8
89#define BUSY 0x80
90#define DAS800_GAIN 3
eca7cf72
HS
91#define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
92#define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
3726e56b
FMH
93#define CONTROL1 0x80
94#define CONV_CONTROL 0xa0
95#define SCAN_LIMITS 0xc0
96#define ID 0xe0
97#define DAS800_8254 4
98#define DAS800_STATUS2 7
99#define STATUS2_HCEN 0x80
100#define STATUS2_INTE 0X20
101#define DAS800_ID 7
102
7f340859
HS
103#define DAS802_16_HALF_FIFO_SZ 128
104
febc2ed6 105struct das800_board {
3726e56b
FMH
106 const char *name;
107 int ai_speed;
9ced1de6 108 const struct comedi_lrange *ai_range;
3726e56b 109 int resolution;
febc2ed6 110};
3726e56b 111
9ced1de6 112static const struct comedi_lrange range_das801_ai = {
b678075f
HS
113 9, {
114 BIP_RANGE(5),
115 BIP_RANGE(10),
116 UNI_RANGE(10),
117 BIP_RANGE(0.5),
118 UNI_RANGE(1),
119 BIP_RANGE(0.05),
120 UNI_RANGE(0.1),
121 BIP_RANGE(0.01),
122 UNI_RANGE(0.02)
123 }
3726e56b
FMH
124};
125
9ced1de6 126static const struct comedi_lrange range_cio_das801_ai = {
b678075f
HS
127 9, {
128 BIP_RANGE(5),
129 BIP_RANGE(10),
130 UNI_RANGE(10),
131 BIP_RANGE(0.5),
132 UNI_RANGE(1),
133 BIP_RANGE(0.05),
134 UNI_RANGE(0.1),
135 BIP_RANGE(0.005),
136 UNI_RANGE(0.01)
137 }
3726e56b
FMH
138};
139
9ced1de6 140static const struct comedi_lrange range_das802_ai = {
b678075f
HS
141 9, {
142 BIP_RANGE(5),
143 BIP_RANGE(10),
144 UNI_RANGE(10),
145 BIP_RANGE(2.5),
146 UNI_RANGE(5),
147 BIP_RANGE(1.25),
148 UNI_RANGE(2.5),
149 BIP_RANGE(0.625),
150 UNI_RANGE(1.25)
151 }
3726e56b
FMH
152};
153
9ced1de6 154static const struct comedi_lrange range_das80216_ai = {
b678075f
HS
155 8, {
156 BIP_RANGE(10),
157 UNI_RANGE(10),
158 BIP_RANGE(5),
159 UNI_RANGE(5),
160 BIP_RANGE(2.5),
161 UNI_RANGE(2.5),
162 BIP_RANGE(1.25),
163 UNI_RANGE(1.25)
164 }
3726e56b
FMH
165};
166
c1a59171
HS
167enum das800_boardinfo {
168 BOARD_DAS800,
169 BOARD_CIODAS800,
170 BOARD_DAS801,
171 BOARD_CIODAS801,
172 BOARD_DAS802,
173 BOARD_CIODAS802,
174 BOARD_CIODAS80216,
175};
3726e56b 176
febc2ed6 177static const struct das800_board das800_boards[] = {
c1a59171
HS
178 [BOARD_DAS800] = {
179 .name = "das-800",
180 .ai_speed = 25000,
181 .ai_range = &range_bipolar5,
182 .resolution = 12,
183 },
184 [BOARD_CIODAS800] = {
185 .name = "cio-das800",
186 .ai_speed = 20000,
187 .ai_range = &range_bipolar5,
188 .resolution = 12,
189 },
190 [BOARD_DAS801] = {
191 .name = "das-801",
192 .ai_speed = 25000,
193 .ai_range = &range_das801_ai,
194 .resolution = 12,
195 },
196 [BOARD_CIODAS801] = {
197 .name = "cio-das801",
198 .ai_speed = 20000,
199 .ai_range = &range_cio_das801_ai,
200 .resolution = 12,
201 },
202 [BOARD_DAS802] = {
203 .name = "das-802",
204 .ai_speed = 25000,
205 .ai_range = &range_das802_ai,
206 .resolution = 12,
207 },
208 [BOARD_CIODAS802] = {
209 .name = "cio-das802",
210 .ai_speed = 20000,
211 .ai_range = &range_das802_ai,
212 .resolution = 12,
213 },
214 [BOARD_CIODAS80216] = {
215 .name = "cio-das802/16",
216 .ai_speed = 10000,
217 .ai_range = &range_das80216_ai,
218 .resolution = 16,
219 },
3726e56b
FMH
220};
221
938f185d 222struct das800_private {
808cea5a 223 unsigned int count; /* number of data points left to be taken */
808cea5a
HS
224 unsigned int divisor1; /* counter 1 value for timed conversions */
225 unsigned int divisor2; /* counter 2 value for timed conversions */
ad5774fc
HS
226 unsigned int do_bits; /* digital output bits */
227 bool forever; /* flag that we should take data forever */
938f185d 228};
3726e56b 229
0a8fc089
HS
230static void das800_ind_write(struct comedi_device *dev,
231 unsigned val, unsigned reg)
232{
233 /*
234 * Select dev->iobase + 2 to be desired register
235 * then write to that register.
236 */
237 outb(reg, dev->iobase + DAS800_GAIN);
238 outb(val, dev->iobase + 2);
239}
240
241static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
242{
243 /*
244 * Select dev->iobase + 7 to be desired register
245 * then read from that register.
246 */
247 outb(reg, dev->iobase + DAS800_GAIN);
248 return inb(dev->iobase + 7);
249}
250
57d1ebf7 251static void das800_enable(struct comedi_device *dev)
3726e56b 252{
3cb65d4d 253 const struct das800_board *thisboard = comedi_board(dev);
9a1a6cf8 254 struct das800_private *devpriv = dev->private;
3726e56b 255 unsigned long irq_flags;
9a1a6cf8 256
5f74ea14 257 spin_lock_irqsave(&dev->spinlock, irq_flags);
2696fb57 258 /* enable fifo-half full interrupts for cio-das802/16 */
3726e56b
FMH
259 if (thisboard->resolution == 16)
260 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
0a8fc089
HS
261 /* enable hardware triggering */
262 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
263 /* enable card's interrupt */
264 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
5f74ea14 265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
266}
267
57d1ebf7 268static void das800_disable(struct comedi_device *dev)
3726e56b
FMH
269{
270 unsigned long irq_flags;
0a8fc089 271
5f74ea14 272 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089
HS
273 /* disable hardware triggering of conversions */
274 das800_ind_write(dev, 0x0, CONV_CONTROL);
5f74ea14 275 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
276}
277
62c3b4bf
HS
278static int das800_set_frequency(struct comedi_device *dev)
279{
280 struct das800_private *devpriv = dev->private;
281 int err = 0;
282
283 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
284 err++;
285 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
286 err++;
287 if (err)
288 return -1;
289
290 return 0;
291}
292
fe9b0850
HS
293static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
294{
295 struct das800_private *devpriv = dev->private;
296
ad5774fc 297 devpriv->forever = false;
fe9b0850 298 devpriv->count = 0;
57d1ebf7 299 das800_disable(dev);
fe9b0850
HS
300 return 0;
301}
302
0a85b6f0
MT
303static int das800_ai_do_cmdtest(struct comedi_device *dev,
304 struct comedi_subdevice *s,
305 struct comedi_cmd *cmd)
3726e56b 306{
3cb65d4d 307 const struct das800_board *thisboard = comedi_board(dev);
9a1a6cf8 308 struct das800_private *devpriv = dev->private;
3726e56b 309 int err = 0;
3726e56b 310
27020ffe 311 /* Step 1 : check if triggers are trivially valid */
3726e56b 312
27020ffe
HS
313 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
314 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
315 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
316 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
317 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
3726e56b
FMH
318
319 if (err)
320 return 1;
321
27020ffe 322 /* Step 2a : make sure trigger sources are unique */
3726e56b 323
27020ffe
HS
324 err |= cfc_check_trigger_is_unique(cmd->start_src);
325 err |= cfc_check_trigger_is_unique(cmd->convert_src);
326 err |= cfc_check_trigger_is_unique(cmd->stop_src);
327
328 /* Step 2b : and mutually compatible */
3726e56b
FMH
329
330 if (err)
331 return 2;
332
50b825ff 333 /* Step 3: check if arguments are trivially valid */
3726e56b 334
50b825ff
HS
335 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
336
337 if (cmd->convert_src == TRIG_TIMER)
338 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
339 thisboard->ai_speed);
340
341 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
343
344 if (cmd->stop_src == TRIG_COUNT)
345 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
346 else /* TRIG_NONE */
347 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
3726e56b
FMH
348
349 if (err)
350 return 3;
351
352 /* step 4: fix up any arguments */
353
354 if (cmd->convert_src == TRIG_TIMER) {
2bc40583
HS
355 int tmp = cmd->convert_arg;
356
3726e56b 357 /* calculate counter values that give desired timing */
cb9cfd7e
HS
358 i8253_cascade_ns_to_timer(I8254_OSC_BASE_1MHZ,
359 &devpriv->divisor1,
360 &devpriv->divisor2,
361 &cmd->convert_arg, cmd->flags);
3726e56b
FMH
362 if (tmp != cmd->convert_arg)
363 err++;
364 }
365
366 if (err)
367 return 4;
368
2696fb57 369 /* check channel/gain list against card's limitations */
3726e56b 370 if (cmd->chanlist) {
2bc40583
HS
371 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
372 unsigned int range = CR_RANGE(cmd->chanlist[0]);
373 unsigned int next;
374 int i;
375
3726e56b 376 for (i = 1; i < cmd->chanlist_len; i++) {
2bc40583
HS
377 next = cmd->chanlist[i];
378 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
379 dev_err(dev->class_dev,
380 "chanlist must be consecutive, counting upwards\n");
3726e56b
FMH
381 err++;
382 }
2bc40583
HS
383 if (CR_RANGE(next) != range) {
384 dev_err(dev->class_dev,
385 "chanlist must all have the same gain\n");
3726e56b
FMH
386 err++;
387 }
388 }
389 }
390
391 if (err)
392 return 5;
393
394 return 0;
395}
396
0a85b6f0
MT
397static int das800_ai_do_cmd(struct comedi_device *dev,
398 struct comedi_subdevice *s)
3726e56b 399{
3cb65d4d 400 const struct das800_board *thisboard = comedi_board(dev);
9a1a6cf8 401 struct das800_private *devpriv = dev->private;
d24160f8
HS
402 struct comedi_async *async = s->async;
403 unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
404 unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
405 unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
406 unsigned int scan_chans = (end_chan << 3) | start_chan;
3726e56b
FMH
407 int conv_bits;
408 unsigned long irq_flags;
3726e56b 409
57d1ebf7 410 das800_disable(dev);
3726e56b 411
5f74ea14 412 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 413 /* set scan limits */
d24160f8 414 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
5f74ea14 415 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
416
417 /* set gain */
3726e56b
FMH
418 if (thisboard->resolution == 12 && gain > 0)
419 gain += 0x7;
420 gain &= 0xf;
421 outb(gain, dev->iobase + DAS800_GAIN);
422
b41b267d 423 if (async->cmd.stop_src == TRIG_COUNT) {
3726e56b 424 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
ad5774fc 425 devpriv->forever = false;
b41b267d 426 } else { /* TRIG_NONE */
ad5774fc 427 devpriv->forever = true;
3726e56b 428 devpriv->count = 0;
3726e56b
FMH
429 }
430
431 /* enable auto channel scan, send interrupts on end of conversion
432 * and set clock source to internal or external
433 */
434 conv_bits = 0;
435 conv_bits |= EACS | IEOC;
436 if (async->cmd.start_src == TRIG_EXT)
437 conv_bits |= DTEN;
b41b267d 438 if (async->cmd.convert_src == TRIG_TIMER) {
3726e56b
FMH
439 conv_bits |= CASC | ITE;
440 /* set conversion frequency */
3726e56b
FMH
441 if (das800_set_frequency(dev) < 0) {
442 comedi_error(dev, "Error setting up counters");
443 return -1;
444 }
3726e56b
FMH
445 }
446
5f74ea14 447 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 448 das800_ind_write(dev, conv_bits, CONV_CONTROL);
5f74ea14 449 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
0a8fc089 450
57d1ebf7 451 das800_enable(dev);
3726e56b
FMH
452 return 0;
453}
454
b4780a3a
HS
455static unsigned int das800_ai_get_sample(struct comedi_device *dev)
456{
457 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
458 unsigned int msb = inb(dev->iobase + DAS800_MSB);
459
460 return (msb << 8) | lsb;
461}
462
fe9b0850
HS
463static irqreturn_t das800_interrupt(int irq, void *d)
464{
fe9b0850 465 struct comedi_device *dev = d;
fe9b0850 466 struct das800_private *devpriv = dev->private;
7f340859
HS
467 struct comedi_subdevice *s = dev->read_subdev;
468 struct comedi_async *async = s ? s->async : NULL;
fe9b0850 469 unsigned long irq_flags;
7f340859
HS
470 unsigned int status;
471 unsigned int val;
472 bool fifo_empty;
473 bool fifo_overflow;
474 int i;
fe9b0850
HS
475
476 status = inb(dev->iobase + DAS800_STATUS);
fe9b0850
HS
477 if (!(status & IRQ))
478 return IRQ_NONE;
7f340859 479 if (!dev->attached)
fe9b0850
HS
480 return IRQ_HANDLED;
481
fe9b0850 482 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 483 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
7f340859
HS
484 /*
485 * Don't release spinlock yet since we want to make sure
486 * no one else disables hardware conversions.
487 */
488
489 /* if hardware conversions are not enabled, then quit */
fe9b0850
HS
490 if (status == 0) {
491 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
492 return IRQ_HANDLED;
493 }
494
7f340859
HS
495 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
496 val = das800_ai_get_sample(dev);
497 if (s->maxdata == 0x0fff) {
498 fifo_empty = !!(val & FIFO_EMPTY);
499 fifo_overflow = !!(val & FIFO_OVF);
fe9b0850 500 } else {
7f340859
HS
501 /* cio-das802/16 has no fifo empty status bit */
502 fifo_empty = false;
503 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
504 CIO_FFOV);
fe9b0850 505 }
7f340859 506 if (fifo_empty || fifo_overflow)
fe9b0850 507 break;
7f340859
HS
508
509 if (s->maxdata == 0x0fff)
510 val >>= 4; /* 12-bit sample */
511
fe9b0850 512 /* if there are more data points to collect */
7f340859 513 if (devpriv->count > 0 || devpriv->forever) {
fe9b0850 514 /* write data point to buffer */
7f340859
HS
515 cfc_write_to_buffer(s, val & s->maxdata);
516 devpriv->count--;
fe9b0850
HS
517 }
518 }
519 async->events |= COMEDI_CB_BLOCK;
7f340859 520
fe9b0850
HS
521 if (fifo_overflow) {
522 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
fe9b0850 523 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
f382898f 524 cfc_handle_events(dev, s);
fe9b0850
HS
525 return IRQ_HANDLED;
526 }
7f340859
HS
527
528 if (devpriv->count > 0 || devpriv->forever) {
fe9b0850
HS
529 /* Re-enable card's interrupt.
530 * We already have spinlock, so indirect addressing is safe */
0a8fc089
HS
531 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
532 CONTROL1);
fe9b0850 533 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
fe9b0850 534 } else {
7f340859 535 /* otherwise, stop taking data */
fe9b0850 536 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
57d1ebf7 537 das800_disable(dev);
fe9b0850
HS
538 async->events |= COMEDI_CB_EOA;
539 }
f382898f 540 cfc_handle_events(dev, s);
fe9b0850
HS
541 return IRQ_HANDLED;
542}
543
90bc68ec
HS
544static int das800_ai_eoc(struct comedi_device *dev,
545 struct comedi_subdevice *s,
546 struct comedi_insn *insn,
547 unsigned long context)
b4780a3a 548{
90bc68ec 549 unsigned int status;
b4780a3a 550
90bc68ec
HS
551 status = inb(dev->iobase + DAS800_STATUS);
552 if ((status & BUSY) == 0)
553 return 0;
554 return -EBUSY;
b4780a3a
HS
555}
556
d7427345
HS
557static int das800_ai_insn_read(struct comedi_device *dev,
558 struct comedi_subdevice *s,
559 struct comedi_insn *insn,
560 unsigned int *data)
3726e56b 561{
9a1a6cf8 562 struct das800_private *devpriv = dev->private;
b4780a3a
HS
563 unsigned int chan = CR_CHAN(insn->chanspec);
564 unsigned int range = CR_RANGE(insn->chanspec);
3726e56b 565 unsigned long irq_flags;
b4780a3a
HS
566 unsigned int val;
567 int ret;
568 int i;
3726e56b 569
57d1ebf7 570 das800_disable(dev);
3726e56b
FMH
571
572 /* set multiplexer */
5f74ea14 573 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 574 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
5f74ea14 575 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
3726e56b
FMH
576
577 /* set gain / range */
b4780a3a 578 if (s->maxdata == 0x0fff && range)
3726e56b
FMH
579 range += 0x7;
580 range &= 0xf;
581 outb(range, dev->iobase + DAS800_GAIN);
582
5f74ea14 583 udelay(5);
3726e56b 584
b4780a3a 585 for (i = 0; i < insn->n; i++) {
3726e56b
FMH
586 /* trigger conversion */
587 outb_p(0, dev->iobase + DAS800_MSB);
588
90bc68ec 589 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
b4780a3a
HS
590 if (ret)
591 return ret;
592
593 val = das800_ai_get_sample(dev);
594 if (s->maxdata == 0x0fff)
595 val >>= 4; /* 12-bit sample */
596 data[i] = val & s->maxdata;
3726e56b
FMH
597 }
598
b4780a3a 599 return insn->n;
3726e56b
FMH
600}
601
d7427345
HS
602static int das800_di_insn_bits(struct comedi_device *dev,
603 struct comedi_subdevice *s,
604 struct comedi_insn *insn,
605 unsigned int *data)
3726e56b 606{
e245b6d1 607 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
3726e56b 608
a2714e3e 609 return insn->n;
3726e56b
FMH
610}
611
d7427345
HS
612static int das800_do_insn_bits(struct comedi_device *dev,
613 struct comedi_subdevice *s,
614 struct comedi_insn *insn,
615 unsigned int *data)
3726e56b 616{
9a1a6cf8 617 struct das800_private *devpriv = dev->private;
3726e56b
FMH
618 unsigned long irq_flags;
619
97f4289a 620 if (comedi_dio_update_state(s, data)) {
26234771 621 devpriv->do_bits = s->state << 4;
3726e56b 622
26234771
HS
623 spin_lock_irqsave(&dev->spinlock, irq_flags);
624 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
625 CONTROL1);
626 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
627 }
3726e56b 628
26234771 629 data[1] = s->state;
3726e56b 630
a2714e3e 631 return insn->n;
3726e56b
FMH
632}
633
4f71ceeb
HS
634static int das800_probe(struct comedi_device *dev)
635{
636 const struct das800_board *thisboard = comedi_board(dev);
c1a59171 637 int board = thisboard ? thisboard - das800_boards : -EINVAL;
4f71ceeb
HS
638 int id_bits;
639 unsigned long irq_flags;
4f71ceeb
HS
640
641 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 642 id_bits = das800_ind_read(dev, ID) & 0x3;
4f71ceeb
HS
643 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
644
4f71ceeb
HS
645 switch (id_bits) {
646 case 0x0:
c1a59171
HS
647 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
648 break;
4f71ceeb 649 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
c1a59171 650 board = BOARD_DAS800;
4f71ceeb
HS
651 break;
652 case 0x2:
c1a59171
HS
653 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
654 break;
4f71ceeb 655 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
c1a59171 656 board = BOARD_DAS801;
4f71ceeb
HS
657 break;
658 case 0x3:
c1a59171
HS
659 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
660 board == BOARD_CIODAS80216)
661 break;
4f71ceeb 662 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
c1a59171 663 board = BOARD_DAS802;
4f71ceeb
HS
664 break;
665 default:
c1a59171 666 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
4f71ceeb 667 id_bits);
c1a59171 668 board = -EINVAL;
4f71ceeb
HS
669 break;
670 }
c1a59171 671 return board;
4f71ceeb
HS
672}
673
92046ae4
HS
674static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
675{
676 const struct das800_board *thisboard = comedi_board(dev);
677 struct das800_private *devpriv;
678 struct comedi_subdevice *s;
679 unsigned int irq = it->options[1];
680 unsigned long irq_flags;
681 int board;
682 int ret;
683
0bdab509 684 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
92046ae4
HS
685 if (!devpriv)
686 return -ENOMEM;
92046ae4
HS
687
688 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
689 if (ret)
690 return ret;
691
692 board = das800_probe(dev);
693 if (board < 0) {
694 dev_dbg(dev->class_dev, "unable to determine board type\n");
695 return -ENODEV;
696 }
697 dev->board_ptr = das800_boards + board;
698 thisboard = comedi_board(dev);
f05ffb6e 699 dev->board_name = thisboard->name;
92046ae4 700
f05ffb6e
HS
701 if (irq > 1 && irq <= 7) {
702 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
703 dev);
704 if (ret == 0)
705 dev->irq = irq;
92046ae4 706 }
92046ae4
HS
707
708 ret = comedi_alloc_subdevices(dev, 3);
709 if (ret)
710 return ret;
711
d7427345 712 /* Analog Input subdevice */
92046ae4
HS
713 s = &dev->subdevices[0];
714 dev->read_subdev = s;
77630119
HS
715 s->type = COMEDI_SUBD_AI;
716 s->subdev_flags = SDF_READABLE | SDF_GROUND;
717 s->n_chan = 8;
718 s->maxdata = (1 << thisboard->resolution) - 1;
719 s->range_table = thisboard->ai_range;
d7427345 720 s->insn_read = das800_ai_insn_read;
77630119
HS
721 if (dev->irq) {
722 s->subdev_flags |= SDF_CMD_READ;
723 s->len_chanlist = 8;
724 s->do_cmdtest = das800_ai_do_cmdtest;
725 s->do_cmd = das800_ai_do_cmd;
726 s->cancel = das800_cancel;
727 }
92046ae4 728
d7427345 729 /* Digital Input subdevice */
92046ae4 730 s = &dev->subdevices[1];
d7427345
HS
731 s->type = COMEDI_SUBD_DI;
732 s->subdev_flags = SDF_READABLE;
733 s->n_chan = 3;
734 s->maxdata = 1;
735 s->range_table = &range_digital;
736 s->insn_bits = das800_di_insn_bits;
737
738 /* Digital Output subdevice */
92046ae4 739 s = &dev->subdevices[2];
d7427345
HS
740 s->type = COMEDI_SUBD_DO;
741 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
742 s->n_chan = 4;
743 s->maxdata = 1;
744 s->range_table = &range_digital;
745 s->insn_bits = das800_do_insn_bits;
92046ae4 746
57d1ebf7 747 das800_disable(dev);
92046ae4
HS
748
749 /* initialize digital out channels */
750 spin_lock_irqsave(&dev->spinlock, irq_flags);
0a8fc089 751 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
92046ae4
HS
752 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
753
754 return 0;
755};
756
79a8c0e2
HS
757static struct comedi_driver driver_das800 = {
758 .driver_name = "das800",
759 .module = THIS_MODULE,
760 .attach = das800_attach,
761 .detach = comedi_legacy_detach,
762 .num_names = ARRAY_SIZE(das800_boards),
763 .board_name = &das800_boards[0].name,
764 .offset = sizeof(struct das800_board),
765};
766module_comedi_driver(driver_das800);
767
90f703d3
AT
768MODULE_AUTHOR("Comedi http://www.comedi.org");
769MODULE_DESCRIPTION("Comedi low-level driver");
770MODULE_LICENSE("GPL");