Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dt2814.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/dt2814.c
3  Hardware driver for Data Translation DT2814
4 
5  COMEDI - Linux Control and Measurement Device Interface
6  Copyright (C) 1998 David A. Schleef <[email protected]>
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.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22 */
23 /*
24 Driver: dt2814
25 Description: Data Translation DT2814
26 Author: ds
27 Status: complete
28 Devices: [Data Translation] DT2814 (dt2814)
29 
30 Configuration options:
31  [0] - I/O port base address
32  [1] - IRQ
33 
34 This card has 16 analog inputs multiplexed onto a 12 bit ADC. There
35 is a minimally useful onboard clock. The base frequency for the
36 clock is selected by jumpers, and the clock divider can be selected
37 via programmed I/O. Unfortunately, the clock divider can only be
38 a power of 10, from 1 to 10^7, of which only 3 or 4 are useful. In
39 addition, the clock does not seem to be very accurate.
40 */
41 
42 #include <linux/interrupt.h>
43 #include "../comedidev.h"
44 
45 #include <linux/ioport.h>
46 #include <linux/delay.h>
47 
48 #include "comedi_fc.h"
49 
50 #define DT2814_SIZE 2
51 
52 #define DT2814_CSR 0
53 #define DT2814_DATA 1
54 
55 /*
56  * flags
57  */
58 
59 #define DT2814_FINISH 0x80
60 #define DT2814_ERR 0x40
61 #define DT2814_BUSY 0x20
62 #define DT2814_ENB 0x10
63 #define DT2814_CHANMASK 0x0f
64 
66 
67  int ntrig;
68  int curadchan;
69 };
70 
71 #define devpriv ((struct dt2814_private *)dev->private)
72 
73 #define DT2814_TIMEOUT 10
74 #define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
75 
76 static int dt2814_ai_insn_read(struct comedi_device *dev,
77  struct comedi_subdevice *s,
78  struct comedi_insn *insn, unsigned int *data)
79 {
80  int n, i, hi, lo;
81  int chan;
82  int status = 0;
83 
84  for (n = 0; n < insn->n; n++) {
85  chan = CR_CHAN(insn->chanspec);
86 
87  outb(chan, dev->iobase + DT2814_CSR);
88  for (i = 0; i < DT2814_TIMEOUT; i++) {
89  status = inb(dev->iobase + DT2814_CSR);
90  printk(KERN_INFO "dt2814: status: %02x\n", status);
91  udelay(10);
92  if (status & DT2814_FINISH)
93  break;
94  }
95  if (i >= DT2814_TIMEOUT) {
96  printk(KERN_INFO "dt2814: status: %02x\n", status);
97  return -ETIMEDOUT;
98  }
99 
100  hi = inb(dev->iobase + DT2814_DATA);
101  lo = inb(dev->iobase + DT2814_DATA);
102 
103  data[n] = (hi << 4) | (lo >> 4);
104  }
105 
106  return n;
107 }
108 
109 static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
110 {
111  int i;
112  unsigned int f;
113 
114  /* XXX ignores flags */
115 
116  f = 10000; /* ns */
117  for (i = 0; i < 8; i++) {
118  if ((2 * (*ns)) < (f * 11))
119  break;
120  f *= 10;
121  }
122 
123  *ns = f;
124 
125  return i;
126 }
127 
128 static int dt2814_ai_cmdtest(struct comedi_device *dev,
129  struct comedi_subdevice *s, struct comedi_cmd *cmd)
130 {
131  int err = 0;
132  int tmp;
133 
134  /* Step 1 : check if triggers are trivially valid */
135 
136  err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
137  err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
138  err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
139  err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
140  err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
141 
142  if (err)
143  return 1;
144 
145  /* Step 2a : make sure trigger sources are unique */
146 
147  err |= cfc_check_trigger_is_unique(cmd->stop_src);
148 
149  /* Step 2b : and mutually compatible */
150 
151  if (err)
152  return 2;
153 
154  /* step 3: make sure arguments are trivially compatible */
155 
156  if (cmd->start_arg != 0) {
157  cmd->start_arg = 0;
158  err++;
159  }
160  if (cmd->scan_begin_arg > 1000000000) {
161  cmd->scan_begin_arg = 1000000000;
162  err++;
163  }
164  if (cmd->scan_begin_arg < DT2814_MAX_SPEED) {
166  err++;
167  }
168  if (cmd->scan_end_arg != cmd->chanlist_len) {
169  cmd->scan_end_arg = cmd->chanlist_len;
170  err++;
171  }
172  if (cmd->stop_src == TRIG_COUNT) {
173  if (cmd->stop_arg < 2) {
174  cmd->stop_arg = 2;
175  err++;
176  }
177  } else {
178  /* TRIG_NONE */
179  if (cmd->stop_arg != 0) {
180  cmd->stop_arg = 0;
181  err++;
182  }
183  }
184 
185  if (err)
186  return 3;
187 
188  /* step 4: fix up any arguments */
189 
190  tmp = cmd->scan_begin_arg;
191  dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
192  if (tmp != cmd->scan_begin_arg)
193  err++;
194 
195  if (err)
196  return 4;
197 
198  return 0;
199 }
200 
201 static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
202 {
203  struct comedi_cmd *cmd = &s->async->cmd;
204  int chan;
205  int trigvar;
206 
207  trigvar =
208  dt2814_ns_to_timer(&cmd->scan_begin_arg,
209  cmd->flags & TRIG_ROUND_MASK);
210 
211  chan = CR_CHAN(cmd->chanlist[0]);
212 
213  devpriv->ntrig = cmd->stop_arg;
214  outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
215 
216  return 0;
217 
218 }
219 
220 static irqreturn_t dt2814_interrupt(int irq, void *d)
221 {
222  int lo, hi;
223  struct comedi_device *dev = d;
224  struct comedi_subdevice *s;
225  int data;
226 
227  if (!dev->attached) {
228  comedi_error(dev, "spurious interrupt");
229  return IRQ_HANDLED;
230  }
231 
232  s = &dev->subdevices[0];
233 
234  hi = inb(dev->iobase + DT2814_DATA);
235  lo = inb(dev->iobase + DT2814_DATA);
236 
237  data = (hi << 4) | (lo >> 4);
238 
239  if (!(--devpriv->ntrig)) {
240  int i;
241 
242  outb(0, dev->iobase + DT2814_CSR);
243  /* note: turning off timed mode triggers another
244  sample. */
245 
246  for (i = 0; i < DT2814_TIMEOUT; i++) {
247  if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
248  break;
249  }
250  inb(dev->iobase + DT2814_DATA);
251  inb(dev->iobase + DT2814_DATA);
252 
253  s->async->events |= COMEDI_CB_EOA;
254  }
255  comedi_event(dev, s);
256  return IRQ_HANDLED;
257 }
258 
259 static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
260 {
261  int i, irq;
262  int ret;
263  struct comedi_subdevice *s;
264  unsigned long iobase;
265 
266  iobase = it->options[0];
267  printk(KERN_INFO "comedi%d: dt2814: 0x%04lx ", dev->minor, iobase);
268  if (!request_region(iobase, DT2814_SIZE, "dt2814")) {
269  printk(KERN_ERR "I/O port conflict\n");
270  return -EIO;
271  }
272  dev->iobase = iobase;
273  dev->board_name = "dt2814";
274 
275  outb(0, dev->iobase + DT2814_CSR);
276  udelay(100);
277  if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
278  printk(KERN_ERR "reset error (fatal)\n");
279  return -EIO;
280  }
281  i = inb(dev->iobase + DT2814_DATA);
282  i = inb(dev->iobase + DT2814_DATA);
283 
284  irq = it->options[1];
285 #if 0
286  if (irq < 0) {
287  save_flags(flags);
288  sti();
289  irqs = probe_irq_on();
290 
291  outb(0, dev->iobase + DT2814_CSR);
292 
293  udelay(100);
294 
295  irq = probe_irq_off(irqs);
296  restore_flags(flags);
297  if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR)
298  printk(KERN_DEBUG "error probing irq (bad)\n");
299 
300 
301  i = inb(dev->iobase + DT2814_DATA);
302  i = inb(dev->iobase + DT2814_DATA);
303  }
304 #endif
305  dev->irq = 0;
306  if (irq > 0) {
307  if (request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) {
308  printk(KERN_WARNING "(irq %d unavailable)\n", irq);
309  } else {
310  printk(KERN_INFO "( irq = %d )\n", irq);
311  dev->irq = irq;
312  }
313  } else if (irq == 0) {
314  printk(KERN_WARNING "(no irq)\n");
315  } else {
316 #if 0
317  printk(KERN_DEBUG "(probe returned multiple irqs--bad)\n");
318 #else
319  printk(KERN_WARNING "(irq probe not implemented)\n");
320 #endif
321  }
322 
323  ret = comedi_alloc_subdevices(dev, 1);
324  if (ret)
325  return ret;
326 
327  ret = alloc_private(dev, sizeof(struct dt2814_private));
328  if (ret < 0)
329  return ret;
330 
331  s = &dev->subdevices[0];
332  dev->read_subdev = s;
333  s->type = COMEDI_SUBD_AI;
335  s->n_chan = 16; /* XXX */
336  s->len_chanlist = 1;
337  s->insn_read = dt2814_ai_insn_read;
338  s->do_cmd = dt2814_ai_cmd;
339  s->do_cmdtest = dt2814_ai_cmdtest;
340  s->maxdata = 0xfff;
341  s->range_table = &range_unknown; /* XXX */
342 
343  return 0;
344 }
345 
346 static void dt2814_detach(struct comedi_device *dev)
347 {
348  if (dev->irq)
349  free_irq(dev->irq, dev);
350  if (dev->iobase)
352 }
353 
354 static struct comedi_driver dt2814_driver = {
355  .driver_name = "dt2814",
356  .module = THIS_MODULE,
357  .attach = dt2814_attach,
358  .detach = dt2814_detach,
359 };
360 module_comedi_driver(dt2814_driver);
361 
362 MODULE_AUTHOR("Comedi http://www.comedi.org");
363 MODULE_DESCRIPTION("Comedi low-level driver");
364 MODULE_LICENSE("GPL");