staging: comedi: pcl711: tidy up the register map defines
[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_AD_LO            0x04
73 #define PCL711_DA0_LO           0x04
74 #define PCL711_AD_HI            0x05
75 #define PCL711_DA0_HI           0x05
76 #define PCL711_DI_LO            0x06
77 #define PCL711_DA1_LO           0x06
78 #define PCL711_DI_HI            0x07
79 #define PCL711_DA1_HI           0x07
80 #define PCL711_CLRINTR          0x08
81 #define PCL711_GAIN             0x09
82 #define PCL711_MUX              0x0a
83 #define PCL711_MODE             0x0b
84 #define PCL711_SOFTTRIG         0x0c
85 #define PCL711_DO_LO            0x0d
86 #define PCL711_DO_HI            0x0e
87
88 static const struct comedi_lrange range_pcl711b_ai = {
89         5, {
90                 BIP_RANGE(5),
91                 BIP_RANGE(2.5),
92                 BIP_RANGE(1.25),
93                 BIP_RANGE(0.625),
94                 BIP_RANGE(0.3125)
95         }
96 };
97
98 static const struct comedi_lrange range_acl8112hg_ai = {
99         12, {
100                 BIP_RANGE(5),
101                 BIP_RANGE(0.5),
102                 BIP_RANGE(0.05),
103                 BIP_RANGE(0.005),
104                 UNI_RANGE(10),
105                 UNI_RANGE(1),
106                 UNI_RANGE(0.1),
107                 UNI_RANGE(0.01),
108                 BIP_RANGE(10),
109                 BIP_RANGE(1),
110                 BIP_RANGE(0.1),
111                 BIP_RANGE(0.01)
112         }
113 };
114
115 static const struct comedi_lrange range_acl8112dg_ai = {
116         9, {
117                 BIP_RANGE(5),
118                 BIP_RANGE(2.5),
119                 BIP_RANGE(1.25),
120                 BIP_RANGE(0.625),
121                 UNI_RANGE(10),
122                 UNI_RANGE(5),
123                 UNI_RANGE(2.5),
124                 UNI_RANGE(1.25),
125                 BIP_RANGE(10)
126         }
127 };
128
129 /*
130  * flags
131  */
132
133 #define PCL711_TIMEOUT 100
134 #define PCL711_DRDY 0x10
135
136 static const int i8253_osc_base = 500;  /* 2 Mhz */
137
138 struct pcl711_board {
139         const char *name;
140         unsigned int is_pcl711b:1;
141         unsigned int is_8112:1;
142         int n_aichan;
143         int n_aochan;
144         int maxirq;
145         const struct comedi_lrange *ai_range_type;
146 };
147
148 static const struct pcl711_board boardtypes[] = {
149         {
150                 .name           = "pcl711",
151                 .n_aichan       = 8,
152                 .n_aochan       = 1,
153                 .ai_range_type  = &range_bipolar5,
154         }, {
155                 .name           = "pcl711b",
156                 .is_pcl711b     = 1,
157                 .n_aichan       = 8,
158                 .n_aochan       = 1,
159                 .maxirq         = 7,
160                 .ai_range_type  = &range_pcl711b_ai,
161         }, {
162                 .name           = "acl8112hg",
163                 .is_8112        = 1,
164                 .n_aichan       = 16,
165                 .n_aochan       = 2,
166                 .maxirq         = 15,
167                 .ai_range_type  = &range_acl8112hg_ai,
168         }, {
169                 .name           = "acl8112dg",
170                 .is_8112        = 1,
171                 .n_aichan       = 16,
172                 .n_aochan       = 2,
173                 .maxirq         = 15,
174                 .ai_range_type  = &range_acl8112dg_ai,
175         },
176 };
177
178 struct pcl711_private {
179
180         int board;
181         int adchan;
182         int ntrig;
183         int aip[8];
184         int mode;
185         unsigned int ao_readback[2];
186         unsigned int divisor1;
187         unsigned int divisor2;
188 };
189
190 static irqreturn_t pcl711_interrupt(int irq, void *d)
191 {
192         int lo, hi;
193         int data;
194         struct comedi_device *dev = d;
195         const struct pcl711_board *board = comedi_board(dev);
196         struct pcl711_private *devpriv = dev->private;
197         struct comedi_subdevice *s = &dev->subdevices[0];
198
199         if (!dev->attached) {
200                 comedi_error(dev, "spurious interrupt");
201                 return IRQ_HANDLED;
202         }
203
204         hi = inb(dev->iobase + PCL711_AD_HI);
205         lo = inb(dev->iobase + PCL711_AD_LO);
206         outb(0, dev->iobase + PCL711_CLRINTR);
207
208         data = (hi << 8) | lo;
209
210         /* FIXME! Nothing else sets ntrig! */
211         if (!(--devpriv->ntrig)) {
212                 if (board->is_8112)
213                         outb(1, dev->iobase + PCL711_MODE);
214                 else
215                         outb(0, dev->iobase + PCL711_MODE);
216
217                 s->async->events |= COMEDI_CB_EOA;
218         }
219         comedi_event(dev, s);
220         return IRQ_HANDLED;
221 }
222
223 static void pcl711_set_changain(struct comedi_device *dev, int chan)
224 {
225         const struct pcl711_board *board = comedi_board(dev);
226         int chan_register;
227
228         outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
229
230         chan_register = CR_CHAN(chan);
231
232         if (board->is_8112) {
233
234                 /*
235                  *  Set the correct channel.  The two channel banks are switched
236                  *  using the mask value.
237                  *  NB: To use differential channels, you should use
238                  *  mask = 0x30, but I haven't written the support for this
239                  *  yet. /JJ
240                  */
241
242                 if (chan_register >= 8)
243                         chan_register = 0x20 | (chan_register & 0x7);
244                 else
245                         chan_register |= 0x10;
246         } else {
247                 outb(chan_register, dev->iobase + PCL711_MUX);
248         }
249 }
250
251 static int pcl711_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
252                           struct comedi_insn *insn, unsigned int *data)
253 {
254         const struct pcl711_board *board = comedi_board(dev);
255         int i, n;
256         int hi, lo;
257
258         pcl711_set_changain(dev, insn->chanspec);
259
260         for (n = 0; n < insn->n; n++) {
261                 /*
262                  *  Write the correct mode (software polling) and start polling
263                  *  by writing to the trigger register
264                  */
265                 outb(1, dev->iobase + PCL711_MODE);
266
267                 if (!board->is_8112)
268                         outb(0, dev->iobase + PCL711_SOFTTRIG);
269
270                 i = PCL711_TIMEOUT;
271                 while (--i) {
272                         hi = inb(dev->iobase + PCL711_AD_HI);
273                         if (!(hi & PCL711_DRDY))
274                                 goto ok;
275                         udelay(1);
276                 }
277                 printk(KERN_ERR "comedi%d: pcl711: A/D timeout\n", dev->minor);
278                 return -ETIME;
279
280 ok:
281                 lo = inb(dev->iobase + PCL711_AD_LO);
282
283                 data[n] = ((hi & 0xf) << 8) | lo;
284         }
285
286         return n;
287 }
288
289 static int pcl711_ai_cmdtest(struct comedi_device *dev,
290                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
291 {
292         struct pcl711_private *devpriv = dev->private;
293         int tmp;
294         int err = 0;
295
296         /* Step 1 : check if triggers are trivially valid */
297
298         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
299         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
300                                         TRIG_TIMER | TRIG_EXT);
301         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
302         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
303         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
304
305         if (err)
306                 return 1;
307
308         /* Step 2a : make sure trigger sources are unique */
309
310         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
311         err |= cfc_check_trigger_is_unique(cmd->stop_src);
312
313         /* Step 2b : and mutually compatible */
314
315         if (err)
316                 return 2;
317
318         /* Step 3: check if arguments are trivially valid */
319
320         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
321
322         if (cmd->scan_begin_src == TRIG_EXT) {
323                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
324         } else {
325 #define MAX_SPEED 1000
326 #define TIMER_BASE 100
327                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
328                                                  MAX_SPEED);
329         }
330
331         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
332         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
333
334         if (cmd->stop_src == TRIG_NONE) {
335                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
336         } else {
337                 /* ignore */
338         }
339
340         if (err)
341                 return 3;
342
343         /* step 4 */
344
345         if (cmd->scan_begin_src == TRIG_TIMER) {
346                 tmp = cmd->scan_begin_arg;
347                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
348                                                &devpriv->divisor1,
349                                                &devpriv->divisor2,
350                                                &cmd->scan_begin_arg,
351                                                cmd->flags & TRIG_ROUND_MASK);
352                 if (tmp != cmd->scan_begin_arg)
353                         err++;
354         }
355
356         if (err)
357                 return 4;
358
359         return 0;
360 }
361
362 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
363 {
364         struct pcl711_private *devpriv = dev->private;
365         int timer1, timer2;
366         struct comedi_cmd *cmd = &s->async->cmd;
367
368         pcl711_set_changain(dev, cmd->chanlist[0]);
369
370         if (cmd->scan_begin_src == TRIG_TIMER) {
371                 /*
372                  *  Set timers
373                  *      timer chip is an 8253, with timers 1 and 2
374                  *      cascaded
375                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
376                  *        Mode 2 = Rate generator
377                  *
378                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
379                  */
380
381                 timer1 = timer2 = 0;
382                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
383                                           &cmd->scan_begin_arg,
384                                           TRIG_ROUND_NEAREST);
385
386                 outb(0x74, dev->iobase + PCL711_CTRCTL);
387                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
388                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
389                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
390                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
391                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
392
393                 /* clear pending interrupts (just in case) */
394                 outb(0, dev->iobase + PCL711_CLRINTR);
395
396                 /*
397                  *  Set mode to IRQ transfer
398                  */
399                 outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
400         } else {
401                 /* external trigger */
402                 outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
403         }
404
405         return 0;
406 }
407
408 /*
409    analog output
410 */
411 static int pcl711_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
412                           struct comedi_insn *insn, unsigned int *data)
413 {
414         struct pcl711_private *devpriv = dev->private;
415         int n;
416         int chan = CR_CHAN(insn->chanspec);
417
418         for (n = 0; n < insn->n; n++) {
419                 outb((data[n] & 0xff),
420                      dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
421                 outb((data[n] >> 8),
422                      dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
423
424                 devpriv->ao_readback[chan] = data[n];
425         }
426
427         return n;
428 }
429
430 static int pcl711_ao_insn_read(struct comedi_device *dev,
431                                struct comedi_subdevice *s,
432                                struct comedi_insn *insn, unsigned int *data)
433 {
434         struct pcl711_private *devpriv = dev->private;
435         int n;
436         int chan = CR_CHAN(insn->chanspec);
437
438         for (n = 0; n < insn->n; n++)
439                 data[n] = devpriv->ao_readback[chan];
440
441         return n;
442
443 }
444
445 /* Digital port read - Untested on 8112 */
446 static int pcl711_di_insn_bits(struct comedi_device *dev,
447                                struct comedi_subdevice *s,
448                                struct comedi_insn *insn, unsigned int *data)
449 {
450         data[1] = inb(dev->iobase + PCL711_DI_LO) |
451             (inb(dev->iobase + PCL711_DI_HI) << 8);
452
453         return insn->n;
454 }
455
456 static int pcl711_do_insn_bits(struct comedi_device *dev,
457                                struct comedi_subdevice *s,
458                                struct comedi_insn *insn,
459                                unsigned int *data)
460 {
461         unsigned int mask;
462
463         mask = comedi_dio_update_state(s, data);
464         if (mask) {
465                 if (mask & 0x00ff)
466                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
467                 if (mask & 0xff00)
468                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
469         }
470
471         data[1] = s->state;
472
473         return insn->n;
474 }
475
476 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
477 {
478         const struct pcl711_board *board = comedi_board(dev);
479         struct pcl711_private *devpriv;
480         struct comedi_subdevice *s;
481         int ret;
482
483         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
484         if (!devpriv)
485                 return -ENOMEM;
486
487         ret = comedi_request_region(dev, it->options[0], 0x10);
488         if (ret)
489                 return ret;
490
491         if (it->options[1] && it->options[1] <= board->maxirq) {
492                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
493                                   dev->board_name, dev);
494                 if (ret == 0) {
495                         dev->irq = it->options[1];
496
497                         /*
498                          * The PCL711b needs the irq number in the
499                          * mode register.
500                          */
501                         if (board->is_pcl711b)
502                                 devpriv->mode = (dev->irq << 4);
503                 }
504         }
505
506         ret = comedi_alloc_subdevices(dev, 4);
507         if (ret)
508                 return ret;
509
510         /* Analog Input subdevice */
511         s = &dev->subdevices[0];
512         s->type         = COMEDI_SUBD_AI;
513         s->subdev_flags = SDF_READABLE | SDF_GROUND;
514         s->n_chan       = board->n_aichan;
515         s->maxdata      = 0xfff;
516         s->range_table  = board->ai_range_type;
517         s->insn_read    = pcl711_ai_insn;
518         if (dev->irq) {
519                 dev->read_subdev = s;
520                 s->subdev_flags |= SDF_CMD_READ;
521                 s->len_chanlist = 1;
522                 s->do_cmdtest   = pcl711_ai_cmdtest;
523                 s->do_cmd       = pcl711_ai_cmd;
524         }
525
526         /* Analog Output subdevice */
527         s = &dev->subdevices[1];
528         s->type         = COMEDI_SUBD_AO;
529         s->subdev_flags = SDF_WRITABLE;
530         s->n_chan       = board->n_aochan;
531         s->maxdata      = 0xfff;
532         s->range_table  = &range_bipolar5;
533         s->insn_write   = pcl711_ao_insn;
534         s->insn_read    = pcl711_ao_insn_read;
535
536         /* Digital Input subdevice */
537         s = &dev->subdevices[2];
538         s->type         = COMEDI_SUBD_DI;
539         s->subdev_flags = SDF_READABLE;
540         s->n_chan       = 16;
541         s->maxdata      = 1;
542         s->range_table  = &range_digital;
543         s->insn_bits    = pcl711_di_insn_bits;
544
545         /* Digital Output subdevice */
546         s = &dev->subdevices[3];
547         s->type         = COMEDI_SUBD_DO;
548         s->subdev_flags = SDF_WRITABLE;
549         s->n_chan       = 16;
550         s->maxdata      = 1;
551         s->range_table  = &range_digital;
552         s->insn_bits    = pcl711_do_insn_bits;
553
554         /* clear DAC */
555         outb(0, dev->iobase + PCL711_DA0_LO);
556         outb(0, dev->iobase + PCL711_DA0_HI);
557         outb(0, dev->iobase + PCL711_DA1_LO);
558         outb(0, dev->iobase + PCL711_DA1_HI);
559
560         printk(KERN_INFO "\n");
561
562         return 0;
563 }
564
565 static struct comedi_driver pcl711_driver = {
566         .driver_name    = "pcl711",
567         .module         = THIS_MODULE,
568         .attach         = pcl711_attach,
569         .detach         = comedi_legacy_detach,
570         .board_name     = &boardtypes[0].name,
571         .num_names      = ARRAY_SIZE(boardtypes),
572         .offset         = sizeof(struct pcl711_board),
573 };
574 module_comedi_driver(pcl711_driver);
575
576 MODULE_AUTHOR("Comedi http://www.comedi.org");
577 MODULE_DESCRIPTION("Comedi low-level driver");
578 MODULE_LICENSE("GPL");