Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcl726.c
Go to the documentation of this file.
1 /*
2  comedi/drivers/pcl726.c
3 
4  hardware driver for Advantech cards:
5  card: PCL-726, PCL-727, PCL-728
6  driver: pcl726, pcl727, pcl728
7  and for ADLink cards:
8  card: ACL-6126, ACL-6128
9  driver: acl6126, acl6128
10 
11  COMEDI - Linux Control and Measurement Device Interface
12  Copyright (C) 1998 David A. Schleef <[email protected]>
13 
14  This program is free software; you can redistribute it and/or modify
15  it under the terms of the GNU General Public License as published by
16  the Free Software Foundation; either version 2 of the License, or
17  (at your option) any later version.
18 
19  This program is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  GNU General Public License for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with this program; if not, write to the Free Software
26  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 
28 */
29 /*
30 Driver: pcl726
31 Description: Advantech PCL-726 & compatibles
32 Author: ds
33 Status: untested
34 Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
35  [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
36 
37 Interrupts are not supported.
38 
39  Options for PCL-726:
40  [0] - IO Base
41  [2]...[7] - D/A output range for channel 1-6:
42  0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
43  4: 4-20mA, 5: unknown (external reference)
44 
45  Options for PCL-727:
46  [0] - IO Base
47  [2]...[13] - D/A output range for channel 1-12:
48  0: 0-5V, 1: 0-10V, 2: +/-5V,
49  3: 4-20mA
50 
51  Options for PCL-728 and ACL-6128:
52  [0] - IO Base
53  [2], [3] - D/A output range for channel 1 and 2:
54  0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
55  4: 4-20mA, 5: 0-20mA
56 
57  Options for ACL-6126:
58  [0] - IO Base
59  [1] - IRQ (0=disable, 3, 5, 6, 7, 9, 10, 11, 12, 15) (currently ignored)
60  [2]...[7] - D/A output range for channel 1-6:
61  0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
62  4: 4-20mA
63 */
64 
65 /*
66  Thanks to Circuit Specialists for having programming info (!) on
67  their web page. (http://www.cir.com/)
68 */
69 
70 #include "../comedidev.h"
71 
72 #include <linux/ioport.h>
73 
74 #undef ACL6126_IRQ /* no interrupt support (yet) */
75 
76 #define PCL726_SIZE 16
77 #define PCL727_SIZE 32
78 #define PCL728_SIZE 8
79 
80 #define PCL726_DAC0_HI 0
81 #define PCL726_DAC0_LO 1
82 
83 #define PCL726_DO_HI 12
84 #define PCL726_DO_LO 13
85 #define PCL726_DI_HI 14
86 #define PCL726_DI_LO 15
87 
88 #define PCL727_DO_HI 24
89 #define PCL727_DO_LO 25
90 #define PCL727_DI_HI 0
91 #define PCL727_DI_LO 1
92 
93 static const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} };
94 static const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
95 
96 static const struct comedi_lrange *const rangelist_726[] = {
99  &range_4_20mA, &range_unknown
100 };
101 
102 static const struct comedi_lrange *const rangelist_727[] = {
105  &range_4_20mA
106 };
107 
108 static const struct comedi_lrange *const rangelist_728[] = {
111  &range_4_20mA, &range_0_20mA
112 };
113 
114 struct pcl726_board {
115 
116  const char *name; /* driver name */
117  int n_aochan; /* num of D/A chans */
118  int num_of_ranges; /* num of ranges */
119  unsigned int IRQbits; /* allowed interrupts */
120  unsigned int io_range; /* len of IO space */
121  char have_dio; /* 1=card have DI/DO ports */
122  int di_hi; /* ports for DI/DO operations */
123  int di_lo;
124  int do_hi;
125  int do_lo;
126  const struct comedi_lrange *const *range_type_list;
127  /* list of supported ranges */
128 };
129 
130 static const struct pcl726_board boardtypes[] = {
131  {"pcl726", 6, 6, 0x0000, PCL726_SIZE, 1,
133  &rangelist_726[0],},
134  {"pcl727", 12, 4, 0x0000, PCL727_SIZE, 1,
136  &rangelist_727[0],},
137  {"pcl728", 2, 6, 0x0000, PCL728_SIZE, 0,
138  0, 0, 0, 0,
139  &rangelist_728[0],},
140  {"acl6126", 6, 5, 0x96e8, PCL726_SIZE, 1,
142  &rangelist_726[0],},
143  {"acl6128", 2, 6, 0x0000, PCL728_SIZE, 0,
144  0, 0, 0, 0,
145  &rangelist_728[0],},
146 };
147 
149 
150  int bipolar[12];
151  const struct comedi_lrange *rangelist[12];
152  unsigned int ao_readback[12];
153 };
154 
155 #define devpriv ((struct pcl726_private *)dev->private)
156 
157 static int pcl726_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
158  struct comedi_insn *insn, unsigned int *data)
159 {
160  int hi, lo;
161  int n;
162  int chan = CR_CHAN(insn->chanspec);
163 
164  for (n = 0; n < insn->n; n++) {
165  lo = data[n] & 0xff;
166  hi = (data[n] >> 8) & 0xf;
167  if (devpriv->bipolar[chan])
168  hi ^= 0x8;
169  /*
170  * the programming info did not say which order
171  * to write bytes. switch the order of the next
172  * two lines if you get glitches.
173  */
174  outb(hi, dev->iobase + PCL726_DAC0_HI + 2 * chan);
175  outb(lo, dev->iobase + PCL726_DAC0_LO + 2 * chan);
176  devpriv->ao_readback[chan] = data[n];
177  }
178 
179  return n;
180 }
181 
182 static int pcl726_ao_insn_read(struct comedi_device *dev,
183  struct comedi_subdevice *s,
184  struct comedi_insn *insn, unsigned int *data)
185 {
186  int chan = CR_CHAN(insn->chanspec);
187  int n;
188 
189  for (n = 0; n < insn->n; n++)
190  data[n] = devpriv->ao_readback[chan];
191  return n;
192 }
193 
194 static int pcl726_di_insn_bits(struct comedi_device *dev,
195  struct comedi_subdevice *s,
196  struct comedi_insn *insn, unsigned int *data)
197 {
198  const struct pcl726_board *board = comedi_board(dev);
199 
200  data[1] = inb(dev->iobase + board->di_lo) |
201  (inb(dev->iobase + board->di_hi) << 8);
202 
203  return insn->n;
204 }
205 
206 static int pcl726_do_insn_bits(struct comedi_device *dev,
207  struct comedi_subdevice *s,
208  struct comedi_insn *insn, unsigned int *data)
209 {
210  const struct pcl726_board *board = comedi_board(dev);
211 
212  if (data[0]) {
213  s->state &= ~data[0];
214  s->state |= data[0] & data[1];
215  }
216  if (data[1] & 0x00ff)
217  outb(s->state & 0xff, dev->iobase + board->do_lo);
218  if (data[1] & 0xff00)
219  outb((s->state >> 8), dev->iobase + board->do_hi);
220 
221  data[1] = s->state;
222 
223  return insn->n;
224 }
225 
226 static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
227 {
228  const struct pcl726_board *board = comedi_board(dev);
229  struct comedi_subdevice *s;
230  unsigned long iobase;
231  unsigned int iorange;
232  int ret, i;
233 #ifdef ACL6126_IRQ
234  unsigned int irq;
235 #endif
236 
237  iobase = it->options[0];
238  iorange = board->io_range;
239  printk(KERN_WARNING "comedi%d: pcl726: board=%s, 0x%03lx ", dev->minor,
240  board->name, iobase);
241  if (!request_region(iobase, iorange, "pcl726")) {
242  printk(KERN_WARNING "I/O port conflict\n");
243  return -EIO;
244  }
245 
246  dev->iobase = iobase;
247 
248  dev->board_name = board->name;
249 
250  ret = alloc_private(dev, sizeof(struct pcl726_private));
251  if (ret < 0)
252  return -ENOMEM;
253 
254  for (i = 0; i < 12; i++) {
255  devpriv->bipolar[i] = 0;
256  devpriv->rangelist[i] = &range_unknown;
257  }
258 
259 #ifdef ACL6126_IRQ
260  irq = 0;
261  if (boardtypes[board].IRQbits != 0) { /* board support IRQ */
262  irq = it->options[1];
263  devpriv->first_chan = 2;
264  if (irq) { /* we want to use IRQ */
265  if (((1 << irq) & boardtypes[board].IRQbits) == 0) {
267  ", IRQ %d is out of allowed range,"
268  " DISABLING IT", irq);
269  irq = 0; /* Bad IRQ */
270  } else {
271  if (request_irq(irq, interrupt_pcl818, 0,
272  "pcl726", dev)) {
274  ", unable to allocate IRQ %d,"
275  " DISABLING IT", irq);
276  irq = 0; /* Can't use IRQ */
277  } else {
278  printk(", irq=%d", irq);
279  }
280  }
281  }
282  }
283 
284  dev->irq = irq;
285 #endif
286 
287  printk("\n");
288 
289  ret = comedi_alloc_subdevices(dev, 3);
290  if (ret)
291  return ret;
292 
293  s = &dev->subdevices[0];
294  /* ao */
295  s->type = COMEDI_SUBD_AO;
297  s->n_chan = board->n_aochan;
298  s->maxdata = 0xfff;
299  s->len_chanlist = 1;
300  s->insn_write = pcl726_ao_insn;
301  s->insn_read = pcl726_ao_insn_read;
302  s->range_table_list = devpriv->rangelist;
303  for (i = 0; i < board->n_aochan; i++) {
304  int j;
305 
306  j = it->options[2 + 1];
307  if ((j < 0) || (j >= board->num_of_ranges)) {
308  printk
309  ("Invalid range for channel %d! Must be 0<=%d<%d\n",
310  i, j, board->num_of_ranges - 1);
311  j = 0;
312  }
313  devpriv->rangelist[i] = board->range_type_list[j];
314  if (devpriv->rangelist[i]->range[0].min ==
315  -devpriv->rangelist[i]->range[0].max)
316  devpriv->bipolar[i] = 1; /* bipolar range */
317  }
318 
319  s = &dev->subdevices[1];
320  /* di */
321  if (!board->have_dio) {
323  } else {
324  s->type = COMEDI_SUBD_DI;
326  s->n_chan = 16;
327  s->maxdata = 1;
328  s->len_chanlist = 1;
329  s->insn_bits = pcl726_di_insn_bits;
331  }
332 
333  s = &dev->subdevices[2];
334  /* do */
335  if (!board->have_dio) {
337  } else {
338  s->type = COMEDI_SUBD_DO;
340  s->n_chan = 16;
341  s->maxdata = 1;
342  s->len_chanlist = 1;
343  s->insn_bits = pcl726_do_insn_bits;
345  }
346 
347  return 0;
348 }
349 
350 static void pcl726_detach(struct comedi_device *dev)
351 {
352  const struct pcl726_board *board = comedi_board(dev);
353 
354 #ifdef ACL6126_IRQ
355  if (dev->irq)
356  free_irq(dev->irq, dev);
357 #endif
358  if (dev->iobase)
359  release_region(dev->iobase, board->io_range);
360 }
361 
362 static struct comedi_driver pcl726_driver = {
363  .driver_name = "pcl726",
364  .module = THIS_MODULE,
365  .attach = pcl726_attach,
366  .detach = pcl726_detach,
367  .board_name = &boardtypes[0].name,
368  .num_names = ARRAY_SIZE(boardtypes),
369  .offset = sizeof(struct pcl726_board),
370 };
371 module_comedi_driver(pcl726_driver);
372 
373 MODULE_AUTHOR("Comedi http://www.comedi.org");
374 MODULE_DESCRIPTION("Comedi low-level driver");
375 MODULE_LICENSE("GPL");