staging: comedi: icp_multi: remove unused members from private data
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / icp_multi.c
1 /*
2  * icp_multi.c
3  * Comedi driver for Inova ICP_MULTI board
4  *
5  * COMEDI - Linux Control and Measurement Device Interface
6  * Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 /*
20  * Driver: icp_multi
21  * Description: Inova ICP_MULTI
22  * Devices: [Inova] ICP_MULTI (icp_multi)
23  * Author: Anne Smorthit <anne.smorthit@sfwte.ch>
24  * Status: works
25  *
26  * Configuration options: not applicable, uses PCI auto config
27  *
28  * The driver works for analog input and output and digital input and
29  * output. It does not work with interrupts or with the counters. Currently
30  * no support for DMA.
31  *
32  * It has 16 single-ended or 8 differential Analogue Input channels with
33  * 12-bit resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.
34  * Input ranges can be individually programmed for each channel.  Voltage or
35  * current measurement is selected by jumper.
36  *
37  * There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
38  *
39  * 16 x Digital Inputs, 24V
40  *
41  * 8 x Digital Outputs, 24V, 1A
42  *
43  * 4 x 16-bit counters
44  */
45
46 #include <linux/module.h>
47 #include <linux/delay.h>
48 #include <linux/interrupt.h>
49
50 #include "../comedi_pci.h"
51
52 #define ICP_MULTI_ADC_CSR       0x00    /* R/W: ADC command/status register */
53 #define ICP_MULTI_ADC_CSR_ST    BIT(0)  /* Start ADC */
54 #define ICP_MULTI_ADC_CSR_BSY   BIT(0)  /* ADC busy */
55 #define ICP_MULTI_ADC_CSR_BI    BIT(4)  /* Bipolar input range */
56 #define ICP_MULTI_ADC_CSR_RA    BIT(5)  /* Input range 0 = 5V, 1 = 10V */
57 #define ICP_MULTI_ADC_CSR_DI    BIT(6)  /* Input mode 1 = differential */
58 #define ICP_MULTI_AI            2       /* R:   Analogue input data */
59 #define ICP_MULTI_DAC_CSR       0x04    /* R/W: DAC command/status register */
60 #define ICP_MULTI_DAC_CSR_ST    BIT(0)  /* Start DAC */
61 #define ICP_MULTI_DAC_CSR_BSY   BIT(0)  /* DAC busy */
62 #define ICP_MULTI_DAC_CSR_BI    BIT(4)  /* Bipolar output range */
63 #define ICP_MULTI_DAC_CSR_RA    BIT(5)  /* Output range 0 = 5V, 1 = 10V */
64 #define ICP_MULTI_AO            6       /* R/W: Analogue output data */
65 #define ICP_MULTI_DI            8       /* R/W: Digital inputs */
66 #define ICP_MULTI_DO            0x0A    /* R/W: Digital outputs */
67 #define ICP_MULTI_INT_EN        0x0c    /* R/W: Interrupt enable register */
68 #define ICP_MULTI_INT_STAT      0x0e    /* R/W: Interrupt status register */
69 #define ICP_MULTI_INT_ADC_RDY   BIT(0)  /* A/D conversion ready interrupt */
70 #define ICP_MULTI_INT_DAC_RDY   BIT(1)  /* D/A conversion ready interrupt */
71 #define ICP_MULTI_INT_DOUT_ERR  BIT(2)  /* Digital output error interrupt */
72 #define ICP_MULTI_INT_DIN_STAT  BIT(3)  /* Digital input status change int. */
73 #define ICP_MULTI_INT_CIE0      BIT(4)  /* Counter 0 overrun interrupt */
74 #define ICP_MULTI_INT_CIE1      BIT(5)  /* Counter 1 overrun interrupt */
75 #define ICP_MULTI_INT_CIE2      BIT(6)  /* Counter 2 overrun interrupt */
76 #define ICP_MULTI_INT_CIE3      BIT(7)  /* Counter 3 overrun interrupt */
77 #define ICP_MULTI_INT_MASK      0xff    /* All interrupts */
78 #define ICP_MULTI_CNTR0         0x10    /* R/W: Counter 0 */
79 #define ICP_MULTI_CNTR1         0x12    /* R/W: counter 1 */
80 #define ICP_MULTI_CNTR2         0x14    /* R/W: Counter 2 */
81 #define ICP_MULTI_CNTR3         0x16    /* R/W: Counter 3 */
82
83 /* analog input and output have the same range options */
84 static const struct comedi_lrange icp_multi_ranges = {
85         4, {
86                 UNI_RANGE(5),
87                 UNI_RANGE(10),
88                 BIP_RANGE(5),
89                 BIP_RANGE(10)
90         }
91 };
92
93 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
94
95 struct icp_multi_private {
96         unsigned int AdcCmdStatus;      /*  ADC Command/Status register */
97         unsigned int DacCmdStatus;      /*  DAC Command/Status register */
98         unsigned int IntEnable; /*  Interrupt Enable register */
99         unsigned int IntStatus; /*  Interrupt Status register */
100 };
101
102 static void setup_channel_list(struct comedi_device *dev,
103                                struct comedi_subdevice *s,
104                                unsigned int *chanlist, unsigned int n_chan)
105 {
106         struct icp_multi_private *devpriv = dev->private;
107         unsigned int i, range, chanprog;
108         unsigned int diff;
109
110         for (i = 0; i < n_chan; i++) {
111                 /*  Get channel */
112                 chanprog = CR_CHAN(chanlist[i]);
113
114                 /*  Determine if it is a differential channel (Bit 15  = 1) */
115                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
116                         diff = 1;
117                         chanprog &= 0x0007;
118                 } else {
119                         diff = 0;
120                         chanprog &= 0x000f;
121                 }
122
123                 /*  Clear channel, range and input mode bits
124                  *  in A/D command/status register */
125                 devpriv->AdcCmdStatus &= 0xf00f;
126
127                 /*  Set channel number and differential mode status bit */
128                 if (diff) {
129                         /*  Set channel number, bits 9-11 & mode, bit 6 */
130                         devpriv->AdcCmdStatus |= (chanprog << 9);
131                         devpriv->AdcCmdStatus |= ICP_MULTI_ADC_CSR_DI;
132                 } else
133                         /*  Set channel number, bits 8-11 */
134                         devpriv->AdcCmdStatus |= (chanprog << 8);
135
136                 /*  Get range for current channel */
137                 range = range_codes_analog[CR_RANGE(chanlist[i])];
138                 /*  Set range. bits 4-5 */
139                 devpriv->AdcCmdStatus |= range;
140
141                 /* Output channel, range, mode to ICP Multi */
142                 writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
143         }
144 }
145
146 static int icp_multi_ai_eoc(struct comedi_device *dev,
147                             struct comedi_subdevice *s,
148                             struct comedi_insn *insn,
149                             unsigned long context)
150 {
151         unsigned int status;
152
153         status = readw(dev->mmio + ICP_MULTI_ADC_CSR);
154         if ((status & ICP_MULTI_ADC_CSR_BSY) == 0)
155                 return 0;
156         return -EBUSY;
157 }
158
159 static int icp_multi_insn_read_ai(struct comedi_device *dev,
160                                   struct comedi_subdevice *s,
161                                   struct comedi_insn *insn,
162                                   unsigned int *data)
163 {
164         struct icp_multi_private *devpriv = dev->private;
165         int ret = 0;
166         int n;
167
168         /*  Disable A/D conversion ready interrupt */
169         devpriv->IntEnable &= ~ICP_MULTI_INT_ADC_RDY;
170         writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
171
172         /*  Clear interrupt status */
173         devpriv->IntStatus |= ICP_MULTI_INT_ADC_RDY;
174         writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
175
176         /*  Set up appropriate channel, mode and range data, for specified ch */
177         setup_channel_list(dev, s, &insn->chanspec, 1);
178
179         for (n = 0; n < insn->n; n++) {
180                 /*  Set start ADC bit */
181                 devpriv->AdcCmdStatus |= ICP_MULTI_ADC_CSR_ST;
182                 writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
183                 devpriv->AdcCmdStatus &= ~ICP_MULTI_ADC_CSR_ST;
184
185                 udelay(1);
186
187                 /*  Wait for conversion to complete, or get fed up waiting */
188                 ret = comedi_timeout(dev, s, insn, icp_multi_ai_eoc, 0);
189                 if (ret)
190                         break;
191
192                 data[n] = (readw(dev->mmio + ICP_MULTI_AI) >> 4) & 0x0fff;
193         }
194
195         /*  Disable interrupt */
196         devpriv->IntEnable &= ~ICP_MULTI_INT_ADC_RDY;
197         writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
198
199         /*  Clear interrupt status */
200         devpriv->IntStatus |= ICP_MULTI_INT_ADC_RDY;
201         writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
202
203         return ret ? ret : n;
204 }
205
206 static int icp_multi_ao_eoc(struct comedi_device *dev,
207                             struct comedi_subdevice *s,
208                             struct comedi_insn *insn,
209                             unsigned long context)
210 {
211         unsigned int status;
212
213         status = readw(dev->mmio + ICP_MULTI_DAC_CSR);
214         if ((status & ICP_MULTI_DAC_CSR_BSY) == 0)
215                 return 0;
216         return -EBUSY;
217 }
218
219 static int icp_multi_ao_insn_write(struct comedi_device *dev,
220                                    struct comedi_subdevice *s,
221                                    struct comedi_insn *insn,
222                                    unsigned int *data)
223 {
224         struct icp_multi_private *devpriv = dev->private;
225         unsigned int chan = CR_CHAN(insn->chanspec);
226         unsigned int range = CR_RANGE(insn->chanspec);
227         int i;
228
229         /*  Disable D/A conversion ready interrupt */
230         devpriv->IntEnable &= ~ICP_MULTI_INT_DAC_RDY;
231         writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
232
233         /*  Clear interrupt status */
234         devpriv->IntStatus |= ICP_MULTI_INT_DAC_RDY;
235         writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
236
237         /*  Set up range and channel data */
238         /*  Bit 4 = 1 : Bipolar */
239         /*  Bit 5 = 0 : 5V */
240         /*  Bit 5 = 1 : 10V */
241         /*  Bits 8-9 : Channel number */
242         devpriv->DacCmdStatus &= 0xfccf;
243         devpriv->DacCmdStatus |= range_codes_analog[range];
244         devpriv->DacCmdStatus |= (chan << 8);
245
246         writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
247
248         for (i = 0; i < insn->n; i++) {
249                 unsigned int val = data[i];
250                 int ret;
251
252                 /*  Wait for analogue output data register to be
253                  *  ready for new data, or get fed up waiting */
254                 ret = comedi_timeout(dev, s, insn, icp_multi_ao_eoc, 0);
255                 if (ret) {
256                         /*  Disable interrupt */
257                         devpriv->IntEnable &= ~ICP_MULTI_INT_DAC_RDY;
258                         writew(devpriv->IntEnable,
259                                dev->mmio + ICP_MULTI_INT_EN);
260
261                         /*  Clear interrupt status */
262                         devpriv->IntStatus |= ICP_MULTI_INT_DAC_RDY;
263                         writew(devpriv->IntStatus,
264                                dev->mmio + ICP_MULTI_INT_STAT);
265
266                         return ret;
267                 }
268
269                 writew(val, dev->mmio + ICP_MULTI_AO);
270
271                 /* Set start conversion bit to write data to channel */
272                 devpriv->DacCmdStatus |= ICP_MULTI_DAC_CSR_ST;
273                 writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
274                 devpriv->DacCmdStatus &= ~ICP_MULTI_DAC_CSR_ST;
275
276                 s->readback[chan] = val;
277         }
278
279         return insn->n;
280 }
281
282 static int icp_multi_insn_bits_di(struct comedi_device *dev,
283                                   struct comedi_subdevice *s,
284                                   struct comedi_insn *insn,
285                                   unsigned int *data)
286 {
287         data[1] = readw(dev->mmio + ICP_MULTI_DI);
288
289         return insn->n;
290 }
291
292 static int icp_multi_insn_bits_do(struct comedi_device *dev,
293                                   struct comedi_subdevice *s,
294                                   struct comedi_insn *insn,
295                                   unsigned int *data)
296 {
297         if (comedi_dio_update_state(s, data))
298                 writew(s->state, dev->mmio + ICP_MULTI_DO);
299
300         data[1] = s->state;
301
302         return insn->n;
303 }
304
305 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
306                                    struct comedi_subdevice *s,
307                                    struct comedi_insn *insn, unsigned int *data)
308 {
309         return 0;
310 }
311
312 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
313                                     struct comedi_subdevice *s,
314                                     struct comedi_insn *insn,
315                                     unsigned int *data)
316 {
317         return 0;
318 }
319
320 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
321 {
322         struct comedi_device *dev = d;
323         int int_no;
324
325         /*  Is this interrupt from our board? */
326         int_no = readw(dev->mmio + ICP_MULTI_INT_STAT) & ICP_MULTI_INT_MASK;
327         if (!int_no)
328                 /*  No, exit */
329                 return IRQ_NONE;
330
331         /*  Determine which interrupt is active & handle it */
332         switch (int_no) {
333         case ICP_MULTI_INT_ADC_RDY:
334                 break;
335         case ICP_MULTI_INT_DAC_RDY:
336                 break;
337         case ICP_MULTI_INT_DOUT_ERR:
338                 break;
339         case ICP_MULTI_INT_DIN_STAT:
340                 break;
341         case ICP_MULTI_INT_CIE0:
342                 break;
343         case ICP_MULTI_INT_CIE1:
344                 break;
345         case ICP_MULTI_INT_CIE2:
346                 break;
347         case ICP_MULTI_INT_CIE3:
348                 break;
349         default:
350                 break;
351         }
352
353         return IRQ_HANDLED;
354 }
355
356 #if 0
357 static int check_channel_list(struct comedi_device *dev,
358                               struct comedi_subdevice *s,
359                               unsigned int *chanlist, unsigned int n_chan)
360 {
361         unsigned int i;
362
363         /*  Check that we at least have one channel to check */
364         if (n_chan < 1) {
365                 dev_err(dev->class_dev, "range/channel list is empty!\n");
366                 return 0;
367         }
368         /*  Check all channels */
369         for (i = 0; i < n_chan; i++) {
370                 /*  Check that channel number is < maximum */
371                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
372                         if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
373                                 dev_err(dev->class_dev,
374                                         "Incorrect differential ai ch-nr\n");
375                                 return 0;
376                         }
377                 } else {
378                         if (CR_CHAN(chanlist[i]) > s->n_chan) {
379                                 dev_err(dev->class_dev,
380                                         "Incorrect ai channel number\n");
381                                 return 0;
382                         }
383                 }
384         }
385         return 1;
386 }
387 #endif
388
389 static int icp_multi_reset(struct comedi_device *dev)
390 {
391         struct icp_multi_private *devpriv = dev->private;
392         unsigned int i;
393
394         /*  Clear INT enables and requests */
395         writew(0, dev->mmio + ICP_MULTI_INT_EN);
396         writew(0x00ff, dev->mmio + ICP_MULTI_INT_STAT);
397
398         /* Set DACs to 0..5V range and 0V output */
399         for (i = 0; i < 4; i++) {
400                 devpriv->DacCmdStatus &= 0xfcce;
401
402                 /*  Set channel number */
403                 devpriv->DacCmdStatus |= (i << 8);
404
405                 /*  Output 0V */
406                 writew(0, dev->mmio + ICP_MULTI_AO);
407
408                 /*  Set start conversion bit */
409                 devpriv->DacCmdStatus |= ICP_MULTI_DAC_CSR_ST;
410
411                 /*  Output to command / status register */
412                 writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
413
414                 /*  Delay to allow DAC time to recover */
415                 udelay(1);
416         }
417
418         /* Digital outputs to 0 */
419         writew(0, dev->mmio + ICP_MULTI_DO);
420
421         return 0;
422 }
423
424 static int icp_multi_auto_attach(struct comedi_device *dev,
425                                  unsigned long context_unused)
426 {
427         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
428         struct icp_multi_private *devpriv;
429         struct comedi_subdevice *s;
430         int ret;
431
432         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
433         if (!devpriv)
434                 return -ENOMEM;
435
436         ret = comedi_pci_enable(dev);
437         if (ret)
438                 return ret;
439
440         dev->mmio = pci_ioremap_bar(pcidev, 2);
441         if (!dev->mmio)
442                 return -ENOMEM;
443
444         ret = comedi_alloc_subdevices(dev, 5);
445         if (ret)
446                 return ret;
447
448         icp_multi_reset(dev);
449
450         if (pcidev->irq) {
451                 ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
452                                   IRQF_SHARED, dev->board_name, dev);
453                 if (ret == 0)
454                         dev->irq = pcidev->irq;
455         }
456
457         s = &dev->subdevices[0];
458         dev->read_subdev = s;
459         s->type = COMEDI_SUBD_AI;
460         s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
461         s->n_chan = 16;
462         s->maxdata = 0x0fff;
463         s->len_chanlist = 16;
464         s->range_table = &icp_multi_ranges;
465         s->insn_read = icp_multi_insn_read_ai;
466
467         s = &dev->subdevices[1];
468         s->type = COMEDI_SUBD_AO;
469         s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
470         s->n_chan = 4;
471         s->maxdata = 0x0fff;
472         s->len_chanlist = 4;
473         s->range_table = &icp_multi_ranges;
474         s->insn_write = icp_multi_ao_insn_write;
475
476         ret = comedi_alloc_subdev_readback(s);
477         if (ret)
478                 return ret;
479
480         s = &dev->subdevices[2];
481         s->type = COMEDI_SUBD_DI;
482         s->subdev_flags = SDF_READABLE;
483         s->n_chan = 16;
484         s->maxdata = 1;
485         s->len_chanlist = 16;
486         s->range_table = &range_digital;
487         s->insn_bits = icp_multi_insn_bits_di;
488
489         s = &dev->subdevices[3];
490         s->type = COMEDI_SUBD_DO;
491         s->subdev_flags = SDF_WRITABLE;
492         s->n_chan = 8;
493         s->maxdata = 1;
494         s->len_chanlist = 8;
495         s->range_table = &range_digital;
496         s->insn_bits = icp_multi_insn_bits_do;
497
498         s = &dev->subdevices[4];
499         s->type = COMEDI_SUBD_COUNTER;
500         s->subdev_flags = SDF_WRITABLE;
501         s->n_chan = 4;
502         s->maxdata = 0xffff;
503         s->len_chanlist = 4;
504         s->state = 0;
505         s->insn_read = icp_multi_insn_read_ctr;
506         s->insn_write = icp_multi_insn_write_ctr;
507
508         return 0;
509 }
510
511 static void icp_multi_detach(struct comedi_device *dev)
512 {
513         if (dev->mmio)
514                 icp_multi_reset(dev);
515         comedi_pci_detach(dev);
516 }
517
518 static struct comedi_driver icp_multi_driver = {
519         .driver_name    = "icp_multi",
520         .module         = THIS_MODULE,
521         .auto_attach    = icp_multi_auto_attach,
522         .detach         = icp_multi_detach,
523 };
524
525 static int icp_multi_pci_probe(struct pci_dev *dev,
526                                const struct pci_device_id *id)
527 {
528         return comedi_pci_auto_config(dev, &icp_multi_driver, id->driver_data);
529 }
530
531 static const struct pci_device_id icp_multi_pci_table[] = {
532         { PCI_DEVICE(PCI_VENDOR_ID_ICP, 0x8000) },
533         { 0 }
534 };
535 MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
536
537 static struct pci_driver icp_multi_pci_driver = {
538         .name           = "icp_multi",
539         .id_table       = icp_multi_pci_table,
540         .probe          = icp_multi_pci_probe,
541         .remove         = comedi_pci_auto_unconfig,
542 };
543 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
544
545 MODULE_AUTHOR("Comedi http://www.comedi.org");
546 MODULE_DESCRIPTION("Comedi low-level driver");
547 MODULE_LICENSE("GPL");