Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / icp_multi.c
1 /*
2     comedi/drivers/icp_multi.c
3
4     COMEDI - Linux Control and Measurement Device Interface
5     Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22
23 /*
24 Driver: icp_multi
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
28 Status: works
29
30 The driver works for analog input and output and digital input and output.
31 It does not work with interrupts or with the counters.  Currently no support
32 for DMA.
33
34 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35 resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.  Input
36 ranges can be individually programmed for each channel.  Voltage or current
37 measurement is selected by jumper.
38
39 There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
40
41 16 x Digital Inputs, 24V
42
43 8 x Digital Outputs, 24V, 1A
44
45 4 x 16-bit counters
46
47 Configuration options: not applicable, uses PCI auto config
48 */
49
50 #include <linux/pci.h>
51 #include <linux/delay.h>
52 #include <linux/interrupt.h>
53
54 #include "../comedidev.h"
55
56 #define PCI_DEVICE_ID_ICP_MULTI 0x8000
57
58 #define ICP_MULTI_ADC_CSR       0       /* R/W: ADC command/status register */
59 #define ICP_MULTI_AI            2       /* R:   Analogue input data */
60 #define ICP_MULTI_DAC_CSR       4       /* R/W: DAC command/status register */
61 #define ICP_MULTI_AO            6       /* R/W: Analogue output data */
62 #define ICP_MULTI_DI            8       /* R/W: Digital inouts */
63 #define ICP_MULTI_DO            0x0A    /* R/W: Digital outputs */
64 #define ICP_MULTI_INT_EN        0x0C    /* R/W: Interrupt enable register */
65 #define ICP_MULTI_INT_STAT      0x0E    /* R/W: Interrupt status register */
66 #define ICP_MULTI_CNTR0         0x10    /* R/W: Counter 0 */
67 #define ICP_MULTI_CNTR1         0x12    /* R/W: counter 1 */
68 #define ICP_MULTI_CNTR2         0x14    /* R/W: Counter 2 */
69 #define ICP_MULTI_CNTR3         0x16    /* R/W: Counter 3 */
70
71 /*  Define bits from ADC command/status register */
72 #define ADC_ST          0x0001  /* Start ADC */
73 #define ADC_BSY         0x0001  /* ADC busy */
74 #define ADC_BI          0x0010  /* Bipolar input range 1 = bipolar */
75 #define ADC_RA          0x0020  /* Input range 0 = 5V, 1 = 10V */
76 #define ADC_DI          0x0040  /* Differential input mode 1 = differential */
77
78 /*  Define bits from DAC command/status register */
79 #define DAC_ST          0x0001  /* Start DAC */
80 #define DAC_BSY         0x0001  /* DAC busy */
81 #define DAC_BI          0x0010  /* Bipolar input range 1 = bipolar */
82 #define DAC_RA          0x0020  /* Input range 0 = 5V, 1 = 10V */
83
84 /*  Define bits from interrupt enable/status registers */
85 #define ADC_READY       0x0001  /* A/d conversion ready interrupt */
86 #define DAC_READY       0x0002  /* D/a conversion ready interrupt */
87 #define DOUT_ERROR      0x0004  /* Digital output error interrupt */
88 #define DIN_STATUS      0x0008  /* Digital input status change interrupt */
89 #define CIE0            0x0010  /* Counter 0 overrun interrupt */
90 #define CIE1            0x0020  /* Counter 1 overrun interrupt */
91 #define CIE2            0x0040  /* Counter 2 overrun interrupt */
92 #define CIE3            0x0080  /* Counter 3 overrun interrupt */
93
94 /*  Useful definitions */
95 #define Status_IRQ      0x00ff  /*  All interrupts */
96
97 /*  Define analogue range */
98 static const struct comedi_lrange range_analog = { 4, {
99                                                        UNI_RANGE(5),
100                                                        UNI_RANGE(10),
101                                                        BIP_RANGE(5),
102                                                        BIP_RANGE(10)
103                                                        }
104 };
105
106 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
107
108 /*
109 ==============================================================================
110         Data & Structure declarations
111 ==============================================================================
112 */
113
114 struct icp_multi_private {
115         char valid;             /*  card is usable */
116         void __iomem *io_addr;          /*  Pointer to mapped io address */
117         unsigned int AdcCmdStatus;      /*  ADC Command/Status register */
118         unsigned int DacCmdStatus;      /*  DAC Command/Status register */
119         unsigned int IntEnable; /*  Interrupt Enable register */
120         unsigned int IntStatus; /*  Interrupt Status register */
121         unsigned int act_chanlist[32];  /*  list of scanned channel */
122         unsigned char act_chanlist_len; /*  len of scanlist */
123         unsigned char act_chanlist_pos; /*  actual position in MUX list */
124         unsigned int *ai_chanlist;      /*  actaul chanlist */
125         short *ai_data;         /*  data buffer */
126         short ao_data[4];       /*  data output buffer */
127         short di_data;          /*  Digital input data */
128         unsigned int do_data;   /*  Remember digital output data */
129 };
130
131 static void setup_channel_list(struct comedi_device *dev,
132                                struct comedi_subdevice *s,
133                                unsigned int *chanlist, unsigned int n_chan)
134 {
135         struct icp_multi_private *devpriv = dev->private;
136         unsigned int i, range, chanprog;
137         unsigned int diff;
138
139         devpriv->act_chanlist_len = n_chan;
140         devpriv->act_chanlist_pos = 0;
141
142         for (i = 0; i < n_chan; i++) {
143                 /*  Get channel */
144                 chanprog = CR_CHAN(chanlist[i]);
145
146                 /*  Determine if it is a differential channel (Bit 15  = 1) */
147                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
148                         diff = 1;
149                         chanprog &= 0x0007;
150                 } else {
151                         diff = 0;
152                         chanprog &= 0x000f;
153                 }
154
155                 /*  Clear channel, range and input mode bits
156                  *  in A/D command/status register */
157                 devpriv->AdcCmdStatus &= 0xf00f;
158
159                 /*  Set channel number and differential mode status bit */
160                 if (diff) {
161                         /*  Set channel number, bits 9-11 & mode, bit 6 */
162                         devpriv->AdcCmdStatus |= (chanprog << 9);
163                         devpriv->AdcCmdStatus |= ADC_DI;
164                 } else
165                         /*  Set channel number, bits 8-11 */
166                         devpriv->AdcCmdStatus |= (chanprog << 8);
167
168                 /*  Get range for current channel */
169                 range = range_codes_analog[CR_RANGE(chanlist[i])];
170                 /*  Set range. bits 4-5 */
171                 devpriv->AdcCmdStatus |= range;
172
173                 /* Output channel, range, mode to ICP Multi */
174                 writew(devpriv->AdcCmdStatus,
175                        devpriv->io_addr + ICP_MULTI_ADC_CSR);
176         }
177 }
178
179 static int icp_multi_insn_read_ai(struct comedi_device *dev,
180                                   struct comedi_subdevice *s,
181                                   struct comedi_insn *insn, unsigned int *data)
182 {
183         struct icp_multi_private *devpriv = dev->private;
184         int n, timeout;
185
186         /*  Disable A/D conversion ready interrupt */
187         devpriv->IntEnable &= ~ADC_READY;
188         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
189
190         /*  Clear interrupt status */
191         devpriv->IntStatus |= ADC_READY;
192         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
193
194         /*  Set up appropriate channel, mode and range data, for specified ch */
195         setup_channel_list(dev, s, &insn->chanspec, 1);
196
197         for (n = 0; n < insn->n; n++) {
198                 /*  Set start ADC bit */
199                 devpriv->AdcCmdStatus |= ADC_ST;
200                 writew(devpriv->AdcCmdStatus,
201                        devpriv->io_addr + ICP_MULTI_ADC_CSR);
202                 devpriv->AdcCmdStatus &= ~ADC_ST;
203
204                 udelay(1);
205
206                 /*  Wait for conversion to complete, or get fed up waiting */
207                 timeout = 100;
208                 while (timeout--) {
209                         if (!(readw(devpriv->io_addr +
210                                     ICP_MULTI_ADC_CSR) & ADC_BSY))
211                                 goto conv_finish;
212
213                         udelay(1);
214                 }
215
216                 /*  If we reach here, a timeout has occurred */
217                 comedi_error(dev, "A/D insn timeout");
218
219                 /*  Disable interrupt */
220                 devpriv->IntEnable &= ~ADC_READY;
221                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
222
223                 /*  Clear interrupt status */
224                 devpriv->IntStatus |= ADC_READY;
225                 writew(devpriv->IntStatus,
226                        devpriv->io_addr + ICP_MULTI_INT_STAT);
227
228                 /*  Clear data received */
229                 data[n] = 0;
230
231                 return -ETIME;
232
233 conv_finish:
234                 data[n] =
235                     (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
236         }
237
238         /*  Disable interrupt */
239         devpriv->IntEnable &= ~ADC_READY;
240         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
241
242         /*  Clear interrupt status */
243         devpriv->IntStatus |= ADC_READY;
244         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
245
246         return n;
247 }
248
249 static int icp_multi_insn_write_ao(struct comedi_device *dev,
250                                    struct comedi_subdevice *s,
251                                    struct comedi_insn *insn, unsigned int *data)
252 {
253         struct icp_multi_private *devpriv = dev->private;
254         int n, chan, range, timeout;
255
256         /*  Disable D/A conversion ready interrupt */
257         devpriv->IntEnable &= ~DAC_READY;
258         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
259
260         /*  Clear interrupt status */
261         devpriv->IntStatus |= DAC_READY;
262         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
263
264         /*  Get channel number and range */
265         chan = CR_CHAN(insn->chanspec);
266         range = CR_RANGE(insn->chanspec);
267
268         /*  Set up range and channel data */
269         /*  Bit 4 = 1 : Bipolar */
270         /*  Bit 5 = 0 : 5V */
271         /*  Bit 5 = 1 : 10V */
272         /*  Bits 8-9 : Channel number */
273         devpriv->DacCmdStatus &= 0xfccf;
274         devpriv->DacCmdStatus |= range_codes_analog[range];
275         devpriv->DacCmdStatus |= (chan << 8);
276
277         writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
278
279         for (n = 0; n < insn->n; n++) {
280                 /*  Wait for analogue output data register to be
281                  *  ready for new data, or get fed up waiting */
282                 timeout = 100;
283                 while (timeout--) {
284                         if (!(readw(devpriv->io_addr +
285                                     ICP_MULTI_DAC_CSR) & DAC_BSY))
286                                 goto dac_ready;
287
288                         udelay(1);
289                 }
290
291                 /*  If we reach here, a timeout has occurred */
292                 comedi_error(dev, "D/A insn timeout");
293
294                 /*  Disable interrupt */
295                 devpriv->IntEnable &= ~DAC_READY;
296                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
297
298                 /*  Clear interrupt status */
299                 devpriv->IntStatus |= DAC_READY;
300                 writew(devpriv->IntStatus,
301                        devpriv->io_addr + ICP_MULTI_INT_STAT);
302
303                 /*  Clear data received */
304                 devpriv->ao_data[chan] = 0;
305
306                 return -ETIME;
307
308 dac_ready:
309                 /*  Write data to analogue output data register */
310                 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
311
312                 /*  Set DAC_ST bit to write the data to selected channel */
313                 devpriv->DacCmdStatus |= DAC_ST;
314                 writew(devpriv->DacCmdStatus,
315                        devpriv->io_addr + ICP_MULTI_DAC_CSR);
316                 devpriv->DacCmdStatus &= ~DAC_ST;
317
318                 /*  Save analogue output data */
319                 devpriv->ao_data[chan] = data[n];
320         }
321
322         return n;
323 }
324
325 static int icp_multi_insn_read_ao(struct comedi_device *dev,
326                                   struct comedi_subdevice *s,
327                                   struct comedi_insn *insn, unsigned int *data)
328 {
329         struct icp_multi_private *devpriv = dev->private;
330         int n, chan;
331
332         /*  Get channel number */
333         chan = CR_CHAN(insn->chanspec);
334
335         /*  Read analogue outputs */
336         for (n = 0; n < insn->n; n++)
337                 data[n] = devpriv->ao_data[chan];
338
339         return n;
340 }
341
342 static int icp_multi_insn_bits_di(struct comedi_device *dev,
343                                   struct comedi_subdevice *s,
344                                   struct comedi_insn *insn, unsigned int *data)
345 {
346         struct icp_multi_private *devpriv = dev->private;
347
348         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
349
350         return insn->n;
351 }
352
353 static int icp_multi_insn_bits_do(struct comedi_device *dev,
354                                   struct comedi_subdevice *s,
355                                   struct comedi_insn *insn, unsigned int *data)
356 {
357         struct icp_multi_private *devpriv = dev->private;
358
359         if (data[0]) {
360                 s->state &= ~data[0];
361                 s->state |= (data[0] & data[1]);
362
363                 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
364
365                 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
366         }
367
368         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
369
370         return insn->n;
371 }
372
373 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
374                                    struct comedi_subdevice *s,
375                                    struct comedi_insn *insn, unsigned int *data)
376 {
377         return 0;
378 }
379
380 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
381                                     struct comedi_subdevice *s,
382                                     struct comedi_insn *insn,
383                                     unsigned int *data)
384 {
385         return 0;
386 }
387
388 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
389 {
390         struct comedi_device *dev = d;
391         struct icp_multi_private *devpriv = dev->private;
392         int int_no;
393
394         /*  Is this interrupt from our board? */
395         int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
396         if (!int_no)
397                 /*  No, exit */
398                 return IRQ_NONE;
399
400         /*  Determine which interrupt is active & handle it */
401         switch (int_no) {
402         case ADC_READY:
403                 break;
404         case DAC_READY:
405                 break;
406         case DOUT_ERROR:
407                 break;
408         case DIN_STATUS:
409                 break;
410         case CIE0:
411                 break;
412         case CIE1:
413                 break;
414         case CIE2:
415                 break;
416         case CIE3:
417                 break;
418         default:
419                 break;
420
421         }
422
423         return IRQ_HANDLED;
424 }
425
426 #if 0
427 static int check_channel_list(struct comedi_device *dev,
428                               struct comedi_subdevice *s,
429                               unsigned int *chanlist, unsigned int n_chan)
430 {
431         unsigned int i;
432
433         /*  Check that we at least have one channel to check */
434         if (n_chan < 1) {
435                 comedi_error(dev, "range/channel list is empty!");
436                 return 0;
437         }
438         /*  Check all channels */
439         for (i = 0; i < n_chan; i++) {
440                 /*  Check that channel number is < maximum */
441                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
442                         if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
443                                 comedi_error(dev,
444                                              "Incorrect differential ai ch-nr");
445                                 return 0;
446                         }
447                 } else {
448                         if (CR_CHAN(chanlist[i]) > s->n_chan) {
449                                 comedi_error(dev,
450                                              "Incorrect ai channel number");
451                                 return 0;
452                         }
453                 }
454         }
455         return 1;
456 }
457 #endif
458
459 static int icp_multi_reset(struct comedi_device *dev)
460 {
461         struct icp_multi_private *devpriv = dev->private;
462         unsigned int i;
463
464         /*  Clear INT enables and requests */
465         writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
466         writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
467
468         /* Set DACs to 0..5V range and 0V output */
469         for (i = 0; i < 4; i++) {
470                 devpriv->DacCmdStatus &= 0xfcce;
471
472                 /*  Set channel number */
473                 devpriv->DacCmdStatus |= (i << 8);
474
475                 /*  Output 0V */
476                 writew(0, devpriv->io_addr + ICP_MULTI_AO);
477
478                 /*  Set start conversion bit */
479                 devpriv->DacCmdStatus |= DAC_ST;
480
481                 /*  Output to command / status register */
482                 writew(devpriv->DacCmdStatus,
483                         devpriv->io_addr + ICP_MULTI_DAC_CSR);
484
485                 /*  Delay to allow DAC time to recover */
486                 udelay(1);
487         }
488
489         /* Digital outputs to 0 */
490         writew(0, devpriv->io_addr + ICP_MULTI_DO);
491
492         return 0;
493 }
494
495 static int icp_multi_auto_attach(struct comedi_device *dev,
496                                            unsigned long context_unused)
497 {
498         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
499         struct icp_multi_private *devpriv;
500         struct comedi_subdevice *s;
501         int ret;
502
503         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
504         if (!devpriv)
505                 return -ENOMEM;
506         dev->private = devpriv;
507
508         ret = comedi_pci_enable(dev);
509         if (ret)
510                 return ret;
511
512         devpriv->io_addr = pci_ioremap_bar(pcidev, 2);
513         if (!devpriv->io_addr)
514                 return -ENOMEM;
515
516         ret = comedi_alloc_subdevices(dev, 5);
517         if (ret)
518                 return ret;
519
520         icp_multi_reset(dev);
521
522         if (pcidev->irq) {
523                 ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
524                                   IRQF_SHARED, dev->board_name, dev);
525                 if (ret == 0)
526                         dev->irq = pcidev->irq;
527         }
528
529         s = &dev->subdevices[0];
530         dev->read_subdev = s;
531         s->type = COMEDI_SUBD_AI;
532         s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
533         s->n_chan = 16;
534         s->maxdata = 0x0fff;
535         s->len_chanlist = 16;
536         s->range_table = &range_analog;
537         s->insn_read = icp_multi_insn_read_ai;
538
539         s = &dev->subdevices[1];
540         s->type = COMEDI_SUBD_AO;
541         s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
542         s->n_chan = 4;
543         s->maxdata = 0x0fff;
544         s->len_chanlist = 4;
545         s->range_table = &range_analog;
546         s->insn_write = icp_multi_insn_write_ao;
547         s->insn_read = icp_multi_insn_read_ao;
548
549         s = &dev->subdevices[2];
550         s->type = COMEDI_SUBD_DI;
551         s->subdev_flags = SDF_READABLE;
552         s->n_chan = 16;
553         s->maxdata = 1;
554         s->len_chanlist = 16;
555         s->range_table = &range_digital;
556         s->io_bits = 0;
557         s->insn_bits = icp_multi_insn_bits_di;
558
559         s = &dev->subdevices[3];
560         s->type = COMEDI_SUBD_DO;
561         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
562         s->n_chan = 8;
563         s->maxdata = 1;
564         s->len_chanlist = 8;
565         s->range_table = &range_digital;
566         s->io_bits = 0xff;
567         s->state = 0;
568         s->insn_bits = icp_multi_insn_bits_do;
569
570         s = &dev->subdevices[4];
571         s->type = COMEDI_SUBD_COUNTER;
572         s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
573         s->n_chan = 4;
574         s->maxdata = 0xffff;
575         s->len_chanlist = 4;
576         s->state = 0;
577         s->insn_read = icp_multi_insn_read_ctr;
578         s->insn_write = icp_multi_insn_write_ctr;
579
580         devpriv->valid = 1;
581
582         dev_info(dev->class_dev, "%s attached, irq %sabled\n",
583                 dev->board_name, dev->irq ? "en" : "dis");
584
585         return 0;
586 }
587
588 static void icp_multi_detach(struct comedi_device *dev)
589 {
590         struct icp_multi_private *devpriv = dev->private;
591
592         if (devpriv)
593                 if (devpriv->valid)
594                         icp_multi_reset(dev);
595         if (dev->irq)
596                 free_irq(dev->irq, dev);
597         if (devpriv && devpriv->io_addr)
598                 iounmap(devpriv->io_addr);
599         comedi_pci_disable(dev);
600 }
601
602 static struct comedi_driver icp_multi_driver = {
603         .driver_name    = "icp_multi",
604         .module         = THIS_MODULE,
605         .auto_attach    = icp_multi_auto_attach,
606         .detach         = icp_multi_detach,
607 };
608
609 static int icp_multi_pci_probe(struct pci_dev *dev,
610                                const struct pci_device_id *id)
611 {
612         return comedi_pci_auto_config(dev, &icp_multi_driver, id->driver_data);
613 }
614
615 static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
616         { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
617         { 0 }
618 };
619 MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
620
621 static struct pci_driver icp_multi_pci_driver = {
622         .name           = "icp_multi",
623         .id_table       = icp_multi_pci_table,
624         .probe          = icp_multi_pci_probe,
625         .remove         = comedi_pci_auto_unconfig,
626 };
627 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
628
629 MODULE_AUTHOR("Comedi http://www.comedi.org");
630 MODULE_DESCRIPTION("Comedi low-level driver");
631 MODULE_LICENSE("GPL");