staging: comedi: remove FSF address from boilerplate text
[deliverable/linux.git] / drivers / staging / comedi / drivers / dmm32at.c
CommitLineData
3c501880
PP
1/*
2 comedi/drivers/dmm32at.c
3 Diamond Systems mm32at code for a Comedi driver
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
3c501880
PP
17*/
18/*
19Driver: dmm32at
20Description: Diamond Systems mm32at driver.
21Devices:
22Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
23Updated: Fri Jun 4 09:13:24 CDT 2004
24Status: experimental
25
26This driver is for the Diamond Systems MM-32-AT board
27http://www.diamondsystems.com/products/diamondmm32at It is being used
28on serveral projects inside NASA, without problems so far. For analog
29input commands, TRIG_EXT is not yet supported at all..
30
31Configuration Options:
32 comedi_config /dev/comedi0 dmm32at baseaddr,irq
33*/
34
25436dc9 35#include <linux/interrupt.h>
3c501880
PP
36#include "../comedidev.h"
37#include <linux/ioport.h>
38
27020ffe
HS
39#include "comedi_fc.h"
40
3c501880
PP
41/* Board register addresses */
42
43#define DMM32AT_MEMSIZE 0x10
44
45#define DMM32AT_CONV 0x00
46#define DMM32AT_AILSB 0x00
47#define DMM32AT_AUXDOUT 0x01
48#define DMM32AT_AIMSB 0x01
49#define DMM32AT_AILOW 0x02
50#define DMM32AT_AIHIGH 0x03
51
52#define DMM32AT_DACLSB 0x04
53#define DMM32AT_DACSTAT 0x04
54#define DMM32AT_DACMSB 0x05
55
56#define DMM32AT_FIFOCNTRL 0x07
57#define DMM32AT_FIFOSTAT 0x07
58
59#define DMM32AT_CNTRL 0x08
60#define DMM32AT_AISTAT 0x08
61
62#define DMM32AT_INTCLOCK 0x09
63
64#define DMM32AT_CNTRDIO 0x0a
65
66#define DMM32AT_AICONF 0x0b
67#define DMM32AT_AIRBACK 0x0b
68
69#define DMM32AT_CLK1 0x0d
70#define DMM32AT_CLK2 0x0e
71#define DMM32AT_CLKCT 0x0f
72
73#define DMM32AT_DIOA 0x0c
74#define DMM32AT_DIOB 0x0d
75#define DMM32AT_DIOC 0x0e
76#define DMM32AT_DIOCONF 0x0f
77
3c501880
PP
78/* Board register values. */
79
80/* DMM32AT_DACSTAT 0x04 */
81#define DMM32AT_DACBUSY 0x80
82
83/* DMM32AT_FIFOCNTRL 0x07 */
84#define DMM32AT_FIFORESET 0x02
85#define DMM32AT_SCANENABLE 0x04
86
87/* DMM32AT_CNTRL 0x08 */
88#define DMM32AT_RESET 0x20
89#define DMM32AT_INTRESET 0x08
90#define DMM32AT_CLKACC 0x00
91#define DMM32AT_DIOACC 0x01
92
93/* DMM32AT_AISTAT 0x08 */
94#define DMM32AT_STATUS 0x80
95
96/* DMM32AT_INTCLOCK 0x09 */
97#define DMM32AT_ADINT 0x80
98#define DMM32AT_CLKSEL 0x03
99
100/* DMM32AT_CNTRDIO 0x0a */
101#define DMM32AT_FREQ12 0x80
102
103/* DMM32AT_AICONF 0x0b */
104#define DMM32AT_RANGE_U10 0x0c
105#define DMM32AT_RANGE_U5 0x0d
106#define DMM32AT_RANGE_B10 0x08
107#define DMM32AT_RANGE_B5 0x00
108#define DMM32AT_SCINT_20 0x00
109#define DMM32AT_SCINT_15 0x10
110#define DMM32AT_SCINT_10 0x20
111#define DMM32AT_SCINT_5 0x30
112
113/* DMM32AT_CLKCT 0x0f */
114#define DMM32AT_CLKCT1 0x56 /* mode3 counter 1 - write low byte only */
115#define DMM32AT_CLKCT2 0xb6 /* mode3 counter 2 - write high and low byte */
116
117/* DMM32AT_DIOCONF 0x0f */
118#define DMM32AT_DIENABLE 0x80
119#define DMM32AT_DIRA 0x10
120#define DMM32AT_DIRB 0x02
121#define DMM32AT_DIRCL 0x01
122#define DMM32AT_DIRCH 0x08
123
124/* board AI ranges in comedi structure */
9ced1de6 125static const struct comedi_lrange dmm32at_airanges = {
3c501880
PP
126 4,
127 {
0a85b6f0
MT
128 UNI_RANGE(10),
129 UNI_RANGE(5),
130 BIP_RANGE(10),
131 BIP_RANGE(5),
132 }
3c501880
PP
133};
134
135/* register values for above ranges */
136static const unsigned char dmm32at_rangebits[] = {
137 DMM32AT_RANGE_U10,
138 DMM32AT_RANGE_U5,
139 DMM32AT_RANGE_B10,
140 DMM32AT_RANGE_B5,
141};
142
143/* only one of these ranges is valid, as set by a jumper on the
144 * board. The application should only use the range set by the jumper
145 */
9ced1de6 146static const struct comedi_lrange dmm32at_aoranges = {
3c501880
PP
147 4,
148 {
0a85b6f0
MT
149 UNI_RANGE(10),
150 UNI_RANGE(5),
151 BIP_RANGE(10),
152 BIP_RANGE(5),
153 }
3c501880
PP
154};
155
39d31e09 156struct dmm32at_private {
3c501880
PP
157
158 int data;
159 int ai_inuse;
160 unsigned int ai_scans_left;
161
162 /* Used for AO readback */
790c5541 163 unsigned int ao_readback[4];
3c501880
PP
164 unsigned char dio_config;
165
39d31e09 166};
3c501880 167
0a85b6f0
MT
168static int dmm32at_ai_rinsn(struct comedi_device *dev,
169 struct comedi_subdevice *s,
170 struct comedi_insn *insn, unsigned int *data)
3c501880
PP
171{
172 int n, i;
173 unsigned int d;
174 unsigned char status;
175 unsigned short msb, lsb;
176 unsigned char chan;
177 int range;
178
179 /* get the channel and range number */
180
181 chan = CR_CHAN(insn->chanspec) & (s->n_chan - 1);
182 range = CR_RANGE(insn->chanspec);
183
2696fb57 184 /* printk("channel=0x%02x, range=%d\n",chan,range); */
3c501880
PP
185
186 /* zero scan and fifo control and reset fifo */
29f747c2 187 outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
3c501880
PP
188
189 /* write the ai channel range regs */
29f747c2
HS
190 outb(chan, dev->iobase + DMM32AT_AILOW);
191 outb(chan, dev->iobase + DMM32AT_AIHIGH);
3c501880 192 /* set the range bits */
29f747c2 193 outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
3c501880
PP
194
195 /* wait for circuit to settle */
196 for (i = 0; i < 40000; i++) {
99953ea1 197 status = inb(dev->iobase + DMM32AT_AIRBACK);
3c501880
PP
198 if ((status & DMM32AT_STATUS) == 0)
199 break;
200 }
201 if (i == 40000) {
27bf0bc9 202 printk(KERN_WARNING "dmm32at: timeout\n");
3c501880
PP
203 return -ETIMEDOUT;
204 }
205
206 /* convert n samples */
207 for (n = 0; n < insn->n; n++) {
208 /* trigger conversion */
29f747c2 209 outb(0xff, dev->iobase + DMM32AT_CONV);
3c501880
PP
210 /* wait for conversion to end */
211 for (i = 0; i < 40000; i++) {
99953ea1 212 status = inb(dev->iobase + DMM32AT_AISTAT);
3c501880
PP
213 if ((status & DMM32AT_STATUS) == 0)
214 break;
215 }
216 if (i == 40000) {
27bf0bc9 217 printk(KERN_WARNING "dmm32at: timeout\n");
3c501880
PP
218 return -ETIMEDOUT;
219 }
220
221 /* read data */
99953ea1
HS
222 lsb = inb(dev->iobase + DMM32AT_AILSB);
223 msb = inb(dev->iobase + DMM32AT_AIMSB);
3c501880
PP
224
225 /* invert sign bit to make range unsigned, this is an
25985edc 226 idiosyncrasy of the diamond board, it return
3c501880
PP
227 conversions as a signed value, i.e. -32768 to
228 32767, flipping the bit and interpreting it as
229 signed gives you a range of 0 to 65535 which is
230 used by comedi */
231 d = ((msb ^ 0x0080) << 8) + lsb;
232
233 data[n] = d;
234 }
235
236 /* return the number of samples read/written */
237 return n;
238}
239
47ae6a72
HS
240static int dmm32at_ns_to_timer(unsigned int *ns, int round)
241{
242 /* trivial timer */
47ae6a72
HS
243 return *ns;
244}
245
0a85b6f0
MT
246static int dmm32at_ai_cmdtest(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 struct comedi_cmd *cmd)
3c501880
PP
249{
250 int err = 0;
251 int tmp;
252 int start_chan, gain, i;
253
27020ffe 254 /* Step 1 : check if triggers are trivially valid */
3c501880 255
27020ffe
HS
256 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
257 err |= cfc_check_trigger_src(&cmd->scan_begin_src,
258 TRIG_TIMER /*| TRIG_EXT */);
259 err |= cfc_check_trigger_src(&cmd->convert_src,
260 TRIG_TIMER /*| TRIG_EXT */);
261 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
262 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
3c501880
PP
263
264 if (err)
265 return 1;
266
27020ffe 267 /* Step 2a : make sure trigger sources are unique */
3c501880 268
27020ffe
HS
269 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
270 err |= cfc_check_trigger_is_unique(cmd->convert_src);
271 err |= cfc_check_trigger_is_unique(cmd->stop_src);
272
273 /* Step 2b : and mutually compatible */
3c501880
PP
274
275 if (err)
276 return 2;
277
e43ed5fa
HS
278 /* Step 3: check if arguments are trivially valid */
279
280 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
3c501880 281
3c501880
PP
282#define MAX_SCAN_SPEED 1000000 /* in nanoseconds */
283#define MIN_SCAN_SPEED 1000000000 /* in nanoseconds */
284
285 if (cmd->scan_begin_src == TRIG_TIMER) {
e43ed5fa
HS
286 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
287 MAX_SCAN_SPEED);
288 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
289 MIN_SCAN_SPEED);
3c501880
PP
290 } else {
291 /* external trigger */
292 /* should be level/edge, hi/lo specification here */
293 /* should specify multiple external triggers */
e43ed5fa 294 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 9);
3c501880 295 }
e43ed5fa 296
3c501880
PP
297 if (cmd->convert_src == TRIG_TIMER) {
298 if (cmd->convert_arg >= 17500)
299 cmd->convert_arg = 20000;
300 else if (cmd->convert_arg >= 12500)
301 cmd->convert_arg = 15000;
302 else if (cmd->convert_arg >= 7500)
303 cmd->convert_arg = 10000;
304 else
305 cmd->convert_arg = 5000;
3c501880
PP
306 } else {
307 /* external trigger */
308 /* see above */
e43ed5fa 309 err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 9);
3c501880
PP
310 }
311
e43ed5fa
HS
312 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
313
3c501880 314 if (cmd->stop_src == TRIG_COUNT) {
e43ed5fa
HS
315 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0xfffffff0);
316 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
3c501880
PP
317 } else {
318 /* TRIG_NONE */
e43ed5fa 319 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
3c501880
PP
320 }
321
322 if (err)
323 return 3;
324
325 /* step 4: fix up any arguments */
326
327 if (cmd->scan_begin_src == TRIG_TIMER) {
328 tmp = cmd->scan_begin_arg;
329 dmm32at_ns_to_timer(&cmd->scan_begin_arg,
0a85b6f0 330 cmd->flags & TRIG_ROUND_MASK);
3c501880
PP
331 if (tmp != cmd->scan_begin_arg)
332 err++;
333 }
334 if (cmd->convert_src == TRIG_TIMER) {
335 tmp = cmd->convert_arg;
336 dmm32at_ns_to_timer(&cmd->convert_arg,
0a85b6f0 337 cmd->flags & TRIG_ROUND_MASK);
3c501880
PP
338 if (tmp != cmd->convert_arg)
339 err++;
340 if (cmd->scan_begin_src == TRIG_TIMER &&
0a85b6f0
MT
341 cmd->scan_begin_arg <
342 cmd->convert_arg * cmd->scan_end_arg) {
3c501880 343 cmd->scan_begin_arg =
0a85b6f0 344 cmd->convert_arg * cmd->scan_end_arg;
3c501880
PP
345 err++;
346 }
347 }
348
349 if (err)
350 return 4;
351
352 /* step 5 check the channel list, the channel list for this
353 board must be consecutive and gains must be the same */
354
355 if (cmd->chanlist) {
356 gain = CR_RANGE(cmd->chanlist[0]);
357 start_chan = CR_CHAN(cmd->chanlist[0]);
358 for (i = 1; i < cmd->chanlist_len; i++) {
359 if (CR_CHAN(cmd->chanlist[i]) !=
0a85b6f0 360 (start_chan + i) % s->n_chan) {
3c501880 361 comedi_error(dev,
0a85b6f0 362 "entries in chanlist must be consecutive channels, counting upwards\n");
3c501880
PP
363 err++;
364 }
365 if (CR_RANGE(cmd->chanlist[i]) != gain) {
366 comedi_error(dev,
0a85b6f0 367 "entries in chanlist must all have the same gain\n");
3c501880
PP
368 err++;
369 }
370 }
371 }
372
373 if (err)
374 return 5;
375
376 return 0;
377}
378
47ae6a72
HS
379static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
380{
381 unsigned char lo1, lo2, hi2;
382 unsigned short both2;
383
384 /* based on 10mhz clock */
385 lo1 = 200;
386 both2 = nansec / 20000;
387 hi2 = (both2 & 0xff00) >> 8;
388 lo2 = both2 & 0x00ff;
389
390 /* set the counter frequency to 10mhz */
29f747c2 391 outb(0, dev->iobase + DMM32AT_CNTRDIO);
47ae6a72
HS
392
393 /* get access to the clock regs */
29f747c2 394 outb(DMM32AT_CLKACC, dev->iobase + DMM32AT_CNTRL);
47ae6a72
HS
395
396 /* write the counter 1 control word and low byte to counter */
29f747c2
HS
397 outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
398 outb(lo1, dev->iobase + DMM32AT_CLK1);
47ae6a72
HS
399
400 /* write the counter 2 control word and low byte then to counter */
29f747c2
HS
401 outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
402 outb(lo2, dev->iobase + DMM32AT_CLK2);
403 outb(hi2, dev->iobase + DMM32AT_CLK2);
47ae6a72
HS
404
405 /* enable the ai conversion interrupt and the clock to start scans */
29f747c2 406 outb(DMM32AT_ADINT | DMM32AT_CLKSEL, dev->iobase + DMM32AT_INTCLOCK);
47ae6a72
HS
407}
408
da91b269 409static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
3c501880 410{
3eff0174 411 struct dmm32at_private *devpriv = dev->private;
ea6d0d4c 412 struct comedi_cmd *cmd = &s->async->cmd;
3c501880
PP
413 int i, range;
414 unsigned char chanlo, chanhi, status;
415
416 if (!cmd->chanlist)
417 return -EINVAL;
418
419 /* get the channel list and range */
420 chanlo = CR_CHAN(cmd->chanlist[0]) & (s->n_chan - 1);
421 chanhi = chanlo + cmd->chanlist_len - 1;
422 if (chanhi >= s->n_chan)
423 return -EINVAL;
424 range = CR_RANGE(cmd->chanlist[0]);
425
426 /* reset fifo */
29f747c2 427 outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
3c501880
PP
428
429 /* set scan enable */
29f747c2 430 outb(DMM32AT_SCANENABLE, dev->iobase + DMM32AT_FIFOCNTRL);
3c501880
PP
431
432 /* write the ai channel range regs */
29f747c2
HS
433 outb(chanlo, dev->iobase + DMM32AT_AILOW);
434 outb(chanhi, dev->iobase + DMM32AT_AIHIGH);
3c501880
PP
435
436 /* set the range bits */
29f747c2 437 outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
3c501880
PP
438
439 /* reset the interrupt just in case */
29f747c2 440 outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
3c501880
PP
441
442 if (cmd->stop_src == TRIG_COUNT)
443 devpriv->ai_scans_left = cmd->stop_arg;
444 else { /* TRIG_NONE */
27bf0bc9
M
445 devpriv->ai_scans_left = 0xffffffff; /* indicates TRIG_NONE to
446 * isr */
3c501880
PP
447 }
448
449 /* wait for circuit to settle */
450 for (i = 0; i < 40000; i++) {
99953ea1 451 status = inb(dev->iobase + DMM32AT_AIRBACK);
3c501880
PP
452 if ((status & DMM32AT_STATUS) == 0)
453 break;
454 }
455 if (i == 40000) {
27bf0bc9 456 printk(KERN_WARNING "dmm32at: timeout\n");
3c501880
PP
457 return -ETIMEDOUT;
458 }
459
460 if (devpriv->ai_scans_left > 1) {
461 /* start the clock and enable the interrupts */
462 dmm32at_setaitimer(dev, cmd->scan_begin_arg);
463 } else {
464 /* start the interrups and initiate a single scan */
29f747c2
HS
465 outb(DMM32AT_ADINT, dev->iobase + DMM32AT_INTCLOCK);
466 outb(0xff, dev->iobase + DMM32AT_CONV);
3c501880
PP
467 }
468
27bf0bc9 469/* printk("dmmat32 in command\n"); */
3c501880 470
27bf0bc9
M
471/* for(i=0;i<cmd->chanlist_len;i++) */
472/* comedi_buf_put(s->async,i*100); */
3c501880 473
27bf0bc9
M
474/* s->async->events |= COMEDI_CB_EOA; */
475/* comedi_event(dev, s); */
3c501880
PP
476
477 return 0;
478
479}
480
0a85b6f0
MT
481static int dmm32at_ai_cancel(struct comedi_device *dev,
482 struct comedi_subdevice *s)
3c501880 483{
3eff0174
HS
484 struct dmm32at_private *devpriv = dev->private;
485
3c501880
PP
486 devpriv->ai_scans_left = 1;
487 return 0;
488}
489
70265d24 490static irqreturn_t dmm32at_isr(int irq, void *d)
3c501880 491{
3eff0174
HS
492 struct comedi_device *dev = d;
493 struct dmm32at_private *devpriv = dev->private;
3c501880
PP
494 unsigned char intstat;
495 unsigned int samp;
496 unsigned short msb, lsb;
497 int i;
3c501880
PP
498
499 if (!dev->attached) {
500 comedi_error(dev, "spurious interrupt");
501 return IRQ_HANDLED;
502 }
503
99953ea1 504 intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
3c501880
PP
505
506 if (intstat & DMM32AT_ADINT) {
34c43922 507 struct comedi_subdevice *s = dev->read_subdev;
ea6d0d4c 508 struct comedi_cmd *cmd = &s->async->cmd;
3c501880
PP
509
510 for (i = 0; i < cmd->chanlist_len; i++) {
511 /* read data */
99953ea1
HS
512 lsb = inb(dev->iobase + DMM32AT_AILSB);
513 msb = inb(dev->iobase + DMM32AT_AIMSB);
3c501880
PP
514
515 /* invert sign bit to make range unsigned */
516 samp = ((msb ^ 0x0080) << 8) + lsb;
517 comedi_buf_put(s->async, samp);
518 }
519
520 if (devpriv->ai_scans_left != 0xffffffff) { /* TRIG_COUNT */
521 devpriv->ai_scans_left--;
522 if (devpriv->ai_scans_left == 0) {
523 /* disable further interrupts and clocks */
29f747c2 524 outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
3c501880
PP
525 /* set the buffer to be flushed with an EOF */
526 s->async->events |= COMEDI_CB_EOA;
527 }
528
529 }
530 /* flush the buffer */
531 comedi_event(dev, s);
532 }
533
534 /* reset the interrupt */
29f747c2 535 outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
3c501880
PP
536 return IRQ_HANDLED;
537}
538
0a85b6f0
MT
539static int dmm32at_ao_winsn(struct comedi_device *dev,
540 struct comedi_subdevice *s,
541 struct comedi_insn *insn, unsigned int *data)
3c501880 542{
3eff0174 543 struct dmm32at_private *devpriv = dev->private;
3c501880
PP
544 int i;
545 int chan = CR_CHAN(insn->chanspec);
546 unsigned char hi, lo, status;
547
548 /* Writing a list of values to an AO channel is probably not
549 * very useful, but that's how the interface is defined. */
550 for (i = 0; i < insn->n; i++) {
551
552 devpriv->ao_readback[chan] = data[i];
553
554 /* get the low byte */
555 lo = data[i] & 0x00ff;
556 /* high byte also contains channel number */
557 hi = (data[i] >> 8) + chan * (1 << 6);
2696fb57 558 /* printk("writing 0x%02x 0x%02x\n",hi,lo); */
3c501880 559 /* write the low and high values to the board */
29f747c2
HS
560 outb(lo, dev->iobase + DMM32AT_DACLSB);
561 outb(hi, dev->iobase + DMM32AT_DACMSB);
3c501880
PP
562
563 /* wait for circuit to settle */
564 for (i = 0; i < 40000; i++) {
99953ea1 565 status = inb(dev->iobase + DMM32AT_DACSTAT);
3c501880
PP
566 if ((status & DMM32AT_DACBUSY) == 0)
567 break;
568 }
569 if (i == 40000) {
27bf0bc9 570 printk(KERN_WARNING "dmm32at: timeout\n");
3c501880
PP
571 return -ETIMEDOUT;
572 }
573 /* dummy read to update trigger the output */
99953ea1 574 status = inb(dev->iobase + DMM32AT_DACMSB);
3c501880
PP
575
576 }
577
578 /* return the number of samples read/written */
579 return i;
580}
581
0a85b6f0
MT
582static int dmm32at_ao_rinsn(struct comedi_device *dev,
583 struct comedi_subdevice *s,
584 struct comedi_insn *insn, unsigned int *data)
3c501880 585{
3eff0174 586 struct dmm32at_private *devpriv = dev->private;
3c501880
PP
587 int i;
588 int chan = CR_CHAN(insn->chanspec);
589
590 for (i = 0; i < insn->n; i++)
591 data[i] = devpriv->ao_readback[chan];
592
593 return i;
594}
595
0a85b6f0
MT
596static int dmm32at_dio_insn_bits(struct comedi_device *dev,
597 struct comedi_subdevice *s,
598 struct comedi_insn *insn, unsigned int *data)
3c501880 599{
3eff0174 600 struct dmm32at_private *devpriv = dev->private;
3c501880
PP
601 unsigned char diobits;
602
3c501880
PP
603 /* The insn data is a mask in data[0] and the new data
604 * in data[1], each channel cooresponding to a bit. */
605 if (data[0]) {
606 s->state &= ~data[0];
607 s->state |= data[0] & data[1];
608 /* Write out the new digital output lines */
2696fb57 609 /* outw(s->state,dev->iobase + DMM32AT_DIO); */
3c501880
PP
610 }
611
612 /* get access to the DIO regs */
29f747c2 613 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
3c501880
PP
614
615 /* if either part of dio is set for output */
616 if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) ||
0a85b6f0 617 ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) {
3c501880 618 diobits = (s->state & 0x00ff0000) >> 16;
29f747c2 619 outb(diobits, dev->iobase + DMM32AT_DIOC);
3c501880
PP
620 }
621 if ((devpriv->dio_config & DMM32AT_DIRB) == 0) {
622 diobits = (s->state & 0x0000ff00) >> 8;
29f747c2 623 outb(diobits, dev->iobase + DMM32AT_DIOB);
3c501880
PP
624 }
625 if ((devpriv->dio_config & DMM32AT_DIRA) == 0) {
626 diobits = (s->state & 0x000000ff);
29f747c2 627 outb(diobits, dev->iobase + DMM32AT_DIOA);
3c501880
PP
628 }
629
630 /* now read the state back in */
99953ea1 631 s->state = inb(dev->iobase + DMM32AT_DIOC);
3c501880 632 s->state <<= 8;
99953ea1 633 s->state |= inb(dev->iobase + DMM32AT_DIOB);
3c501880 634 s->state <<= 8;
99953ea1 635 s->state |= inb(dev->iobase + DMM32AT_DIOA);
3c501880
PP
636 data[1] = s->state;
637
638 /* on return, data[1] contains the value of the digital
639 * input and output lines. */
2696fb57 640 /* data[1]=inw(dev->iobase + DMM32AT_DIO); */
3c501880
PP
641 /* or we could just return the software copy of the output values if
642 * it was a purely digital output subdevice */
2696fb57 643 /* data[1]=s->state; */
3c501880 644
a2714e3e 645 return insn->n;
3c501880
PP
646}
647
0a85b6f0
MT
648static int dmm32at_dio_insn_config(struct comedi_device *dev,
649 struct comedi_subdevice *s,
650 struct comedi_insn *insn, unsigned int *data)
3c501880 651{
3eff0174 652 struct dmm32at_private *devpriv = dev->private;
3c501880
PP
653 unsigned char chanbit;
654 int chan = CR_CHAN(insn->chanspec);
655
656 if (insn->n != 1)
657 return -EINVAL;
658
659 if (chan < 8)
660 chanbit = DMM32AT_DIRA;
661 else if (chan < 16)
662 chanbit = DMM32AT_DIRB;
663 else if (chan < 20)
664 chanbit = DMM32AT_DIRCL;
665 else
666 chanbit = DMM32AT_DIRCH;
667
668 /* The input or output configuration of each digital line is
669 * configured by a special insn_config instruction. chanspec
670 * contains the channel to be changed, and data[0] contains the
671 * value COMEDI_INPUT or COMEDI_OUTPUT. */
672
673 /* if output clear the bit, otherwise set it */
20962c10 674 if (data[0] == COMEDI_OUTPUT)
3c501880 675 devpriv->dio_config &= ~chanbit;
20962c10 676 else
3c501880 677 devpriv->dio_config |= chanbit;
3c501880 678 /* get access to the DIO regs */
29f747c2 679 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
3c501880 680 /* set the DIO's to the new configuration setting */
29f747c2 681 outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
3c501880
PP
682
683 return 1;
684}
685
4f793db3
HS
686static int dmm32at_attach(struct comedi_device *dev,
687 struct comedi_devconfig *it)
688{
3eff0174 689 struct dmm32at_private *devpriv;
4f793db3
HS
690 int ret;
691 struct comedi_subdevice *s;
692 unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
4f793db3
HS
693 unsigned int irq;
694
4f793db3
HS
695 irq = it->options[1];
696
2dd11a81
HS
697 ret = comedi_request_region(dev, it->options[0], DMM32AT_MEMSIZE);
698 if (ret)
699 return ret;
4f793db3
HS
700
701 /* the following just makes sure the board is there and gets
702 it to a known state */
703
704 /* reset the board */
29f747c2 705 outb(DMM32AT_RESET, dev->iobase + DMM32AT_CNTRL);
4f793db3
HS
706
707 /* allow a millisecond to reset */
708 udelay(1000);
709
710 /* zero scan and fifo control */
29f747c2 711 outb(0x0, dev->iobase + DMM32AT_FIFOCNTRL);
4f793db3
HS
712
713 /* zero interrupt and clock control */
29f747c2 714 outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
4f793db3
HS
715
716 /* write a test channel range, the high 3 bits should drop */
29f747c2
HS
717 outb(0x80, dev->iobase + DMM32AT_AILOW);
718 outb(0xff, dev->iobase + DMM32AT_AIHIGH);
4f793db3
HS
719
720 /* set the range at 10v unipolar */
29f747c2 721 outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AICONF);
4f793db3
HS
722
723 /* should take 10 us to settle, here's a hundred */
724 udelay(100);
725
726 /* read back the values */
99953ea1
HS
727 ailo = inb(dev->iobase + DMM32AT_AILOW);
728 aihi = inb(dev->iobase + DMM32AT_AIHIGH);
729 fifostat = inb(dev->iobase + DMM32AT_FIFOSTAT);
730 aistat = inb(dev->iobase + DMM32AT_AISTAT);
731 intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
732 airback = inb(dev->iobase + DMM32AT_AIRBACK);
4f793db3
HS
733
734 printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
735 ailo, aihi, fifostat);
736 printk(KERN_DEBUG
737 "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
738 aistat, intstat, airback);
739
740 if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
741 (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
742 printk(KERN_ERR "dmmat32: board detection failed\n");
743 return -EIO;
744 }
745
746 /* board is there, register interrupt */
747 if (irq) {
4e9cd213 748 ret = request_irq(irq, dmm32at_isr, 0, dev->board_name, dev);
4f793db3
HS
749 if (ret < 0) {
750 printk(KERN_ERR "dmm32at: irq conflict\n");
751 return ret;
752 }
753 dev->irq = irq;
754 }
755
c34fa261
HS
756 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
757 if (!devpriv)
758 return -ENOMEM;
759 dev->private = devpriv;
4f793db3
HS
760
761 ret = comedi_alloc_subdevices(dev, 3);
762 if (ret)
763 return ret;
764
2930d0ba 765 s = &dev->subdevices[0];
4f793db3
HS
766 dev->read_subdev = s;
767 /* analog input subdevice */
768 s->type = COMEDI_SUBD_AI;
769 /* we support single-ended (ground) and differential */
770 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
27921828
HS
771 s->n_chan = 32;
772 s->maxdata = 0xffff;
773 s->range_table = &dmm32at_airanges;
4f793db3
HS
774 s->len_chanlist = 32; /* This is the maximum chanlist length that
775 the board can handle */
776 s->insn_read = dmm32at_ai_rinsn;
777 s->do_cmd = dmm32at_ai_cmd;
778 s->do_cmdtest = dmm32at_ai_cmdtest;
779 s->cancel = dmm32at_ai_cancel;
780
2930d0ba 781 s = &dev->subdevices[1];
4f793db3
HS
782 /* analog output subdevice */
783 s->type = COMEDI_SUBD_AO;
784 s->subdev_flags = SDF_WRITABLE;
27921828
HS
785 s->n_chan = 4;
786 s->maxdata = 0x0fff;
787 s->range_table = &dmm32at_aoranges;
4f793db3
HS
788 s->insn_write = dmm32at_ao_winsn;
789 s->insn_read = dmm32at_ao_rinsn;
790
2930d0ba 791 s = &dev->subdevices[2];
4f793db3 792 /* digital i/o subdevice */
27921828
HS
793
794 /* get access to the DIO regs */
795 outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
796 /* set the DIO's to the defualt input setting */
797 devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
798 DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
799 outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
800
801 /* set up the subdevice */
802 s->type = COMEDI_SUBD_DIO;
803 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
804 s->n_chan = 24;
805 s->maxdata = 1;
806 s->state = 0;
807 s->range_table = &range_digital;
808 s->insn_bits = dmm32at_dio_insn_bits;
809 s->insn_config = dmm32at_dio_insn_config;
4f793db3
HS
810
811 /* success */
812 printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor);
813
814 return 1;
815
816}
817
17f49dd4
HS
818static struct comedi_driver dmm32at_driver = {
819 .driver_name = "dmm32at",
820 .module = THIS_MODULE,
821 .attach = dmm32at_attach,
3d1fe3f7 822 .detach = comedi_legacy_detach,
17f49dd4
HS
823};
824module_comedi_driver(dmm32at_driver);
90f703d3
AT
825
826MODULE_AUTHOR("Comedi http://www.comedi.org");
827MODULE_DESCRIPTION("Comedi low-level driver");
828MODULE_LICENSE("GPL");
This page took 0.456025 seconds and 5 git commands to generate.