1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50#include <linux/pci.h>
51#include <linux/delay.h>
52#include <linux/interrupt.h>
53
54#include "../comedidev.h"
55
56#define PCI_DEVICE_ID_ICP_MULTI 0x8000
57
58#define ICP_MULTI_ADC_CSR 0
59#define ICP_MULTI_AI 2
60#define ICP_MULTI_DAC_CSR 4
61#define ICP_MULTI_AO 6
62#define ICP_MULTI_DI 8
63#define ICP_MULTI_DO 0x0A
64#define ICP_MULTI_INT_EN 0x0C
65#define ICP_MULTI_INT_STAT 0x0E
66#define ICP_MULTI_CNTR0 0x10
67#define ICP_MULTI_CNTR1 0x12
68#define ICP_MULTI_CNTR2 0x14
69#define ICP_MULTI_CNTR3 0x16
70
71
72#define ADC_ST 0x0001
73#define ADC_BSY 0x0001
74#define ADC_BI 0x0010
75#define ADC_RA 0x0020
76#define ADC_DI 0x0040
77
78
79#define DAC_ST 0x0001
80#define DAC_BSY 0x0001
81#define DAC_BI 0x0010
82#define DAC_RA 0x0020
83
84
85#define ADC_READY 0x0001
86#define DAC_READY 0x0002
87#define DOUT_ERROR 0x0004
88#define DIN_STATUS 0x0008
89#define CIE0 0x0010
90#define CIE1 0x0020
91#define CIE2 0x0040
92#define CIE3 0x0080
93
94
95#define Status_IRQ 0x00ff
96
97
98static const struct comedi_lrange range_analog = { 4, {
99 UNI_RANGE(5),
100 UNI_RANGE(10),
101 BIP_RANGE(5),
102 BIP_RANGE(10)
103 }
104};
105
106static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
107
108
109
110
111
112
113
114struct icp_multi_private {
115 char valid;
116 void __iomem *io_addr;
117 unsigned int AdcCmdStatus;
118 unsigned int DacCmdStatus;
119 unsigned int IntEnable;
120 unsigned int IntStatus;
121 unsigned int act_chanlist[32];
122 unsigned char act_chanlist_len;
123 unsigned char act_chanlist_pos;
124 unsigned int *ai_chanlist;
125 short *ai_data;
126 short ao_data[4];
127 short di_data;
128 unsigned int do_data;
129};
130
131static void setup_channel_list(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 unsigned int *chanlist, unsigned int n_chan)
134{
135 struct icp_multi_private *devpriv = dev->private;
136 unsigned int i, range, chanprog;
137 unsigned int diff;
138
139 devpriv->act_chanlist_len = n_chan;
140 devpriv->act_chanlist_pos = 0;
141
142 for (i = 0; i < n_chan; i++) {
143
144 chanprog = CR_CHAN(chanlist[i]);
145
146
147 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
148 diff = 1;
149 chanprog &= 0x0007;
150 } else {
151 diff = 0;
152 chanprog &= 0x000f;
153 }
154
155
156
157 devpriv->AdcCmdStatus &= 0xf00f;
158
159
160 if (diff) {
161
162 devpriv->AdcCmdStatus |= (chanprog << 9);
163 devpriv->AdcCmdStatus |= ADC_DI;
164 } else
165
166 devpriv->AdcCmdStatus |= (chanprog << 8);
167
168
169 range = range_codes_analog[CR_RANGE(chanlist[i])];
170
171 devpriv->AdcCmdStatus |= range;
172
173
174 writew(devpriv->AdcCmdStatus,
175 devpriv->io_addr + ICP_MULTI_ADC_CSR);
176 }
177}
178
179static int icp_multi_insn_read_ai(struct comedi_device *dev,
180 struct comedi_subdevice *s,
181 struct comedi_insn *insn, unsigned int *data)
182{
183 struct icp_multi_private *devpriv = dev->private;
184 int n, timeout;
185
186
187 devpriv->IntEnable &= ~ADC_READY;
188 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
189
190
191 devpriv->IntStatus |= ADC_READY;
192 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
193
194
195 setup_channel_list(dev, s, &insn->chanspec, 1);
196
197 for (n = 0; n < insn->n; n++) {
198
199 devpriv->AdcCmdStatus |= ADC_ST;
200 writew(devpriv->AdcCmdStatus,
201 devpriv->io_addr + ICP_MULTI_ADC_CSR);
202 devpriv->AdcCmdStatus &= ~ADC_ST;
203
204 udelay(1);
205
206
207 timeout = 100;
208 while (timeout--) {
209 if (!(readw(devpriv->io_addr +
210 ICP_MULTI_ADC_CSR) & ADC_BSY))
211 goto conv_finish;
212
213 udelay(1);
214 }
215
216
217 comedi_error(dev, "A/D insn timeout");
218
219
220 devpriv->IntEnable &= ~ADC_READY;
221 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
222
223
224 devpriv->IntStatus |= ADC_READY;
225 writew(devpriv->IntStatus,
226 devpriv->io_addr + ICP_MULTI_INT_STAT);
227
228
229 data[n] = 0;
230
231 return -ETIME;
232
233conv_finish:
234 data[n] =
235 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
236 }
237
238
239 devpriv->IntEnable &= ~ADC_READY;
240 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
241
242
243 devpriv->IntStatus |= ADC_READY;
244 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
245
246 return n;
247}
248
249static int icp_multi_insn_write_ao(struct comedi_device *dev,
250 struct comedi_subdevice *s,
251 struct comedi_insn *insn, unsigned int *data)
252{
253 struct icp_multi_private *devpriv = dev->private;
254 int n, chan, range, timeout;
255
256
257 devpriv->IntEnable &= ~DAC_READY;
258 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
259
260
261 devpriv->IntStatus |= DAC_READY;
262 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
263
264
265 chan = CR_CHAN(insn->chanspec);
266 range = CR_RANGE(insn->chanspec);
267
268
269
270
271
272
273 devpriv->DacCmdStatus &= 0xfccf;
274 devpriv->DacCmdStatus |= range_codes_analog[range];
275 devpriv->DacCmdStatus |= (chan << 8);
276
277 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
278
279 for (n = 0; n < insn->n; n++) {
280
281
282 timeout = 100;
283 while (timeout--) {
284 if (!(readw(devpriv->io_addr +
285 ICP_MULTI_DAC_CSR) & DAC_BSY))
286 goto dac_ready;
287
288 udelay(1);
289 }
290
291
292 comedi_error(dev, "D/A insn timeout");
293
294
295 devpriv->IntEnable &= ~DAC_READY;
296 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
297
298
299 devpriv->IntStatus |= DAC_READY;
300 writew(devpriv->IntStatus,
301 devpriv->io_addr + ICP_MULTI_INT_STAT);
302
303
304 devpriv->ao_data[chan] = 0;
305
306 return -ETIME;
307
308dac_ready:
309
310 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
311
312
313 devpriv->DacCmdStatus |= DAC_ST;
314 writew(devpriv->DacCmdStatus,
315 devpriv->io_addr + ICP_MULTI_DAC_CSR);
316 devpriv->DacCmdStatus &= ~DAC_ST;
317
318
319 devpriv->ao_data[chan] = data[n];
320 }
321
322 return n;
323}
324
325static int icp_multi_insn_read_ao(struct comedi_device *dev,
326 struct comedi_subdevice *s,
327 struct comedi_insn *insn, unsigned int *data)
328{
329 struct icp_multi_private *devpriv = dev->private;
330 int n, chan;
331
332
333 chan = CR_CHAN(insn->chanspec);
334
335
336 for (n = 0; n < insn->n; n++)
337 data[n] = devpriv->ao_data[chan];
338
339 return n;
340}
341
342static int icp_multi_insn_bits_di(struct comedi_device *dev,
343 struct comedi_subdevice *s,
344 struct comedi_insn *insn, unsigned int *data)
345{
346 struct icp_multi_private *devpriv = dev->private;
347
348 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
349
350 return insn->n;
351}
352
353static int icp_multi_insn_bits_do(struct comedi_device *dev,
354 struct comedi_subdevice *s,
355 struct comedi_insn *insn, unsigned int *data)
356{
357 struct icp_multi_private *devpriv = dev->private;
358
359 if (data[0]) {
360 s->state &= ~data[0];
361 s->state |= (data[0] & data[1]);
362
363 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
364
365 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
366 }
367
368 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
369
370 return insn->n;
371}
372
373static int icp_multi_insn_read_ctr(struct comedi_device *dev,
374 struct comedi_subdevice *s,
375 struct comedi_insn *insn, unsigned int *data)
376{
377 return 0;
378}
379
380static int icp_multi_insn_write_ctr(struct comedi_device *dev,
381 struct comedi_subdevice *s,
382 struct comedi_insn *insn,
383 unsigned int *data)
384{
385 return 0;
386}
387
388static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
389{
390 struct comedi_device *dev = d;
391 struct icp_multi_private *devpriv = dev->private;
392 int int_no;
393
394
395 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
396 if (!int_no)
397
398 return IRQ_NONE;
399
400
401 switch (int_no) {
402 case ADC_READY:
403 break;
404 case DAC_READY:
405 break;
406 case DOUT_ERROR:
407 break;
408 case DIN_STATUS:
409 break;
410 case CIE0:
411 break;
412 case CIE1:
413 break;
414 case CIE2:
415 break;
416 case CIE3:
417 break;
418 default:
419 break;
420
421 }
422
423 return IRQ_HANDLED;
424}
425
426#if 0
427static int check_channel_list(struct comedi_device *dev,
428 struct comedi_subdevice *s,
429 unsigned int *chanlist, unsigned int n_chan)
430{
431 unsigned int i;
432
433
434 if (n_chan < 1) {
435 comedi_error(dev, "range/channel list is empty!");
436 return 0;
437 }
438
439 for (i = 0; i < n_chan; i++) {
440
441 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
442 if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
443 comedi_error(dev,
444 "Incorrect differential ai ch-nr");
445 return 0;
446 }
447 } else {
448 if (CR_CHAN(chanlist[i]) > s->n_chan) {
449 comedi_error(dev,
450 "Incorrect ai channel number");
451 return 0;
452 }
453 }
454 }
455 return 1;
456}
457#endif
458
459static int icp_multi_reset(struct comedi_device *dev)
460{
461 struct icp_multi_private *devpriv = dev->private;
462 unsigned int i;
463
464
465 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
466 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
467
468
469 for (i = 0; i < 4; i++) {
470 devpriv->DacCmdStatus &= 0xfcce;
471
472
473 devpriv->DacCmdStatus |= (i << 8);
474
475
476 writew(0, devpriv->io_addr + ICP_MULTI_AO);
477
478
479 devpriv->DacCmdStatus |= DAC_ST;
480
481
482 writew(devpriv->DacCmdStatus,
483 devpriv->io_addr + ICP_MULTI_DAC_CSR);
484
485
486 udelay(1);
487 }
488
489
490 writew(0, devpriv->io_addr + ICP_MULTI_DO);
491
492 return 0;
493}
494
495static int icp_multi_auto_attach(struct comedi_device *dev,
496 unsigned long context_unused)
497{
498 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
499 struct icp_multi_private *devpriv;
500 struct comedi_subdevice *s;
501 int ret;
502
503 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
504 if (!devpriv)
505 return -ENOMEM;
506 dev->private = devpriv;
507
508 ret = comedi_pci_enable(dev);
509 if (ret)
510 return ret;
511
512 devpriv->io_addr = pci_ioremap_bar(pcidev, 2);
513 if (!devpriv->io_addr)
514 return -ENOMEM;
515
516 ret = comedi_alloc_subdevices(dev, 5);
517 if (ret)
518 return ret;
519
520 icp_multi_reset(dev);
521
522 if (pcidev->irq) {
523 ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
524 IRQF_SHARED, dev->board_name, dev);
525 if (ret == 0)
526 dev->irq = pcidev->irq;
527 }
528
529 s = &dev->subdevices[0];
530 dev->read_subdev = s;
531 s->type = COMEDI_SUBD_AI;
532 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
533 s->n_chan = 16;
534 s->maxdata = 0x0fff;
535 s->len_chanlist = 16;
536 s->range_table = &range_analog;
537 s->insn_read = icp_multi_insn_read_ai;
538
539 s = &dev->subdevices[1];
540 s->type = COMEDI_SUBD_AO;
541 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
542 s->n_chan = 4;
543 s->maxdata = 0x0fff;
544 s->len_chanlist = 4;
545 s->range_table = &range_analog;
546 s->insn_write = icp_multi_insn_write_ao;
547 s->insn_read = icp_multi_insn_read_ao;
548
549 s = &dev->subdevices[2];
550 s->type = COMEDI_SUBD_DI;
551 s->subdev_flags = SDF_READABLE;
552 s->n_chan = 16;
553 s->maxdata = 1;
554 s->len_chanlist = 16;
555 s->range_table = &range_digital;
556 s->io_bits = 0;
557 s->insn_bits = icp_multi_insn_bits_di;
558
559 s = &dev->subdevices[3];
560 s->type = COMEDI_SUBD_DO;
561 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
562 s->n_chan = 8;
563 s->maxdata = 1;
564 s->len_chanlist = 8;
565 s->range_table = &range_digital;
566 s->io_bits = 0xff;
567 s->state = 0;
568 s->insn_bits = icp_multi_insn_bits_do;
569
570 s = &dev->subdevices[4];
571 s->type = COMEDI_SUBD_COUNTER;
572 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
573 s->n_chan = 4;
574 s->maxdata = 0xffff;
575 s->len_chanlist = 4;
576 s->state = 0;
577 s->insn_read = icp_multi_insn_read_ctr;
578 s->insn_write = icp_multi_insn_write_ctr;
579
580 devpriv->valid = 1;
581
582 dev_info(dev->class_dev, "%s attached, irq %sabled\n",
583 dev->board_name, dev->irq ? "en" : "dis");
584
585 return 0;
586}
587
588static void icp_multi_detach(struct comedi_device *dev)
589{
590 struct icp_multi_private *devpriv = dev->private;
591
592 if (devpriv)
593 if (devpriv->valid)
594 icp_multi_reset(dev);
595 if (dev->irq)
596 free_irq(dev->irq, dev);
597 if (devpriv && devpriv->io_addr)
598 iounmap(devpriv->io_addr);
599 comedi_pci_disable(dev);
600}
601
602static struct comedi_driver icp_multi_driver = {
603 .driver_name = "icp_multi",
604 .module = THIS_MODULE,
605 .auto_attach = icp_multi_auto_attach,
606 .detach = icp_multi_detach,
607};
608
609static int icp_multi_pci_probe(struct pci_dev *dev,
610 const struct pci_device_id *id)
611{
612 return comedi_pci_auto_config(dev, &icp_multi_driver, id->driver_data);
613}
614
615static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
616 { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
617 { 0 }
618};
619MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
620
621static struct pci_driver icp_multi_pci_driver = {
622 .name = "icp_multi",
623 .id_table = icp_multi_pci_table,
624 .probe = icp_multi_pci_probe,
625 .remove = comedi_pci_auto_unconfig,
626};
627module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
628
629MODULE_AUTHOR("Comedi http://www.comedi.org");
630MODULE_DESCRIPTION("Comedi low-level driver");
631MODULE_LICENSE("GPL");
632