2 comedi/drivers/dt3000.c
3 Data Translation DT3000 series driver
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 David A. Schleef <ds@schleef.org>
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.
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.
20 Description: Data Translation DT3000 series
22 Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
23 DT3003-PGL, DT3004, DT3005, DT3004-200
24 Updated: Mon, 14 Apr 2008 15:41:24 +0100
27 Configuration Options: not applicable, uses PCI auto config
29 There is code to support AI commands, but it may not work.
31 AO commands are not supported.
35 The DT3000 series is Data Translation's attempt to make a PCI
36 data acquisition board. The design of this series is very nice,
37 since each board has an on-board DSP (Texas Instruments TMS320C52).
38 However, a few details are a little annoying. The boards lack
39 bus-mastering DMA, which eliminates them from serious work.
40 They also are not capable of autocalibration, which is a common
41 feature in modern hardware. The default firmware is pretty bad,
42 making it nearly impossible to write an RT compatible driver.
43 It would make an interesting project to write a decent firmware
46 Data Translation originally wanted an NDA for the documentation
47 for the 3k series. However, if you ask nicely, they might send
48 you the docs without one, also.
51 #include <linux/module.h>
52 #include <linux/delay.h>
53 #include <linux/interrupt.h>
55 #include "../comedi_pci.h"
57 static const struct comedi_lrange range_dt3000_ai = {
66 static const struct comedi_lrange range_dt3000_ai_pgl = {
85 struct dt3k_boardtype {
90 const struct comedi_lrange *adrange;
95 static const struct dt3k_boardtype dt3k_boardtypes[] = {
100 .adrange = &range_dt3000_ai,
105 [BOARD_DT3001_PGL] = {
106 .name = "dt3001-pgl",
109 .adrange = &range_dt3000_ai_pgl,
118 .adrange = &range_dt3000_ai,
125 .adrange = &range_dt3000_ai,
130 [BOARD_DT3003_PGL] = {
131 .name = "dt3003-pgl",
134 .adrange = &range_dt3000_ai_pgl,
143 .adrange = &range_dt3000_ai,
149 .name = "dt3005", /* a.k.a. 3004-200 */
152 .adrange = &range_dt3000_ai,
159 /* dual-ported RAM location definitions */
161 #define DPR_DAC_buffer (4*0x000)
162 #define DPR_ADC_buffer (4*0x800)
163 #define DPR_Command (4*0xfd3)
164 #define DPR_SubSys (4*0xfd3)
165 #define DPR_Encode (4*0xfd4)
166 #define DPR_Params(a) (4*(0xfd5+(a)))
167 #define DPR_Tick_Reg_Lo (4*0xff5)
168 #define DPR_Tick_Reg_Hi (4*0xff6)
169 #define DPR_DA_Buf_Front (4*0xff7)
170 #define DPR_DA_Buf_Rear (4*0xff8)
171 #define DPR_AD_Buf_Front (4*0xff9)
172 #define DPR_AD_Buf_Rear (4*0xffa)
173 #define DPR_Int_Mask (4*0xffb)
174 #define DPR_Intr_Flag (4*0xffc)
175 #define DPR_Response_Mbx (4*0xffe)
176 #define DPR_Command_Mbx (4*0xfff)
178 #define AI_FIFO_DEPTH 2003
179 #define AO_FIFO_DEPTH 2048
183 #define CMD_GETBRDINFO 0
185 #define CMD_GETCONFIG 2
188 #define CMD_READSINGLE 5
189 #define CMD_WRITESINGLE 6
190 #define CMD_CALCCLOCK 7
191 #define CMD_READEVENTS 8
192 #define CMD_WRITECTCTRL 16
193 #define CMD_READCTCTRL 17
194 #define CMD_WRITECT 18
195 #define CMD_READCT 19
196 #define CMD_WRITEDATA 32
197 #define CMD_READDATA 33
198 #define CMD_WRITEIO 34
199 #define CMD_READIO 35
200 #define CMD_WRITECODE 36
201 #define CMD_READCODE 37
202 #define CMD_EXECUTE 38
212 /* interrupt flags */
213 #define DT3000_CMDONE 0x80
214 #define DT3000_CTDONE 0x40
215 #define DT3000_DAHWERR 0x20
216 #define DT3000_DASWERR 0x10
217 #define DT3000_DAEMPTY 0x08
218 #define DT3000_ADHWERR 0x04
219 #define DT3000_ADSWERR 0x02
220 #define DT3000_ADFULL 0x01
222 #define DT3000_COMPLETION_MASK 0xff00
223 #define DT3000_COMMAND_MASK 0x00ff
224 #define DT3000_NOTPROCESSED 0x0000
225 #define DT3000_NOERROR 0x5500
226 #define DT3000_ERROR 0xaa00
227 #define DT3000_NOTSUPPORTED 0xff00
229 #define DT3000_EXTERNAL_CLOCK 1
230 #define DT3000_RISING_EDGE 2
232 #define TMODE_MASK 0x1c
234 #define DT3000_AD_TRIG_INTERNAL (0<<2)
235 #define DT3000_AD_TRIG_EXTERNAL (1<<2)
236 #define DT3000_AD_RETRIG_INTERNAL (2<<2)
237 #define DT3000_AD_RETRIG_EXTERNAL (3<<2)
238 #define DT3000_AD_EXTRETRIG (4<<2)
240 #define DT3000_CHANNEL_MODE_SE 0
241 #define DT3000_CHANNEL_MODE_DI 1
243 struct dt3k_private {
245 unsigned int ai_front;
246 unsigned int ai_rear;
251 static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
254 unsigned int status = 0;
256 writew(cmd, dev->mmio + DPR_Command_Mbx);
258 for (i = 0; i < TIMEOUT; i++) {
259 status = readw(dev->mmio + DPR_Command_Mbx);
260 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
265 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR)
266 dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
270 static unsigned int dt3k_readsingle(struct comedi_device *dev,
271 unsigned int subsys, unsigned int chan,
274 writew(subsys, dev->mmio + DPR_SubSys);
276 writew(chan, dev->mmio + DPR_Params(0));
277 writew(gain, dev->mmio + DPR_Params(1));
279 dt3k_send_cmd(dev, CMD_READSINGLE);
281 return readw(dev->mmio + DPR_Params(2));
284 static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
285 unsigned int chan, unsigned int data)
287 writew(subsys, dev->mmio + DPR_SubSys);
289 writew(chan, dev->mmio + DPR_Params(0));
290 writew(0, dev->mmio + DPR_Params(1));
291 writew(data, dev->mmio + DPR_Params(2));
293 dt3k_send_cmd(dev, CMD_WRITESINGLE);
296 static void dt3k_ai_empty_fifo(struct comedi_device *dev,
297 struct comedi_subdevice *s)
299 struct dt3k_private *devpriv = dev->private;
306 front = readw(dev->mmio + DPR_AD_Buf_Front);
307 count = front - devpriv->ai_front;
309 count += AI_FIFO_DEPTH;
311 rear = devpriv->ai_rear;
313 for (i = 0; i < count; i++) {
314 data = readw(dev->mmio + DPR_ADC_buffer + rear);
315 comedi_buf_write_samples(s, &data, 1);
317 if (rear >= AI_FIFO_DEPTH)
321 devpriv->ai_rear = rear;
322 writew(rear, dev->mmio + DPR_AD_Buf_Rear);
325 static int dt3k_ai_cancel(struct comedi_device *dev,
326 struct comedi_subdevice *s)
328 writew(SUBS_AI, dev->mmio + DPR_SubSys);
329 dt3k_send_cmd(dev, CMD_STOP);
331 writew(0, dev->mmio + DPR_Int_Mask);
336 static int debug_n_ints;
338 /* FIXME! Assumes shared interrupt is for this card. */
339 /* What's this debug_n_ints stuff? Obviously needs some work... */
340 static irqreturn_t dt3k_interrupt(int irq, void *d)
342 struct comedi_device *dev = d;
343 struct comedi_subdevice *s = dev->read_subdev;
349 status = readw(dev->mmio + DPR_Intr_Flag);
351 if (status & DT3000_ADFULL)
352 dt3k_ai_empty_fifo(dev, s);
354 if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
355 s->async->events |= COMEDI_CB_ERROR;
358 if (debug_n_ints >= 10)
359 s->async->events |= COMEDI_CB_EOA;
361 comedi_handle_events(dev, s);
365 static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
368 int divider, base, prescale;
370 /* This function needs improvment */
371 /* Don't know if divider==0 works. */
373 for (prescale = 0; prescale < 16; prescale++) {
374 base = timer_base * (prescale + 1);
375 switch (flags & CMDF_ROUND_MASK) {
376 case CMDF_ROUND_NEAREST:
378 divider = (*nanosec + base / 2) / base;
380 case CMDF_ROUND_DOWN:
381 divider = (*nanosec) / base;
384 divider = (*nanosec) / base;
387 if (divider < 65536) {
388 *nanosec = divider * base;
389 return (prescale << 16) | (divider);
394 base = timer_base * (1 << prescale);
396 *nanosec = divider * base;
397 return (prescale << 16) | (divider);
400 static int dt3k_ai_cmdtest(struct comedi_device *dev,
401 struct comedi_subdevice *s, struct comedi_cmd *cmd)
403 const struct dt3k_boardtype *board = dev->board_ptr;
407 /* Step 1 : check if triggers are trivially valid */
409 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
410 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
411 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
412 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
413 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
418 /* Step 2a : make sure trigger sources are unique */
419 /* Step 2b : and mutually compatible */
421 /* Step 3: check if arguments are trivially valid */
423 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
425 if (cmd->scan_begin_src == TRIG_TIMER) {
426 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
428 err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
432 if (cmd->convert_src == TRIG_TIMER) {
433 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
435 err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
439 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
442 if (cmd->stop_src == TRIG_COUNT)
443 err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
445 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
450 /* step 4: fix up any arguments */
452 if (cmd->scan_begin_src == TRIG_TIMER) {
453 arg = cmd->scan_begin_arg;
454 dt3k_ns_to_timer(100, &arg, cmd->flags);
455 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
458 if (cmd->convert_src == TRIG_TIMER) {
459 arg = cmd->convert_arg;
460 dt3k_ns_to_timer(50, &arg, cmd->flags);
461 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
463 if (cmd->scan_begin_src == TRIG_TIMER) {
464 arg = cmd->convert_arg * cmd->scan_end_arg;
465 err |= comedi_check_trigger_arg_min(&cmd->
477 static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
479 struct comedi_cmd *cmd = &s->async->cmd;
481 unsigned int chan, range, aref;
482 unsigned int divider;
483 unsigned int tscandiv;
485 for (i = 0; i < cmd->chanlist_len; i++) {
486 chan = CR_CHAN(cmd->chanlist[i]);
487 range = CR_RANGE(cmd->chanlist[i]);
489 writew((range << 6) | chan, dev->mmio + DPR_ADC_buffer + i);
491 aref = CR_AREF(cmd->chanlist[0]);
493 writew(cmd->scan_end_arg, dev->mmio + DPR_Params(0));
495 if (cmd->convert_src == TRIG_TIMER) {
496 divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
497 writew((divider >> 16), dev->mmio + DPR_Params(1));
498 writew((divider & 0xffff), dev->mmio + DPR_Params(2));
501 if (cmd->scan_begin_src == TRIG_TIMER) {
502 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
504 writew((tscandiv >> 16), dev->mmio + DPR_Params(3));
505 writew((tscandiv & 0xffff), dev->mmio + DPR_Params(4));
508 writew(DT3000_AD_RETRIG_INTERNAL, dev->mmio + DPR_Params(5));
509 writew(aref == AREF_DIFF, dev->mmio + DPR_Params(6));
511 writew(AI_FIFO_DEPTH / 2, dev->mmio + DPR_Params(7));
513 writew(SUBS_AI, dev->mmio + DPR_SubSys);
514 dt3k_send_cmd(dev, CMD_CONFIG);
516 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
517 dev->mmio + DPR_Int_Mask);
521 writew(SUBS_AI, dev->mmio + DPR_SubSys);
522 dt3k_send_cmd(dev, CMD_START);
527 static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
528 struct comedi_insn *insn, unsigned int *data)
531 unsigned int chan, gain, aref;
533 chan = CR_CHAN(insn->chanspec);
534 gain = CR_RANGE(insn->chanspec);
535 /* XXX docs don't explain how to select aref */
536 aref = CR_AREF(insn->chanspec);
538 for (i = 0; i < insn->n; i++)
539 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
544 static int dt3k_ao_insn_write(struct comedi_device *dev,
545 struct comedi_subdevice *s,
546 struct comedi_insn *insn,
549 unsigned int chan = CR_CHAN(insn->chanspec);
550 unsigned int val = s->readback[chan];
553 for (i = 0; i < insn->n; i++) {
555 dt3k_writesingle(dev, SUBS_AO, chan, val);
557 s->readback[chan] = val;
562 static void dt3k_dio_config(struct comedi_device *dev, int bits)
565 writew(SUBS_DOUT, dev->mmio + DPR_SubSys);
567 writew(bits, dev->mmio + DPR_Params(0));
570 writew(0, dev->mmio + DPR_Params(1));
571 writew(0, dev->mmio + DPR_Params(2));
574 dt3k_send_cmd(dev, CMD_CONFIG);
577 static int dt3k_dio_insn_config(struct comedi_device *dev,
578 struct comedi_subdevice *s,
579 struct comedi_insn *insn,
582 unsigned int chan = CR_CHAN(insn->chanspec);
591 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
595 dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
600 static int dt3k_dio_insn_bits(struct comedi_device *dev,
601 struct comedi_subdevice *s,
602 struct comedi_insn *insn,
605 if (comedi_dio_update_state(s, data))
606 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
608 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
613 static int dt3k_mem_insn_read(struct comedi_device *dev,
614 struct comedi_subdevice *s,
615 struct comedi_insn *insn,
618 unsigned int addr = CR_CHAN(insn->chanspec);
621 for (i = 0; i < insn->n; i++) {
622 writew(SUBS_MEM, dev->mmio + DPR_SubSys);
623 writew(addr, dev->mmio + DPR_Params(0));
624 writew(1, dev->mmio + DPR_Params(1));
626 dt3k_send_cmd(dev, CMD_READCODE);
628 data[i] = readw(dev->mmio + DPR_Params(2));
634 static int dt3000_auto_attach(struct comedi_device *dev,
635 unsigned long context)
637 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
638 const struct dt3k_boardtype *board = NULL;
639 struct dt3k_private *devpriv;
640 struct comedi_subdevice *s;
643 if (context < ARRAY_SIZE(dt3k_boardtypes))
644 board = &dt3k_boardtypes[context];
647 dev->board_ptr = board;
648 dev->board_name = board->name;
650 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
654 ret = comedi_pci_enable(dev);
658 dev->mmio = pci_ioremap_bar(pcidev, 0);
663 ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
664 dev->board_name, dev);
666 dev->irq = pcidev->irq;
669 ret = comedi_alloc_subdevices(dev, 4);
673 s = &dev->subdevices[0];
675 s->type = COMEDI_SUBD_AI;
676 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
677 s->n_chan = board->adchan;
678 s->insn_read = dt3k_ai_insn;
679 s->maxdata = (1 << board->adbits) - 1;
680 s->range_table = &range_dt3000_ai; /* XXX */
682 dev->read_subdev = s;
683 s->subdev_flags |= SDF_CMD_READ;
684 s->len_chanlist = 512;
685 s->do_cmd = dt3k_ai_cmd;
686 s->do_cmdtest = dt3k_ai_cmdtest;
687 s->cancel = dt3k_ai_cancel;
690 s = &dev->subdevices[1];
692 s->type = COMEDI_SUBD_AO;
693 s->subdev_flags = SDF_WRITABLE;
695 s->maxdata = (1 << board->dabits) - 1;
697 s->range_table = &range_bipolar10;
698 s->insn_write = dt3k_ao_insn_write;
700 ret = comedi_alloc_subdev_readback(s);
704 s = &dev->subdevices[2];
706 s->type = COMEDI_SUBD_DIO;
707 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
709 s->insn_config = dt3k_dio_insn_config;
710 s->insn_bits = dt3k_dio_insn_bits;
713 s->range_table = &range_digital;
715 s = &dev->subdevices[3];
717 s->type = COMEDI_SUBD_MEMORY;
718 s->subdev_flags = SDF_READABLE;
720 s->insn_read = dt3k_mem_insn_read;
723 s->range_table = &range_unknown;
726 s = &dev->subdevices[4];
728 s->type = COMEDI_SUBD_PROC;
734 static struct comedi_driver dt3000_driver = {
735 .driver_name = "dt3000",
736 .module = THIS_MODULE,
737 .auto_attach = dt3000_auto_attach,
738 .detach = comedi_pci_detach,
741 static int dt3000_pci_probe(struct pci_dev *dev,
742 const struct pci_device_id *id)
744 return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
747 static const struct pci_device_id dt3000_pci_table[] = {
748 { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
749 { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
750 { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
751 { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
752 { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
753 { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
754 { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
757 MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
759 static struct pci_driver dt3000_pci_driver = {
761 .id_table = dt3000_pci_table,
762 .probe = dt3000_pci_probe,
763 .remove = comedi_pci_auto_unconfig,
765 module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
767 MODULE_AUTHOR("Comedi http://www.comedi.org");
768 MODULE_DESCRIPTION("Comedi low-level driver");
769 MODULE_LICENSE("GPL");