Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dt2811.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/dt2811.c
3  Hardware driver for Data Translation DT2811
4 
5  COMEDI - Linux Control and Measurement Device Interface
6  History:
7  Base Version - David A. Schleef <[email protected]>
8  December 1998 - Updated to work. David does not have a DT2811
9  board any longer so this was suffering from bitrot.
10  Updated performed by ...
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26 /*
27 Driver: dt2811
28 Description: Data Translation DT2811
29 Author: ds
30 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
31 Status: works
32 
33 Configuration options:
34  [0] - I/O port base address
35  [1] - IRQ, although this is currently unused
36  [2] - A/D reference
37  0 = signle-ended
38  1 = differential
39  2 = pseudo-differential (common reference)
40  [3] - A/D range
41  0 = [-5, 5]
42  1 = [-2.5, 2.5]
43  2 = [0, 5]
44  [4] - D/A 0 range (same choices)
45  [4] - D/A 1 range (same choices)
46 */
47 
48 #include <linux/interrupt.h>
49 #include "../comedidev.h"
50 
51 #include <linux/ioport.h>
52 
53 static const char *driver_name = "dt2811";
54 
55 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
56  4, {
57  RANGE(0, 5),
58  RANGE(0, 2.5),
59  RANGE(0, 1.25),
60  RANGE(0, 0.625)
61  }
62 };
63 
64 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
65  4, {
66  RANGE(-2.5, 2.5),
67  RANGE(-1.25, 1.25),
68  RANGE(-0.625, 0.625),
69  RANGE(-0.3125, 0.3125)
70  }
71 };
72 
73 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
74  4, {
75  RANGE(-5, 5),
76  RANGE(-2.5, 2.5),
77  RANGE(-1.25, 1.25),
78  RANGE(-0.625, 0.625)
79  }
80 };
81 
82 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
83  4, {
84  RANGE(0, 5),
85  RANGE(0, 0.5),
86  RANGE(0, 0.05),
87  RANGE(0, 0.01)
88  }
89 };
90 
91 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
92  4, {
93  RANGE(-2.5, 2.5),
94  RANGE(-0.25, 0.25),
95  RANGE(-0.025, 0.025),
96  RANGE(-0.005, 0.005)
97  }
98 };
99 
100 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
101  4, {
102  RANGE(-5, 5),
103  RANGE(-0.5, 0.5),
104  RANGE(-0.05, 0.05),
105  RANGE(-0.01, 0.01)
106  }
107 };
108 
109 /*
110 
111  0x00 ADCSR R/W A/D Control/Status Register
112  bit 7 - (R) 1 indicates A/D conversion done
113  reading ADDAT clears bit
114  (W) ignored
115  bit 6 - (R) 1 indicates A/D error
116  (W) ignored
117  bit 5 - (R) 1 indicates A/D busy, cleared at end
118  of conversion
119  (W) ignored
120  bit 4 - (R) 0
121  (W)
122  bit 3 - (R) 0
123  bit 2 - (R/W) 1 indicates interrupts enabled
124  bits 1,0 - (R/W) mode bits
125  00 single conversion on ADGCR load
126  01 continuous conversion, internal clock,
127  (clock enabled on ADGCR load)
128  10 continuous conversion, internal clock,
129  external trigger
130  11 continuous conversion, external clock,
131  external trigger
132 
133  0x01 ADGCR R/W A/D Gain/Channel Register
134  bit 6,7 - (R/W) gain select
135  00 gain=1, both PGH, PGL models
136  01 gain=2 PGH, 10 PGL
137  10 gain=4 PGH, 100 PGL
138  11 gain=8 PGH, 500 PGL
139  bit 4,5 - reserved
140  bit 3-0 - (R/W) channel select
141  channel number from 0-15
142 
143  0x02,0x03 (R) ADDAT A/D Data Register
144  (W) DADAT0 D/A Data Register 0
145  0x02 low byte
146  0x03 high byte
147 
148  0x04,0x05 (W) DADAT0 D/A Data Register 1
149 
150  0x06 (R) DIO0 Digital Input Port 0
151  (W) DIO1 Digital Output Port 1
152 
153  0x07 TMRCTR (R/W) Timer/Counter Register
154  bits 6,7 - reserved
155  bits 5-3 - Timer frequency control (mantissa)
156  543 divisor freqency (kHz)
157  000 1 600
158  001 10 60
159  010 2 300
160  011 3 200
161  100 4 150
162  101 5 120
163  110 6 100
164  111 12 50
165  bits 2-0 - Timer frequency control (exponent)
166  210 multiply divisor/divide frequency by
167  000 1
168  001 10
169  010 100
170  011 1000
171  100 10000
172  101 100000
173  110 1000000
174  111 10000000
175 
176  */
177 
178 #define TIMEOUT 10000
179 
180 #define DT2811_SIZE 8
181 
182 #define DT2811_ADCSR 0
183 #define DT2811_ADGCR 1
184 #define DT2811_ADDATLO 2
185 #define DT2811_ADDATHI 3
186 #define DT2811_DADAT0LO 2
187 #define DT2811_DADAT0HI 3
188 #define DT2811_DADAT1LO 4
189 #define DT2811_DADAT1HI 5
190 #define DT2811_DIO 6
191 #define DT2811_TMRCTR 7
192 
193 /*
194  * flags
195  */
196 
197 /* ADCSR */
198 
199 #define DT2811_ADDONE 0x80
200 #define DT2811_ADERROR 0x40
201 #define DT2811_ADBUSY 0x20
202 #define DT2811_CLRERROR 0x10
203 #define DT2811_INTENB 0x04
204 #define DT2811_ADMODE 0x03
205 
206 struct dt2811_board {
207 
208  const char *name;
209  const struct comedi_lrange *bip_5;
210  const struct comedi_lrange *bip_2_5;
211  const struct comedi_lrange *unip_5;
212 };
213 
215 
217  int ntrig;
219  enum {
221  } adc_mux;
222  enum {
224  } dac_range[2];
225  const struct comedi_lrange *range_type_list[2];
226  unsigned int ao_readback[2];
227 };
228 
229 #define devpriv ((struct dt2811_private *)dev->private)
230 
231 static const struct comedi_lrange *dac_range_types[] = {
235 };
236 
237 #define DT2811_TIMEOUT 5
238 
239 #if 0
240 static irqreturn_t dt2811_interrupt(int irq, void *d)
241 {
242  int lo, hi;
243  int data;
244  struct comedi_device *dev = d;
245 
246  if (!dev->attached) {
247  comedi_error(dev, "spurious interrupt");
248  return IRQ_HANDLED;
249  }
250 
251  lo = inb(dev->iobase + DT2811_ADDATLO);
252  hi = inb(dev->iobase + DT2811_ADDATHI);
253 
254  data = lo + (hi << 8);
255 
256  if (!(--devpriv->ntrig)) {
257  /* how to turn off acquisition */
258  s->async->events |= COMEDI_SB_EOA;
259  }
260  comedi_event(dev, s);
261  return IRQ_HANDLED;
262 }
263 #endif
264 
265 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
266  struct comedi_insn *insn, unsigned int *data)
267 {
268  int chan = CR_CHAN(insn->chanspec);
269  int timeout = DT2811_TIMEOUT;
270  int i;
271 
272  for (i = 0; i < insn->n; i++) {
273  outb(chan, dev->iobase + DT2811_ADGCR);
274 
275  while (timeout
276  && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
277  timeout--;
278  if (!timeout)
279  return -ETIME;
280 
281  data[i] = inb(dev->iobase + DT2811_ADDATLO);
282  data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
283  data[i] &= 0xfff;
284  }
285 
286  return i;
287 }
288 
289 #if 0
290 /* Wow. This is code from the Comedi stone age. But it hasn't been
291  * replaced, so I'll let it stay. */
292 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
293 {
294  struct comedi_device *dev = comedi_devices + minor;
295 
296  if (adtrig->n < 1)
297  return 0;
298  dev->curadchan = adtrig->chan;
299  switch (dev->i_admode) {
300  case COMEDI_MDEMAND:
301  dev->ntrig = adtrig->n - 1;
302  /* not necessary */
303  /*printk("dt2811: AD soft trigger\n"); */
304  /*outb(DT2811_CLRERROR|DT2811_INTENB,
305  dev->iobase+DT2811_ADCSR); */
306  outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
307  do_gettimeofday(&trigtime);
308  break;
309  case COMEDI_MCONTS:
310  dev->ntrig = adtrig->n;
311  break;
312  }
313 
314  return 0;
315 }
316 #endif
317 
318 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
319  struct comedi_insn *insn, unsigned int *data)
320 {
321  int i;
322  int chan;
323 
324  chan = CR_CHAN(insn->chanspec);
325 
326  for (i = 0; i < insn->n; i++) {
327  outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
328  outb((data[i] >> 8) & 0xff,
329  dev->iobase + DT2811_DADAT0HI + 2 * chan);
330  devpriv->ao_readback[chan] = data[i];
331  }
332 
333  return i;
334 }
335 
336 static int dt2811_ao_insn_read(struct comedi_device *dev,
337  struct comedi_subdevice *s,
338  struct comedi_insn *insn, unsigned int *data)
339 {
340  int i;
341  int chan;
342 
343  chan = CR_CHAN(insn->chanspec);
344 
345  for (i = 0; i < insn->n; i++)
346  data[i] = devpriv->ao_readback[chan];
347 
348  return i;
349 }
350 
351 static int dt2811_di_insn_bits(struct comedi_device *dev,
352  struct comedi_subdevice *s,
353  struct comedi_insn *insn, unsigned int *data)
354 {
355  data[1] = inb(dev->iobase + DT2811_DIO);
356 
357  return insn->n;
358 }
359 
360 static int dt2811_do_insn_bits(struct comedi_device *dev,
361  struct comedi_subdevice *s,
362  struct comedi_insn *insn, unsigned int *data)
363 {
364  s->state &= ~data[0];
365  s->state |= data[0] & data[1];
366  outb(s->state, dev->iobase + DT2811_DIO);
367 
368  data[1] = s->state;
369 
370  return insn->n;
371 }
372 
373 /*
374  options[0] Board base address
375  options[1] IRQ
376  options[2] Input configuration
377  0 == single-ended
378  1 == differential
379  2 == pseudo-differential
380  options[3] Analog input range configuration
381  0 == bipolar 5 (-5V -- +5V)
382  1 == bipolar 2.5V (-2.5V -- +2.5V)
383  2 == unipolar 5V (0V -- +5V)
384  options[4] Analog output 0 range configuration
385  0 == bipolar 5 (-5V -- +5V)
386  1 == bipolar 2.5V (-2.5V -- +2.5V)
387  2 == unipolar 5V (0V -- +5V)
388  options[5] Analog output 1 range configuration
389  0 == bipolar 5 (-5V -- +5V)
390  1 == bipolar 2.5V (-2.5V -- +2.5V)
391  2 == unipolar 5V (0V -- +5V)
392 */
393 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
394 {
395  /* int i, irq; */
396  /* unsigned long irqs; */
397  /* long flags; */
398 
399  const struct dt2811_board *board = comedi_board(dev);
400  int ret;
401  struct comedi_subdevice *s;
402  unsigned long iobase;
403 
404  iobase = it->options[0];
405 
406  printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
407 
408  if (!request_region(iobase, DT2811_SIZE, driver_name)) {
409  printk(KERN_ERR "I/O port conflict\n");
410  return -EIO;
411  }
412 
413  dev->iobase = iobase;
414  dev->board_name = board->name;
415 
416 #if 0
417  outb(0, dev->iobase + DT2811_ADCSR);
418  udelay(100);
419  i = inb(dev->iobase + DT2811_ADDATLO);
420  i = inb(dev->iobase + DT2811_ADDATHI);
421 #endif
422 
423 #if 0
424  irq = it->options[1];
425  if (irq < 0) {
426  save_flags(flags);
427  sti();
428  irqs = probe_irq_on();
429 
431  dev->iobase + DT2811_ADCSR);
432  outb(0, dev->iobase + DT2811_ADGCR);
433 
434  udelay(100);
435 
436  irq = probe_irq_off(irqs);
437  restore_flags(flags);
438 
439  /*outb(DT2811_CLRERROR|DT2811_INTENB,
440  dev->iobase+DT2811_ADCSR);*/
441 
442  if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
443  printk(KERN_ERR "error probing irq (bad)\n");
444  dev->irq = 0;
445  if (irq > 0) {
446  i = inb(dev->iobase + DT2811_ADDATLO);
447  i = inb(dev->iobase + DT2811_ADDATHI);
448  printk(KERN_INFO "(irq = %d)\n", irq);
449  ret = request_irq(irq, dt2811_interrupt, 0,
450  driver_name, dev);
451  if (ret < 0)
452  return -EIO;
453  dev->irq = irq;
454  } else if (irq == 0) {
455  printk(KERN_INFO "(no irq)\n");
456  } else {
457  printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
458  }
459  }
460 #endif
461 
462  ret = comedi_alloc_subdevices(dev, 4);
463  if (ret)
464  return ret;
465 
466  ret = alloc_private(dev, sizeof(struct dt2811_private));
467  if (ret < 0)
468  return ret;
469 
470  switch (it->options[2]) {
471  case 0:
472  devpriv->adc_mux = adc_singleended;
473  break;
474  case 1:
475  devpriv->adc_mux = adc_diff;
476  break;
477  case 2:
478  devpriv->adc_mux = adc_pseudo_diff;
479  break;
480  default:
481  devpriv->adc_mux = adc_singleended;
482  break;
483  }
484  switch (it->options[4]) {
485  case 0:
486  devpriv->dac_range[0] = dac_bipolar_5;
487  break;
488  case 1:
489  devpriv->dac_range[0] = dac_bipolar_2_5;
490  break;
491  case 2:
492  devpriv->dac_range[0] = dac_unipolar_5;
493  break;
494  default:
495  devpriv->dac_range[0] = dac_bipolar_5;
496  break;
497  }
498  switch (it->options[5]) {
499  case 0:
500  devpriv->dac_range[1] = dac_bipolar_5;
501  break;
502  case 1:
503  devpriv->dac_range[1] = dac_bipolar_2_5;
504  break;
505  case 2:
506  devpriv->dac_range[1] = dac_unipolar_5;
507  break;
508  default:
509  devpriv->dac_range[1] = dac_bipolar_5;
510  break;
511  }
512 
513  s = &dev->subdevices[0];
514  /* initialize the ADC subdevice */
515  s->type = COMEDI_SUBD_AI;
517  s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
518  s->insn_read = dt2811_ai_insn;
519  s->maxdata = 0xfff;
520  switch (it->options[3]) {
521  case 0:
522  default:
523  s->range_table = board->bip_5;
524  break;
525  case 1:
526  s->range_table = board->bip_2_5;
527  break;
528  case 2:
529  s->range_table = board->unip_5;
530  break;
531  }
532 
533  s = &dev->subdevices[1];
534  /* ao subdevice */
535  s->type = COMEDI_SUBD_AO;
537  s->n_chan = 2;
538  s->insn_write = dt2811_ao_insn;
539  s->insn_read = dt2811_ao_insn_read;
540  s->maxdata = 0xfff;
541  s->range_table_list = devpriv->range_type_list;
542  devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
543  devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
544 
545  s = &dev->subdevices[2];
546  /* di subdevice */
547  s->type = COMEDI_SUBD_DI;
549  s->n_chan = 8;
550  s->insn_bits = dt2811_di_insn_bits;
551  s->maxdata = 1;
553 
554  s = &dev->subdevices[3];
555  /* do subdevice */
556  s->type = COMEDI_SUBD_DO;
558  s->n_chan = 8;
559  s->insn_bits = dt2811_do_insn_bits;
560  s->maxdata = 1;
561  s->state = 0;
563 
564  return 0;
565 }
566 
567 static void dt2811_detach(struct comedi_device *dev)
568 {
569  if (dev->irq)
570  free_irq(dev->irq, dev);
571  if (dev->iobase)
573 }
574 
575 static const struct dt2811_board boardtypes[] = {
576  {
577  .name = "dt2811-pgh",
578  .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
579  .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
580  .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
581  }, {
582  .name = "dt2811-pgl",
583  .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
584  .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
585  .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
586  },
587 };
588 
589 static struct comedi_driver dt2811_driver = {
590  .driver_name = "dt2811",
591  .module = THIS_MODULE,
592  .attach = dt2811_attach,
593  .detach = dt2811_detach,
594  .board_name = &boardtypes[0].name,
595  .num_names = ARRAY_SIZE(boardtypes),
596  .offset = sizeof(struct dt2811_board),
597 };
598 module_comedi_driver(dt2811_driver);
599 
600 MODULE_AUTHOR("Comedi http://www.comedi.org");
601 MODULE_DESCRIPTION("Comedi low-level driver");
602 MODULE_LICENSE("GPL");