staging: comedi, remove interrupt.h
[GitHub/mt8127/android_kernel_alcatel_ttab.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.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23************************************************************************
24*/
25/*
26Driver: das800
27Description: Keithley Metrabyte DAS800 (& compatibles)
28Author: Frank Mori Hess <fmhess@users.sourceforge.net>
29Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
30 DAS-802 (das-802),
31 [Measurement Computing] CIO-DAS800 (cio-das800),
32 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
33 CIO-DAS802/16 (cio-das802/16)
34Status: works, cio-das802/16 untested - email me if you have tested it
35
36Configuration options:
37 [0] - I/O port base address
38 [1] - IRQ (optional, required for timed or externally triggered conversions)
39
40Notes:
41 IRQ can be omitted, although the cmd interface will not work without it.
42
43 All entries in the channel/gain list must use the same gain and be
44 consecutive channels counting upwards in channel number (these are
45 hardware limitations.)
46
47 I've never tested the gain setting stuff since I only have a
48 DAS-800 board with fixed gain.
49
50 The cio-das802/16 does not have a fifo-empty status bit! Therefore
51 only fifo-half-full transfers are possible with this card.
52*/
53/*
54
55cmd triggers supported:
56 start_src: TRIG_NOW | TRIG_EXT
57 scan_begin_src: TRIG_FOLLOW
58 scan_end_src: TRIG_COUNT
59 convert_src: TRIG_TIMER | TRIG_EXT
60 stop_src: TRIG_NONE | TRIG_COUNT
61
62
63*/
64
65#include "../comedidev.h"
66
67#include <linux/ioport.h>
68#include <linux/delay.h>
69
70#include "8253.h"
71#include "comedi_fc.h"
72
73#define DAS800_SIZE 8
74#define TIMER_BASE 1000
75#define N_CHAN_AI 8 // number of analog input channels
76
77/* Registers for the das800 */
78
79#define DAS800_LSB 0
80#define FIFO_EMPTY 0x1
81#define FIFO_OVF 0x2
82#define DAS800_MSB 1
83#define DAS800_CONTROL1 2
84#define CONTROL1_INTE 0x8
85#define DAS800_CONV_CONTROL 2
86#define ITE 0x1
87#define CASC 0x2
88#define DTEN 0x4
89#define IEOC 0x8
90#define EACS 0x10
91#define CONV_HCEN 0x80
92#define DAS800_SCAN_LIMITS 2
93#define DAS800_STATUS 2
94#define IRQ 0x8
95#define BUSY 0x80
96#define DAS800_GAIN 3
97#define CIO_FFOV 0x8 // fifo overflow for cio-das802/16
98#define CIO_ENHF 0x90 // interrupt fifo half full for cio-das802/16
99#define CONTROL1 0x80
100#define CONV_CONTROL 0xa0
101#define SCAN_LIMITS 0xc0
102#define ID 0xe0
103#define DAS800_8254 4
104#define DAS800_STATUS2 7
105#define STATUS2_HCEN 0x80
106#define STATUS2_INTE 0X20
107#define DAS800_ID 7
108
febc2ed6 109struct das800_board {
3726e56b
FMH
110 const char *name;
111 int ai_speed;
9ced1de6 112 const struct comedi_lrange *ai_range;
3726e56b 113 int resolution;
febc2ed6 114};
3726e56b
FMH
115
116//analog input ranges
9ced1de6 117static const struct comedi_lrange range_das800_ai = {
3726e56b
FMH
118 1,
119 {
120 RANGE(-5, 5),
121 }
122};
123
9ced1de6 124static const struct comedi_lrange range_das801_ai = {
3726e56b
FMH
125 9,
126 {
127 RANGE(-5, 5),
128 RANGE(-10, 10),
129 RANGE(0, 10),
130 RANGE(-0.5, 0.5),
131 RANGE(0, 1),
132 RANGE(-0.05, 0.05),
133 RANGE(0, 0.1),
134 RANGE(-0.01, 0.01),
135 RANGE(0, 0.02),
136 }
137};
138
9ced1de6 139static const struct comedi_lrange range_cio_das801_ai = {
3726e56b
FMH
140 9,
141 {
142 RANGE(-5, 5),
143 RANGE(-10, 10),
144 RANGE(0, 10),
145 RANGE(-0.5, 0.5),
146 RANGE(0, 1),
147 RANGE(-0.05, 0.05),
148 RANGE(0, 0.1),
149 RANGE(-0.005, 0.005),
150 RANGE(0, 0.01),
151 }
152};
153
9ced1de6 154static const struct comedi_lrange range_das802_ai = {
3726e56b
FMH
155 9,
156 {
157 RANGE(-5, 5),
158 RANGE(-10, 10),
159 RANGE(0, 10),
160 RANGE(-2.5, 2.5),
161 RANGE(0, 5),
162 RANGE(-1.25, 1.25),
163 RANGE(0, 2.5),
164 RANGE(-0.625, 0.625),
165 RANGE(0, 1.25),
166 }
167};
168
9ced1de6 169static const struct comedi_lrange range_das80216_ai = {
3726e56b
FMH
170 8,
171 {
172 RANGE(-10, 10),
173 RANGE(0, 10),
174 RANGE(-5, 5),
175 RANGE(0, 5),
176 RANGE(-2.5, 2.5),
177 RANGE(0, 2.5),
178 RANGE(-1.25, 1.25),
179 RANGE(0, 1.25),
180 }
181};
182
183enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
184
febc2ed6 185static const struct das800_board das800_boards[] = {
3726e56b
FMH
186 {
187 name: "das-800",
188 ai_speed:25000,
189 ai_range:&range_das800_ai,
190 resolution:12,
191 },
192 {
193 name: "cio-das800",
194 ai_speed:20000,
195 ai_range:&range_das800_ai,
196 resolution:12,
197 },
198 {
199 name: "das-801",
200 ai_speed:25000,
201 ai_range:&range_das801_ai,
202 resolution:12,
203 },
204 {
205 name: "cio-das801",
206 ai_speed:20000,
207 ai_range:&range_cio_das801_ai,
208 resolution:12,
209 },
210 {
211 name: "das-802",
212 ai_speed:25000,
213 ai_range:&range_das802_ai,
214 resolution:12,
215 },
216 {
217 name: "cio-das802",
218 ai_speed:20000,
219 ai_range:&range_das802_ai,
220 resolution:12,
221 },
222 {
223 name: "cio-das802/16",
224 ai_speed:10000,
225 ai_range:&range_das80216_ai,
226 resolution:16,
227 },
228};
229
230/*
231 * Useful for shorthand access to the particular board structure
232 */
febc2ed6 233#define thisboard ((const struct das800_board *)dev->board_ptr)
3726e56b 234
938f185d 235struct das800_private {
3726e56b
FMH
236 volatile unsigned int count; /* number of data points left to be taken */
237 volatile int forever; /* flag indicating whether we should take data forever */
238 unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
239 unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
240 volatile int do_bits; /* digital output bits */
938f185d 241};
3726e56b 242
938f185d 243#define devpriv ((struct das800_private *)dev->private)
3726e56b 244
0707bb04 245static int das800_attach(struct comedi_device * dev, struct comedi_devconfig * it);
71b5f4f1 246static int das800_detach(struct comedi_device * dev);
34c43922 247static int das800_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
3726e56b 248
139dfbdf 249static struct comedi_driver driver_das800 = {
3726e56b
FMH
250 driver_name:"das800",
251 module:THIS_MODULE,
252 attach:das800_attach,
253 detach:das800_detach,
febc2ed6 254 num_names:sizeof(das800_boards) / sizeof(struct das800_board),
3726e56b 255 board_name:&das800_boards[0].name,
febc2ed6 256 offset:sizeof(struct das800_board),
3726e56b
FMH
257};
258
70265d24 259static irqreturn_t das800_interrupt(int irq, void *d);
71b5f4f1
BP
260static void enable_das800(struct comedi_device * dev);
261static void disable_das800(struct comedi_device * dev);
34c43922 262static int das800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
ea6d0d4c 263 struct comedi_cmd * cmd);
34c43922
BP
264static int das800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
265static int das800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 266 struct comedi_insn * insn, unsigned int * data);
34c43922 267static int das800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 268 struct comedi_insn * insn, unsigned int * data);
34c43922 269static int das800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 270 struct comedi_insn * insn, unsigned int * data);
71b5f4f1
BP
271static int das800_probe(struct comedi_device * dev);
272static int das800_set_frequency(struct comedi_device * dev);
3726e56b
FMH
273
274/* checks and probes das-800 series board type */
71b5f4f1 275static int das800_probe(struct comedi_device * dev)
3726e56b
FMH
276{
277 int id_bits;
278 unsigned long irq_flags;
279 int board;
280
281 // 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing
282 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
283 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
284 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
285 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
286
287 board = thisboard - das800_boards;
288
289 switch (id_bits) {
290 case 0x0:
291 if (board == das800) {
292 printk(" Board model: DAS-800\n");
293 return board;
294 }
295 if (board == ciodas800) {
296 printk(" Board model: CIO-DAS800\n");
297 return board;
298 }
299 printk(" Board model (probed): DAS-800\n");
300 return das800;
301 break;
302 case 0x2:
303 if (board == das801) {
304 printk(" Board model: DAS-801\n");
305 return board;
306 }
307 if (board == ciodas801) {
308 printk(" Board model: CIO-DAS801\n");
309 return board;
310 }
311 printk(" Board model (probed): DAS-801\n");
312 return das801;
313 break;
314 case 0x3:
315 if (board == das802) {
316 printk(" Board model: DAS-802\n");
317 return board;
318 }
319 if (board == ciodas802) {
320 printk(" Board model: CIO-DAS802\n");
321 return board;
322 }
323 if (board == ciodas80216) {
324 printk(" Board model: CIO-DAS802/16\n");
325 return board;
326 }
327 printk(" Board model (probed): DAS-802\n");
328 return das802;
329 break;
330 default:
331 printk(" Board model: probe returned 0x%x (unknown)\n",
332 id_bits);
333 return board;
334 break;
335 }
336 return -1;
337}
338
339/*
340 * A convenient macro that defines init_module() and cleanup_module(),
341 * as necessary.
342 */
343COMEDI_INITCLEANUP(driver_das800);
344
345/* interrupt service routine */
70265d24 346static irqreturn_t das800_interrupt(int irq, void *d)
3726e56b
FMH
347{
348 short i; /* loop index */
790c5541 349 short dataPoint = 0;
71b5f4f1 350 struct comedi_device *dev = d;
34c43922 351 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
d163679c 352 struct comedi_async *async;
3726e56b
FMH
353 int status;
354 unsigned long irq_flags;
355 static const int max_loops = 128; // half-fifo size for cio-das802/16
356 // flags
357 int fifo_empty = 0;
358 int fifo_overflow = 0;
359
360 status = inb(dev->iobase + DAS800_STATUS);
361 /* if interrupt was not generated by board or driver not attached, quit */
362 if (!(status & IRQ))
363 return IRQ_NONE;
364 if (!(dev->attached))
365 return IRQ_HANDLED;
366
367 /* wait until here to initialize async, since we will get null dereference
368 * if interrupt occurs before driver is fully attached!
369 */
370 async = s->async;
371
372 // if hardware conversions are not enabled, then quit
373 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
374 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
375 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
376 /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
377 if (status == 0) {
378 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
379 return IRQ_HANDLED;
380 }
381
382 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
383 for (i = 0; i < max_loops; i++) {
384 /* read 16 bits from dev->iobase and dev->iobase + 1 */
385 dataPoint = inb(dev->iobase + DAS800_LSB);
386 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
387 if (thisboard->resolution == 12) {
388 fifo_empty = dataPoint & FIFO_EMPTY;
389 fifo_overflow = dataPoint & FIFO_OVF;
390 if (fifo_overflow)
391 break;
392 } else {
393 fifo_empty = 0; // cio-das802/16 has no fifo empty status bit
394 }
395 if (fifo_empty) {
396 break;
397 }
398 /* strip off extraneous bits for 12 bit cards */
399 if (thisboard->resolution == 12)
400 dataPoint = (dataPoint >> 4) & 0xfff;
401 /* if there are more data points to collect */
402 if (devpriv->count > 0 || devpriv->forever == 1) {
403 /* write data point to buffer */
404 cfc_write_to_buffer(s, dataPoint);
405 if (devpriv->count > 0)
406 devpriv->count--;
407 }
408 }
409 async->events |= COMEDI_CB_BLOCK;
410 /* check for fifo overflow */
411 if (thisboard->resolution == 12) {
412 fifo_overflow = dataPoint & FIFO_OVF;
413 // else cio-das802/16
414 } else {
415 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
416 }
417 if (fifo_overflow) {
418 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
419 comedi_error(dev, "DAS800 FIFO overflow");
420 das800_cancel(dev, dev->subdevices + 0);
421 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
422 comedi_event(dev, s);
423 async->events = 0;
424 return IRQ_HANDLED;
425 }
426 if (devpriv->count > 0 || devpriv->forever == 1) {
427 /* Re-enable card's interrupt.
428 * We already have spinlock, so indirect addressing is safe */
429 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
430 outb(CONTROL1_INTE | devpriv->do_bits,
431 dev->iobase + DAS800_CONTROL1);
432 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
433 /* otherwise, stop taking data */
434 } else {
435 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
436 disable_das800(dev); /* diable hardware triggered conversions */
437 async->events |= COMEDI_CB_EOA;
438 }
439 comedi_event(dev, s);
440 async->events = 0;
441 return IRQ_HANDLED;
442}
443
0707bb04 444static int das800_attach(struct comedi_device * dev, struct comedi_devconfig * it)
3726e56b 445{
34c43922 446 struct comedi_subdevice *s;
3726e56b
FMH
447 unsigned long iobase = it->options[0];
448 unsigned int irq = it->options[1];
449 unsigned long irq_flags;
450 int board;
451
452 printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
453 if (irq) {
454 printk(", irq %u", irq);
455 }
456 printk("\n");
457
458 /* allocate and initialize dev->private */
938f185d 459 if (alloc_private(dev, sizeof(struct das800_private)) < 0)
3726e56b
FMH
460 return -ENOMEM;
461
462 if (iobase == 0) {
463 printk("io base address required for das800\n");
464 return -EINVAL;
465 }
466
467 /* check if io addresses are available */
468 if (!request_region(iobase, DAS800_SIZE, "das800")) {
469 printk("I/O port conflict\n");
470 return -EIO;
471 }
472 dev->iobase = iobase;
473
474 board = das800_probe(dev);
475 if (board < 0) {
476 printk("unable to determine board type\n");
477 return -ENODEV;
478 }
479 dev->board_ptr = das800_boards + board;
480
481 /* grab our IRQ */
482 if (irq == 1 || irq > 7) {
483 printk("irq out of range\n");
484 return -EINVAL;
485 }
486 if (irq) {
487 if (comedi_request_irq(irq, das800_interrupt, 0, "das800", dev)) {
488 printk("unable to allocate irq %u\n", irq);
489 return -EINVAL;
490 }
491 }
492 dev->irq = irq;
493
494 dev->board_name = thisboard->name;
495
496 if (alloc_subdevices(dev, 3) < 0)
497 return -ENOMEM;
498
499 /* analog input subdevice */
500 s = dev->subdevices + 0;
501 dev->read_subdev = s;
502 s->type = COMEDI_SUBD_AI;
503 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
504 s->n_chan = 8;
505 s->len_chanlist = 8;
506 s->maxdata = (1 << thisboard->resolution) - 1;
507 s->range_table = thisboard->ai_range;
508 s->do_cmd = das800_ai_do_cmd;
509 s->do_cmdtest = das800_ai_do_cmdtest;
510 s->insn_read = das800_ai_rinsn;
511 s->cancel = das800_cancel;
512
513 /* di */
514 s = dev->subdevices + 1;
515 s->type = COMEDI_SUBD_DI;
516 s->subdev_flags = SDF_READABLE;
517 s->n_chan = 3;
518 s->maxdata = 1;
519 s->range_table = &range_digital;
520 s->insn_bits = das800_di_rbits;
521
522 /* do */
523 s = dev->subdevices + 2;
524 s->type = COMEDI_SUBD_DO;
525 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
526 s->n_chan = 4;
527 s->maxdata = 1;
528 s->range_table = &range_digital;
529 s->insn_bits = das800_do_wbits;
530
531 disable_das800(dev);
532
533 /* initialize digital out channels */
534 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
535 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
536 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
537 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
538
539 return 0;
540};
541
71b5f4f1 542static int das800_detach(struct comedi_device * dev)
3726e56b
FMH
543{
544 printk("comedi%d: das800: remove\n", dev->minor);
545
546 /* only free stuff if it has been allocated by _attach */
547 if (dev->iobase)
548 release_region(dev->iobase, DAS800_SIZE);
549 if (dev->irq)
550 comedi_free_irq(dev->irq, dev);
551 return 0;
552};
553
34c43922 554static int das800_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
3726e56b
FMH
555{
556 devpriv->forever = 0;
557 devpriv->count = 0;
558 disable_das800(dev);
559 return 0;
560}
561
562/* enable_das800 makes the card start taking hardware triggered conversions */
71b5f4f1 563static void enable_das800(struct comedi_device * dev)
3726e56b
FMH
564{
565 unsigned long irq_flags;
566 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
567 // enable fifo-half full interrupts for cio-das802/16
568 if (thisboard->resolution == 16)
569 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
570 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
571 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
572 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
573 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
574 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
575}
576
577/* disable_das800 stops hardware triggered conversions */
71b5f4f1 578static void disable_das800(struct comedi_device * dev)
3726e56b
FMH
579{
580 unsigned long irq_flags;
581 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
582 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
583 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
584 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
585}
586
34c43922 587static int das800_ai_do_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
ea6d0d4c 588 struct comedi_cmd * cmd)
3726e56b
FMH
589{
590 int err = 0;
591 int tmp;
592 int gain, startChan;
593 int i;
594
595 /* step 1: make sure trigger sources are trivially valid */
596
597 tmp = cmd->start_src;
598 cmd->start_src &= TRIG_NOW | TRIG_EXT;
599 if (!cmd->start_src || tmp != cmd->start_src)
600 err++;
601
602 tmp = cmd->scan_begin_src;
603 cmd->scan_begin_src &= TRIG_FOLLOW;
604 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
605 err++;
606
607 tmp = cmd->convert_src;
608 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
609 if (!cmd->convert_src || tmp != cmd->convert_src)
610 err++;
611
612 tmp = cmd->scan_end_src;
613 cmd->scan_end_src &= TRIG_COUNT;
614 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
615 err++;
616
617 tmp = cmd->stop_src;
618 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
619 if (!cmd->stop_src || tmp != cmd->stop_src)
620 err++;
621
622 if (err)
623 return 1;
624
625 /* step 2: make sure trigger sources are unique and mutually compatible */
626
627 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
628 err++;
629 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
630 err++;
631 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
632 err++;
633
634 if (err)
635 return 2;
636
637 /* step 3: make sure arguments are trivially compatible */
638
639 if (cmd->start_arg != 0) {
640 cmd->start_arg = 0;
641 err++;
642 }
643 if (cmd->convert_src == TRIG_TIMER) {
644 if (cmd->convert_arg < thisboard->ai_speed) {
645 cmd->convert_arg = thisboard->ai_speed;
646 err++;
647 }
648 }
649 if (!cmd->chanlist_len) {
650 cmd->chanlist_len = 1;
651 err++;
652 }
653 if (cmd->scan_end_arg != cmd->chanlist_len) {
654 cmd->scan_end_arg = cmd->chanlist_len;
655 err++;
656 }
657 if (cmd->stop_src == TRIG_COUNT) {
658 if (!cmd->stop_arg) {
659 cmd->stop_arg = 1;
660 err++;
661 }
662 } else { /* TRIG_NONE */
663 if (cmd->stop_arg != 0) {
664 cmd->stop_arg = 0;
665 err++;
666 }
667 }
668
669 if (err)
670 return 3;
671
672 /* step 4: fix up any arguments */
673
674 if (cmd->convert_src == TRIG_TIMER) {
675 tmp = cmd->convert_arg;
676 /* calculate counter values that give desired timing */
677 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
678 &(devpriv->divisor2), &(cmd->convert_arg),
679 cmd->flags & TRIG_ROUND_MASK);
680 if (tmp != cmd->convert_arg)
681 err++;
682 }
683
684 if (err)
685 return 4;
686
687 // check channel/gain list against card's limitations
688 if (cmd->chanlist) {
689 gain = CR_RANGE(cmd->chanlist[0]);
690 startChan = CR_CHAN(cmd->chanlist[0]);
691 for (i = 1; i < cmd->chanlist_len; i++) {
692 if (CR_CHAN(cmd->chanlist[i]) !=
693 (startChan + i) % N_CHAN_AI) {
694 comedi_error(dev,
695 "entries in chanlist must be consecutive channels, counting upwards\n");
696 err++;
697 }
698 if (CR_RANGE(cmd->chanlist[i]) != gain) {
699 comedi_error(dev,
700 "entries in chanlist must all have the same gain\n");
701 err++;
702 }
703 }
704 }
705
706 if (err)
707 return 5;
708
709 return 0;
710}
711
34c43922 712static int das800_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
3726e56b
FMH
713{
714 int startChan, endChan, scan, gain;
715 int conv_bits;
716 unsigned long irq_flags;
d163679c 717 struct comedi_async *async = s->async;
3726e56b
FMH
718
719 if (!dev->irq) {
720 comedi_error(dev,
721 "no irq assigned for das-800, cannot do hardware conversions");
722 return -1;
723 }
724
725 disable_das800(dev);
726
727 /* set channel scan limits */
728 startChan = CR_CHAN(async->cmd.chanlist[0]);
729 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
730 scan = (endChan << 3) | startChan;
731
732 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
733 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
734 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
735 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
736
737 /* set gain */
738 gain = CR_RANGE(async->cmd.chanlist[0]);
739 if (thisboard->resolution == 12 && gain > 0)
740 gain += 0x7;
741 gain &= 0xf;
742 outb(gain, dev->iobase + DAS800_GAIN);
743
744 switch (async->cmd.stop_src) {
745 case TRIG_COUNT:
746 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
747 devpriv->forever = 0;
748 break;
749 case TRIG_NONE:
750 devpriv->forever = 1;
751 devpriv->count = 0;
752 break;
753 default:
754 break;
755 }
756
757 /* enable auto channel scan, send interrupts on end of conversion
758 * and set clock source to internal or external
759 */
760 conv_bits = 0;
761 conv_bits |= EACS | IEOC;
762 if (async->cmd.start_src == TRIG_EXT)
763 conv_bits |= DTEN;
764 switch (async->cmd.convert_src) {
765 case TRIG_TIMER:
766 conv_bits |= CASC | ITE;
767 /* set conversion frequency */
768 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
769 &(devpriv->divisor2), &(async->cmd.convert_arg),
770 async->cmd.flags & TRIG_ROUND_MASK);
771 if (das800_set_frequency(dev) < 0) {
772 comedi_error(dev, "Error setting up counters");
773 return -1;
774 }
775 break;
776 case TRIG_EXT:
777 break;
778 default:
779 break;
780 }
781
782 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
783 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
784 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
785 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
786 async->events = 0;
787 enable_das800(dev);
788 return 0;
789}
790
34c43922 791static int das800_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 792 struct comedi_insn * insn, unsigned int * data)
3726e56b
FMH
793{
794 int i, n;
795 int chan;
796 int range;
797 int lsb, msb;
798 int timeout = 1000;
799 unsigned long irq_flags;
800
801 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
802
803 /* set multiplexer */
804 chan = CR_CHAN(insn->chanspec);
805
806 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
807 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
808 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
809 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
810
811 /* set gain / range */
812 range = CR_RANGE(insn->chanspec);
813 if (thisboard->resolution == 12 && range)
814 range += 0x7;
815 range &= 0xf;
816 outb(range, dev->iobase + DAS800_GAIN);
817
818 comedi_udelay(5);
819
820 for (n = 0; n < insn->n; n++) {
821 /* trigger conversion */
822 outb_p(0, dev->iobase + DAS800_MSB);
823
824 for (i = 0; i < timeout; i++) {
825 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
826 break;
827 }
828 if (i == timeout) {
829 comedi_error(dev, "timeout");
830 return -ETIME;
831 }
832 lsb = inb(dev->iobase + DAS800_LSB);
833 msb = inb(dev->iobase + DAS800_MSB);
834 if (thisboard->resolution == 12) {
835 data[n] = (lsb >> 4) & 0xff;
836 data[n] |= (msb << 4);
837 } else {
838 data[n] = (msb << 8) | lsb;
839 }
840 }
841
842 return n;
843}
844
34c43922 845static int das800_di_rbits(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 846 struct comedi_insn * insn, unsigned int * data)
3726e56b 847{
790c5541 848 unsigned int bits;
3726e56b
FMH
849
850 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
851 bits &= 0x7;
852 data[1] = bits;
853 data[0] = 0;
854
855 return 2;
856}
857
34c43922 858static int das800_do_wbits(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 859 struct comedi_insn * insn, unsigned int * data)
3726e56b
FMH
860{
861 int wbits;
862 unsigned long irq_flags;
863
864 // only set bits that have been masked
865 data[0] &= 0xf;
866 wbits = devpriv->do_bits >> 4;
867 wbits &= ~data[0];
868 wbits |= data[0] & data[1];
869 devpriv->do_bits = wbits << 4;
870
871 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
872 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
873 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
874 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
875
876 data[1] = wbits;
877
878 return 2;
879}
880
881/* loads counters with divisor1, divisor2 from private structure */
71b5f4f1 882static int das800_set_frequency(struct comedi_device * dev)
3726e56b
FMH
883{
884 int err = 0;
885
886 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
887 err++;
888 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
889 err++;
890 if (err)
891 return -1;
892
893 return 0;
894}