Version:  2.0.40 2.2.26 2.4.37 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 3.19 4.0 4.1

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

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