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