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