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