1 // SPDX-License-Identifier: GPL-2.0+
4 * Hardware driver for PCI9111 ADLink cards: PCI-9111HR
5 * Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
10 * Description: Adlink PCI-9111HR
11 * Devices: [ADLink] PCI-9111HR (adl_pci9111)
12 * Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
13 * Status: experimental
15 * Configuration options: not applicable, uses PCI auto config
19 * - ao_insn read/write
21 * - do_insn read/write
22 * - ai_do_cmd mode with the following sources:
23 * - start_src TRIG_NOW
24 * - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT
25 * - convert_src TRIG_TIMER TRIG_EXT
26 * - scan_end_src TRIG_COUNT
27 * - stop_src TRIG_COUNT TRIG_NONE
29 * The scanned channels must be consecutive and start from 0. They must
30 * all have the same range and aref.
35 * - Really test implemented functionality.
36 * - Add support for the PCI-9111DG with a probe routine to identify
37 * the card type (perhaps with the help of the channel number readback
38 * of the A/D Data register).
39 * - Add external multiplexer support.
42 #include <linux/module.h>
43 #include <linux/delay.h>
44 #include <linux/interrupt.h>
46 #include "../comedi_pci.h"
49 #include "comedi_8254.h"
51 #define PCI9111_FIFO_HALF_SIZE 512
53 #define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
55 #define PCI9111_RANGE_SETTING_DELAY 10
56 #define PCI9111_AI_INSTANT_READ_UDELAY_US 2
59 * IO address map and bit defines
61 #define PCI9111_AI_FIFO_REG 0x00
62 #define PCI9111_AO_REG 0x00
63 #define PCI9111_DIO_REG 0x02
64 #define PCI9111_EDIO_REG 0x04
65 #define PCI9111_AI_CHANNEL_REG 0x06
66 #define PCI9111_AI_RANGE_STAT_REG 0x08
67 #define PCI9111_AI_STAT_AD_BUSY BIT(7)
68 #define PCI9111_AI_STAT_FF_FF BIT(6)
69 #define PCI9111_AI_STAT_FF_HF BIT(5)
70 #define PCI9111_AI_STAT_FF_EF BIT(4)
71 #define PCI9111_AI_RANGE(x) (((x) & 0x7) << 0)
72 #define PCI9111_AI_RANGE_MASK PCI9111_AI_RANGE(7)
73 #define PCI9111_AI_TRIG_CTRL_REG 0x0a
74 #define PCI9111_AI_TRIG_CTRL_TRGEVENT BIT(5)
75 #define PCI9111_AI_TRIG_CTRL_POTRG BIT(4)
76 #define PCI9111_AI_TRIG_CTRL_PTRG BIT(3)
77 #define PCI9111_AI_TRIG_CTRL_ETIS BIT(2)
78 #define PCI9111_AI_TRIG_CTRL_TPST BIT(1)
79 #define PCI9111_AI_TRIG_CTRL_ASCAN BIT(0)
80 #define PCI9111_INT_CTRL_REG 0x0c
81 #define PCI9111_INT_CTRL_ISC2 BIT(3)
82 #define PCI9111_INT_CTRL_FFEN BIT(2)
83 #define PCI9111_INT_CTRL_ISC1 BIT(1)
84 #define PCI9111_INT_CTRL_ISC0 BIT(0)
85 #define PCI9111_SOFT_TRIG_REG 0x0e
86 #define PCI9111_8254_BASE_REG 0x40
87 #define PCI9111_INT_CLR_REG 0x48
89 /* PLX 9052 Local Interrupt 1 enabled and active */
90 #define PCI9111_LI1_ACTIVE (PLX9052_INTCSR_LI1ENAB | \
91 PLX9052_INTCSR_LI1STAT)
93 /* PLX 9052 Local Interrupt 2 enabled and active */
94 #define PCI9111_LI2_ACTIVE (PLX9052_INTCSR_LI2ENAB | \
95 PLX9052_INTCSR_LI2STAT)
97 static const struct comedi_lrange pci9111_ai_range = {
107 struct pci9111_private_data {
108 unsigned long lcr_io_base;
110 unsigned int scan_delay;
111 unsigned int chunk_counter;
112 unsigned int chunk_num_samples;
114 unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
117 static void plx9050_interrupt_control(unsigned long io_base,
119 bool int1_active_high,
121 bool int2_active_high,
122 bool interrupt_enable)
127 flags |= PLX9052_INTCSR_LI1ENAB;
128 if (int1_active_high)
129 flags |= PLX9052_INTCSR_LI1POL;
131 flags |= PLX9052_INTCSR_LI2ENAB;
132 if (int2_active_high)
133 flags |= PLX9052_INTCSR_LI2POL;
135 if (interrupt_enable)
136 flags |= PLX9052_INTCSR_PCIENAB;
138 outb(flags, io_base + PLX9052_INTCSR);
141 enum pci9111_ISC0_sources {
143 irq_on_fifo_half_full
146 enum pci9111_ISC1_sources {
148 irq_on_external_trigger
151 static void pci9111_interrupt_source_set(struct comedi_device *dev,
152 enum pci9111_ISC0_sources irq_0_source,
153 enum pci9111_ISC1_sources irq_1_source)
157 /* Read the current interrupt control bits */
158 flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
159 /* Shift the bits so they are compatible with the write register */
161 /* Mask off the ISCx bits */
164 /* Now set the new ISCx bits */
165 if (irq_0_source == irq_on_fifo_half_full)
166 flags |= PCI9111_INT_CTRL_ISC0;
168 if (irq_1_source == irq_on_external_trigger)
169 flags |= PCI9111_INT_CTRL_ISC1;
171 outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
174 static void pci9111_fifo_reset(struct comedi_device *dev)
176 unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
178 /* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */
179 outb(0, int_ctrl_reg);
180 outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
181 outb(0, int_ctrl_reg);
184 static int pci9111_ai_cancel(struct comedi_device *dev,
185 struct comedi_subdevice *s)
187 struct pci9111_private_data *dev_private = dev->private;
189 /* Disable interrupts */
190 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
193 /* disable A/D triggers (software trigger mode) and auto scan off */
194 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
196 pci9111_fifo_reset(dev);
201 static int pci9111_ai_check_chanlist(struct comedi_device *dev,
202 struct comedi_subdevice *s,
203 struct comedi_cmd *cmd)
205 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
206 unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
209 for (i = 1; i < cmd->chanlist_len; i++) {
210 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
211 unsigned int range = CR_RANGE(cmd->chanlist[i]);
212 unsigned int aref = CR_AREF(cmd->chanlist[i]);
215 dev_dbg(dev->class_dev,
216 "entries in chanlist must be consecutive channels,counting upwards from 0\n");
220 if (range != range0) {
221 dev_dbg(dev->class_dev,
222 "entries in chanlist must all have the same gain\n");
227 dev_dbg(dev->class_dev,
228 "entries in chanlist must all have the same reference\n");
236 static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
237 struct comedi_subdevice *s,
238 struct comedi_cmd *cmd)
243 /* Step 1 : check if triggers are trivially valid */
245 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
246 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
247 TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
248 err |= comedi_check_trigger_src(&cmd->convert_src,
249 TRIG_TIMER | TRIG_EXT);
250 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
251 err |= comedi_check_trigger_src(&cmd->stop_src,
252 TRIG_COUNT | TRIG_NONE);
257 /* Step 2a : make sure trigger sources are unique */
259 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
260 err |= comedi_check_trigger_is_unique(cmd->convert_src);
261 err |= comedi_check_trigger_is_unique(cmd->stop_src);
263 /* Step 2b : and mutually compatible */
265 if (cmd->scan_begin_src != TRIG_FOLLOW) {
266 if (cmd->scan_begin_src != cmd->convert_src)
273 /* Step 3: check if arguments are trivially valid */
275 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
277 if (cmd->convert_src == TRIG_TIMER) {
278 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
279 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
280 } else { /* TRIG_EXT */
281 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
284 if (cmd->scan_begin_src == TRIG_TIMER) {
285 err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
286 PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
287 } else { /* TRIG_FOLLOW || TRIG_EXT */
288 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
291 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
294 if (cmd->stop_src == TRIG_COUNT)
295 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
297 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
302 /* Step 4: fix up any arguments */
304 if (cmd->convert_src == TRIG_TIMER) {
305 arg = cmd->convert_arg;
306 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
307 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
311 * There's only one timer on this card, so the scan_begin timer
312 * must be a multiple of chanlist_len*convert_arg
314 if (cmd->scan_begin_src == TRIG_TIMER) {
315 arg = cmd->chanlist_len * cmd->convert_arg;
317 if (arg < cmd->scan_begin_arg)
318 arg *= (cmd->scan_begin_arg / arg);
320 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
326 /* Step 5: check channel list if it exists */
327 if (cmd->chanlist && cmd->chanlist_len > 0)
328 err |= pci9111_ai_check_chanlist(dev, s, cmd);
336 static int pci9111_ai_do_cmd(struct comedi_device *dev,
337 struct comedi_subdevice *s)
339 struct pci9111_private_data *dev_private = dev->private;
340 struct comedi_cmd *cmd = &s->async->cmd;
341 unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
342 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
343 unsigned int trig = 0;
345 /* Set channel scan limit */
346 /* PCI9111 allows only scanning from channel 0 to channel n */
347 /* TODO: handle the case of an external multiplexer */
349 if (cmd->chanlist_len > 1)
350 trig |= PCI9111_AI_TRIG_CTRL_ASCAN;
352 outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
354 /* Set gain - all channels use the same range */
355 outb(PCI9111_AI_RANGE(range0), dev->iobase + PCI9111_AI_RANGE_STAT_REG);
357 /* Set timer pacer */
358 dev_private->scan_delay = 0;
359 if (cmd->convert_src == TRIG_TIMER) {
360 trig |= PCI9111_AI_TRIG_CTRL_TPST;
361 comedi_8254_update_divisors(dev->pacer);
362 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
363 pci9111_fifo_reset(dev);
364 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
366 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
369 if (cmd->scan_begin_src == TRIG_TIMER) {
370 dev_private->scan_delay = (cmd->scan_begin_arg /
371 (cmd->convert_arg * cmd->chanlist_len)) - 1;
373 } else { /* TRIG_EXT */
374 trig |= PCI9111_AI_TRIG_CTRL_ETIS;
375 pci9111_fifo_reset(dev);
376 pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
378 plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
381 outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
383 dev_private->chunk_counter = 0;
384 dev_private->chunk_num_samples = cmd->chanlist_len *
385 (1 + dev_private->scan_delay);
390 static void pci9111_ai_munge(struct comedi_device *dev,
391 struct comedi_subdevice *s, void *data,
392 unsigned int num_bytes,
393 unsigned int start_chan_index)
395 unsigned short *array = data;
396 unsigned int maxdata = s->maxdata;
397 unsigned int invert = (maxdata + 1) >> 1;
398 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
399 unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
402 for (i = 0; i < num_samples; i++)
403 array[i] = ((array[i] >> shift) & maxdata) ^ invert;
406 static void pci9111_handle_fifo_half_full(struct comedi_device *dev,
407 struct comedi_subdevice *s)
409 struct pci9111_private_data *devpriv = dev->private;
410 struct comedi_cmd *cmd = &s->async->cmd;
411 unsigned short *buf = devpriv->ai_bounce_buffer;
412 unsigned int samples;
414 samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE);
415 insw(dev->iobase + PCI9111_AI_FIFO_REG, buf, samples);
417 if (devpriv->scan_delay < 1) {
418 comedi_buf_write_samples(s, buf, samples);
420 unsigned int pos = 0;
421 unsigned int to_read;
423 while (pos < samples) {
424 if (devpriv->chunk_counter < cmd->chanlist_len) {
425 to_read = cmd->chanlist_len -
426 devpriv->chunk_counter;
428 if (to_read > samples - pos)
429 to_read = samples - pos;
431 comedi_buf_write_samples(s, buf + pos, to_read);
433 to_read = devpriv->chunk_num_samples -
434 devpriv->chunk_counter;
436 if (to_read > samples - pos)
437 to_read = samples - pos;
441 devpriv->chunk_counter += to_read;
443 if (devpriv->chunk_counter >=
444 devpriv->chunk_num_samples)
445 devpriv->chunk_counter = 0;
450 static irqreturn_t pci9111_interrupt(int irq, void *p_device)
452 struct comedi_device *dev = p_device;
453 struct pci9111_private_data *dev_private = dev->private;
454 struct comedi_subdevice *s = dev->read_subdev;
455 struct comedi_async *async;
456 struct comedi_cmd *cmd;
458 unsigned long irq_flags;
459 unsigned char intcsr;
461 if (!dev->attached) {
462 /* Ignore interrupt before device fully attached. */
463 /* Might not even have allocated subdevices yet! */
470 spin_lock_irqsave(&dev->spinlock, irq_flags);
472 /* Check if we are source of interrupt */
473 intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR);
474 if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) &&
475 (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) ||
476 ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) {
477 /* Not the source of the interrupt. */
478 /* (N.B. not using PLX9052_INTCSR_SOFTINT) */
479 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
483 if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) {
484 /* Interrupt comes from fifo_half-full signal */
486 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
488 /* '0' means FIFO is full, data may have been lost */
489 if (!(status & PCI9111_AI_STAT_FF_FF)) {
490 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
491 dev_dbg(dev->class_dev, "fifo overflow\n");
492 outb(0, dev->iobase + PCI9111_INT_CLR_REG);
493 async->events |= COMEDI_CB_ERROR;
494 comedi_handle_events(dev, s);
499 /* '0' means FIFO is half-full */
500 if (!(status & PCI9111_AI_STAT_FF_HF))
501 pci9111_handle_fifo_half_full(dev, s);
504 if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
505 async->events |= COMEDI_CB_EOA;
507 outb(0, dev->iobase + PCI9111_INT_CLR_REG);
509 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
511 comedi_handle_events(dev, s);
516 static int pci9111_ai_eoc(struct comedi_device *dev,
517 struct comedi_subdevice *s,
518 struct comedi_insn *insn,
519 unsigned long context)
523 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
524 if (status & PCI9111_AI_STAT_FF_EF)
529 static int pci9111_ai_insn_read(struct comedi_device *dev,
530 struct comedi_subdevice *s,
531 struct comedi_insn *insn, unsigned int *data)
533 unsigned int chan = CR_CHAN(insn->chanspec);
534 unsigned int range = CR_RANGE(insn->chanspec);
535 unsigned int maxdata = s->maxdata;
536 unsigned int invert = (maxdata + 1) >> 1;
537 unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
542 outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
544 status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
545 if ((status & PCI9111_AI_RANGE_MASK) != range) {
546 outb(PCI9111_AI_RANGE(range),
547 dev->iobase + PCI9111_AI_RANGE_STAT_REG);
550 pci9111_fifo_reset(dev);
552 for (i = 0; i < insn->n; i++) {
553 /* Generate a software trigger */
554 outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
556 ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0);
558 pci9111_fifo_reset(dev);
562 data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
563 data[i] = ((data[i] >> shift) & maxdata) ^ invert;
569 static int pci9111_ao_insn_write(struct comedi_device *dev,
570 struct comedi_subdevice *s,
571 struct comedi_insn *insn,
574 unsigned int chan = CR_CHAN(insn->chanspec);
575 unsigned int val = s->readback[chan];
578 for (i = 0; i < insn->n; i++) {
580 outw(val, dev->iobase + PCI9111_AO_REG);
582 s->readback[chan] = val;
587 static int pci9111_di_insn_bits(struct comedi_device *dev,
588 struct comedi_subdevice *s,
589 struct comedi_insn *insn,
592 data[1] = inw(dev->iobase + PCI9111_DIO_REG);
597 static int pci9111_do_insn_bits(struct comedi_device *dev,
598 struct comedi_subdevice *s,
599 struct comedi_insn *insn,
602 if (comedi_dio_update_state(s, data))
603 outw(s->state, dev->iobase + PCI9111_DIO_REG);
610 static int pci9111_reset(struct comedi_device *dev)
612 struct pci9111_private_data *dev_private = dev->private;
614 /* Set trigger source to software */
615 plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
618 /* disable A/D triggers (software trigger mode) and auto scan off */
619 outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
624 static int pci9111_auto_attach(struct comedi_device *dev,
625 unsigned long context_unused)
627 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
628 struct pci9111_private_data *dev_private;
629 struct comedi_subdevice *s;
632 dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
636 ret = comedi_pci_enable(dev);
639 dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
640 dev->iobase = pci_resource_start(pcidev, 2);
645 ret = request_irq(pcidev->irq, pci9111_interrupt,
646 IRQF_SHARED, dev->board_name, dev);
648 dev->irq = pcidev->irq;
651 dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG,
652 I8254_OSC_BASE_2MHZ, I8254_IO16, 0);
656 ret = comedi_alloc_subdevices(dev, 4);
660 s = &dev->subdevices[0];
661 s->type = COMEDI_SUBD_AI;
662 s->subdev_flags = SDF_READABLE | SDF_COMMON;
665 s->range_table = &pci9111_ai_range;
666 s->insn_read = pci9111_ai_insn_read;
668 dev->read_subdev = s;
669 s->subdev_flags |= SDF_CMD_READ;
670 s->len_chanlist = s->n_chan;
671 s->do_cmdtest = pci9111_ai_do_cmd_test;
672 s->do_cmd = pci9111_ai_do_cmd;
673 s->cancel = pci9111_ai_cancel;
674 s->munge = pci9111_ai_munge;
677 s = &dev->subdevices[1];
678 s->type = COMEDI_SUBD_AO;
679 s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
683 s->range_table = &range_bipolar10;
684 s->insn_write = pci9111_ao_insn_write;
686 ret = comedi_alloc_subdev_readback(s);
690 s = &dev->subdevices[2];
691 s->type = COMEDI_SUBD_DI;
692 s->subdev_flags = SDF_READABLE;
695 s->range_table = &range_digital;
696 s->insn_bits = pci9111_di_insn_bits;
698 s = &dev->subdevices[3];
699 s->type = COMEDI_SUBD_DO;
700 s->subdev_flags = SDF_WRITABLE;
703 s->range_table = &range_digital;
704 s->insn_bits = pci9111_do_insn_bits;
709 static void pci9111_detach(struct comedi_device *dev)
713 comedi_pci_detach(dev);
716 static struct comedi_driver adl_pci9111_driver = {
717 .driver_name = "adl_pci9111",
718 .module = THIS_MODULE,
719 .auto_attach = pci9111_auto_attach,
720 .detach = pci9111_detach,
723 static int pci9111_pci_probe(struct pci_dev *dev,
724 const struct pci_device_id *id)
726 return comedi_pci_auto_config(dev, &adl_pci9111_driver,
730 static const struct pci_device_id pci9111_pci_table[] = {
731 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) },
732 /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */
735 MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
737 static struct pci_driver adl_pci9111_pci_driver = {
738 .name = "adl_pci9111",
739 .id_table = pci9111_pci_table,
740 .probe = pci9111_pci_probe,
741 .remove = comedi_pci_auto_unconfig,
743 module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
745 MODULE_AUTHOR("Comedi https://www.comedi.org");
746 MODULE_DESCRIPTION("Comedi low-level driver");
747 MODULE_LICENSE("GPL");