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 "../comedidev.h"
51#include <linux/semaphore.h>
52
53#include <pcmcia/cistpl.h>
54#include <pcmcia/cisreg.h>
55#include <pcmcia/ds.h>
56
57#include <linux/completion.h>
58
59#include "comedi_fc.h"
60
61struct daqp_private {
62 int stop;
63
64 enum { semaphore, buffer } interrupt_mode;
65
66 struct completion eos;
67
68 int count;
69};
70
71
72
73#define DAQP_FIFO_SIZE 4096
74
75#define DAQP_FIFO 0
76#define DAQP_SCANLIST 1
77#define DAQP_CONTROL 2
78#define DAQP_STATUS 2
79#define DAQP_DIGITAL_IO 3
80#define DAQP_PACER_LOW 4
81#define DAQP_PACER_MID 5
82#define DAQP_PACER_HIGH 6
83#define DAQP_COMMAND 7
84#define DAQP_DA 8
85#define DAQP_TIMER 10
86#define DAQP_AUX 15
87
88#define DAQP_SCANLIST_DIFFERENTIAL 0x4000
89#define DAQP_SCANLIST_GAIN(x) ((x)<<12)
90#define DAQP_SCANLIST_CHANNEL(x) ((x)<<8)
91#define DAQP_SCANLIST_START 0x0080
92#define DAQP_SCANLIST_EXT_GAIN(x) ((x)<<4)
93#define DAQP_SCANLIST_EXT_CHANNEL(x) (x)
94
95#define DAQP_CONTROL_PACER_100kHz 0xc0
96#define DAQP_CONTROL_PACER_1MHz 0x80
97#define DAQP_CONTROL_PACER_5MHz 0x40
98#define DAQP_CONTROL_PACER_EXTERNAL 0x00
99#define DAQP_CONTORL_EXPANSION 0x20
100#define DAQP_CONTROL_EOS_INT_ENABLE 0x10
101#define DAQP_CONTROL_FIFO_INT_ENABLE 0x08
102#define DAQP_CONTROL_TRIGGER_ONESHOT 0x00
103#define DAQP_CONTROL_TRIGGER_CONTINUOUS 0x04
104#define DAQP_CONTROL_TRIGGER_INTERNAL 0x00
105#define DAQP_CONTROL_TRIGGER_EXTERNAL 0x02
106#define DAQP_CONTROL_TRIGGER_RISING 0x00
107#define DAQP_CONTROL_TRIGGER_FALLING 0x01
108
109#define DAQP_STATUS_IDLE 0x80
110#define DAQP_STATUS_RUNNING 0x40
111#define DAQP_STATUS_EVENTS 0x38
112#define DAQP_STATUS_DATA_LOST 0x20
113#define DAQP_STATUS_END_OF_SCAN 0x10
114#define DAQP_STATUS_FIFO_THRESHOLD 0x08
115#define DAQP_STATUS_FIFO_FULL 0x04
116#define DAQP_STATUS_FIFO_NEARFULL 0x02
117#define DAQP_STATUS_FIFO_EMPTY 0x01
118
119#define DAQP_COMMAND_ARM 0x80
120#define DAQP_COMMAND_RSTF 0x40
121#define DAQP_COMMAND_RSTQ 0x20
122#define DAQP_COMMAND_STOP 0x10
123#define DAQP_COMMAND_LATCH 0x08
124#define DAQP_COMMAND_100kHz 0x00
125#define DAQP_COMMAND_50kHz 0x02
126#define DAQP_COMMAND_25kHz 0x04
127#define DAQP_COMMAND_FIFO_DATA 0x01
128#define DAQP_COMMAND_FIFO_PROGRAM 0x00
129
130#define DAQP_AUX_TRIGGER_TTL 0x00
131#define DAQP_AUX_TRIGGER_ANALOG 0x80
132#define DAQP_AUX_TRIGGER_PRETRIGGER 0x40
133#define DAQP_AUX_TIMER_INT_ENABLE 0x20
134#define DAQP_AUX_TIMER_RELOAD 0x00
135#define DAQP_AUX_TIMER_PAUSE 0x08
136#define DAQP_AUX_TIMER_GO 0x10
137#define DAQP_AUX_TIMER_GO_EXTERNAL 0x18
138#define DAQP_AUX_TIMER_EXTERNAL_SRC 0x04
139#define DAQP_AUX_TIMER_INTERNAL_SRC 0x00
140#define DAQP_AUX_DA_DIRECT 0x00
141#define DAQP_AUX_DA_OVERFLOW 0x01
142#define DAQP_AUX_DA_EXTERNAL 0x02
143#define DAQP_AUX_DA_PACER 0x03
144
145#define DAQP_AUX_RUNNING 0x80
146#define DAQP_AUX_TRIGGERED 0x40
147#define DAQP_AUX_DA_BUFFER 0x20
148#define DAQP_AUX_TIMER_OVERFLOW 0x10
149#define DAQP_AUX_CONVERSION 0x08
150#define DAQP_AUX_DATA_LOST 0x04
151#define DAQP_AUX_FIFO_NEARFULL 0x02
152#define DAQP_AUX_FIFO_EMPTY 0x01
153
154static const struct comedi_lrange range_daqp_ai = {
155 4, {
156 BIP_RANGE(10),
157 BIP_RANGE(5),
158 BIP_RANGE(2.5),
159 BIP_RANGE(1.25)
160 }
161};
162
163
164
165static int daqp_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
166{
167 struct daqp_private *devpriv = dev->private;
168
169 if (devpriv->stop)
170 return -EIO;
171
172 outb(DAQP_COMMAND_STOP, dev->iobase + DAQP_COMMAND);
173
174
175
176
177 devpriv->interrupt_mode = semaphore;
178
179 return 0;
180}
181
182
183
184
185
186
187
188
189
190
191static enum irqreturn daqp_interrupt(int irq, void *dev_id)
192{
193 struct comedi_device *dev = dev_id;
194 struct daqp_private *devpriv = dev->private;
195 struct comedi_subdevice *s = dev->read_subdev;
196 int loop_limit = 10000;
197 int status;
198
199 if (!dev->attached)
200 return IRQ_NONE;
201
202 switch (devpriv->interrupt_mode) {
203 case semaphore:
204 complete(&devpriv->eos);
205 break;
206
207 case buffer:
208 while (!((status = inb(dev->iobase + DAQP_STATUS))
209 & DAQP_STATUS_FIFO_EMPTY)) {
210
211 short data;
212
213 if (status & DAQP_STATUS_DATA_LOST) {
214 s->async->events |=
215 COMEDI_CB_EOA | COMEDI_CB_OVERFLOW;
216 dev_warn(dev->class_dev, "data lost\n");
217 daqp_ai_cancel(dev, s);
218 break;
219 }
220
221 data = inb(dev->iobase + DAQP_FIFO);
222 data |= inb(dev->iobase + DAQP_FIFO) << 8;
223 data ^= 0x8000;
224
225 comedi_buf_put(s->async, data);
226
227
228
229
230
231 if (devpriv->count > 0) {
232 devpriv->count--;
233 if (devpriv->count == 0) {
234 daqp_ai_cancel(dev, s);
235 s->async->events |= COMEDI_CB_EOA;
236 break;
237 }
238 }
239
240 if ((loop_limit--) <= 0)
241 break;
242 }
243
244 if (loop_limit <= 0) {
245 dev_warn(dev->class_dev,
246 "loop_limit reached in daqp_interrupt()\n");
247 daqp_ai_cancel(dev, s);
248 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
249 }
250
251 s->async->events |= COMEDI_CB_BLOCK;
252
253 comedi_event(dev, s);
254 }
255 return IRQ_HANDLED;
256}
257
258static void daqp_ai_set_one_scanlist_entry(struct comedi_device *dev,
259 unsigned int chanspec,
260 int start)
261{
262 unsigned int chan = CR_CHAN(chanspec);
263 unsigned int range = CR_RANGE(chanspec);
264 unsigned int aref = CR_AREF(chanspec);
265 unsigned int val;
266
267 val = DAQP_SCANLIST_CHANNEL(chan) | DAQP_SCANLIST_GAIN(range);
268
269 if (aref == AREF_DIFF)
270 val |= DAQP_SCANLIST_DIFFERENTIAL;
271
272 if (start)
273 val |= DAQP_SCANLIST_START;
274
275 outb(val & 0xff, dev->iobase + DAQP_SCANLIST);
276 outb((val >> 8) & 0xff, dev->iobase + DAQP_SCANLIST);
277}
278
279
280
281static int daqp_ai_insn_read(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn, unsigned int *data)
284{
285 struct daqp_private *devpriv = dev->private;
286 int i;
287 int v;
288 int counter = 10000;
289
290 if (devpriv->stop)
291 return -EIO;
292
293
294 daqp_ai_cancel(dev, s);
295
296 outb(0, dev->iobase + DAQP_AUX);
297
298
299 outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
300
301
302 daqp_ai_set_one_scanlist_entry(dev, insn->chanspec, 1);
303
304
305
306 outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
307
308
309
310 v = DAQP_CONTROL_TRIGGER_ONESHOT | DAQP_CONTROL_TRIGGER_INTERNAL
311 | DAQP_CONTROL_PACER_100kHz | DAQP_CONTROL_EOS_INT_ENABLE;
312
313 outb(v, dev->iobase + DAQP_CONTROL);
314
315
316
317
318
319 while (--counter
320 && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS))
321 ;
322 if (!counter) {
323 dev_err(dev->class_dev,
324 "couldn't clear interrupts in status register\n");
325 return -1;
326 }
327
328 init_completion(&devpriv->eos);
329 devpriv->interrupt_mode = semaphore;
330
331 for (i = 0; i < insn->n; i++) {
332
333
334 outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
335 dev->iobase + DAQP_COMMAND);
336
337
338
339 if (wait_for_completion_interruptible(&devpriv->eos))
340 return -EINTR;
341
342 data[i] = inb(dev->iobase + DAQP_FIFO);
343 data[i] |= inb(dev->iobase + DAQP_FIFO) << 8;
344 data[i] ^= 0x8000;
345 }
346
347 return insn->n;
348}
349
350
351
352
353
354
355
356
357static int daqp_ns_to_timer(unsigned int *ns, int round)
358{
359 int timer;
360
361 timer = *ns / 200;
362 *ns = timer * 200;
363
364 return timer;
365}
366
367
368
369
370
371
372
373
374
375static int daqp_ai_cmdtest(struct comedi_device *dev,
376 struct comedi_subdevice *s, struct comedi_cmd *cmd)
377{
378 int err = 0;
379 int tmp;
380
381
382
383 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
384 err |= cfc_check_trigger_src(&cmd->scan_begin_src,
385 TRIG_TIMER | TRIG_FOLLOW);
386 err |= cfc_check_trigger_src(&cmd->convert_src,
387 TRIG_TIMER | TRIG_NOW);
388 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
389 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
390
391 if (err)
392 return 1;
393
394
395
396 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
397 err |= cfc_check_trigger_is_unique(cmd->convert_src);
398 err |= cfc_check_trigger_is_unique(cmd->stop_src);
399
400
401
402 if (err)
403 return 2;
404
405
406
407 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
408
409#define MAX_SPEED 10000
410
411 if (cmd->scan_begin_src == TRIG_TIMER)
412 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
413 MAX_SPEED);
414
415
416
417
418
419
420 if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER
421 && cmd->scan_begin_arg != cmd->convert_arg * cmd->scan_end_arg) {
422 err |= -EINVAL;
423 }
424
425 if (cmd->convert_src == TRIG_TIMER)
426 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, MAX_SPEED);
427
428 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
429
430 if (cmd->stop_src == TRIG_COUNT)
431 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
432 else
433 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
434
435 if (err)
436 return 3;
437
438
439
440 if (cmd->scan_begin_src == TRIG_TIMER) {
441 tmp = cmd->scan_begin_arg;
442 daqp_ns_to_timer(&cmd->scan_begin_arg,
443 cmd->flags & TRIG_ROUND_MASK);
444 if (tmp != cmd->scan_begin_arg)
445 err++;
446 }
447
448 if (cmd->convert_src == TRIG_TIMER) {
449 tmp = cmd->convert_arg;
450 daqp_ns_to_timer(&cmd->convert_arg,
451 cmd->flags & TRIG_ROUND_MASK);
452 if (tmp != cmd->convert_arg)
453 err++;
454 }
455
456 if (err)
457 return 4;
458
459 return 0;
460}
461
462static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
463{
464 struct daqp_private *devpriv = dev->private;
465 struct comedi_cmd *cmd = &s->async->cmd;
466 int counter;
467 int scanlist_start_on_every_entry;
468 int threshold;
469
470 int i;
471 int v;
472
473 if (devpriv->stop)
474 return -EIO;
475
476
477 daqp_ai_cancel(dev, s);
478
479 outb(0, dev->iobase + DAQP_AUX);
480
481
482 outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497 if (cmd->convert_src == TRIG_TIMER) {
498 counter = daqp_ns_to_timer(&cmd->convert_arg,
499 cmd->flags & TRIG_ROUND_MASK);
500 outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
501 outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
502 outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
503 scanlist_start_on_every_entry = 1;
504 } else {
505 counter = daqp_ns_to_timer(&cmd->scan_begin_arg,
506 cmd->flags & TRIG_ROUND_MASK);
507 outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
508 outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
509 outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
510 scanlist_start_on_every_entry = 0;
511 }
512
513
514 for (i = 0; i < cmd->chanlist_len; i++) {
515 int start = (i == 0 || scanlist_start_on_every_entry);
516
517 daqp_ai_set_one_scanlist_entry(dev, cmd->chanlist[i], start);
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586 if (cmd->stop_src == TRIG_COUNT) {
587 devpriv->count = cmd->stop_arg * cmd->scan_end_arg;
588 threshold = 2 * devpriv->count;
589 while (threshold > DAQP_FIFO_SIZE * 3 / 4)
590 threshold /= 2;
591 } else {
592 devpriv->count = -1;
593 threshold = DAQP_FIFO_SIZE / 2;
594 }
595
596
597
598 outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
599
600
601
602
603
604
605
606
607
608 outb(0x00, dev->iobase + DAQP_FIFO);
609 outb(0x00, dev->iobase + DAQP_FIFO);
610
611 outb((DAQP_FIFO_SIZE - threshold) & 0xff, dev->iobase + DAQP_FIFO);
612 outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_FIFO);
613
614
615
616 v = DAQP_CONTROL_TRIGGER_CONTINUOUS | DAQP_CONTROL_TRIGGER_INTERNAL
617 | DAQP_CONTROL_PACER_5MHz | DAQP_CONTROL_FIFO_INT_ENABLE;
618
619 outb(v, dev->iobase + DAQP_CONTROL);
620
621
622
623
624 counter = 100;
625 while (--counter
626 && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS))
627 ;
628 if (!counter) {
629 dev_err(dev->class_dev,
630 "couldn't clear interrupts in status register\n");
631 return -1;
632 }
633
634 devpriv->interrupt_mode = buffer;
635
636
637 outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
638 dev->iobase + DAQP_COMMAND);
639
640 return 0;
641}
642
643static int daqp_ao_insn_write(struct comedi_device *dev,
644 struct comedi_subdevice *s,
645 struct comedi_insn *insn,
646 unsigned int *data)
647{
648 struct daqp_private *devpriv = dev->private;
649 unsigned int chan = CR_CHAN(insn->chanspec);
650 unsigned int val;
651 int i;
652
653 if (devpriv->stop)
654 return -EIO;
655
656
657 outb(0, dev->iobase + DAQP_AUX);
658
659 for (i = 0; i > insn->n; i++) {
660 val = data[0];
661 val &= 0x0fff;
662 val ^= 0x0800;
663 val |= (chan << 12);
664
665 outw(val, dev->iobase + DAQP_DA);
666 }
667
668 return insn->n;
669}
670
671static int daqp_di_insn_bits(struct comedi_device *dev,
672 struct comedi_subdevice *s,
673 struct comedi_insn *insn,
674 unsigned int *data)
675{
676 struct daqp_private *devpriv = dev->private;
677
678 if (devpriv->stop)
679 return -EIO;
680
681 data[0] = inb(dev->iobase + DAQP_DIGITAL_IO);
682
683 return insn->n;
684}
685
686static int daqp_do_insn_bits(struct comedi_device *dev,
687 struct comedi_subdevice *s,
688 struct comedi_insn *insn,
689 unsigned int *data)
690{
691 struct daqp_private *devpriv = dev->private;
692 unsigned int mask = data[0];
693 unsigned int bits = data[1];
694
695 if (devpriv->stop)
696 return -EIO;
697
698 if (mask) {
699 s->state &= ~mask;
700 s->state |= (bits & mask);
701
702 outb(s->state, dev->iobase + DAQP_DIGITAL_IO);
703 }
704
705 data[1] = s->state;
706
707 return insn->n;
708}
709
710static int daqp_auto_attach(struct comedi_device *dev,
711 unsigned long context)
712{
713 struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
714 struct daqp_private *devpriv;
715 struct comedi_subdevice *s;
716 int ret;
717
718 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
719 if (!devpriv)
720 return -ENOMEM;
721 dev->private = devpriv;
722
723 link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
724 ret = comedi_pcmcia_enable(dev, NULL);
725 if (ret)
726 return ret;
727 dev->iobase = link->resource[0]->start;
728
729 link->priv = dev;
730 ret = pcmcia_request_irq(link, daqp_interrupt);
731 if (ret)
732 return ret;
733
734 ret = comedi_alloc_subdevices(dev, 4);
735 if (ret)
736 return ret;
737
738 s = &dev->subdevices[0];
739 dev->read_subdev = s;
740 s->type = COMEDI_SUBD_AI;
741 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
742 s->n_chan = 8;
743 s->len_chanlist = 2048;
744 s->maxdata = 0xffff;
745 s->range_table = &range_daqp_ai;
746 s->insn_read = daqp_ai_insn_read;
747 s->do_cmdtest = daqp_ai_cmdtest;
748 s->do_cmd = daqp_ai_cmd;
749 s->cancel = daqp_ai_cancel;
750
751 s = &dev->subdevices[1];
752 s->type = COMEDI_SUBD_AO;
753 s->subdev_flags = SDF_WRITEABLE;
754 s->n_chan = 2;
755 s->maxdata = 0x0fff;
756 s->range_table = &range_bipolar5;
757 s->insn_write = daqp_ao_insn_write;
758
759 s = &dev->subdevices[2];
760 s->type = COMEDI_SUBD_DI;
761 s->subdev_flags = SDF_READABLE;
762 s->n_chan = 1;
763 s->maxdata = 1;
764 s->insn_bits = daqp_di_insn_bits;
765
766 s = &dev->subdevices[3];
767 s->type = COMEDI_SUBD_DO;
768 s->subdev_flags = SDF_WRITEABLE;
769 s->n_chan = 1;
770 s->maxdata = 1;
771 s->insn_bits = daqp_do_insn_bits;
772
773 return 0;
774}
775
776static struct comedi_driver driver_daqp = {
777 .driver_name = "quatech_daqp_cs",
778 .module = THIS_MODULE,
779 .auto_attach = daqp_auto_attach,
780 .detach = comedi_pcmcia_disable,
781};
782
783static int daqp_cs_suspend(struct pcmcia_device *link)
784{
785 struct comedi_device *dev = link->priv;
786 struct daqp_private *devpriv = dev ? dev->private : NULL;
787
788
789 if (devpriv)
790 devpriv->stop = 1;
791
792 return 0;
793}
794
795static int daqp_cs_resume(struct pcmcia_device *link)
796{
797 struct comedi_device *dev = link->priv;
798 struct daqp_private *devpriv = dev ? dev->private : NULL;
799
800 if (devpriv)
801 devpriv->stop = 0;
802
803 return 0;
804}
805
806static int daqp_cs_attach(struct pcmcia_device *link)
807{
808 return comedi_pcmcia_auto_config(link, &driver_daqp);
809}
810
811static const struct pcmcia_device_id daqp_cs_id_table[] = {
812 PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027),
813 PCMCIA_DEVICE_NULL
814};
815MODULE_DEVICE_TABLE(pcmcia, daqp_cs_id_table);
816
817static struct pcmcia_driver daqp_cs_driver = {
818 .name = "quatech_daqp_cs",
819 .owner = THIS_MODULE,
820 .id_table = daqp_cs_id_table,
821 .probe = daqp_cs_attach,
822 .remove = comedi_pcmcia_auto_unconfig,
823 .suspend = daqp_cs_suspend,
824 .resume = daqp_cs_resume,
825};
826module_comedi_pcmcia_driver(driver_daqp, daqp_cs_driver);
827
828MODULE_DESCRIPTION("Comedi driver for Quatech DAQP PCMCIA data capture cards");
829MODULE_AUTHOR("Brent Baccala <baccala@freesoft.org>");
830MODULE_LICENSE("GPL");
831