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