Lines Matching +full:int +full:- +full:threshold
1 // SPDX-License-Identifier: GPL-2.0
8 * COMEDI - Linux Control and Measurement Device Interface
13 * ftp://ftp.quatech.com/Manuals/daqp-208.pdf
15 * This manual is for both the DAQP-208 and the DAQP-308.
18 * - A/D conversion
19 * - 8 channels
20 * - 4 gain ranges
21 * - ground ref or differential
22 * - single-shot and timed both supported
23 * - D/A conversion, single-shot
24 * - digital I/O
27 * - any kind of triggering - external or D/A channel 1
28 * - the card's optional expansion board
29 * - the card's timer (for anything other than A/D conversion)
30 * - D/A update modes other than immediate (i.e, timed)
31 * - fancier timing modes
32 * - setting card's FIFO buffer thresholds to anything but default
38 * Devices: [Quatech] DAQP-208 (daqp), DAQP-308
49 * The D/A and timer registers can be accessed with 16-bit or 8-bit I/O
50 * instructions. All other registers can only use 8-bit instructions.
52 * The FIFO and scanlist registers require two 8-bit instructions to
53 * access the 16-bit data. Data is transferred LSB then MSB.
74 #define DAQP_CTRL_TRIG_MODE BIT(2) /* 0=one-shot; 1=continuous */
100 /* the monostable bits are self-clearing after the function is complete */
112 #define DAQP_AO_REG 0x08 /* and 0x09 (16-bit) */
114 #define DAQP_TIMER_REG 0x0a /* and 0x0b (16-bit) */
147 unsigned int pacer_div;
148 int stop;
160 static int daqp_clear_events(struct comedi_device *dev, int loops) in daqp_clear_events()
162 unsigned int status; in daqp_clear_events()
168 while (--loops) { in daqp_clear_events()
169 status = inb(dev->iobase + DAQP_STATUS_REG); in daqp_clear_events()
173 dev_err(dev->class_dev, "couldn't clear events in status register\n"); in daqp_clear_events()
174 return -EBUSY; in daqp_clear_events()
177 static int daqp_ai_cancel(struct comedi_device *dev, in daqp_ai_cancel()
180 struct daqp_private *devpriv = dev->private; in daqp_ai_cancel()
182 if (devpriv->stop) in daqp_ai_cancel()
183 return -EIO; in daqp_ai_cancel()
189 outb(DAQP_CMD_STOP, dev->iobase + DAQP_CMD_REG); in daqp_ai_cancel()
190 outb(0, dev->iobase + DAQP_CTRL_REG); in daqp_ai_cancel()
191 inb(dev->iobase + DAQP_STATUS_REG); in daqp_ai_cancel()
196 static unsigned int daqp_ai_get_sample(struct comedi_device *dev, in daqp_ai_get_sample()
199 unsigned int val; in daqp_ai_get_sample()
205 val = inb(dev->iobase + DAQP_AI_FIFO_REG); in daqp_ai_get_sample()
206 val |= inb(dev->iobase + DAQP_AI_FIFO_REG) << 8; in daqp_ai_get_sample()
210 static irqreturn_t daqp_interrupt(int irq, void *dev_id) in daqp_interrupt()
213 struct comedi_subdevice *s = dev->read_subdev; in daqp_interrupt()
214 struct comedi_cmd *cmd = &s->async->cmd; in daqp_interrupt()
215 int loop_limit = 10000; in daqp_interrupt()
216 int status; in daqp_interrupt()
218 if (!dev->attached) in daqp_interrupt()
221 status = inb(dev->iobase + DAQP_STATUS_REG); in daqp_interrupt()
229 s->async->events |= COMEDI_CB_OVERFLOW; in daqp_interrupt()
230 dev_warn(dev->class_dev, "data lost\n"); in daqp_interrupt()
237 if (cmd->stop_src == TRIG_COUNT && in daqp_interrupt()
238 s->async->scans_done >= cmd->stop_arg) { in daqp_interrupt()
239 s->async->events |= COMEDI_CB_EOA; in daqp_interrupt()
243 if ((loop_limit--) <= 0) in daqp_interrupt()
246 status = inb(dev->iobase + DAQP_STATUS_REG); in daqp_interrupt()
250 dev_warn(dev->class_dev, in daqp_interrupt()
252 s->async->events |= COMEDI_CB_ERROR; in daqp_interrupt()
261 unsigned int chanspec, in daqp_ai_set_one_scanlist_entry()
262 int start) in daqp_ai_set_one_scanlist_entry()
264 unsigned int chan = CR_CHAN(chanspec); in daqp_ai_set_one_scanlist_entry()
265 unsigned int range = CR_RANGE(chanspec); in daqp_ai_set_one_scanlist_entry()
266 unsigned int aref = CR_AREF(chanspec); in daqp_ai_set_one_scanlist_entry()
267 unsigned int val; in daqp_ai_set_one_scanlist_entry()
277 outb(val & 0xff, dev->iobase + DAQP_SCANLIST_REG); in daqp_ai_set_one_scanlist_entry()
278 outb((val >> 8) & 0xff, dev->iobase + DAQP_SCANLIST_REG); in daqp_ai_set_one_scanlist_entry()
281 static int daqp_ai_eos(struct comedi_device *dev, in daqp_ai_eos()
286 unsigned int status; in daqp_ai_eos()
288 status = inb(dev->iobase + DAQP_AUX_REG); in daqp_ai_eos()
291 return -EBUSY; in daqp_ai_eos()
294 static int daqp_ai_insn_read(struct comedi_device *dev, in daqp_ai_insn_read()
297 unsigned int *data) in daqp_ai_insn_read()
299 struct daqp_private *devpriv = dev->private; in daqp_ai_insn_read()
300 int ret = 0; in daqp_ai_insn_read()
301 int i; in daqp_ai_insn_read()
303 if (devpriv->stop) in daqp_ai_insn_read()
304 return -EIO; in daqp_ai_insn_read()
306 outb(0, dev->iobase + DAQP_AUX_REG); in daqp_ai_insn_read()
309 outb(DAQP_CMD_RSTQ, dev->iobase + DAQP_CMD_REG); in daqp_ai_insn_read()
312 daqp_ai_set_one_scanlist_entry(dev, insn->chanspec, 1); in daqp_ai_insn_read()
315 outb(DAQP_CMD_RSTF, dev->iobase + DAQP_CMD_REG); in daqp_ai_insn_read()
317 /* Set trigger - one-shot, internal, no interrupts */ in daqp_ai_insn_read()
318 outb(DAQP_CTRL_PACER_CLK_100KHZ, dev->iobase + DAQP_CTRL_REG); in daqp_ai_insn_read()
324 for (i = 0; i < insn->n; i++) { in daqp_ai_insn_read()
327 dev->iobase + DAQP_CMD_REG); in daqp_ai_insn_read()
334 inb(dev->iobase + DAQP_STATUS_REG); in daqp_ai_insn_read()
340 outb(DAQP_CMD_STOP, dev->iobase + DAQP_CMD_REG); in daqp_ai_insn_read()
341 inb(dev->iobase + DAQP_STATUS_REG); in daqp_ai_insn_read()
343 return ret ? ret : insn->n; in daqp_ai_insn_read()
348 * which with its 24-bit counter, allows values up to 84 seconds.
353 static int daqp_ns_to_timer(unsigned int *ns, unsigned int flags) in daqp_ns_to_timer()
355 int timer; in daqp_ns_to_timer()
363 static void daqp_set_pacer(struct comedi_device *dev, unsigned int val) in daqp_set_pacer()
365 outb(val & 0xff, dev->iobase + DAQP_PACER_LOW_REG); in daqp_set_pacer()
366 outb((val >> 8) & 0xff, dev->iobase + DAQP_PACER_MID_REG); in daqp_set_pacer()
367 outb((val >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH_REG); in daqp_set_pacer()
370 static int daqp_ai_cmdtest(struct comedi_device *dev, in daqp_ai_cmdtest()
374 struct daqp_private *devpriv = dev->private; in daqp_ai_cmdtest()
375 int err = 0; in daqp_ai_cmdtest()
376 unsigned int arg; in daqp_ai_cmdtest()
380 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); in daqp_ai_cmdtest()
381 err |= comedi_check_trigger_src(&cmd->scan_begin_src, in daqp_ai_cmdtest()
383 err |= comedi_check_trigger_src(&cmd->convert_src, in daqp_ai_cmdtest()
385 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); in daqp_ai_cmdtest()
386 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); in daqp_ai_cmdtest()
393 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); in daqp_ai_cmdtest()
394 err |= comedi_check_trigger_is_unique(cmd->convert_src); in daqp_ai_cmdtest()
395 err |= comedi_check_trigger_is_unique(cmd->stop_src); in daqp_ai_cmdtest()
400 if (cmd->scan_begin_src != TRIG_TIMER && cmd->convert_src != TRIG_TIMER) in daqp_ai_cmdtest()
401 err |= -EINVAL; in daqp_ai_cmdtest()
408 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); in daqp_ai_cmdtest()
410 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); in daqp_ai_cmdtest()
411 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, in daqp_ai_cmdtest()
412 cmd->chanlist_len); in daqp_ai_cmdtest()
414 if (cmd->scan_begin_src == TRIG_TIMER) in daqp_ai_cmdtest()
415 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, in daqp_ai_cmdtest()
418 if (cmd->convert_src == TRIG_TIMER) { in daqp_ai_cmdtest()
419 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, in daqp_ai_cmdtest()
422 if (cmd->scan_begin_src == TRIG_TIMER) { in daqp_ai_cmdtest()
429 arg = cmd->convert_arg * cmd->scan_end_arg; in daqp_ai_cmdtest()
430 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, in daqp_ai_cmdtest()
435 if (cmd->stop_src == TRIG_COUNT) in daqp_ai_cmdtest()
436 err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); in daqp_ai_cmdtest()
438 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); in daqp_ai_cmdtest()
445 if (cmd->convert_src == TRIG_TIMER) { in daqp_ai_cmdtest()
446 arg = cmd->convert_arg; in daqp_ai_cmdtest()
447 devpriv->pacer_div = daqp_ns_to_timer(&arg, cmd->flags); in daqp_ai_cmdtest()
448 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); in daqp_ai_cmdtest()
449 } else if (cmd->scan_begin_src == TRIG_TIMER) { in daqp_ai_cmdtest()
450 arg = cmd->scan_begin_arg; in daqp_ai_cmdtest()
451 devpriv->pacer_div = daqp_ns_to_timer(&arg, cmd->flags); in daqp_ai_cmdtest()
452 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); in daqp_ai_cmdtest()
461 static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) in daqp_ai_cmd()
463 struct daqp_private *devpriv = dev->private; in daqp_ai_cmd()
464 struct comedi_cmd *cmd = &s->async->cmd; in daqp_ai_cmd()
465 int scanlist_start_on_every_entry; in daqp_ai_cmd()
466 int threshold; in daqp_ai_cmd() local
467 int ret; in daqp_ai_cmd()
468 int i; in daqp_ai_cmd()
470 if (devpriv->stop) in daqp_ai_cmd()
471 return -EIO; in daqp_ai_cmd()
473 outb(0, dev->iobase + DAQP_AUX_REG); in daqp_ai_cmd()
476 outb(DAQP_CMD_RSTQ, dev->iobase + DAQP_CMD_REG); in daqp_ai_cmd()
490 daqp_set_pacer(dev, devpriv->pacer_div); in daqp_ai_cmd()
492 if (cmd->convert_src == TRIG_TIMER) in daqp_ai_cmd()
498 for (i = 0; i < cmd->chanlist_len; i++) { in daqp_ai_cmd()
499 int start = (i == 0 || scanlist_start_on_every_entry); in daqp_ai_cmd()
501 daqp_ai_set_one_scanlist_entry(dev, cmd->chanlist[i], start); in daqp_ai_cmd()
504 /* Now it's time to program the FIFO threshold, basically the in daqp_ai_cmd()
511 * threshold set at half the FIFO size, we have a margin of in daqp_ai_cmd()
522 * three-quarters of the FIFO size - see below), we just use in daqp_ai_cmd()
523 * the stop count itself as the threshold, the card interrupts in daqp_ai_cmd()
529 * interrupt response) and use that as the threshold. So, if in daqp_ai_cmd()
531 * get 1000 samples, use that as the threshold, take four in daqp_ai_cmd()
535 * samples are requested, we could set the threshold to 1500 in daqp_ai_cmd()
538 * Instead, it'll set the threshold at 1266 and take 64 in daqp_ai_cmd()
544 * threshold (1500 = 3 * 5^3 * 2^2) and an interrupt count (54 in daqp_ai_cmd()
545 * = 3^3 * 2). Hmmm... a one-line while loop or prime in daqp_ai_cmd()
548 * I'll also note a mini-race condition before ignoring it in in daqp_ai_cmd()
566 * compute the FIFO threshold (in bytes, not samples - that's in daqp_ai_cmd()
567 * why we multiple devpriv->count by 2 = sizeof(sample)) in daqp_ai_cmd()
570 if (cmd->stop_src == TRIG_COUNT) { in daqp_ai_cmd()
574 nsamples = (unsigned long long)cmd->stop_arg * in daqp_ai_cmd()
575 cmd->scan_end_arg; in daqp_ai_cmd()
579 threshold = nbytes; in daqp_ai_cmd()
581 threshold = DAQP_FIFO_SIZE / 2; in daqp_ai_cmd()
586 outb(DAQP_CMD_RSTF, dev->iobase + DAQP_CMD_REG); in daqp_ai_cmd()
588 /* Set FIFO threshold. First two bytes are near-empty in daqp_ai_cmd()
589 * threshold, which is unused; next two bytes are near-full in daqp_ai_cmd()
590 * threshold. We computed the number of bytes we want in the in daqp_ai_cmd()
596 outb(0x00, dev->iobase + DAQP_AI_FIFO_REG); in daqp_ai_cmd()
597 outb(0x00, dev->iobase + DAQP_AI_FIFO_REG); in daqp_ai_cmd()
599 outb((DAQP_FIFO_SIZE - threshold) & 0xff, in daqp_ai_cmd()
600 dev->iobase + DAQP_AI_FIFO_REG); in daqp_ai_cmd()
601 outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_AI_FIFO_REG); in daqp_ai_cmd()
603 /* Set trigger - continuous, internal */ in daqp_ai_cmd()
605 DAQP_CTRL_FIFO_INT_ENA, dev->iobase + DAQP_CTRL_REG); in daqp_ai_cmd()
612 outb(DAQP_CMD_ARM | DAQP_CMD_FIFO_DATA, dev->iobase + DAQP_CMD_REG); in daqp_ai_cmd()
617 static int daqp_ao_empty(struct comedi_device *dev, in daqp_ao_empty()
622 unsigned int status; in daqp_ao_empty()
624 status = inb(dev->iobase + DAQP_AUX_REG); in daqp_ao_empty()
627 return -EBUSY; in daqp_ao_empty()
630 static int daqp_ao_insn_write(struct comedi_device *dev, in daqp_ao_insn_write()
633 unsigned int *data) in daqp_ao_insn_write()
635 struct daqp_private *devpriv = dev->private; in daqp_ao_insn_write()
636 unsigned int chan = CR_CHAN(insn->chanspec); in daqp_ao_insn_write()
637 int i; in daqp_ao_insn_write()
639 if (devpriv->stop) in daqp_ao_insn_write()
640 return -EIO; in daqp_ao_insn_write()
643 outb(0, dev->iobase + DAQP_AUX_REG); in daqp_ao_insn_write()
645 for (i = 0; i < insn->n; i++) { in daqp_ao_insn_write()
646 unsigned int val = data[i]; in daqp_ao_insn_write()
647 int ret; in daqp_ao_insn_write()
656 dev->iobase + DAQP_AO_REG); in daqp_ao_insn_write()
658 s->readback[chan] = val; in daqp_ao_insn_write()
661 return insn->n; in daqp_ao_insn_write()
664 static int daqp_di_insn_bits(struct comedi_device *dev, in daqp_di_insn_bits()
667 unsigned int *data) in daqp_di_insn_bits()
669 struct daqp_private *devpriv = dev->private; in daqp_di_insn_bits()
671 if (devpriv->stop) in daqp_di_insn_bits()
672 return -EIO; in daqp_di_insn_bits()
674 data[0] = inb(dev->iobase + DAQP_DI_REG); in daqp_di_insn_bits()
676 return insn->n; in daqp_di_insn_bits()
679 static int daqp_do_insn_bits(struct comedi_device *dev, in daqp_do_insn_bits()
682 unsigned int *data) in daqp_do_insn_bits()
684 struct daqp_private *devpriv = dev->private; in daqp_do_insn_bits()
686 if (devpriv->stop) in daqp_do_insn_bits()
687 return -EIO; in daqp_do_insn_bits()
690 outb(s->state, dev->iobase + DAQP_DO_REG); in daqp_do_insn_bits()
692 data[1] = s->state; in daqp_do_insn_bits()
694 return insn->n; in daqp_do_insn_bits()
697 static int daqp_auto_attach(struct comedi_device *dev, in daqp_auto_attach()
703 int ret; in daqp_auto_attach()
707 return -ENOMEM; in daqp_auto_attach()
709 link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; in daqp_auto_attach()
713 dev->iobase = link->resource[0]->start; in daqp_auto_attach()
715 link->priv = dev; in daqp_auto_attach()
718 dev->irq = link->irq; in daqp_auto_attach()
724 s = &dev->subdevices[0]; in daqp_auto_attach()
725 s->type = COMEDI_SUBD_AI; in daqp_auto_attach()
726 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; in daqp_auto_attach()
727 s->n_chan = 8; in daqp_auto_attach()
728 s->maxdata = 0xffff; in daqp_auto_attach()
729 s->range_table = &range_daqp_ai; in daqp_auto_attach()
730 s->insn_read = daqp_ai_insn_read; in daqp_auto_attach()
731 if (dev->irq) { in daqp_auto_attach()
732 dev->read_subdev = s; in daqp_auto_attach()
733 s->subdev_flags |= SDF_CMD_READ; in daqp_auto_attach()
734 s->len_chanlist = 2048; in daqp_auto_attach()
735 s->do_cmdtest = daqp_ai_cmdtest; in daqp_auto_attach()
736 s->do_cmd = daqp_ai_cmd; in daqp_auto_attach()
737 s->cancel = daqp_ai_cancel; in daqp_auto_attach()
740 s = &dev->subdevices[1]; in daqp_auto_attach()
741 s->type = COMEDI_SUBD_AO; in daqp_auto_attach()
742 s->subdev_flags = SDF_WRITABLE; in daqp_auto_attach()
743 s->n_chan = 2; in daqp_auto_attach()
744 s->maxdata = 0x0fff; in daqp_auto_attach()
745 s->range_table = &range_bipolar5; in daqp_auto_attach()
746 s->insn_write = daqp_ao_insn_write; in daqp_auto_attach()
757 * ---- ----------------- ---------------------------- in daqp_auto_attach()
763 s = &dev->subdevices[2]; in daqp_auto_attach()
764 s->type = COMEDI_SUBD_DI; in daqp_auto_attach()
765 s->subdev_flags = SDF_READABLE; in daqp_auto_attach()
766 s->n_chan = 4; in daqp_auto_attach()
767 s->maxdata = 1; in daqp_auto_attach()
768 s->insn_bits = daqp_di_insn_bits; in daqp_auto_attach()
777 s = &dev->subdevices[3]; in daqp_auto_attach()
778 s->type = COMEDI_SUBD_DO; in daqp_auto_attach()
779 s->subdev_flags = SDF_WRITABLE; in daqp_auto_attach()
780 s->n_chan = 4; in daqp_auto_attach()
781 s->maxdata = 1; in daqp_auto_attach()
782 s->insn_bits = daqp_do_insn_bits; in daqp_auto_attach()
794 static int daqp_cs_suspend(struct pcmcia_device *link) in daqp_cs_suspend()
796 struct comedi_device *dev = link->priv; in daqp_cs_suspend()
797 struct daqp_private *devpriv = dev ? dev->private : NULL; in daqp_cs_suspend()
801 devpriv->stop = 1; in daqp_cs_suspend()
806 static int daqp_cs_resume(struct pcmcia_device *link) in daqp_cs_resume()
808 struct comedi_device *dev = link->priv; in daqp_cs_resume()
809 struct daqp_private *devpriv = dev ? dev->private : NULL; in daqp_cs_resume()
812 devpriv->stop = 0; in daqp_cs_resume()
817 static int daqp_cs_attach(struct pcmcia_device *link) in daqp_cs_attach()