staging: comedi: pcl711: remove unused private data members
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / pcl711.c
1 /*
2    comedi/drivers/pcl711.c
3    hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
4    and compatibles
5
6    COMEDI - Linux Control and Measurement Device Interface
7    Copyright (C) 1998 David A. Schleef <ds@schleef.org>
8    Janne Jalkanen <jalkanen@cs.hut.fi>
9    Eric Bunn <ebu@cs.hut.fi>
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20  */
21 /*
22 Driver: pcl711
23 Description: Advantech PCL-711 and 711b, ADLink ACL-8112
24 Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
25 Status: mostly complete
26 Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
27   [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
28
29 Since these boards do not have DMA or FIFOs, only immediate mode is
30 supported.
31
32 */
33
34 /*
35    Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
36    driver for the PCL-711.  I used a few ideas from his driver
37    here.  His driver also has more comments, if you are
38    interested in understanding how this driver works.
39    http://tech.buffalostate.edu/~dave/driver/
40
41    The ACL-8112 driver was hacked from the sources of the PCL-711
42    driver (the 744 chip used on the 8112 is almost the same as
43    the 711b chip, but it has more I/O channels) by
44    Janne Jalkanen (jalkanen@cs.hut.fi) and
45    Erik Bunn (ebu@cs.hut.fi).  Remerged with the PCL-711 driver
46    by ds.
47
48    [acl-8112]
49    This driver supports both TRIGNOW and TRIGCLK,
50    but does not yet support DMA transfers.  It also supports
51    both high (HG) and low (DG) versions of the card, though
52    the HG version has been untested.
53
54  */
55
56 #include <linux/module.h>
57 #include <linux/interrupt.h>
58 #include "../comedidev.h"
59
60 #include <linux/delay.h>
61
62 #include "comedi_fc.h"
63 #include "8253.h"
64
65 /*
66  * I/O port register map
67  */
68 #define PCL711_CTR0             0x00
69 #define PCL711_CTR1             0x01
70 #define PCL711_CTR2             0x02
71 #define PCL711_CTRCTL           0x03
72 #define PCL711_AI_LSB_REG       0x04
73 #define PCL711_AI_MSB_REG       0x05
74 #define PCL711_AI_MSB_DRDY      (1 << 4)
75 #define PCL711_AO_LSB_REG(x)    (0x04 + ((x) * 2))
76 #define PCL711_AO_MSB_REG(x)    (0x05 + ((x) * 2))
77 #define PCL711_DI_LO            0x06
78 #define PCL711_DI_HI            0x07
79 #define PCL711_CLRINTR          0x08
80 #define PCL711_GAIN             0x09
81 #define PCL711_MUX_REG          0x0a
82 #define PCL711_MUX_CHAN(x)      (((x) & 0xf) << 0)
83 #define PCL711_MUX_CS0          (1 << 4)
84 #define PCL711_MUX_CS1          (1 << 5)
85 #define PCL711_MUX_DIFF         (PCL711_MUX_CS0 | PCL711_MUX_CS1)
86 #define PCL711_MODE_REG         0x0b
87 #define PCL711_MODE_DEFAULT     (0 << 0)
88 #define PCL711_MODE_SOFTTRIG    (1 << 0)
89 #define PCL711_MODE_EXT         (2 << 0)
90 #define PCL711_MODE_EXT_IRQ     (3 << 0)
91 #define PCL711_MODE_PACER       (4 << 0)
92 #define PCL711_MODE_PACER_IRQ   (6 << 0)
93 #define PCL711_MODE_IRQ(x)      (((x) & 0x7) << 4)
94 #define PCL711_SOFTTRIG_REG     0x0c
95 #define PCL711_SOFTTRIG         (0 << 0)  /* any value will work */
96 #define PCL711_DO_LO            0x0d
97 #define PCL711_DO_HI            0x0e
98
99 static const struct comedi_lrange range_pcl711b_ai = {
100         5, {
101                 BIP_RANGE(5),
102                 BIP_RANGE(2.5),
103                 BIP_RANGE(1.25),
104                 BIP_RANGE(0.625),
105                 BIP_RANGE(0.3125)
106         }
107 };
108
109 static const struct comedi_lrange range_acl8112hg_ai = {
110         12, {
111                 BIP_RANGE(5),
112                 BIP_RANGE(0.5),
113                 BIP_RANGE(0.05),
114                 BIP_RANGE(0.005),
115                 UNI_RANGE(10),
116                 UNI_RANGE(1),
117                 UNI_RANGE(0.1),
118                 UNI_RANGE(0.01),
119                 BIP_RANGE(10),
120                 BIP_RANGE(1),
121                 BIP_RANGE(0.1),
122                 BIP_RANGE(0.01)
123         }
124 };
125
126 static const struct comedi_lrange range_acl8112dg_ai = {
127         9, {
128                 BIP_RANGE(5),
129                 BIP_RANGE(2.5),
130                 BIP_RANGE(1.25),
131                 BIP_RANGE(0.625),
132                 UNI_RANGE(10),
133                 UNI_RANGE(5),
134                 UNI_RANGE(2.5),
135                 UNI_RANGE(1.25),
136                 BIP_RANGE(10)
137         }
138 };
139
140 static const int i8253_osc_base = 500;  /* 2 Mhz */
141
142 struct pcl711_board {
143         const char *name;
144         int n_aichan;
145         int n_aochan;
146         int maxirq;
147         const struct comedi_lrange *ai_range_type;
148 };
149
150 static const struct pcl711_board boardtypes[] = {
151         {
152                 .name           = "pcl711",
153                 .n_aichan       = 8,
154                 .n_aochan       = 1,
155                 .ai_range_type  = &range_bipolar5,
156         }, {
157                 .name           = "pcl711b",
158                 .n_aichan       = 8,
159                 .n_aochan       = 1,
160                 .maxirq         = 7,
161                 .ai_range_type  = &range_pcl711b_ai,
162         }, {
163                 .name           = "acl8112hg",
164                 .n_aichan       = 16,
165                 .n_aochan       = 2,
166                 .maxirq         = 15,
167                 .ai_range_type  = &range_acl8112hg_ai,
168         }, {
169                 .name           = "acl8112dg",
170                 .n_aichan       = 16,
171                 .n_aochan       = 2,
172                 .maxirq         = 15,
173                 .ai_range_type  = &range_acl8112dg_ai,
174         },
175 };
176
177 struct pcl711_private {
178         int ntrig;
179         unsigned int ao_readback[2];
180         unsigned int divisor1;
181         unsigned int divisor2;
182 };
183
184 static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode)
185 {
186         /*
187          * The pcl711b board uses bits in the mode register to select the
188          * interrupt. The other boards supported by this driver all use
189          * jumpers on the board.
190          *
191          * Enables the interrupt when needed on the pcl711b board. These
192          * bits do nothing on the other boards.
193          */
194         if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ)
195                 mode |= PCL711_MODE_IRQ(dev->irq);
196
197         outb(mode, dev->iobase + PCL711_MODE_REG);
198 }
199
200 static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
201                                          struct comedi_subdevice *s)
202 {
203         unsigned int val;
204
205         val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
206         val |= inb(dev->iobase + PCL711_AI_LSB_REG);
207
208         return val & s->maxdata;
209 }
210
211 static irqreturn_t pcl711_interrupt(int irq, void *d)
212 {
213         struct comedi_device *dev = d;
214         struct pcl711_private *devpriv = dev->private;
215         struct comedi_subdevice *s = dev->read_subdev;
216         unsigned int data;
217
218         if (!dev->attached) {
219                 comedi_error(dev, "spurious interrupt");
220                 return IRQ_HANDLED;
221         }
222
223         data = pcl711_ai_get_sample(dev, s);
224
225         outb(0, dev->iobase + PCL711_CLRINTR);
226
227         /* FIXME! Nothing else sets ntrig! */
228         if (!(--devpriv->ntrig)) {
229                 pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
230
231                 s->async->events |= COMEDI_CB_EOA;
232         }
233         comedi_event(dev, s);
234         return IRQ_HANDLED;
235 }
236
237 static void pcl711_set_changain(struct comedi_device *dev,
238                                 struct comedi_subdevice *s,
239                                 unsigned int chanspec)
240 {
241         unsigned int chan = CR_CHAN(chanspec);
242         unsigned int range = CR_RANGE(chanspec);
243         unsigned int aref = CR_AREF(chanspec);
244         unsigned int mux = 0;
245
246         outb(range, dev->iobase + PCL711_GAIN);
247
248         if (s->n_chan > 8) {
249                 /* Select the correct MPC508A chip */
250                 if (aref == AREF_DIFF) {
251                         chan &= 0x7;
252                         mux |= PCL711_MUX_DIFF;
253                 } else {
254                         if (chan < 8)
255                                 mux |= PCL711_MUX_CS0;
256                         else
257                                 mux |= PCL711_MUX_CS1;
258                 }
259         }
260         outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG);
261 }
262
263 static int pcl711_ai_wait_for_eoc(struct comedi_device *dev,
264                                   unsigned int timeout)
265 {
266         unsigned int msb;
267
268         while (timeout--) {
269                 msb = inb(dev->iobase + PCL711_AI_MSB_REG);
270                 if ((msb & PCL711_AI_MSB_DRDY) == 0)
271                         return 0;
272                 udelay(1);
273         }
274         return -ETIME;
275 }
276
277 static int pcl711_ai_insn_read(struct comedi_device *dev,
278                                struct comedi_subdevice *s,
279                                struct comedi_insn *insn,
280                                unsigned int *data)
281 {
282         int ret;
283         int i;
284
285         pcl711_set_changain(dev, s, insn->chanspec);
286
287         pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
288
289         for (i = 0; i < insn->n; i++) {
290                 outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG);
291
292                 ret = pcl711_ai_wait_for_eoc(dev, 100);
293                 if (ret)
294                         return ret;
295
296                 data[i] = pcl711_ai_get_sample(dev, s);
297         }
298
299         return insn->n;
300 }
301
302 static int pcl711_ai_cmdtest(struct comedi_device *dev,
303                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
304 {
305         struct pcl711_private *devpriv = dev->private;
306         int tmp;
307         int err = 0;
308
309         /* Step 1 : check if triggers are trivially valid */
310
311         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
312         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
313                                         TRIG_TIMER | TRIG_EXT);
314         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
315         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
317
318         if (err)
319                 return 1;
320
321         /* Step 2a : make sure trigger sources are unique */
322
323         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
324         err |= cfc_check_trigger_is_unique(cmd->stop_src);
325
326         /* Step 2b : and mutually compatible */
327
328         if (err)
329                 return 2;
330
331         /* Step 3: check if arguments are trivially valid */
332
333         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
334
335         if (cmd->scan_begin_src == TRIG_EXT) {
336                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
337         } else {
338 #define MAX_SPEED 1000
339 #define TIMER_BASE 100
340                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
341                                                  MAX_SPEED);
342         }
343
344         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
345         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
346
347         if (cmd->stop_src == TRIG_NONE) {
348                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
349         } else {
350                 /* ignore */
351         }
352
353         if (err)
354                 return 3;
355
356         /* step 4 */
357
358         if (cmd->scan_begin_src == TRIG_TIMER) {
359                 tmp = cmd->scan_begin_arg;
360                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
361                                                &devpriv->divisor1,
362                                                &devpriv->divisor2,
363                                                &cmd->scan_begin_arg,
364                                                cmd->flags & TRIG_ROUND_MASK);
365                 if (tmp != cmd->scan_begin_arg)
366                         err++;
367         }
368
369         if (err)
370                 return 4;
371
372         return 0;
373 }
374
375 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
376 {
377         int timer1, timer2;
378         struct comedi_cmd *cmd = &s->async->cmd;
379
380         pcl711_set_changain(dev, s, cmd->chanlist[0]);
381
382         if (cmd->scan_begin_src == TRIG_TIMER) {
383                 /*
384                  *  Set timers
385                  *      timer chip is an 8253, with timers 1 and 2
386                  *      cascaded
387                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
388                  *        Mode 2 = Rate generator
389                  *
390                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
391                  */
392
393                 timer1 = timer2 = 0;
394                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
395                                           &cmd->scan_begin_arg,
396                                           TRIG_ROUND_NEAREST);
397
398                 outb(0x74, dev->iobase + PCL711_CTRCTL);
399                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
400                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
401                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
402                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
403                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
404
405                 /* clear pending interrupts (just in case) */
406                 outb(0, dev->iobase + PCL711_CLRINTR);
407
408                 pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ);
409         } else {
410                 pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ);
411         }
412
413         return 0;
414 }
415
416 static void pcl711_ao_write(struct comedi_device *dev,
417                             unsigned int chan, unsigned int val)
418 {
419         outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
420         outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
421 }
422
423 static int pcl711_ao_insn_write(struct comedi_device *dev,
424                                 struct comedi_subdevice *s,
425                                 struct comedi_insn *insn,
426                                 unsigned int *data)
427 {
428         struct pcl711_private *devpriv = dev->private;
429         unsigned int chan = CR_CHAN(insn->chanspec);
430         unsigned int val = devpriv->ao_readback[chan];
431         int i;
432
433         for (i = 0; i < insn->n; i++) {
434                 val = data[i];
435                 pcl711_ao_write(dev, chan, val);
436         }
437         devpriv->ao_readback[chan] = val;
438
439         return insn->n;
440 }
441
442 static int pcl711_ao_insn_read(struct comedi_device *dev,
443                                struct comedi_subdevice *s,
444                                struct comedi_insn *insn,
445                                unsigned int *data)
446 {
447         struct pcl711_private *devpriv = dev->private;
448         unsigned int chan = CR_CHAN(insn->chanspec);
449         int i;
450
451         for (i = 0; i < insn->n; i++)
452                 data[i] = devpriv->ao_readback[chan];
453
454         return insn->n;
455 }
456
457 /* Digital port read - Untested on 8112 */
458 static int pcl711_di_insn_bits(struct comedi_device *dev,
459                                struct comedi_subdevice *s,
460                                struct comedi_insn *insn, unsigned int *data)
461 {
462         data[1] = inb(dev->iobase + PCL711_DI_LO) |
463             (inb(dev->iobase + PCL711_DI_HI) << 8);
464
465         return insn->n;
466 }
467
468 static int pcl711_do_insn_bits(struct comedi_device *dev,
469                                struct comedi_subdevice *s,
470                                struct comedi_insn *insn,
471                                unsigned int *data)
472 {
473         unsigned int mask;
474
475         mask = comedi_dio_update_state(s, data);
476         if (mask) {
477                 if (mask & 0x00ff)
478                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
479                 if (mask & 0xff00)
480                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
481         }
482
483         data[1] = s->state;
484
485         return insn->n;
486 }
487
488 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
489 {
490         const struct pcl711_board *board = comedi_board(dev);
491         struct pcl711_private *devpriv;
492         struct comedi_subdevice *s;
493         int ret;
494
495         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
496         if (!devpriv)
497                 return -ENOMEM;
498
499         ret = comedi_request_region(dev, it->options[0], 0x10);
500         if (ret)
501                 return ret;
502
503         if (it->options[1] && it->options[1] <= board->maxirq) {
504                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
505                                   dev->board_name, dev);
506                 if (ret == 0)
507                         dev->irq = it->options[1];
508         }
509
510         ret = comedi_alloc_subdevices(dev, 4);
511         if (ret)
512                 return ret;
513
514         /* Analog Input subdevice */
515         s = &dev->subdevices[0];
516         s->type         = COMEDI_SUBD_AI;
517         s->subdev_flags = SDF_READABLE | SDF_GROUND;
518         if (board->n_aichan > 8)
519                 s->subdev_flags |= SDF_DIFF;
520         s->n_chan       = board->n_aichan;
521         s->maxdata      = 0xfff;
522         s->range_table  = board->ai_range_type;
523         s->insn_read    = pcl711_ai_insn_read;
524         if (dev->irq) {
525                 dev->read_subdev = s;
526                 s->subdev_flags |= SDF_CMD_READ;
527                 s->len_chanlist = 1;
528                 s->do_cmdtest   = pcl711_ai_cmdtest;
529                 s->do_cmd       = pcl711_ai_cmd;
530         }
531
532         /* Analog Output subdevice */
533         s = &dev->subdevices[1];
534         s->type         = COMEDI_SUBD_AO;
535         s->subdev_flags = SDF_WRITABLE;
536         s->n_chan       = board->n_aochan;
537         s->maxdata      = 0xfff;
538         s->range_table  = &range_bipolar5;
539         s->insn_write   = pcl711_ao_insn_write;
540         s->insn_read    = pcl711_ao_insn_read;
541
542         /* Digital Input subdevice */
543         s = &dev->subdevices[2];
544         s->type         = COMEDI_SUBD_DI;
545         s->subdev_flags = SDF_READABLE;
546         s->n_chan       = 16;
547         s->maxdata      = 1;
548         s->range_table  = &range_digital;
549         s->insn_bits    = pcl711_di_insn_bits;
550
551         /* Digital Output subdevice */
552         s = &dev->subdevices[3];
553         s->type         = COMEDI_SUBD_DO;
554         s->subdev_flags = SDF_WRITABLE;
555         s->n_chan       = 16;
556         s->maxdata      = 1;
557         s->range_table  = &range_digital;
558         s->insn_bits    = pcl711_do_insn_bits;
559
560         /* clear DAC */
561         pcl711_ao_write(dev, 0, 0x0);
562         pcl711_ao_write(dev, 1, 0x0);
563
564         return 0;
565 }
566
567 static struct comedi_driver pcl711_driver = {
568         .driver_name    = "pcl711",
569         .module         = THIS_MODULE,
570         .attach         = pcl711_attach,
571         .detach         = comedi_legacy_detach,
572         .board_name     = &boardtypes[0].name,
573         .num_names      = ARRAY_SIZE(boardtypes),
574         .offset         = sizeof(struct pcl711_board),
575 };
576 module_comedi_driver(pcl711_driver);
577
578 MODULE_AUTHOR("Comedi http://www.comedi.org");
579 MODULE_DESCRIPTION("Comedi low-level driver");
580 MODULE_LICENSE("GPL");