Version:  2.0.40 2.2.26 2.4.37 3.0 3.1 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

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

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