staging: comedi: pcl711: tidy up pcl711_ai_insn()
[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         unsigned int is_pcl711b:1;
145         unsigned int is_8112:1;
146         int n_aichan;
147         int n_aochan;
148         int maxirq;
149         const struct comedi_lrange *ai_range_type;
150 };
151
152 static const struct pcl711_board boardtypes[] = {
153         {
154                 .name           = "pcl711",
155                 .n_aichan       = 8,
156                 .n_aochan       = 1,
157                 .ai_range_type  = &range_bipolar5,
158         }, {
159                 .name           = "pcl711b",
160                 .is_pcl711b     = 1,
161                 .n_aichan       = 8,
162                 .n_aochan       = 1,
163                 .maxirq         = 7,
164                 .ai_range_type  = &range_pcl711b_ai,
165         }, {
166                 .name           = "acl8112hg",
167                 .is_8112        = 1,
168                 .n_aichan       = 16,
169                 .n_aochan       = 2,
170                 .maxirq         = 15,
171                 .ai_range_type  = &range_acl8112hg_ai,
172         }, {
173                 .name           = "acl8112dg",
174                 .is_8112        = 1,
175                 .n_aichan       = 16,
176                 .n_aochan       = 2,
177                 .maxirq         = 15,
178                 .ai_range_type  = &range_acl8112dg_ai,
179         },
180 };
181
182 struct pcl711_private {
183
184         int board;
185         int adchan;
186         int ntrig;
187         int aip[8];
188         int mode;
189         unsigned int ao_readback[2];
190         unsigned int divisor1;
191         unsigned int divisor2;
192 };
193
194 static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode)
195 {
196         struct pcl711_private *devpriv = dev->private;
197
198         if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ)
199                 mode |= devpriv->mode;
200
201         outb(mode, dev->iobase + PCL711_MODE_REG);
202 }
203
204 static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
205                                          struct comedi_subdevice *s)
206 {
207         unsigned int val;
208
209         val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
210         val |= inb(dev->iobase + PCL711_AI_LSB_REG);
211
212         return val & s->maxdata;
213 }
214
215 static irqreturn_t pcl711_interrupt(int irq, void *d)
216 {
217         struct comedi_device *dev = d;
218         struct pcl711_private *devpriv = dev->private;
219         struct comedi_subdevice *s = dev->read_subdev;
220         unsigned int data;
221
222         if (!dev->attached) {
223                 comedi_error(dev, "spurious interrupt");
224                 return IRQ_HANDLED;
225         }
226
227         data = pcl711_ai_get_sample(dev, s);
228
229         outb(0, dev->iobase + PCL711_CLRINTR);
230
231         /* FIXME! Nothing else sets ntrig! */
232         if (!(--devpriv->ntrig)) {
233                 pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
234
235                 s->async->events |= COMEDI_CB_EOA;
236         }
237         comedi_event(dev, s);
238         return IRQ_HANDLED;
239 }
240
241 static void pcl711_set_changain(struct comedi_device *dev,
242                                 unsigned int chanspec)
243 {
244         const struct pcl711_board *board = comedi_board(dev);
245         unsigned int chan = CR_CHAN(chanspec);
246         unsigned int range = CR_RANGE(chanspec);
247         unsigned int aref = CR_AREF(chanspec);
248         unsigned int mux = 0;
249
250         outb(range, dev->iobase + PCL711_GAIN);
251
252         if (board->is_8112) {
253                 /* Select the correct MPC508A chip */
254                 if (aref == AREF_DIFF) {
255                         chan &= 0x7;
256                         mux |= PCL711_MUX_DIFF;
257                 } else {
258                         if (chan < 8)
259                                 mux |= PCL711_MUX_CS0;
260                         else
261                                 mux |= PCL711_MUX_CS1;
262                 }
263         }
264         outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG);
265 }
266
267 static int pcl711_ai_wait_for_eoc(struct comedi_device *dev,
268                                   unsigned int timeout)
269 {
270         unsigned int msb;
271
272         while (timeout--) {
273                 msb = inb(dev->iobase + PCL711_AI_MSB_REG);
274                 if ((msb & PCL711_AI_MSB_DRDY) == 0)
275                         return 0;
276                 udelay(1);
277         }
278         return -ETIME;
279 }
280
281 static int pcl711_ai_insn_read(struct comedi_device *dev,
282                                struct comedi_subdevice *s,
283                                struct comedi_insn *insn,
284                                unsigned int *data)
285 {
286         int ret;
287         int i;
288
289         pcl711_set_changain(dev, insn->chanspec);
290
291         pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
292
293         for (i = 0; i < insn->n; i++) {
294                 outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG);
295
296                 ret = pcl711_ai_wait_for_eoc(dev, 100);
297                 if (ret)
298                         return ret;
299
300                 data[i] = pcl711_ai_get_sample(dev, s);
301         }
302
303         return insn->n;
304 }
305
306 static int pcl711_ai_cmdtest(struct comedi_device *dev,
307                              struct comedi_subdevice *s, struct comedi_cmd *cmd)
308 {
309         struct pcl711_private *devpriv = dev->private;
310         int tmp;
311         int err = 0;
312
313         /* Step 1 : check if triggers are trivially valid */
314
315         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
316         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
317                                         TRIG_TIMER | TRIG_EXT);
318         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
319         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
320         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
321
322         if (err)
323                 return 1;
324
325         /* Step 2a : make sure trigger sources are unique */
326
327         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
328         err |= cfc_check_trigger_is_unique(cmd->stop_src);
329
330         /* Step 2b : and mutually compatible */
331
332         if (err)
333                 return 2;
334
335         /* Step 3: check if arguments are trivially valid */
336
337         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
338
339         if (cmd->scan_begin_src == TRIG_EXT) {
340                 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
341         } else {
342 #define MAX_SPEED 1000
343 #define TIMER_BASE 100
344                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
345                                                  MAX_SPEED);
346         }
347
348         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
349         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
350
351         if (cmd->stop_src == TRIG_NONE) {
352                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
353         } else {
354                 /* ignore */
355         }
356
357         if (err)
358                 return 3;
359
360         /* step 4 */
361
362         if (cmd->scan_begin_src == TRIG_TIMER) {
363                 tmp = cmd->scan_begin_arg;
364                 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
365                                                &devpriv->divisor1,
366                                                &devpriv->divisor2,
367                                                &cmd->scan_begin_arg,
368                                                cmd->flags & TRIG_ROUND_MASK);
369                 if (tmp != cmd->scan_begin_arg)
370                         err++;
371         }
372
373         if (err)
374                 return 4;
375
376         return 0;
377 }
378
379 static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
380 {
381         int timer1, timer2;
382         struct comedi_cmd *cmd = &s->async->cmd;
383
384         pcl711_set_changain(dev, cmd->chanlist[0]);
385
386         if (cmd->scan_begin_src == TRIG_TIMER) {
387                 /*
388                  *  Set timers
389                  *      timer chip is an 8253, with timers 1 and 2
390                  *      cascaded
391                  *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
392                  *        Mode 2 = Rate generator
393                  *
394                  *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
395                  */
396
397                 timer1 = timer2 = 0;
398                 i8253_cascade_ns_to_timer(i8253_osc_base, &timer1, &timer2,
399                                           &cmd->scan_begin_arg,
400                                           TRIG_ROUND_NEAREST);
401
402                 outb(0x74, dev->iobase + PCL711_CTRCTL);
403                 outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
404                 outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
405                 outb(0xb4, dev->iobase + PCL711_CTRCTL);
406                 outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
407                 outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
408
409                 /* clear pending interrupts (just in case) */
410                 outb(0, dev->iobase + PCL711_CLRINTR);
411
412                 pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ);
413         } else {
414                 pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ);
415         }
416
417         return 0;
418 }
419
420 static void pcl711_ao_write(struct comedi_device *dev,
421                             unsigned int chan, unsigned int val)
422 {
423         outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
424         outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
425 }
426
427 static int pcl711_ao_insn_write(struct comedi_device *dev,
428                                 struct comedi_subdevice *s,
429                                 struct comedi_insn *insn,
430                                 unsigned int *data)
431 {
432         struct pcl711_private *devpriv = dev->private;
433         unsigned int chan = CR_CHAN(insn->chanspec);
434         unsigned int val = devpriv->ao_readback[chan];
435         int i;
436
437         for (i = 0; i < insn->n; i++) {
438                 val = data[i];
439                 pcl711_ao_write(dev, chan, val);
440         }
441         devpriv->ao_readback[chan] = val;
442
443         return insn->n;
444 }
445
446 static int pcl711_ao_insn_read(struct comedi_device *dev,
447                                struct comedi_subdevice *s,
448                                struct comedi_insn *insn,
449                                unsigned int *data)
450 {
451         struct pcl711_private *devpriv = dev->private;
452         unsigned int chan = CR_CHAN(insn->chanspec);
453         int i;
454
455         for (i = 0; i < insn->n; i++)
456                 data[i] = devpriv->ao_readback[chan];
457
458         return insn->n;
459 }
460
461 /* Digital port read - Untested on 8112 */
462 static int pcl711_di_insn_bits(struct comedi_device *dev,
463                                struct comedi_subdevice *s,
464                                struct comedi_insn *insn, unsigned int *data)
465 {
466         data[1] = inb(dev->iobase + PCL711_DI_LO) |
467             (inb(dev->iobase + PCL711_DI_HI) << 8);
468
469         return insn->n;
470 }
471
472 static int pcl711_do_insn_bits(struct comedi_device *dev,
473                                struct comedi_subdevice *s,
474                                struct comedi_insn *insn,
475                                unsigned int *data)
476 {
477         unsigned int mask;
478
479         mask = comedi_dio_update_state(s, data);
480         if (mask) {
481                 if (mask & 0x00ff)
482                         outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
483                 if (mask & 0xff00)
484                         outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
485         }
486
487         data[1] = s->state;
488
489         return insn->n;
490 }
491
492 static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
493 {
494         const struct pcl711_board *board = comedi_board(dev);
495         struct pcl711_private *devpriv;
496         struct comedi_subdevice *s;
497         int ret;
498
499         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
500         if (!devpriv)
501                 return -ENOMEM;
502
503         ret = comedi_request_region(dev, it->options[0], 0x10);
504         if (ret)
505                 return ret;
506
507         if (it->options[1] && it->options[1] <= board->maxirq) {
508                 ret = request_irq(it->options[1], pcl711_interrupt, 0,
509                                   dev->board_name, dev);
510                 if (ret == 0) {
511                         dev->irq = it->options[1];
512
513                         /*
514                          * The PCL711b needs the irq number in the
515                          * mode register.
516                          */
517                         if (board->is_pcl711b)
518                                 devpriv->mode = PCL711_MODE_IRQ(dev->irq);
519                 }
520         }
521
522         ret = comedi_alloc_subdevices(dev, 4);
523         if (ret)
524                 return ret;
525
526         /* Analog Input subdevice */
527         s = &dev->subdevices[0];
528         s->type         = COMEDI_SUBD_AI;
529         s->subdev_flags = SDF_READABLE | SDF_GROUND;
530         if (board->is_8112)
531                 s->subdev_flags |= SDF_DIFF;
532         s->n_chan       = board->n_aichan;
533         s->maxdata      = 0xfff;
534         s->range_table  = board->ai_range_type;
535         s->insn_read    = pcl711_ai_insn_read;
536         if (dev->irq) {
537                 dev->read_subdev = s;
538                 s->subdev_flags |= SDF_CMD_READ;
539                 s->len_chanlist = 1;
540                 s->do_cmdtest   = pcl711_ai_cmdtest;
541                 s->do_cmd       = pcl711_ai_cmd;
542         }
543
544         /* Analog Output subdevice */
545         s = &dev->subdevices[1];
546         s->type         = COMEDI_SUBD_AO;
547         s->subdev_flags = SDF_WRITABLE;
548         s->n_chan       = board->n_aochan;
549         s->maxdata      = 0xfff;
550         s->range_table  = &range_bipolar5;
551         s->insn_write   = pcl711_ao_insn_write;
552         s->insn_read    = pcl711_ao_insn_read;
553
554         /* Digital Input subdevice */
555         s = &dev->subdevices[2];
556         s->type         = COMEDI_SUBD_DI;
557         s->subdev_flags = SDF_READABLE;
558         s->n_chan       = 16;
559         s->maxdata      = 1;
560         s->range_table  = &range_digital;
561         s->insn_bits    = pcl711_di_insn_bits;
562
563         /* Digital Output subdevice */
564         s = &dev->subdevices[3];
565         s->type         = COMEDI_SUBD_DO;
566         s->subdev_flags = SDF_WRITABLE;
567         s->n_chan       = 16;
568         s->maxdata      = 1;
569         s->range_table  = &range_digital;
570         s->insn_bits    = pcl711_do_insn_bits;
571
572         /* clear DAC */
573         pcl711_ao_write(dev, 0, 0x0);
574         pcl711_ao_write(dev, 1, 0x0);
575
576         return 0;
577 }
578
579 static struct comedi_driver pcl711_driver = {
580         .driver_name    = "pcl711",
581         .module         = THIS_MODULE,
582         .attach         = pcl711_attach,
583         .detach         = comedi_legacy_detach,
584         .board_name     = &boardtypes[0].name,
585         .num_names      = ARRAY_SIZE(boardtypes),
586         .offset         = sizeof(struct pcl711_board),
587 };
588 module_comedi_driver(pcl711_driver);
589
590 MODULE_AUTHOR("Comedi http://www.comedi.org");
591 MODULE_DESCRIPTION("Comedi low-level driver");
592 MODULE_LICENSE("GPL");