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