Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / pcl726.c
1 /*
2  * pcl726.c
3  * Comedi driver for 6/12-Channel D/A Output and DIO cards
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 1998 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.
17  */
18
19 /*
20  * Driver: pcl726
21  * Description: Advantech PCL-726 & compatibles
22  * Author: David A. Schleef <ds@schleef.org>
23  * Status: untested
24  * Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
25  *   [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
26  *
27  * Configuration Options:
28  *   [0]  - IO Base
29  *   [1]  - IRQ (ACL-6126 only)
30  *   [2]  - D/A output range for channel 0
31  *   [3]  - D/A output range for channel 1
32  *
33  * Boards with > 2 analog output channels:
34  *   [4]  - D/A output range for channel 2
35  *   [5]  - D/A output range for channel 3
36  *   [6]  - D/A output range for channel 4
37  *   [7]  - D/A output range for channel 5
38  *
39  * Boards with > 6 analog output channels:
40  *   [8]  - D/A output range for channel 6
41  *   [9]  - D/A output range for channel 7
42  *   [10] - D/A output range for channel 8
43  *   [11] - D/A output range for channel 9
44  *   [12] - D/A output range for channel 10
45  *   [13] - D/A output range for channel 11
46  *
47  * For PCL-726 the D/A output ranges are:
48  *   0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: unknown
49  *
50  * For PCL-727:
51  *   0: 0-5V, 1: 0-10V, 2: +/-5V, 3: 4-20mA
52  *
53  * For PCL-728 and ACL-6128:
54  *   0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: 0-20mA
55  *
56  * For ACL-6126:
57  *   0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA
58  */
59
60 #include <linux/module.h>
61 #include <linux/interrupt.h>
62
63 #include "../comedidev.h"
64
65 #define PCL726_AO_MSB_REG(x)    (0x00 + ((x) * 2))
66 #define PCL726_AO_LSB_REG(x)    (0x01 + ((x) * 2))
67 #define PCL726_DO_MSB_REG       0x0c
68 #define PCL726_DO_LSB_REG       0x0d
69 #define PCL726_DI_MSB_REG       0x0e
70 #define PCL726_DI_LSB_REG       0x0f
71
72 #define PCL727_DI_MSB_REG       0x00
73 #define PCL727_DI_LSB_REG       0x01
74 #define PCL727_DO_MSB_REG       0x18
75 #define PCL727_DO_LSB_REG       0x19
76
77 static const struct comedi_lrange *const rangelist_726[] = {
78         &range_unipolar5,
79         &range_unipolar10,
80         &range_bipolar5,
81         &range_bipolar10,
82         &range_4_20mA,
83         &range_unknown
84 };
85
86 static const struct comedi_lrange *const rangelist_727[] = {
87         &range_unipolar5,
88         &range_unipolar10,
89         &range_bipolar5,
90         &range_4_20mA
91 };
92
93 static const struct comedi_lrange *const rangelist_728[] = {
94         &range_unipolar5,
95         &range_unipolar10,
96         &range_bipolar5,
97         &range_bipolar10,
98         &range_4_20mA,
99         &range_0_20mA
100 };
101
102 struct pcl726_board {
103         const char *name;
104         unsigned long io_len;
105         unsigned int irq_mask;
106         const struct comedi_lrange *const *ao_ranges;
107         int ao_num_ranges;
108         int ao_nchan;
109         unsigned int have_dio:1;
110         unsigned int is_pcl727:1;
111 };
112
113 static const struct pcl726_board pcl726_boards[] = {
114         {
115                 .name           = "pcl726",
116                 .io_len         = 0x10,
117                 .ao_ranges      = &rangelist_726[0],
118                 .ao_num_ranges  = ARRAY_SIZE(rangelist_726),
119                 .ao_nchan       = 6,
120                 .have_dio       = 1,
121         }, {
122                 .name           = "pcl727",
123                 .io_len         = 0x20,
124                 .ao_ranges      = &rangelist_727[0],
125                 .ao_num_ranges  = ARRAY_SIZE(rangelist_727),
126                 .ao_nchan       = 12,
127                 .have_dio       = 1,
128                 .is_pcl727      = 1,
129         }, {
130                 .name           = "pcl728",
131                 .io_len         = 0x08,
132                 .ao_num_ranges  = ARRAY_SIZE(rangelist_728),
133                 .ao_ranges      = &rangelist_728[0],
134                 .ao_nchan       = 2,
135         }, {
136                 .name           = "acl6126",
137                 .io_len         = 0x10,
138                 .irq_mask       = 0x96e8,
139                 .ao_num_ranges  = ARRAY_SIZE(rangelist_726),
140                 .ao_ranges      = &rangelist_726[0],
141                 .ao_nchan       = 6,
142                 .have_dio       = 1,
143         }, {
144                 .name           = "acl6128",
145                 .io_len         = 0x08,
146                 .ao_num_ranges  = ARRAY_SIZE(rangelist_728),
147                 .ao_ranges      = &rangelist_728[0],
148                 .ao_nchan       = 2,
149         },
150 };
151
152 struct pcl726_private {
153         const struct comedi_lrange *rangelist[12];
154         unsigned int cmd_running:1;
155 };
156
157 static int pcl726_intr_insn_bits(struct comedi_device *dev,
158                                  struct comedi_subdevice *s,
159                                  struct comedi_insn *insn,
160                                  unsigned int *data)
161 {
162         data[1] = 0;
163         return insn->n;
164 }
165
166 static int pcl726_intr_cmdtest(struct comedi_device *dev,
167                                struct comedi_subdevice *s,
168                                struct comedi_cmd *cmd)
169 {
170         int err = 0;
171
172         /* Step 1 : check if triggers are trivially valid */
173
174         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
175         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
176         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
177         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
178         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
179
180         if (err)
181                 return 1;
182
183         /* Step 2a : make sure trigger sources are unique */
184         /* Step 2b : and mutually compatible */
185
186         /* Step 3: check if arguments are trivially valid */
187
188         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
189         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
190         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
191         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
192                                            cmd->chanlist_len);
193         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
194
195         if (err)
196                 return 3;
197
198         /* Step 4: fix up any arguments */
199
200         /* Step 5: check channel list if it exists */
201
202         return 0;
203 }
204
205 static int pcl726_intr_cmd(struct comedi_device *dev,
206                            struct comedi_subdevice *s)
207 {
208         struct pcl726_private *devpriv = dev->private;
209
210         devpriv->cmd_running = 1;
211
212         return 0;
213 }
214
215 static int pcl726_intr_cancel(struct comedi_device *dev,
216                               struct comedi_subdevice *s)
217 {
218         struct pcl726_private *devpriv = dev->private;
219
220         devpriv->cmd_running = 0;
221
222         return 0;
223 }
224
225 static irqreturn_t pcl726_interrupt(int irq, void *d)
226 {
227         struct comedi_device *dev = d;
228         struct comedi_subdevice *s = dev->read_subdev;
229         struct pcl726_private *devpriv = dev->private;
230
231         if (devpriv->cmd_running) {
232                 pcl726_intr_cancel(dev, s);
233
234                 comedi_buf_write_samples(s, &s->state, 1);
235                 comedi_handle_events(dev, s);
236         }
237
238         return IRQ_HANDLED;
239 }
240
241 static int pcl726_ao_insn_write(struct comedi_device *dev,
242                                 struct comedi_subdevice *s,
243                                 struct comedi_insn *insn,
244                                 unsigned int *data)
245 {
246         unsigned int chan = CR_CHAN(insn->chanspec);
247         unsigned int range = CR_RANGE(insn->chanspec);
248         int i;
249
250         for (i = 0; i < insn->n; i++) {
251                 unsigned int val = data[i];
252
253                 s->readback[chan] = val;
254
255                 /* bipolar data to the DAC is two's complement */
256                 if (comedi_chan_range_is_bipolar(s, chan, range))
257                         val = comedi_offset_munge(s, val);
258
259                 /* order is important, MSB then LSB */
260                 outb((val >> 8) & 0xff, dev->iobase + PCL726_AO_MSB_REG(chan));
261                 outb(val & 0xff, dev->iobase + PCL726_AO_LSB_REG(chan));
262         }
263
264         return insn->n;
265 }
266
267 static int pcl726_di_insn_bits(struct comedi_device *dev,
268                                struct comedi_subdevice *s,
269                                struct comedi_insn *insn,
270                                unsigned int *data)
271 {
272         const struct pcl726_board *board = dev->board_ptr;
273         unsigned int val;
274
275         if (board->is_pcl727) {
276                 val = inb(dev->iobase + PCL727_DI_LSB_REG);
277                 val |= (inb(dev->iobase + PCL727_DI_MSB_REG) << 8);
278         } else {
279                 val = inb(dev->iobase + PCL726_DI_LSB_REG);
280                 val |= (inb(dev->iobase + PCL726_DI_MSB_REG) << 8);
281         }
282
283         data[1] = val;
284
285         return insn->n;
286 }
287
288 static int pcl726_do_insn_bits(struct comedi_device *dev,
289                                struct comedi_subdevice *s,
290                                struct comedi_insn *insn,
291                                unsigned int *data)
292 {
293         const struct pcl726_board *board = dev->board_ptr;
294         unsigned long io = dev->iobase;
295         unsigned int mask;
296
297         mask = comedi_dio_update_state(s, data);
298         if (mask) {
299                 if (board->is_pcl727) {
300                         if (mask & 0x00ff)
301                                 outb(s->state & 0xff, io + PCL727_DO_LSB_REG);
302                         if (mask & 0xff00)
303                                 outb((s->state >> 8), io + PCL727_DO_MSB_REG);
304                 } else {
305                         if (mask & 0x00ff)
306                                 outb(s->state & 0xff, io + PCL726_DO_LSB_REG);
307                         if (mask & 0xff00)
308                                 outb((s->state >> 8), io + PCL726_DO_MSB_REG);
309                 }
310         }
311
312         data[1] = s->state;
313
314         return insn->n;
315 }
316
317 static int pcl726_attach(struct comedi_device *dev,
318                          struct comedi_devconfig *it)
319 {
320         const struct pcl726_board *board = dev->board_ptr;
321         struct pcl726_private *devpriv;
322         struct comedi_subdevice *s;
323         int subdev;
324         int ret;
325         int i;
326
327         ret = comedi_request_region(dev, it->options[0], board->io_len);
328         if (ret)
329                 return ret;
330
331         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
332         if (!devpriv)
333                 return -ENOMEM;
334
335         /*
336          * Hook up the external trigger source interrupt only if the
337          * user config option is valid and the board supports interrupts.
338          */
339         if (it->options[1] && (board->irq_mask & (1 << it->options[1]))) {
340                 ret = request_irq(it->options[1], pcl726_interrupt, 0,
341                                   dev->board_name, dev);
342                 if (ret == 0) {
343                         /* External trigger source is from Pin-17 of CN3 */
344                         dev->irq = it->options[1];
345                 }
346         }
347
348         /* setup the per-channel analog output range_table_list */
349         for (i = 0; i < 12; i++) {
350                 unsigned int opt = it->options[2 + i];
351
352                 if (opt < board->ao_num_ranges && i < board->ao_nchan)
353                         devpriv->rangelist[i] = board->ao_ranges[opt];
354                 else
355                         devpriv->rangelist[i] = &range_unknown;
356         }
357
358         subdev = board->have_dio ? 3 : 1;
359         if (dev->irq)
360                 subdev++;
361         ret = comedi_alloc_subdevices(dev, subdev);
362         if (ret)
363                 return ret;
364
365         subdev = 0;
366
367         /* Analog Output subdevice */
368         s = &dev->subdevices[subdev++];
369         s->type         = COMEDI_SUBD_AO;
370         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
371         s->n_chan       = board->ao_nchan;
372         s->maxdata      = 0x0fff;
373         s->range_table_list = devpriv->rangelist;
374         s->insn_write   = pcl726_ao_insn_write;
375
376         ret = comedi_alloc_subdev_readback(s);
377         if (ret)
378                 return ret;
379
380         if (board->have_dio) {
381                 /* Digital Input subdevice */
382                 s = &dev->subdevices[subdev++];
383                 s->type         = COMEDI_SUBD_DI;
384                 s->subdev_flags = SDF_READABLE;
385                 s->n_chan       = 16;
386                 s->maxdata      = 1;
387                 s->insn_bits    = pcl726_di_insn_bits;
388                 s->range_table  = &range_digital;
389
390                 /* Digital Output subdevice */
391                 s = &dev->subdevices[subdev++];
392                 s->type         = COMEDI_SUBD_DO;
393                 s->subdev_flags = SDF_WRITABLE;
394                 s->n_chan       = 16;
395                 s->maxdata      = 1;
396                 s->insn_bits    = pcl726_do_insn_bits;
397                 s->range_table  = &range_digital;
398         }
399
400         if (dev->irq) {
401                 /* Digial Input subdevice - Interrupt support */
402                 s = &dev->subdevices[subdev++];
403                 dev->read_subdev = s;
404                 s->type         = COMEDI_SUBD_DI;
405                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
406                 s->n_chan       = 1;
407                 s->maxdata      = 1;
408                 s->range_table  = &range_digital;
409                 s->insn_bits    = pcl726_intr_insn_bits;
410                 s->len_chanlist = 1;
411                 s->do_cmdtest   = pcl726_intr_cmdtest;
412                 s->do_cmd       = pcl726_intr_cmd;
413                 s->cancel       = pcl726_intr_cancel;
414         }
415
416         return 0;
417 }
418
419 static struct comedi_driver pcl726_driver = {
420         .driver_name    = "pcl726",
421         .module         = THIS_MODULE,
422         .attach         = pcl726_attach,
423         .detach         = comedi_legacy_detach,
424         .board_name     = &pcl726_boards[0].name,
425         .num_names      = ARRAY_SIZE(pcl726_boards),
426         .offset         = sizeof(struct pcl726_board),
427 };
428 module_comedi_driver(pcl726_driver);
429
430 MODULE_AUTHOR("Comedi http://www.comedi.org");
431 MODULE_DESCRIPTION("Comedi driver for Advantech PCL-726 & compatibles");
432 MODULE_LICENSE("GPL");