Version:  2.0.40 2.2.26 2.4.37 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18

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

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us