#define RtdDma1Status(dev) \
readb(devpriv->lcfg+LCFG_DMACSR1)
-static int rtd_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int rtd_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int rtd_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int rtd_dio_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int rtd_dio_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int rtd_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_cmd *cmd);
-static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
-static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
/*
- * static int rtd_ai_poll(struct comedi_device *dev,
- * struct comedi_subdevice *s);
- */
-static int rtd_ns_to_timer(unsigned int *ns, int roundMode);
-static irqreturn_t rtd_interrupt(int irq, void *d);
-static int rtd520_probe_fifo_depth(struct comedi_device *dev);
+ Given a desired period and the clock period (both in ns),
+ return the proper counter value (divider-1).
+ Sets the original period to be the true value.
+ Note: you have to check if the value is larger than the counter range!
+*/
+static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */
+ int round_mode, int base)
+{ /* clock period (in ns) */
+ int divider;
+
+ switch (round_mode) {
+ case TRIG_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case TRIG_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case TRIG_ROUND_UP:
+ divider = (*nanosec + base - 1) / base;
+ break;
+ }
+ if (divider < 2)
+ divider = 2; /* min is divide by 2 */
+
+ /* Note: we don't check for max, because different timers
+ have different ranges */
+
+ *nanosec = base * divider;
+ return divider - 1; /* countdown is divisor+1 */
+}
/*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board. If you specified a board_name array
- * in the driver structure, dev->board_ptr contains that
- * address.
- */
-static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
-{ /* board name and options flags */
- struct comedi_subdevice *s;
- struct pci_dev *pcidev;
- int ret;
- resource_size_t physLas0; /* configuration */
- resource_size_t physLas1; /* data area */
- resource_size_t physLcfg; /* PLX9080 */
-#ifdef USE_DMA
- int index;
-#endif
+ Given a desired period (in ns),
+ return the proper counter value (divider-1) for the internal clock.
+ Sets the original period to be the true value.
+*/
+static int rtd_ns_to_timer(unsigned int *ns, int round_mode)
+{
+ return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE);
+}
- printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
+/*
+ Convert a single comedi channel-gain entry to a RTD520 table entry
+*/
+static unsigned short rtdConvertChanGain(struct comedi_device *dev,
+ unsigned int comediChan, int chanIndex)
+{ /* index in channel list */
+ unsigned int chan, range, aref;
+ unsigned short r = 0;
-#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
- /* You can set this a load time: modprobe comedi comedi_debug=1 */
- if (0 == comedi_debug) /* force DMA debug printks */
- comedi_debug = 1;
-#endif
+ chan = CR_CHAN(comediChan);
+ range = CR_RANGE(comediChan);
+ aref = CR_AREF(comediChan);
- /*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
- return -ENOMEM;
+ r |= chan & 0xf;
- /*
- * Probe the device to determine what device in the series it is.
- */
- for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
- pcidev != NULL;
- pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
- int i;
+ /* Note: we also setup the channel list bipolar flag array */
+ if (range < thisboard->range10Start) { /* first batch are +-5 */
+ r |= 0x000; /* +-5 range */
+ r |= (range & 0x7) << 4; /* gain */
+ CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
+ } else if (range < thisboard->rangeUniStart) { /* second batch are +-10 */
+ r |= 0x100; /* +-10 range */
+ /* gain */
+ r |= ((range - thisboard->range10Start) & 0x7) << 4;
+ CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
+ } else { /* last batch is +10 */
+ r |= 0x200; /* +10 range */
+ /* gain */
+ r |= ((range - thisboard->rangeUniStart) & 0x7) << 4;
+ CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex);
+ }
- if (it->options[0] || it->options[1]) {
- if (pcidev->bus->number != it->options[0]
- || PCI_SLOT(pcidev->devfn) != it->options[1]) {
- continue;
- }
- }
- for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
- if (pcidev->device == rtd520Boards[i].device_id) {
- dev->board_ptr = &rtd520Boards[i];
- break;
- }
+ switch (aref) {
+ case AREF_GROUND: /* on-board ground */
+ break;
+
+ case AREF_COMMON:
+ r |= 0x80; /* ref external analog common */
+ break;
+
+ case AREF_DIFF:
+ r |= 0x400; /* differential inputs */
+ break;
+
+ case AREF_OTHER: /* ??? */
+ break;
+ }
+ /*printk ("chan=%d r=%d a=%d -> 0x%x\n",
+ chan, range, aref, r); */
+ return r;
+}
+
+/*
+ Setup the channel-gain table from a comedi list
+*/
+static void rtd_load_channelgain_list(struct comedi_device *dev,
+ unsigned int n_chan, unsigned int *list)
+{
+ if (n_chan > 1) { /* setup channel gain table */
+ int ii;
+ RtdClearCGT(dev);
+ RtdEnableCGT(dev, 1); /* enable table */
+ for (ii = 0; ii < n_chan; ii++) {
+ RtdWriteCGTable(dev, rtdConvertChanGain(dev, list[ii],
+ ii));
}
- if (dev->board_ptr)
- break; /* found one */
+ } else { /* just use the channel gain latch */
+ RtdEnableCGT(dev, 0); /* disable table, enable latch */
+ RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0));
}
- if (!pcidev) {
- if (it->options[0] && it->options[1]) {
- printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
- it->options[0], it->options[1]);
- } else {
- printk(KERN_INFO "No RTD card found.\n");
+}
+
+/* determine fifo size by doing adc conversions until the fifo half
+empty status flag clears */
+static int rtd520_probe_fifo_depth(struct comedi_device *dev)
+{
+ unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
+ unsigned i;
+ static const unsigned limit = 0x2000;
+ unsigned fifo_size = 0;
+
+ RtdAdcClearFifo(dev);
+ rtd_load_channelgain_list(dev, 1, &chanspec);
+ RtdAdcConversionSource(dev, 0); /* software */
+ /* convert samples */
+ for (i = 0; i < limit; ++i) {
+ unsigned fifo_status;
+ /* trigger conversion */
+ RtdAdcStart(dev);
+ udelay(1);
+ fifo_status = RtdFifoStatus(dev);
+ if ((fifo_status & FS_ADC_HEMPTY) == 0) {
+ fifo_size = 2 * i;
+ break;
}
+ }
+ if (i == limit) {
+ printk(KERN_INFO "\ncomedi: %s: failed to probe fifo size.\n",
+ DRV_NAME);
return -EIO;
}
- devpriv->pci_dev = pcidev;
- dev->board_name = thisboard->name;
-
- ret = comedi_pci_enable(pcidev, DRV_NAME);
- if (ret < 0) {
- printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
- return ret;
+ RtdAdcClearFifo(dev);
+ if (fifo_size != 0x400 && fifo_size != 0x2000) {
+ printk
+ (KERN_INFO "\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n",
+ DRV_NAME, fifo_size);
+ return -EIO;
}
- devpriv->got_regions = 1;
-
- /*
- * Initialize base addresses
- */
- /* Get the physical address from PCI config */
- physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
- physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
- physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
- /* Now have the kernel map this into memory */
- /* ASSUME page aligned */
- devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
- devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
- devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
+ return fifo_size;
+}
- if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
- return -ENOMEM;
+/*
+ "instructions" read/write data in "one-shot" or "software-triggered"
+ mode (simplest case).
+ This doesn't use interrupts.
+ Note, we don't do any settling delays. Use a instruction list to
+ select, delay, then read.
+ */
+static int rtd_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int n, ii;
+ int stat;
- DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
- (unsigned long long)physLas0, (unsigned long long)physLas1,
- (unsigned long long)physLcfg);
- { /* The RTD driver does this */
- unsigned char pci_latency;
- u16 revision;
- /*uint32_t epld_version; */
+ /* clear any old fifo data */
+ RtdAdcClearFifo(dev);
- pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
- &revision);
- DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
+ /* write channel to multiplexer and clear channel gain table */
+ rtd_load_channelgain_list(dev, 1, &insn->chanspec);
- pci_read_config_byte(devpriv->pci_dev,
- PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency < 32) {
- printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
- dev->board_name, pci_latency, 32);
- pci_write_config_byte(devpriv->pci_dev,
- PCI_LATENCY_TIMER, 32);
- } else {
- DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
- }
+ /* set conversion source */
+ RtdAdcConversionSource(dev, 0); /* software */
- /*
- * Undocumented EPLD version (doesn't match RTD driver results)
- */
- /*DPRINTK ("rtd520: Reading epld from %p\n",
- devpriv->las0+0);
- epld_version = readl (devpriv->las0+0);
- if ((epld_version & 0xF0) >> 4 == 0x0F) {
- DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
- } else {
- DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
- } */
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ s16 d;
+ /* trigger conversion */
+ RtdAdcStart(dev);
+
+ for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
+ stat = RtdFifoStatus(dev);
+ if (stat & FS_ADC_NOT_EMPTY) /* 1 -> not empty */
+ break;
+ WAIT_QUIETLY;
+ }
+ if (ii >= RTD_ADC_TIMEOUT) {
+ DPRINTK
+ ("rtd520: Error: ADC never finished! FifoStatus=0x%x\n",
+ stat ^ 0x6666);
+ return -ETIMEDOUT;
+ }
+
+ /* read data */
+ d = RtdAdcFifoGet(dev); /* get 2s comp value */
+ /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1); */
+ d = d >> 3; /* low 3 bits are marker lines */
+ if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0))
+ /* convert to comedi unsigned data */
+ data[n] = d + 2048;
+ else
+ data[n] = d;
}
- /* Show board configuration */
- printk(KERN_INFO "%s:", dev->board_name);
+ /* return the number of samples read/written */
+ return n;
+}
- /*
- * Allocate the subdevice structures. alloc_subdevice() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_subdevices(dev, 4) < 0)
- return -ENOMEM;
+/*
+ Get what we know is there.... Fast!
+ This uses 1/2 the bus cycles of read_dregs (below).
+ The manual claims that we can do a lword read, but it doesn't work here.
+*/
+static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
+ int count)
+{
+ int ii;
- s = dev->subdevices + 0;
- dev->read_subdev = s;
- /* analog input subdevice */
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags =
- SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
- s->n_chan = thisboard->aiChans;
- s->maxdata = (1 << thisboard->aiBits) - 1;
- if (thisboard->aiMaxGain <= 32)
- s->range_table = &rtd_ai_7520_range;
- else
- s->range_table = &rtd_ai_4520_range;
+ for (ii = 0; ii < count; ii++) {
+ short sample;
+ s16 d;
- s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */
- s->insn_read = rtd_ai_rinsn;
- s->do_cmd = rtd_ai_cmd;
- s->do_cmdtest = rtd_ai_cmdtest;
- s->cancel = rtd_ai_cancel;
- /* s->poll = rtd_ai_poll; *//* not ready yet */
+ if (0 == devpriv->aiCount) { /* done */
+ d = RtdAdcFifoGet(dev); /* Read N and discard */
+ continue;
+ }
+#if 0
+ if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) { /* DEBUG */
+ DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1,
+ count);
+ break;
+ }
+#endif
+ d = RtdAdcFifoGet(dev); /* get 2s comp value */
- s = dev->subdevices + 1;
- /* analog output subdevice */
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = 2;
- s->maxdata = (1 << thisboard->aiBits) - 1;
- s->range_table = &rtd_ao_range;
- s->insn_write = rtd_ao_winsn;
- s->insn_read = rtd_ao_rinsn;
+ d = d >> 3; /* low 3 bits are marker lines */
+ if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+ /* convert to comedi unsigned data */
+ sample = d + 2048;
+ } else
+ sample = d;
- s = dev->subdevices + 2;
- /* digital i/o subdevice */
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- /* we only support port 0 right now. Ignoring port 1 and user IO */
- s->n_chan = 8;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = rtd_dio_insn_bits;
- s->insn_config = rtd_dio_insn_config;
+ if (!comedi_buf_put(s->async, sample))
+ return -1;
- /* timer/counter subdevices (not currently supported) */
- s = dev->subdevices + 3;
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 3;
- s->maxdata = 0xffff;
+ if (devpriv->aiCount > 0) /* < 0, means read forever */
+ devpriv->aiCount--;
+ }
+ return 0;
+}
- /* initialize board, per RTD spec */
- /* also, initialize shadow registers */
- RtdResetBoard(dev);
- udelay(100); /* needed? */
- RtdPlxInterruptWrite(dev, 0);
- RtdInterruptMask(dev, 0); /* and sets shadow */
- RtdInterruptClearMask(dev, ~0); /* and sets shadow */
- RtdInterruptClear(dev); /* clears bits set by mask */
- RtdInterruptOverrunClear(dev);
- RtdClearCGT(dev);
- RtdAdcClearFifo(dev);
- RtdDacClearFifo(dev, 0);
- RtdDacClearFifo(dev, 1);
- /* clear digital IO fifo */
- RtdDioStatusWrite(dev, 0); /* safe state, set shadow */
- RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */
- RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */
- RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */
- RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */
- /* TODO: set user out source ??? */
+/*
+ unknown amout of data is waiting in fifo.
+*/
+static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
+ short sample;
+ s16 d = RtdAdcFifoGet(dev); /* get 2s comp value */
- /* check if our interrupt is available and get it */
- ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
- IRQF_SHARED, DRV_NAME, dev);
+ if (0 == devpriv->aiCount) { /* done */
+ continue; /* read rest */
+ }
- if (ret < 0) {
- printk("Could not get interrupt! (%u)\n",
- devpriv->pci_dev->irq);
- return ret;
- }
- dev->irq = devpriv->pci_dev->irq;
- printk(KERN_INFO "( irq=%u )", dev->irq);
+ d = d >> 3; /* low 3 bits are marker lines */
+ if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+ /* convert to comedi unsigned data */
+ sample = d + 2048;
+ } else
+ sample = d;
- ret = rtd520_probe_fifo_depth(dev);
- if (ret < 0)
- return ret;
+ if (!comedi_buf_put(s->async, sample))
+ return -1;
- devpriv->fifoLen = ret;
- printk("( fifoLen=%d )", devpriv->fifoLen);
+ if (devpriv->aiCount > 0) /* < 0, means read forever */
+ devpriv->aiCount--;
+ }
+ return 0;
+}
#ifdef USE_DMA
- if (dev->irq > 0) {
- printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
- /*
- * The PLX9080 has 2 DMA controllers, but there could be
- * 4 sources: ADC, digital, DAC1, and DAC2. Since only the
- * ADC supports cmd mode right now, this isn't an issue (yet)
- */
- devpriv->dma0Offset = 0;
+/*
+ Terminate a DMA transfer and wait for everything to quiet down
+*/
+void abort_dma(struct comedi_device *dev, unsigned int channel)
+{ /* DMA channel 0, 1 */
+ unsigned long dma_cs_addr; /* the control/status register */
+ uint8_t status;
+ unsigned int ii;
+ /* unsigned long flags; */
- for (index = 0; index < DMA_CHAIN_COUNT; index++) {
- devpriv->dma0Buff[index] =
- pci_alloc_consistent(devpriv->pci_dev,
- sizeof(u16) *
- devpriv->fifoLen / 2,
- &devpriv->
- dma0BuffPhysAddr[index]);
- if (devpriv->dma0Buff[index] == NULL) {
- ret = -ENOMEM;
- goto rtd_attach_die_error;
- }
- /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
- index,
- devpriv->dma0Buff[index],
- devpriv->dma0BuffPhysAddr[index]); */
- }
+ dma_cs_addr = (unsigned long)devpriv->lcfg
+ + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
- /*
- * setup DMA descriptor ring (use cpu_to_le32 for byte
- * ordering?)
- */
- devpriv->dma0Chain =
- pci_alloc_consistent(devpriv->pci_dev,
- sizeof(struct plx_dma_desc) *
- DMA_CHAIN_COUNT,
- &devpriv->dma0ChainPhysAddr);
- for (index = 0; index < DMA_CHAIN_COUNT; index++) {
- devpriv->dma0Chain[index].pci_start_addr =
- devpriv->dma0BuffPhysAddr[index];
- devpriv->dma0Chain[index].local_start_addr =
- DMALADDR_ADC;
- devpriv->dma0Chain[index].transfer_size =
- sizeof(u16) * devpriv->fifoLen / 2;
- devpriv->dma0Chain[index].next =
- (devpriv->dma0ChainPhysAddr + ((index +
- 1) %
- (DMA_CHAIN_COUNT))
- * sizeof(devpriv->dma0Chain[0]))
- | DMA_TRANSFER_BITS;
- /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
- index,
- ((long)devpriv->dma0ChainPhysAddr
- + (index * sizeof(devpriv->dma0Chain[0]))),
- devpriv->dma0Chain[index].pci_start_addr,
- devpriv->dma0Chain[index].local_start_addr,
- devpriv->dma0Chain[index].transfer_size,
- devpriv->dma0Chain[index].next); */
- }
+ /* spinlock for plx dma control/status reg */
+ /* spin_lock_irqsave( &dev->spinlock, flags ); */
- if (devpriv->dma0Chain == NULL) {
- ret = -ENOMEM;
- goto rtd_attach_die_error;
- }
+ /* abort dma transfer if necessary */
+ status = readb(dma_cs_addr);
+ if ((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */
+ DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n",
+ channel, status);
+ goto abortDmaExit;
+ }
- RtdDma0Mode(dev, DMA_MODE_BITS);
- /* set DMA trigger source */
- RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
- } else {
- printk(KERN_INFO "( no IRQ->no DMA )");
+ /* wait to make sure done bit is zero (needed?) */
+ for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) {
+ WAIT_QUIETLY;
+ status = readb(dma_cs_addr);
+ }
+ if (status & PLX_DMA_DONE_BIT) {
+ printk("rtd520: Timeout waiting for dma %i done clear\n",
+ channel);
+ goto abortDmaExit;
}
-#endif /* USE_DMA */
- if (dev->irq) { /* enable plx9080 interrupts */
- RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+ /* disable channel (required) */
+ writeb(0, dma_cs_addr);
+ udelay(1); /* needed?? */
+ /* set abort bit for channel */
+ writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+
+ /* wait for dma done bit to be set */
+ status = readb(dma_cs_addr);
+ for (ii = 0;
+ (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) {
+ status = readb(dma_cs_addr);
+ WAIT_QUIETLY;
+ }
+ if ((status & PLX_DMA_DONE_BIT) == 0) {
+ printk("rtd520: Timeout waiting for dma %i done set\n",
+ channel);
}
- printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
+abortDmaExit:
+ /* spin_unlock_irqrestore( &dev->spinlock, flags ); */
+}
- return 1;
+/*
+ Process what is in the DMA transfer buffer and pass to comedi
+ Note: this is not re-entrant
+*/
+static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ int ii, n;
+ s16 *dp;
-#if 0
- /* hit an error, clean up memory and return ret */
-/* rtd_attach_die_error: */
-#ifdef USE_DMA
- for (index = 0; index < DMA_CHAIN_COUNT; index++) {
- if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
- pci_free_consistent(devpriv->pci_dev,
- sizeof(u16) * devpriv->fifoLen / 2,
- devpriv->dma0Buff[index],
- devpriv->dma0BuffPhysAddr[index]);
- devpriv->dma0Buff[index] = NULL;
+ if (devpriv->aiCount == 0) /* transfer already complete */
+ return 0;
+
+ dp = devpriv->dma0Buff[devpriv->dma0Offset];
+ for (ii = 0; ii < devpriv->fifoLen / 2;) { /* convert samples */
+ short sample;
+
+ if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
+ sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */
+ else
+ sample = *dp >> 3; /* low 3 bits are marker lines */
+
+ *dp++ = sample; /* put processed value back */
+
+ if (++s->async->cur_chan >= s->async->cmd.chanlist_len)
+ s->async->cur_chan = 0;
+
+ ++ii; /* number ready to transfer */
+ if (devpriv->aiCount > 0) { /* < 0, means read forever */
+ if (--devpriv->aiCount == 0) { /* done */
+ /*DPRINTK ("rtd520: Final %d samples\n", ii); */
+ break;
+ }
}
}
- if (NULL != devpriv->dma0Chain) {
- pci_free_consistent(devpriv->pci_dev,
- sizeof(struct plx_dma_desc)
- * DMA_CHAIN_COUNT,
- devpriv->dma0Chain,
- devpriv->dma0ChainPhysAddr);
- devpriv->dma0Chain = NULL;
- }
-#endif /* USE_DMA */
- /* subdevices and priv are freed by the core */
- if (dev->irq) {
- /* disable interrupt controller */
- RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
- & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
- free_irq(dev->irq, dev);
+
+ /* now pass the whole array to the comedi buffer */
+ dp = devpriv->dma0Buff[devpriv->dma0Offset];
+ n = comedi_buf_write_alloc(s->async, ii * sizeof(s16));
+ if (n < (ii * sizeof(s16))) { /* any residual is an error */
+ DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n",
+ ii - (n / sizeof(s16)));
+ s->async->events |= COMEDI_CB_ERROR;
+ return -1;
}
+ comedi_buf_memcpy_to(s->async, 0, dp, n);
+ comedi_buf_write_free(s->async, n);
- /* release all regions that were allocated */
- if (devpriv->las0)
- iounmap(devpriv->las0);
+ /*
+ * always at least 1 scan -- 1/2 FIFO is larger than our max scan list
+ */
+ s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
- if (devpriv->las1)
- iounmap(devpriv->las1);
+ if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */
+ devpriv->dma0Offset = 0;
+ }
+ return 0;
+}
+#endif /* USE_DMA */
- if (devpriv->lcfg)
- iounmap(devpriv->lcfg);
+/*
+ Handle all rtd520 interrupts.
+ Runs atomically and is never re-entered.
+ This is a "slow handler"; other interrupts may be active.
+ The data conversion may someday happen in a "bottom half".
+*/
+static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */
+ void *d)
+{ /* our data *//* cpu context (ignored) */
+ struct comedi_device *dev = d; /* must be called "dev" for devpriv */
+ u16 status;
+ u16 fifoStatus;
+ struct comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
- if (devpriv->pci_dev)
- pci_dev_put(devpriv->pci_dev);
+ if (!dev->attached)
+ return IRQ_NONE;
- return ret;
-#endif
-}
+ devpriv->intCount++; /* DEBUG statistics */
-static void rtd_detach(struct comedi_device *dev)
-{
+ fifoStatus = RtdFifoStatus(dev);
+ /* check for FIFO full, this automatically halts the ADC! */
+ if (!(fifoStatus & FS_ADC_NOT_FULL)) { /* 0 -> full */
+ DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */
+ goto abortTransfer;
+ }
#ifdef USE_DMA
- int index;
-#endif
+ if (devpriv->flags & DMA0_ACTIVE) { /* Check DMA */
+ u32 istatus = RtdPlxInterruptRead(dev);
- if (devpriv) {
- /* Shut down any board ops by resetting it */
-#ifdef USE_DMA
- if (devpriv->lcfg) {
- RtdDma0Control(dev, 0); /* disable DMA */
- RtdDma1Control(dev, 0); /* disable DMA */
- RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
- }
-#endif /* USE_DMA */
- if (devpriv->las0) {
- RtdResetBoard(dev);
- RtdInterruptMask(dev, 0);
- RtdInterruptClearMask(dev, ~0);
- RtdInterruptClear(dev); /* clears bits set by mask */
- }
-#ifdef USE_DMA
- /* release DMA */
- for (index = 0; index < DMA_CHAIN_COUNT; index++) {
- if (NULL != devpriv->dma0Buff[index]) {
- pci_free_consistent(devpriv->pci_dev,
- sizeof(u16) *
- devpriv->fifoLen / 2,
- devpriv->dma0Buff[index],
- devpriv->
- dma0BuffPhysAddr[index]);
- devpriv->dma0Buff[index] = NULL;
+ if (istatus & ICS_DMA0_A) {
+ if (ai_process_dma(dev, s) < 0) {
+ DPRINTK
+ ("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n",
+ devpriv->aiCount);
+ RtdDma0Control(dev,
+ (devpriv->dma0Control &
+ ~PLX_DMA_START_BIT)
+ | PLX_CLEAR_DMA_INTR_BIT);
+ goto abortTransfer;
}
+
+ /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n",
+ devpriv->aiCount, istatus); */
+ RtdDma0Control(dev,
+ (devpriv->
+ dma0Control & ~PLX_DMA_START_BIT)
+ | PLX_CLEAR_DMA_INTR_BIT);
+ if (0 == devpriv->aiCount) { /* counted down */
+ DPRINTK("rtd520: Samples Done (DMA).\n");
+ goto transferDone;
+ }
+ comedi_event(dev, s);
+ } else {
+ /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */
}
- if (NULL != devpriv->dma0Chain) {
- pci_free_consistent(devpriv->pci_dev,
- sizeof(struct plx_dma_desc) *
- DMA_CHAIN_COUNT, devpriv->dma0Chain,
- devpriv->dma0ChainPhysAddr);
- devpriv->dma0Chain = NULL;
- }
+ }
+ /* Fall through and check for other interrupt sources */
#endif /* USE_DMA */
- if (dev->irq) {
- RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
- & ~(ICS_PLIE | ICS_DMA0_E |
- ICS_DMA1_E));
- free_irq(dev->irq, dev);
- }
- if (devpriv->las0)
- iounmap(devpriv->las0);
- if (devpriv->las1)
- iounmap(devpriv->las1);
- if (devpriv->lcfg)
- iounmap(devpriv->lcfg);
- if (devpriv->pci_dev) {
- if (devpriv->got_regions)
- comedi_pci_disable(devpriv->pci_dev);
- pci_dev_put(devpriv->pci_dev);
+
+ status = RtdInterruptStatus(dev);
+ /* if interrupt was not caused by our board, or handled above */
+ if (0 == status)
+ return IRQ_HANDLED;
+
+ if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */
+ /* since the priority interrupt controller may have queued a sample
+ counter interrupt, even though we have already finished,
+ we must handle the possibility that there is no data here */
+ if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */
+ /*DPRINTK("rtd520: Sample int, reading 1/2FIFO. fifo_status 0x%x\n",
+ (fifoStatus ^ 0x6666) & 0x7777); */
+ if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) {
+ DPRINTK
+ ("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n",
+ devpriv->aiCount);
+ goto abortTransfer;
+ }
+ if (0 == devpriv->aiCount) { /* counted down */
+ DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */
+ goto transferDone;
+ }
+ comedi_event(dev, s);
+ } else if (devpriv->transCount > 0) { /* read often */
+ /*DPRINTK("rtd520: Sample int, reading %d fifo_status 0x%x\n",
+ devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */
+ if (fifoStatus & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
+ if (ai_read_n(dev, s, devpriv->transCount) < 0) {
+ DPRINTK
+ ("rtd520: comedi read buffer overflow (N) with %ld to go!\n",
+ devpriv->aiCount);
+ goto abortTransfer;
+ }
+ if (0 == devpriv->aiCount) { /* counted down */
+ DPRINTK
+ ("rtd520: Samples Done (N). fifo_status was 0x%x\n",
+ (fifoStatus ^ 0x6666) & 0x7777);
+ goto transferDone;
+ }
+ comedi_event(dev, s);
+ }
+ } else { /* wait for 1/2 FIFO (old) */
+ DPRINTK
+ ("rtd520: Sample int. Wait for 1/2. fifo_status 0x%x\n",
+ (fifoStatus ^ 0x6666) & 0x7777);
}
+ } else {
+ DPRINTK("rtd520: unknown interrupt source!\n");
}
-}
-/*
- Convert a single comedi channel-gain entry to a RTD520 table entry
-*/
-static unsigned short rtdConvertChanGain(struct comedi_device *dev,
- unsigned int comediChan, int chanIndex)
-{ /* index in channel list */
- unsigned int chan, range, aref;
- unsigned short r = 0;
+ if (0xffff & RtdInterruptOverrunStatus(dev)) { /* interrupt overrun */
+ DPRINTK
+ ("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n",
+ devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev));
+ goto abortTransfer;
+ }
- chan = CR_CHAN(comediChan);
- range = CR_RANGE(comediChan);
- aref = CR_AREF(comediChan);
+ /* clear the interrupt */
+ RtdInterruptClearMask(dev, status);
+ RtdInterruptClear(dev);
+ return IRQ_HANDLED;
- r |= chan & 0xf;
+abortTransfer:
+ RtdAdcClearFifo(dev); /* clears full flag */
+ s->async->events |= COMEDI_CB_ERROR;
+ devpriv->aiCount = 0; /* stop and don't transfer any more */
+ /* fall into transferDone */
- /* Note: we also setup the channel list bipolar flag array */
- if (range < thisboard->range10Start) { /* first batch are +-5 */
- r |= 0x000; /* +-5 range */
- r |= (range & 0x7) << 4; /* gain */
- CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
- } else if (range < thisboard->rangeUniStart) { /* second batch are +-10 */
- r |= 0x100; /* +-10 range */
- /* gain */
- r |= ((range - thisboard->range10Start) & 0x7) << 4;
- CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex);
- } else { /* last batch is +10 */
- r |= 0x200; /* +10 range */
- /* gain */
- r |= ((range - thisboard->rangeUniStart) & 0x7) << 4;
- CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex);
+transferDone:
+ RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */
+ RtdPacerStop(dev); /* Stop PACER */
+ RtdAdcConversionSource(dev, 0); /* software trigger only */
+ RtdInterruptMask(dev, 0); /* mask out SAMPLE */
+#ifdef USE_DMA
+ if (devpriv->flags & DMA0_ACTIVE) {
+ RtdPlxInterruptWrite(dev, /* disable any more interrupts */
+ RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
+ abort_dma(dev, 0);
+ devpriv->flags &= ~DMA0_ACTIVE;
+ /* if Using DMA, then we should have read everything by now */
+ if (devpriv->aiCount > 0) {
+ DPRINTK("rtd520: Lost DMA data! %ld remain\n",
+ devpriv->aiCount);
+ }
}
+#endif /* USE_DMA */
- switch (aref) {
- case AREF_GROUND: /* on-board ground */
- break;
+ if (devpriv->aiCount > 0) { /* there shouldn't be anything left */
+ fifoStatus = RtdFifoStatus(dev);
+ DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777); /* should read all 0s */
+ ai_read_dregs(dev, s); /* read anything left in FIFO */
+ }
- case AREF_COMMON:
- r |= 0x80; /* ref external analog common */
- break;
+ s->async->events |= COMEDI_CB_EOA; /* signal end to comedi */
+ comedi_event(dev, s);
- case AREF_DIFF:
- r |= 0x400; /* differential inputs */
- break;
+ /* clear the interrupt */
+ status = RtdInterruptStatus(dev);
+ RtdInterruptClearMask(dev, status);
+ RtdInterruptClear(dev);
- case AREF_OTHER: /* ??? */
- break;
- }
- /*printk ("chan=%d r=%d a=%d -> 0x%x\n",
- chan, range, aref, r); */
- return r;
+ fifoStatus = RtdFifoStatus(dev); /* DEBUG */
+ DPRINTK
+ ("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n",
+ devpriv->intCount, status,
+ 0xffff & RtdInterruptOverrunStatus(dev));
+
+ return IRQ_HANDLED;
}
+#if 0
/*
- Setup the channel-gain table from a comedi list
+ return the number of samples available
*/
-static void rtd_load_channelgain_list(struct comedi_device *dev,
- unsigned int n_chan, unsigned int *list)
+static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
{
- if (n_chan > 1) { /* setup channel gain table */
- int ii;
- RtdClearCGT(dev);
- RtdEnableCGT(dev, 1); /* enable table */
- for (ii = 0; ii < n_chan; ii++) {
- RtdWriteCGTable(dev, rtdConvertChanGain(dev, list[ii],
- ii));
- }
- } else { /* just use the channel gain latch */
- RtdEnableCGT(dev, 0); /* disable table, enable latch */
- RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0));
- }
+ /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
+ /* Not sure what to do if DMA is active */
+ return s->async->buf_write_count - s->async->buf_read_count;
}
+#endif
-/* determine fifo size by doing adc conversions until the fifo half
-empty status flag clears */
-static int rtd520_probe_fifo_depth(struct comedi_device *dev)
+/*
+ cmdtest tests a particular command to see if it is valid.
+ Using the cmdtest ioctl, a user can create a valid cmd
+ and then have it executed by the cmd ioctl (asyncronously).
+
+ cmdtest returns 1,2,3,4 or 0, depending on which tests
+ the command passes.
+*/
+
+static int rtd_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
- unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
- unsigned i;
- static const unsigned limit = 0x2000;
- unsigned fifo_size = 0;
+ int err = 0;
+ int tmp;
- RtdAdcClearFifo(dev);
- rtd_load_channelgain_list(dev, 1, &chanspec);
- RtdAdcConversionSource(dev, 0); /* software */
- /* convert samples */
- for (i = 0; i < limit; ++i) {
- unsigned fifo_status;
- /* trigger conversion */
- RtdAdcStart(dev);
- udelay(1);
- fifo_status = RtdFifoStatus(dev);
- if ((fifo_status & FS_ADC_HEMPTY) == 0) {
- fifo_size = 2 * i;
- break;
- }
- }
- if (i == limit) {
- printk(KERN_INFO "\ncomedi: %s: failed to probe fifo size.\n",
- DRV_NAME);
- return -EIO;
- }
- RtdAdcClearFifo(dev);
- if (fifo_size != 0x400 && fifo_size != 0x2000) {
- printk
- (KERN_INFO "\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n",
- DRV_NAME, fifo_size);
- return -EIO;
- }
- return fifo_size;
-}
+ /* step 1: make sure trigger sources are trivially valid */
-/*
- "instructions" read/write data in "one-shot" or "software-triggered"
- mode (simplest case).
- This doesn't use interrupts.
+ tmp = cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if (!cmd->start_src || tmp != cmd->start_src)
+ err++;
- Note, we don't do any settling delays. Use a instruction list to
- select, delay, then read.
- */
-static int rtd_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
-{
- int n, ii;
- int stat;
+ tmp = cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
+ err++;
- /* clear any old fifo data */
- RtdAdcClearFifo(dev);
- /* write channel to multiplexer and clear channel gain table */
- rtd_load_channelgain_list(dev, 1, &insn->chanspec);
+ tmp = cmd->convert_src;
+ cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ if (!cmd->convert_src || tmp != cmd->convert_src)
+ err++;
- /* set conversion source */
- RtdAdcConversionSource(dev, 0); /* software */
- /* convert n samples */
- for (n = 0; n < insn->n; n++) {
- s16 d;
- /* trigger conversion */
- RtdAdcStart(dev);
+ tmp = cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
+ err++;
- for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) {
- stat = RtdFifoStatus(dev);
- if (stat & FS_ADC_NOT_EMPTY) /* 1 -> not empty */
- break;
- WAIT_QUIETLY;
- }
- if (ii >= RTD_ADC_TIMEOUT) {
- DPRINTK
- ("rtd520: Error: ADC never finished! FifoStatus=0x%x\n",
- stat ^ 0x6666);
- return -ETIMEDOUT;
- }
- /* read data */
- d = RtdAdcFifoGet(dev); /* get 2s comp value */
- /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1); */
- d = d >> 3; /* low 3 bits are marker lines */
- if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0))
- /* convert to comedi unsigned data */
- data[n] = d + 2048;
- else
- data[n] = d;
+ tmp = cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ if (!cmd->stop_src || tmp != cmd->stop_src)
+ err++;
+
+
+ if (err)
+ return 1;
+
+ /* step 2: make sure trigger sources are unique
+ and mutually compatible */
+ /* note that mutual compatibility is not an issue here */
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT) {
+ err++;
}
+ if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ err++;
- /* return the number of samples read/written */
- return n;
-}
+ if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
+ err++;
-/*
- Get what we know is there.... Fast!
- This uses 1/2 the bus cycles of read_dregs (below).
+ if (err)
+ return 2;
- The manual claims that we can do a lword read, but it doesn't work here.
-*/
-static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
- int count)
-{
- int ii;
+ /* step 3: make sure arguments are trivially compatible */
- for (ii = 0; ii < count; ii++) {
- short sample;
- s16 d;
+ if (cmd->start_arg != 0) {
+ cmd->start_arg = 0;
+ err++;
+ }
- if (0 == devpriv->aiCount) { /* done */
- d = RtdAdcFifoGet(dev); /* Read N and discard */
- continue;
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Note: these are time periods, not actual rates */
+ if (1 == cmd->chanlist_len) { /* no scanning */
+ if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) {
+ cmd->scan_begin_arg = RTD_MAX_SPEED_1;
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_UP);
+ err++;
+ }
+ if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) {
+ cmd->scan_begin_arg = RTD_MIN_SPEED_1;
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_DOWN);
+ err++;
+ }
+ } else {
+ if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
+ cmd->scan_begin_arg = RTD_MAX_SPEED;
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_UP);
+ err++;
+ }
+ if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
+ cmd->scan_begin_arg = RTD_MIN_SPEED;
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_DOWN);
+ err++;
+ }
}
-#if 0
- if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) { /* DEBUG */
- DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1,
- count);
- break;
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ if (cmd->scan_begin_arg > 9) {
+ cmd->scan_begin_arg = 9;
+ err++;
+ }
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (1 == cmd->chanlist_len) { /* no scanning */
+ if (cmd->convert_arg < RTD_MAX_SPEED_1) {
+ cmd->convert_arg = RTD_MAX_SPEED_1;
+ rtd_ns_to_timer(&cmd->convert_arg,
+ TRIG_ROUND_UP);
+ err++;
+ }
+ if (cmd->convert_arg > RTD_MIN_SPEED_1) {
+ cmd->convert_arg = RTD_MIN_SPEED_1;
+ rtd_ns_to_timer(&cmd->convert_arg,
+ TRIG_ROUND_DOWN);
+ err++;
+ }
+ } else {
+ if (cmd->convert_arg < RTD_MAX_SPEED) {
+ cmd->convert_arg = RTD_MAX_SPEED;
+ rtd_ns_to_timer(&cmd->convert_arg,
+ TRIG_ROUND_UP);
+ err++;
+ }
+ if (cmd->convert_arg > RTD_MIN_SPEED) {
+ cmd->convert_arg = RTD_MIN_SPEED;
+ rtd_ns_to_timer(&cmd->convert_arg,
+ TRIG_ROUND_DOWN);
+ err++;
+ }
+ }
+ } else {
+ /* external trigger */
+ /* see above */
+ if (cmd->convert_arg > 9) {
+ cmd->convert_arg = 9;
+ err++;
}
+ }
+
+#if 0
+ if (cmd->scan_end_arg != cmd->chanlist_len) {
+ cmd->scan_end_arg = cmd->chanlist_len;
+ err++;
+ }
#endif
- d = RtdAdcFifoGet(dev); /* get 2s comp value */
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* TODO check for rounding error due to counter wrap */
- d = d >> 3; /* low 3 bits are marker lines */
- if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
- /* convert to comedi unsigned data */
- sample = d + 2048;
- } else
- sample = d;
+ } else {
+ /* TRIG_NONE */
+ if (cmd->stop_arg != 0) {
+ cmd->stop_arg = 0;
+ err++;
+ }
+ }
- if (!comedi_buf_put(s->async, sample))
- return -1;
+ if (err)
+ return 3;
- if (devpriv->aiCount > 0) /* < 0, means read forever */
- devpriv->aiCount--;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->chanlist_len > RTD_MAX_CHANLIST) {
+ cmd->chanlist_len = RTD_MAX_CHANLIST;
+ err++;
}
- return 0;
-}
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
-/*
- unknown amout of data is waiting in fifo.
-*/
-static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
- short sample;
- s16 d = RtdAdcFifoGet(dev); /* get 2s comp value */
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ rtd_ns_to_timer(&cmd->convert_arg,
+ cmd->flags & TRIG_ROUND_MASK);
+ if (tmp != cmd->convert_arg)
+ err++;
- if (0 == devpriv->aiCount) { /* done */
- continue; /* read rest */
+ if (cmd->scan_begin_src == TRIG_TIMER
+ && (cmd->scan_begin_arg
+ < (cmd->convert_arg * cmd->scan_end_arg))) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
}
+ }
- d = d >> 3; /* low 3 bits are marker lines */
- if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
- /* convert to comedi unsigned data */
- sample = d + 2048;
- } else
- sample = d;
-
- if (!comedi_buf_put(s->async, sample))
- return -1;
+ if (err)
+ return 4;
- if (devpriv->aiCount > 0) /* < 0, means read forever */
- devpriv->aiCount--;
- }
return 0;
}
-#ifdef USE_DMA
/*
- Terminate a DMA transfer and wait for everything to quiet down
+ Execute a analog in command with many possible triggering options.
+ The data get stored in the async structure of the subdevice.
+ This is usually done by an interrupt handler.
+ Userland gets to the data using read calls.
*/
-void abort_dma(struct comedi_device *dev, unsigned int channel)
-{ /* DMA channel 0, 1 */
- unsigned long dma_cs_addr; /* the control/status register */
- uint8_t status;
- unsigned int ii;
- /* unsigned long flags; */
-
- dma_cs_addr = (unsigned long)devpriv->lcfg
- + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
-
- /* spinlock for plx dma control/status reg */
- /* spin_lock_irqsave( &dev->spinlock, flags ); */
+static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int timer;
- /* abort dma transfer if necessary */
- status = readb(dma_cs_addr);
- if ((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */
- DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n",
- channel, status);
- goto abortDmaExit;
+ /* stop anything currently running */
+ RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */
+ RtdPacerStop(dev); /* make sure PACER is stopped */
+ RtdAdcConversionSource(dev, 0); /* software trigger only */
+ RtdInterruptMask(dev, 0);
+#ifdef USE_DMA
+ if (devpriv->flags & DMA0_ACTIVE) { /* cancel anything running */
+ RtdPlxInterruptWrite(dev, /* disable any more interrupts */
+ RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
+ abort_dma(dev, 0);
+ devpriv->flags &= ~DMA0_ACTIVE;
+ if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) { /*clear pending int */
+ RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT);
+ }
}
+ RtdDma0Reset(dev); /* reset onboard state */
+#endif /* USE_DMA */
+ RtdAdcClearFifo(dev); /* clear any old data */
+ RtdInterruptOverrunClear(dev);
+ devpriv->intCount = 0;
- /* wait to make sure done bit is zero (needed?) */
- for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) {
- WAIT_QUIETLY;
- status = readb(dma_cs_addr);
- }
- if (status & PLX_DMA_DONE_BIT) {
- printk("rtd520: Timeout waiting for dma %i done clear\n",
- channel);
- goto abortDmaExit;
+ if (!dev->irq) { /* we need interrupts for this */
+ DPRINTK("rtd520: ERROR! No interrupt available!\n");
+ return -ENXIO;
}
- /* disable channel (required) */
- writeb(0, dma_cs_addr);
- udelay(1); /* needed?? */
- /* set abort bit for channel */
- writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+ /* start configuration */
+ /* load channel list and reset CGT */
+ rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
- /* wait for dma done bit to be set */
- status = readb(dma_cs_addr);
- for (ii = 0;
- (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) {
- status = readb(dma_cs_addr);
- WAIT_QUIETLY;
+ /* setup the common case and override if needed */
+ if (cmd->chanlist_len > 1) {
+ /*DPRINTK ("rtd520: Multi channel setup\n"); */
+ RtdPacerStartSource(dev, 0); /* software triggers pacer */
+ RtdBurstStartSource(dev, 1); /* PACER triggers burst */
+ RtdAdcConversionSource(dev, 2); /* BURST triggers ADC */
+ } else { /* single channel */
+ /*DPRINTK ("rtd520: single channel setup\n"); */
+ RtdPacerStartSource(dev, 0); /* software triggers pacer */
+ RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */
}
- if ((status & PLX_DMA_DONE_BIT) == 0) {
- printk("rtd520: Timeout waiting for dma %i done set\n",
- channel);
+ RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */
+
+ if (TRIG_TIMER == cmd->scan_begin_src) {
+ /* scan_begin_arg is in nanoseconds */
+ /* find out how many samples to wait before transferring */
+ if (cmd->flags & TRIG_WAKE_EOS) {
+ /* this may generate un-sustainable interrupt rates */
+ /* the application is responsible for doing the right thing */
+ devpriv->transCount = cmd->chanlist_len;
+ devpriv->flags |= SEND_EOS;
+ } else {
+ /* arrange to transfer data periodically */
+ devpriv->transCount
+ =
+ (TRANS_TARGET_PERIOD * cmd->chanlist_len) /
+ cmd->scan_begin_arg;
+ if (devpriv->transCount < cmd->chanlist_len) {
+ /* transfer after each scan (and avoid 0) */
+ devpriv->transCount = cmd->chanlist_len;
+ } else { /* make a multiple of scan length */
+ devpriv->transCount =
+ (devpriv->transCount +
+ cmd->chanlist_len - 1)
+ / cmd->chanlist_len;
+ devpriv->transCount *= cmd->chanlist_len;
+ }
+ devpriv->flags |= SEND_EOS;
+ }
+ if (devpriv->transCount >= (devpriv->fifoLen / 2)) {
+ /* out of counter range, use 1/2 fifo instead */
+ devpriv->transCount = 0;
+ devpriv->flags &= ~SEND_EOS;
+ } else {
+ /* interrupt for each transfer */
+ RtdAboutCounter(dev, devpriv->transCount - 1);
+ }
+
+ DPRINTK
+ ("rtd520: scanLen=%d transferCount=%d fifoLen=%d\n scanTime(ns)=%d flags=0x%x\n",
+ cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen,
+ cmd->scan_begin_arg, devpriv->flags);
+ } else { /* unknown timing, just use 1/2 FIFO */
+ devpriv->transCount = 0;
+ devpriv->flags &= ~SEND_EOS;
}
+ RtdPacerClockSource(dev, 1); /* use INTERNAL 8Mhz clock source */
+ RtdAboutStopEnable(dev, 1); /* just interrupt, dont stop */
-abortDmaExit:
- /* spin_unlock_irqrestore( &dev->spinlock, flags ); */
-}
+ /* BUG??? these look like enumerated values, but they are bit fields */
-/*
- Process what is in the DMA transfer buffer and pass to comedi
- Note: this is not re-entrant
-*/
-static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- int ii, n;
- s16 *dp;
+ /* First, setup when to stop */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT: /* stop after N scans */
+ devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len;
+ if ((devpriv->transCount > 0)
+ && (devpriv->transCount > devpriv->aiCount)) {
+ devpriv->transCount = devpriv->aiCount;
+ }
+ break;
- if (devpriv->aiCount == 0) /* transfer already complete */
- return 0;
+ case TRIG_NONE: /* stop when cancel is called */
+ devpriv->aiCount = -1; /* read forever */
+ break;
- dp = devpriv->dma0Buff[devpriv->dma0Offset];
- for (ii = 0; ii < devpriv->fifoLen / 2;) { /* convert samples */
- short sample;
+ default:
+ DPRINTK("rtd520: Warning! ignoring stop_src mode %d\n",
+ cmd->stop_src);
+ }
+
+ /* Scan timing */
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER: /* periodic scanning */
+ timer = rtd_ns_to_timer(&cmd->scan_begin_arg,
+ TRIG_ROUND_NEAREST);
+ /* set PACER clock */
+ /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */
+ RtdPacerCounter(dev, timer);
- if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) {
- sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */
- else
- sample = *dp >> 3; /* low 3 bits are marker lines */
+ break;
- *dp++ = sample; /* put processed value back */
+ case TRIG_EXT:
+ RtdPacerStartSource(dev, 1); /* EXTERNALy trigger pacer */
+ break;
- if (++s->async->cur_chan >= s->async->cmd.chanlist_len)
- s->async->cur_chan = 0;
+ default:
+ DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n",
+ cmd->scan_begin_src);
+ }
- ++ii; /* number ready to transfer */
- if (devpriv->aiCount > 0) { /* < 0, means read forever */
- if (--devpriv->aiCount == 0) { /* done */
- /*DPRINTK ("rtd520: Final %d samples\n", ii); */
- break;
- }
+ /* Sample timing within a scan */
+ switch (cmd->convert_src) {
+ case TRIG_TIMER: /* periodic */
+ if (cmd->chanlist_len > 1) { /* only needed for multi-channel */
+ timer = rtd_ns_to_timer(&cmd->convert_arg,
+ TRIG_ROUND_NEAREST);
+ /* setup BURST clock */
+ /*DPRINTK ("rtd520: loading %d into burst\n", timer); */
+ RtdBurstCounter(dev, timer);
}
- }
- /* now pass the whole array to the comedi buffer */
- dp = devpriv->dma0Buff[devpriv->dma0Offset];
- n = comedi_buf_write_alloc(s->async, ii * sizeof(s16));
- if (n < (ii * sizeof(s16))) { /* any residual is an error */
- DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n",
- ii - (n / sizeof(s16)));
- s->async->events |= COMEDI_CB_ERROR;
- return -1;
- }
- comedi_buf_memcpy_to(s->async, 0, dp, n);
- comedi_buf_write_free(s->async, n);
+ break;
- /*
- * always at least 1 scan -- 1/2 FIFO is larger than our max scan list
- */
- s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+ case TRIG_EXT: /* external */
+ RtdBurstStartSource(dev, 2); /* EXTERNALy trigger burst */
+ break;
- if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */
- devpriv->dma0Offset = 0;
+ default:
+ DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n",
+ cmd->convert_src);
}
- return 0;
-}
-#endif /* USE_DMA */
-
-/*
- Handle all rtd520 interrupts.
- Runs atomically and is never re-entered.
- This is a "slow handler"; other interrupts may be active.
- The data conversion may someday happen in a "bottom half".
-*/
-static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */
- void *d)
-{ /* our data *//* cpu context (ignored) */
- struct comedi_device *dev = d; /* must be called "dev" for devpriv */
- u16 status;
- u16 fifoStatus;
- struct comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
-
- if (!dev->attached)
- return IRQ_NONE;
+ /* end configuration */
- devpriv->intCount++; /* DEBUG statistics */
+ /* This doesn't seem to work. There is no way to clear an interrupt
+ that the priority controller has queued! */
+ RtdInterruptClearMask(dev, ~0); /* clear any existing flags */
+ RtdInterruptClear(dev);
- fifoStatus = RtdFifoStatus(dev);
- /* check for FIFO full, this automatically halts the ADC! */
- if (!(fifoStatus & FS_ADC_NOT_FULL)) { /* 0 -> full */
- DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */
- goto abortTransfer;
- }
+ /* TODO: allow multiple interrupt sources */
+ if (devpriv->transCount > 0) { /* transfer every N samples */
+ RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
+ DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount);
+ } else { /* 1/2 FIFO transfers */
#ifdef USE_DMA
- if (devpriv->flags & DMA0_ACTIVE) { /* Check DMA */
- u32 istatus = RtdPlxInterruptRead(dev);
+ devpriv->flags |= DMA0_ACTIVE;
- if (istatus & ICS_DMA0_A) {
- if (ai_process_dma(dev, s) < 0) {
- DPRINTK
- ("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n",
- devpriv->aiCount);
- RtdDma0Control(dev,
- (devpriv->dma0Control &
- ~PLX_DMA_START_BIT)
- | PLX_CLEAR_DMA_INTR_BIT);
- goto abortTransfer;
- }
+ /* point to first transfer in ring */
+ devpriv->dma0Offset = 0;
+ RtdDma0Mode(dev, DMA_MODE_BITS);
+ RtdDma0Next(dev, /* point to first block */
+ devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next);
+ RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */
- /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n",
- devpriv->aiCount, istatus); */
- RtdDma0Control(dev,
- (devpriv->
- dma0Control & ~PLX_DMA_START_BIT)
- | PLX_CLEAR_DMA_INTR_BIT);
- if (0 == devpriv->aiCount) { /* counted down */
- DPRINTK("rtd520: Samples Done (DMA).\n");
- goto transferDone;
- }
- comedi_event(dev, s);
- } else {
- /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */
- }
- }
- /* Fall through and check for other interrupt sources */
+ RtdPlxInterruptWrite(dev, /* enable interrupt */
+ RtdPlxInterruptRead(dev) | ICS_DMA0_E);
+ /* Must be 2 steps. See PLX app note about "Starting a DMA transfer" */
+ RtdDma0Control(dev, PLX_DMA_EN_BIT); /* enable DMA (clear INTR?) */
+ RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT); /*start DMA */
+ DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n",
+ RtdPlxInterruptRead(dev), devpriv->intMask);
+#else /* USE_DMA */
+ RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
+ DPRINTK("rtd520: Transferring every 1/2 FIFO\n");
#endif /* USE_DMA */
-
- status = RtdInterruptStatus(dev);
- /* if interrupt was not caused by our board, or handled above */
- if (0 == status)
- return IRQ_HANDLED;
-
- if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */
- /* since the priority interrupt controller may have queued a sample
- counter interrupt, even though we have already finished,
- we must handle the possibility that there is no data here */
- if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */
- /*DPRINTK("rtd520: Sample int, reading 1/2FIFO. fifo_status 0x%x\n",
- (fifoStatus ^ 0x6666) & 0x7777); */
- if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) {
- DPRINTK
- ("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n",
- devpriv->aiCount);
- goto abortTransfer;
- }
- if (0 == devpriv->aiCount) { /* counted down */
- DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */
- goto transferDone;
- }
- comedi_event(dev, s);
- } else if (devpriv->transCount > 0) { /* read often */
- /*DPRINTK("rtd520: Sample int, reading %d fifo_status 0x%x\n",
- devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */
- if (fifoStatus & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */
- if (ai_read_n(dev, s, devpriv->transCount) < 0) {
- DPRINTK
- ("rtd520: comedi read buffer overflow (N) with %ld to go!\n",
- devpriv->aiCount);
- goto abortTransfer;
- }
- if (0 == devpriv->aiCount) { /* counted down */
- DPRINTK
- ("rtd520: Samples Done (N). fifo_status was 0x%x\n",
- (fifoStatus ^ 0x6666) & 0x7777);
- goto transferDone;
- }
- comedi_event(dev, s);
- }
- } else { /* wait for 1/2 FIFO (old) */
- DPRINTK
- ("rtd520: Sample int. Wait for 1/2. fifo_status 0x%x\n",
- (fifoStatus ^ 0x6666) & 0x7777);
- }
- } else {
- DPRINTK("rtd520: unknown interrupt source!\n");
- }
-
- if (0xffff & RtdInterruptOverrunStatus(dev)) { /* interrupt overrun */
- DPRINTK
- ("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n",
- devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev));
- goto abortTransfer;
}
- /* clear the interrupt */
- RtdInterruptClearMask(dev, status);
- RtdInterruptClear(dev);
- return IRQ_HANDLED;
+ /* BUG: start_src is ASSUMED to be TRIG_NOW */
+ /* BUG? it seems like things are running before the "start" */
+ RtdPacerStart(dev); /* Start PACER */
+ return 0;
+}
-abortTransfer:
- RtdAdcClearFifo(dev); /* clears full flag */
- s->async->events |= COMEDI_CB_ERROR;
- devpriv->aiCount = 0; /* stop and don't transfer any more */
- /* fall into transferDone */
+/*
+ Stop a running data acquisition.
+*/
+static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ u16 status;
-transferDone:
RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */
RtdPacerStop(dev); /* Stop PACER */
RtdAdcConversionSource(dev, 0); /* software trigger only */
- RtdInterruptMask(dev, 0); /* mask out SAMPLE */
+ RtdInterruptMask(dev, 0);
+ devpriv->aiCount = 0; /* stop and don't transfer any more */
#ifdef USE_DMA
if (devpriv->flags & DMA0_ACTIVE) {
RtdPlxInterruptWrite(dev, /* disable any more interrupts */
RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
abort_dma(dev, 0);
devpriv->flags &= ~DMA0_ACTIVE;
- /* if Using DMA, then we should have read everything by now */
- if (devpriv->aiCount > 0) {
- DPRINTK("rtd520: Lost DMA data! %ld remain\n",
- devpriv->aiCount);
- }
}
#endif /* USE_DMA */
-
- if (devpriv->aiCount > 0) { /* there shouldn't be anything left */
- fifoStatus = RtdFifoStatus(dev);
- DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777); /* should read all 0s */
- ai_read_dregs(dev, s); /* read anything left in FIFO */
- }
-
- s->async->events |= COMEDI_CB_EOA; /* signal end to comedi */
- comedi_event(dev, s);
-
- /* clear the interrupt */
status = RtdInterruptStatus(dev);
- RtdInterruptClearMask(dev, status);
- RtdInterruptClear(dev);
-
- fifoStatus = RtdFifoStatus(dev); /* DEBUG */
DPRINTK
- ("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n",
- devpriv->intCount, status,
- 0xffff & RtdInterruptOverrunStatus(dev));
-
- return IRQ_HANDLED;
-}
-
-#if 0
-/*
- return the number of samples available
-*/
-static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
- /* Not sure what to do if DMA is active */
- return s->async->buf_write_count - s->async->buf_read_count;
+ ("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n",
+ devpriv->intCount, status,
+ 0xffff & RtdInterruptOverrunStatus(dev));
+ return 0;
}
-#endif
/*
- cmdtest tests a particular command to see if it is valid.
- Using the cmdtest ioctl, a user can create a valid cmd
- and then have it executed by the cmd ioctl (asyncronously).
-
- cmdtest returns 1,2,3,4 or 0, depending on which tests
- the command passes.
+ Output one (or more) analog values to a single port as fast as possible.
*/
-
-static int rtd_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_cmd *cmd)
+static int rtd_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
{
- int err = 0;
- int tmp;
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+ /* Configure the output range (table index matches the range values) */
+ RtdDacRange(dev, chan, range);
- if (err)
- return 1;
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; ++i) {
+ int val = data[i] << 3;
+ int stat = 0; /* initialize to avoid bogus warning */
+ int ii;
- /* step 2: make sure trigger sources are unique
- and mutually compatible */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT) {
- err++;
- }
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
+ /* VERIFY: comedi range and offset conversions */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ if ((range > 1) /* bipolar */
+ && (data[i] < 2048)) {
+ /* offset and sign extend */
+ val = (((int)data[i]) - 2048) << 3;
+ } else { /* unipolor */
+ val = data[i] << 3;
+ }
- if (err)
- return 2;
+ DPRINTK
+ ("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n",
+ chan, range, data[i], val);
- /* step 3: make sure arguments are trivially compatible */
+ /* a typical programming sequence */
+ RtdDacFifoPut(dev, chan, val); /* put the value in */
+ RtdDacUpdate(dev, chan); /* trigger the conversion */
- if (cmd->start_arg != 0) {
- cmd->start_arg = 0;
- err++;
- }
+ devpriv->aoValue[chan] = data[i]; /* save for read back */
- if (cmd->scan_begin_src == TRIG_TIMER) {
- /* Note: these are time periods, not actual rates */
- if (1 == cmd->chanlist_len) { /* no scanning */
- if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) {
- cmd->scan_begin_arg = RTD_MAX_SPEED_1;
- rtd_ns_to_timer(&cmd->scan_begin_arg,
- TRIG_ROUND_UP);
- err++;
- }
- if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) {
- cmd->scan_begin_arg = RTD_MIN_SPEED_1;
- rtd_ns_to_timer(&cmd->scan_begin_arg,
- TRIG_ROUND_DOWN);
- err++;
- }
- } else {
- if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
- cmd->scan_begin_arg = RTD_MAX_SPEED;
- rtd_ns_to_timer(&cmd->scan_begin_arg,
- TRIG_ROUND_UP);
- err++;
- }
- if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
- cmd->scan_begin_arg = RTD_MIN_SPEED;
- rtd_ns_to_timer(&cmd->scan_begin_arg,
- TRIG_ROUND_DOWN);
- err++;
- }
- }
- } else {
- /* external trigger */
- /* should be level/edge, hi/lo specification here */
- /* should specify multiple external triggers */
- if (cmd->scan_begin_arg > 9) {
- cmd->scan_begin_arg = 9;
- err++;
- }
- }
- if (cmd->convert_src == TRIG_TIMER) {
- if (1 == cmd->chanlist_len) { /* no scanning */
- if (cmd->convert_arg < RTD_MAX_SPEED_1) {
- cmd->convert_arg = RTD_MAX_SPEED_1;
- rtd_ns_to_timer(&cmd->convert_arg,
- TRIG_ROUND_UP);
- err++;
- }
- if (cmd->convert_arg > RTD_MIN_SPEED_1) {
- cmd->convert_arg = RTD_MIN_SPEED_1;
- rtd_ns_to_timer(&cmd->convert_arg,
- TRIG_ROUND_DOWN);
- err++;
- }
- } else {
- if (cmd->convert_arg < RTD_MAX_SPEED) {
- cmd->convert_arg = RTD_MAX_SPEED;
- rtd_ns_to_timer(&cmd->convert_arg,
- TRIG_ROUND_UP);
- err++;
- }
- if (cmd->convert_arg > RTD_MIN_SPEED) {
- cmd->convert_arg = RTD_MIN_SPEED;
- rtd_ns_to_timer(&cmd->convert_arg,
- TRIG_ROUND_DOWN);
- err++;
- }
+ for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) {
+ stat = RtdFifoStatus(dev);
+ /* 1 -> not empty */
+ if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY :
+ FS_DAC2_NOT_EMPTY))
+ break;
+ WAIT_QUIETLY;
}
- } else {
- /* external trigger */
- /* see above */
- if (cmd->convert_arg > 9) {
- cmd->convert_arg = 9;
- err++;
+ if (ii >= RTD_DAC_TIMEOUT) {
+ DPRINTK
+ ("rtd520: Error: DAC never finished! FifoStatus=0x%x\n",
+ stat ^ 0x6666);
+ return -ETIMEDOUT;
}
}
-#if 0
- if (cmd->scan_end_arg != cmd->chanlist_len) {
- cmd->scan_end_arg = cmd->chanlist_len;
- err++;
- }
-#endif
- if (cmd->stop_src == TRIG_COUNT) {
- /* TODO check for rounding error due to counter wrap */
+ /* return the number of samples read/written */
+ return i;
+}
- } else {
- /* TRIG_NONE */
- if (cmd->stop_arg != 0) {
- cmd->stop_arg = 0;
- err++;
- }
- }
+/* AO subdevices should have a read insn as well as a write insn.
+ * Usually this means copying a value stored in devpriv. */
+static int rtd_ao_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
- if (err)
- return 3;
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->aoValue[chan];
- /* step 4: fix up any arguments */
+ return i;
+}
- if (cmd->chanlist_len > RTD_MAX_CHANLIST) {
- cmd->chanlist_len = RTD_MAX_CHANLIST;
- err++;
- }
- if (cmd->scan_begin_src == TRIG_TIMER) {
- tmp = cmd->scan_begin_arg;
- rtd_ns_to_timer(&cmd->scan_begin_arg,
- cmd->flags & TRIG_ROUND_MASK);
- if (tmp != cmd->scan_begin_arg)
- err++;
+/*
+ Write a masked set of bits and the read back the port.
+ We track what the bits should be (i.e. we don't read the port first).
- }
- if (cmd->convert_src == TRIG_TIMER) {
- tmp = cmd->convert_arg;
- rtd_ns_to_timer(&cmd->convert_arg,
- cmd->flags & TRIG_ROUND_MASK);
- if (tmp != cmd->convert_arg)
- err++;
+ DIO devices are slightly special. Although it is possible to
+ * implement the insn_read/insn_write interface, it is much more
+ * useful to applications if you implement the insn_bits interface.
+ * This allows packed reading/writing of the DIO channels. The
+ * comedi core can convert between insn_bits and insn_read/write
+ */
+static int rtd_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ if (insn->n != 2)
+ return -EINVAL;
- if (cmd->scan_begin_src == TRIG_TIMER
- && (cmd->scan_begin_arg
- < (cmd->convert_arg * cmd->scan_end_arg))) {
- cmd->scan_begin_arg =
- cmd->convert_arg * cmd->scan_end_arg;
- err++;
- }
+ /* The insn data is a mask in data[0] and the new data
+ * in data[1], each channel cooresponding to a bit. */
+ if (data[0]) {
+ s->state &= ~data[0];
+ s->state |= data[0] & data[1];
+
+ /* Write out the new digital output lines */
+ RtdDio0Write(dev, s->state);
}
+ /* on return, data[1] contains the value of the digital
+ * input lines. */
+ data[1] = RtdDio0Read(dev);
- if (err)
- return 4;
+ /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */
- return 0;
+ return 2;
}
/*
- Execute a analog in command with many possible triggering options.
- The data get stored in the async structure of the subdevice.
- This is usually done by an interrupt handler.
- Userland gets to the data using read calls.
+ Configure one bit on a IO port as Input or Output (hence the name :-).
*/
-static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+static int rtd_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
- struct comedi_cmd *cmd = &s->async->cmd;
- int timer;
-
- /* stop anything currently running */
- RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */
- RtdPacerStop(dev); /* make sure PACER is stopped */
- RtdAdcConversionSource(dev, 0); /* software trigger only */
- RtdInterruptMask(dev, 0);
-#ifdef USE_DMA
- if (devpriv->flags & DMA0_ACTIVE) { /* cancel anything running */
- RtdPlxInterruptWrite(dev, /* disable any more interrupts */
- RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
- abort_dma(dev, 0);
- devpriv->flags &= ~DMA0_ACTIVE;
- if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) { /*clear pending int */
- RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT);
- }
- }
- RtdDma0Reset(dev); /* reset onboard state */
-#endif /* USE_DMA */
- RtdAdcClearFifo(dev); /* clear any old data */
- RtdInterruptOverrunClear(dev);
- devpriv->intCount = 0;
-
- if (!dev->irq) { /* we need interrupts for this */
- DPRINTK("rtd520: ERROR! No interrupt available!\n");
- return -ENXIO;
- }
-
- /* start configuration */
- /* load channel list and reset CGT */
- rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
+ int chan = CR_CHAN(insn->chanspec);
- /* setup the common case and override if needed */
- if (cmd->chanlist_len > 1) {
- /*DPRINTK ("rtd520: Multi channel setup\n"); */
- RtdPacerStartSource(dev, 0); /* software triggers pacer */
- RtdBurstStartSource(dev, 1); /* PACER triggers burst */
- RtdAdcConversionSource(dev, 2); /* BURST triggers ADC */
- } else { /* single channel */
- /*DPRINTK ("rtd520: single channel setup\n"); */
- RtdPacerStartSource(dev, 0); /* software triggers pacer */
- RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */
+ /* The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * value COMEDI_INPUT or COMEDI_OUTPUT. */
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= 1 << chan; /* 1 means Out */
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~(1 << chan);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+ break;
+ default:
+ return -EINVAL;
}
- RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */
- if (TRIG_TIMER == cmd->scan_begin_src) {
- /* scan_begin_arg is in nanoseconds */
- /* find out how many samples to wait before transferring */
- if (cmd->flags & TRIG_WAKE_EOS) {
- /* this may generate un-sustainable interrupt rates */
- /* the application is responsible for doing the right thing */
- devpriv->transCount = cmd->chanlist_len;
- devpriv->flags |= SEND_EOS;
- } else {
- /* arrange to transfer data periodically */
- devpriv->transCount
- =
- (TRANS_TARGET_PERIOD * cmd->chanlist_len) /
- cmd->scan_begin_arg;
- if (devpriv->transCount < cmd->chanlist_len) {
- /* transfer after each scan (and avoid 0) */
- devpriv->transCount = cmd->chanlist_len;
- } else { /* make a multiple of scan length */
- devpriv->transCount =
- (devpriv->transCount +
- cmd->chanlist_len - 1)
- / cmd->chanlist_len;
- devpriv->transCount *= cmd->chanlist_len;
- }
- devpriv->flags |= SEND_EOS;
- }
- if (devpriv->transCount >= (devpriv->fifoLen / 2)) {
- /* out of counter range, use 1/2 fifo instead */
- devpriv->transCount = 0;
- devpriv->flags &= ~SEND_EOS;
- } else {
- /* interrupt for each transfer */
- RtdAboutCounter(dev, devpriv->transCount - 1);
- }
+ DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits);
+ /* TODO support digital match interrupts and strobes */
+ RtdDioStatusWrite(dev, 0x01); /* make Dio0Ctrl point to direction */
+ RtdDio0CtrlWrite(dev, s->io_bits); /* set direction 1 means Out */
+ RtdDioStatusWrite(dev, 0); /* make Dio0Ctrl clear interrupts */
- DPRINTK
- ("rtd520: scanLen=%d transferCount=%d fifoLen=%d\n scanTime(ns)=%d flags=0x%x\n",
- cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen,
- cmd->scan_begin_arg, devpriv->flags);
- } else { /* unknown timing, just use 1/2 FIFO */
- devpriv->transCount = 0;
- devpriv->flags &= ~SEND_EOS;
- }
- RtdPacerClockSource(dev, 1); /* use INTERNAL 8Mhz clock source */
- RtdAboutStopEnable(dev, 1); /* just interrupt, dont stop */
+ /* port1 can only be all input or all output */
- /* BUG??? these look like enumerated values, but they are bit fields */
+ /* there are also 2 user input lines and 2 user output lines */
- /* First, setup when to stop */
- switch (cmd->stop_src) {
- case TRIG_COUNT: /* stop after N scans */
- devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len;
- if ((devpriv->transCount > 0)
- && (devpriv->transCount > devpriv->aiCount)) {
- devpriv->transCount = devpriv->aiCount;
- }
- break;
+ return 1;
+}
- case TRIG_NONE: /* stop when cancel is called */
- devpriv->aiCount = -1; /* read forever */
- break;
+static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{ /* board name and options flags */
+ struct comedi_subdevice *s;
+ struct pci_dev *pcidev;
+ int ret;
+ resource_size_t physLas0; /* configuration */
+ resource_size_t physLas1; /* data area */
+ resource_size_t physLcfg; /* PLX9080 */
+#ifdef USE_DMA
+ int index;
+#endif
- default:
- DPRINTK("rtd520: Warning! ignoring stop_src mode %d\n",
- cmd->stop_src);
- }
+ printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor);
- /* Scan timing */
- switch (cmd->scan_begin_src) {
- case TRIG_TIMER: /* periodic scanning */
- timer = rtd_ns_to_timer(&cmd->scan_begin_arg,
- TRIG_ROUND_NEAREST);
- /* set PACER clock */
- /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */
- RtdPacerCounter(dev, timer);
+#if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA)
+ /* You can set this a load time: modprobe comedi comedi_debug=1 */
+ if (0 == comedi_debug) /* force DMA debug printks */
+ comedi_debug = 1;
+#endif
- break;
+ /*
+ * Allocate the private structure area. alloc_private() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0)
+ return -ENOMEM;
- case TRIG_EXT:
- RtdPacerStartSource(dev, 1); /* EXTERNALy trigger pacer */
- break;
+ /*
+ * Probe the device to determine what device in the series it is.
+ */
+ for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL);
+ pcidev != NULL;
+ pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) {
+ int i;
- default:
- DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n",
- cmd->scan_begin_src);
+ if (it->options[0] || it->options[1]) {
+ if (pcidev->bus->number != it->options[0]
+ || PCI_SLOT(pcidev->devfn) != it->options[1]) {
+ continue;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) {
+ if (pcidev->device == rtd520Boards[i].device_id) {
+ dev->board_ptr = &rtd520Boards[i];
+ break;
+ }
+ }
+ if (dev->board_ptr)
+ break; /* found one */
}
-
- /* Sample timing within a scan */
- switch (cmd->convert_src) {
- case TRIG_TIMER: /* periodic */
- if (cmd->chanlist_len > 1) { /* only needed for multi-channel */
- timer = rtd_ns_to_timer(&cmd->convert_arg,
- TRIG_ROUND_NEAREST);
- /* setup BURST clock */
- /*DPRINTK ("rtd520: loading %d into burst\n", timer); */
- RtdBurstCounter(dev, timer);
+ if (!pcidev) {
+ if (it->options[0] && it->options[1]) {
+ printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n",
+ it->options[0], it->options[1]);
+ } else {
+ printk(KERN_INFO "No RTD card found.\n");
}
+ return -EIO;
+ }
+ devpriv->pci_dev = pcidev;
+ dev->board_name = thisboard->name;
- break;
-
- case TRIG_EXT: /* external */
- RtdBurstStartSource(dev, 2); /* EXTERNALy trigger burst */
- break;
-
- default:
- DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n",
- cmd->convert_src);
+ ret = comedi_pci_enable(pcidev, DRV_NAME);
+ if (ret < 0) {
+ printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
+ return ret;
}
- /* end configuration */
+ devpriv->got_regions = 1;
- /* This doesn't seem to work. There is no way to clear an interrupt
- that the priority controller has queued! */
- RtdInterruptClearMask(dev, ~0); /* clear any existing flags */
- RtdInterruptClear(dev);
+ /*
+ * Initialize base addresses
+ */
+ /* Get the physical address from PCI config */
+ physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX);
+ physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX);
+ physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX);
+ /* Now have the kernel map this into memory */
+ /* ASSUME page aligned */
+ devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE);
+ devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
+ devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
- /* TODO: allow multiple interrupt sources */
- if (devpriv->transCount > 0) { /* transfer every N samples */
- RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
- DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount);
- } else { /* 1/2 FIFO transfers */
-#ifdef USE_DMA
- devpriv->flags |= DMA0_ACTIVE;
+ if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
+ return -ENOMEM;
- /* point to first transfer in ring */
- devpriv->dma0Offset = 0;
- RtdDma0Mode(dev, DMA_MODE_BITS);
- RtdDma0Next(dev, /* point to first block */
- devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next);
- RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */
- RtdPlxInterruptWrite(dev, /* enable interrupt */
- RtdPlxInterruptRead(dev) | ICS_DMA0_E);
- /* Must be 2 steps. See PLX app note about "Starting a DMA transfer" */
- RtdDma0Control(dev, PLX_DMA_EN_BIT); /* enable DMA (clear INTR?) */
- RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT); /*start DMA */
- DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n",
- RtdPlxInterruptRead(dev), devpriv->intMask);
-#else /* USE_DMA */
- RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT);
- DPRINTK("rtd520: Transferring every 1/2 FIFO\n");
-#endif /* USE_DMA */
- }
+ DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name,
+ (unsigned long long)physLas0, (unsigned long long)physLas1,
+ (unsigned long long)physLcfg);
+ { /* The RTD driver does this */
+ unsigned char pci_latency;
+ u16 revision;
+ /*uint32_t epld_version; */
- /* BUG: start_src is ASSUMED to be TRIG_NOW */
- /* BUG? it seems like things are running before the "start" */
- RtdPacerStart(dev); /* Start PACER */
- return 0;
-}
+ pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID,
+ &revision);
+ DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision);
-/*
- Stop a running data acquisition.
-*/
-static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- u16 status;
+ pci_read_config_byte(devpriv->pci_dev,
+ PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 32) {
+ printk(KERN_INFO "%s: PCI latency changed from %d to %d\n",
+ dev->board_name, pci_latency, 32);
+ pci_write_config_byte(devpriv->pci_dev,
+ PCI_LATENCY_TIMER, 32);
+ } else {
+ DPRINTK("rtd520: PCI latency = %d\n", pci_latency);
+ }
- RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */
- RtdPacerStop(dev); /* Stop PACER */
- RtdAdcConversionSource(dev, 0); /* software trigger only */
- RtdInterruptMask(dev, 0);
- devpriv->aiCount = 0; /* stop and don't transfer any more */
-#ifdef USE_DMA
- if (devpriv->flags & DMA0_ACTIVE) {
- RtdPlxInterruptWrite(dev, /* disable any more interrupts */
- RtdPlxInterruptRead(dev) & ~ICS_DMA0_E);
- abort_dma(dev, 0);
- devpriv->flags &= ~DMA0_ACTIVE;
+ /*
+ * Undocumented EPLD version (doesn't match RTD driver results)
+ */
+ /*DPRINTK ("rtd520: Reading epld from %p\n",
+ devpriv->las0+0);
+ epld_version = readl (devpriv->las0+0);
+ if ((epld_version & 0xF0) >> 4 == 0x0F) {
+ DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version);
+ } else {
+ DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4);
+ } */
}
-#endif /* USE_DMA */
- status = RtdInterruptStatus(dev);
- DPRINTK
- ("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n",
- devpriv->intCount, status,
- 0xffff & RtdInterruptOverrunStatus(dev));
- return 0;
-}
-/*
- Given a desired period and the clock period (both in ns),
- return the proper counter value (divider-1).
- Sets the original period to be the true value.
- Note: you have to check if the value is larger than the counter range!
-*/
-static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */
- int round_mode, int base)
-{ /* clock period (in ns) */
- int divider;
+ /* Show board configuration */
+ printk(KERN_INFO "%s:", dev->board_name);
- switch (round_mode) {
- case TRIG_ROUND_NEAREST:
- default:
- divider = (*nanosec + base / 2) / base;
- break;
- case TRIG_ROUND_DOWN:
- divider = (*nanosec) / base;
- break;
- case TRIG_ROUND_UP:
- divider = (*nanosec + base - 1) / base;
- break;
- }
- if (divider < 2)
- divider = 2; /* min is divide by 2 */
+ /*
+ * Allocate the subdevice structures. alloc_subdevice() is a
+ * convenient macro defined in comedidev.h.
+ */
+ if (alloc_subdevices(dev, 4) < 0)
+ return -ENOMEM;
- /* Note: we don't check for max, because different timers
- have different ranges */
- *nanosec = base * divider;
- return divider - 1; /* countdown is divisor+1 */
-}
+ s = dev->subdevices + 0;
+ dev->read_subdev = s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = thisboard->aiChans;
+ s->maxdata = (1 << thisboard->aiBits) - 1;
+ if (thisboard->aiMaxGain <= 32)
+ s->range_table = &rtd_ai_7520_range;
+ else
+ s->range_table = &rtd_ai_4520_range;
-/*
- Given a desired period (in ns),
- return the proper counter value (divider-1) for the internal clock.
- Sets the original period to be the true value.
-*/
-static int rtd_ns_to_timer(unsigned int *ns, int round_mode)
-{
- return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE);
-}
+ s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */
+ s->insn_read = rtd_ai_rinsn;
+ s->do_cmd = rtd_ai_cmd;
+ s->do_cmdtest = rtd_ai_cmdtest;
+ s->cancel = rtd_ai_cancel;
+ /* s->poll = rtd_ai_poll; *//* not ready yet */
-/*
- Output one (or more) analog values to a single port as fast as possible.
-*/
-static int rtd_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
-{
- int i;
- int chan = CR_CHAN(insn->chanspec);
- int range = CR_RANGE(insn->chanspec);
+ s = dev->subdevices + 1;
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << thisboard->aiBits) - 1;
+ s->range_table = &rtd_ao_range;
+ s->insn_write = rtd_ao_winsn;
+ s->insn_read = rtd_ao_rinsn;
- /* Configure the output range (table index matches the range values) */
- RtdDacRange(dev, chan, range);
+ s = dev->subdevices + 2;
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ /* we only support port 0 right now. Ignoring port 1 and user IO */
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = rtd_dio_insn_bits;
+ s->insn_config = rtd_dio_insn_config;
- /* Writing a list of values to an AO channel is probably not
- * very useful, but that's how the interface is defined. */
- for (i = 0; i < insn->n; ++i) {
- int val = data[i] << 3;
- int stat = 0; /* initialize to avoid bogus warning */
- int ii;
+ /* timer/counter subdevices (not currently supported) */
+ s = dev->subdevices + 3;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xffff;
- /* VERIFY: comedi range and offset conversions */
+ /* initialize board, per RTD spec */
+ /* also, initialize shadow registers */
+ RtdResetBoard(dev);
+ udelay(100); /* needed? */
+ RtdPlxInterruptWrite(dev, 0);
+ RtdInterruptMask(dev, 0); /* and sets shadow */
+ RtdInterruptClearMask(dev, ~0); /* and sets shadow */
+ RtdInterruptClear(dev); /* clears bits set by mask */
+ RtdInterruptOverrunClear(dev);
+ RtdClearCGT(dev);
+ RtdAdcClearFifo(dev);
+ RtdDacClearFifo(dev, 0);
+ RtdDacClearFifo(dev, 1);
+ /* clear digital IO fifo */
+ RtdDioStatusWrite(dev, 0); /* safe state, set shadow */
+ RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */
+ RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */
+ RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */
+ RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */
+ /* TODO: set user out source ??? */
- if ((range > 1) /* bipolar */
- && (data[i] < 2048)) {
- /* offset and sign extend */
- val = (((int)data[i]) - 2048) << 3;
- } else { /* unipolor */
- val = data[i] << 3;
- }
+ /* check if our interrupt is available and get it */
+ ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt,
+ IRQF_SHARED, DRV_NAME, dev);
- DPRINTK
- ("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n",
- chan, range, data[i], val);
+ if (ret < 0) {
+ printk("Could not get interrupt! (%u)\n",
+ devpriv->pci_dev->irq);
+ return ret;
+ }
+ dev->irq = devpriv->pci_dev->irq;
+ printk(KERN_INFO "( irq=%u )", dev->irq);
- /* a typical programming sequence */
- RtdDacFifoPut(dev, chan, val); /* put the value in */
- RtdDacUpdate(dev, chan); /* trigger the conversion */
+ ret = rtd520_probe_fifo_depth(dev);
+ if (ret < 0)
+ return ret;
- devpriv->aoValue[chan] = data[i]; /* save for read back */
+ devpriv->fifoLen = ret;
+ printk("( fifoLen=%d )", devpriv->fifoLen);
+
+#ifdef USE_DMA
+ if (dev->irq > 0) {
+ printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT);
+ /*
+ * The PLX9080 has 2 DMA controllers, but there could be
+ * 4 sources: ADC, digital, DAC1, and DAC2. Since only the
+ * ADC supports cmd mode right now, this isn't an issue (yet)
+ */
+ devpriv->dma0Offset = 0;
- for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) {
- stat = RtdFifoStatus(dev);
- /* 1 -> not empty */
- if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY :
- FS_DAC2_NOT_EMPTY))
- break;
- WAIT_QUIETLY;
+ for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+ devpriv->dma0Buff[index] =
+ pci_alloc_consistent(devpriv->pci_dev,
+ sizeof(u16) *
+ devpriv->fifoLen / 2,
+ &devpriv->
+ dma0BuffPhysAddr[index]);
+ if (devpriv->dma0Buff[index] == NULL) {
+ ret = -ENOMEM;
+ goto rtd_attach_die_error;
+ }
+ /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
+ index,
+ devpriv->dma0Buff[index],
+ devpriv->dma0BuffPhysAddr[index]); */
}
- if (ii >= RTD_DAC_TIMEOUT) {
- DPRINTK
- ("rtd520: Error: DAC never finished! FifoStatus=0x%x\n",
- stat ^ 0x6666);
- return -ETIMEDOUT;
+
+ /*
+ * setup DMA descriptor ring (use cpu_to_le32 for byte
+ * ordering?)
+ */
+ devpriv->dma0Chain =
+ pci_alloc_consistent(devpriv->pci_dev,
+ sizeof(struct plx_dma_desc) *
+ DMA_CHAIN_COUNT,
+ &devpriv->dma0ChainPhysAddr);
+ for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+ devpriv->dma0Chain[index].pci_start_addr =
+ devpriv->dma0BuffPhysAddr[index];
+ devpriv->dma0Chain[index].local_start_addr =
+ DMALADDR_ADC;
+ devpriv->dma0Chain[index].transfer_size =
+ sizeof(u16) * devpriv->fifoLen / 2;
+ devpriv->dma0Chain[index].next =
+ (devpriv->dma0ChainPhysAddr + ((index +
+ 1) %
+ (DMA_CHAIN_COUNT))
+ * sizeof(devpriv->dma0Chain[0]))
+ | DMA_TRANSFER_BITS;
+ /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
+ index,
+ ((long)devpriv->dma0ChainPhysAddr
+ + (index * sizeof(devpriv->dma0Chain[0]))),
+ devpriv->dma0Chain[index].pci_start_addr,
+ devpriv->dma0Chain[index].local_start_addr,
+ devpriv->dma0Chain[index].transfer_size,
+ devpriv->dma0Chain[index].next); */
}
- }
- /* return the number of samples read/written */
- return i;
-}
+ if (devpriv->dma0Chain == NULL) {
+ ret = -ENOMEM;
+ goto rtd_attach_die_error;
+ }
-/* AO subdevices should have a read insn as well as a write insn.
- * Usually this means copying a value stored in devpriv. */
-static int rtd_ao_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
-{
- int i;
- int chan = CR_CHAN(insn->chanspec);
+ RtdDma0Mode(dev, DMA_MODE_BITS);
+ /* set DMA trigger source */
+ RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL);
+ } else {
+ printk(KERN_INFO "( no IRQ->no DMA )");
+ }
+#endif /* USE_DMA */
- for (i = 0; i < insn->n; i++)
- data[i] = devpriv->aoValue[chan];
+ if (dev->irq) { /* enable plx9080 interrupts */
+ RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+ }
+ printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor);
- return i;
-}
+ return 1;
-/*
- Write a masked set of bits and the read back the port.
- We track what the bits should be (i.e. we don't read the port first).
+#if 0
+ /* hit an error, clean up memory and return ret */
+/* rtd_attach_die_error: */
+#ifdef USE_DMA
+ for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+ if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */
+ pci_free_consistent(devpriv->pci_dev,
+ sizeof(u16) * devpriv->fifoLen / 2,
+ devpriv->dma0Buff[index],
+ devpriv->dma0BuffPhysAddr[index]);
+ devpriv->dma0Buff[index] = NULL;
+ }
+ }
+ if (NULL != devpriv->dma0Chain) {
+ pci_free_consistent(devpriv->pci_dev,
+ sizeof(struct plx_dma_desc)
+ * DMA_CHAIN_COUNT,
+ devpriv->dma0Chain,
+ devpriv->dma0ChainPhysAddr);
+ devpriv->dma0Chain = NULL;
+ }
+#endif /* USE_DMA */
+ /* subdevices and priv are freed by the core */
+ if (dev->irq) {
+ /* disable interrupt controller */
+ RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
+ & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
+ free_irq(dev->irq, dev);
+ }
- DIO devices are slightly special. Although it is possible to
- * implement the insn_read/insn_write interface, it is much more
- * useful to applications if you implement the insn_bits interface.
- * This allows packed reading/writing of the DIO channels. The
- * comedi core can convert between insn_bits and insn_read/write
- */
-static int rtd_dio_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
-{
- if (insn->n != 2)
- return -EINVAL;
+ /* release all regions that were allocated */
+ if (devpriv->las0)
+ iounmap(devpriv->las0);
- /* The insn data is a mask in data[0] and the new data
- * in data[1], each channel cooresponding to a bit. */
- if (data[0]) {
- s->state &= ~data[0];
- s->state |= data[0] & data[1];
+ if (devpriv->las1)
+ iounmap(devpriv->las1);
- /* Write out the new digital output lines */
- RtdDio0Write(dev, s->state);
- }
- /* on return, data[1] contains the value of the digital
- * input lines. */
- data[1] = RtdDio0Read(dev);
+ if (devpriv->lcfg)
+ iounmap(devpriv->lcfg);
- /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */
+ if (devpriv->pci_dev)
+ pci_dev_put(devpriv->pci_dev);
- return 2;
+ return ret;
+#endif
}
-/*
- Configure one bit on a IO port as Input or Output (hence the name :-).
-*/
-static int rtd_dio_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static void rtd_detach(struct comedi_device *dev)
{
- int chan = CR_CHAN(insn->chanspec);
+#ifdef USE_DMA
+ int index;
+#endif
- /* The input or output configuration of each digital line is
- * configured by a special insn_config instruction. chanspec
- * contains the channel to be changed, and data[0] contains the
- * value COMEDI_INPUT or COMEDI_OUTPUT. */
- switch (data[0]) {
- case INSN_CONFIG_DIO_OUTPUT:
- s->io_bits |= 1 << chan; /* 1 means Out */
- break;
- case INSN_CONFIG_DIO_INPUT:
- s->io_bits &= ~(1 << chan);
- break;
- case INSN_CONFIG_DIO_QUERY:
- data[1] =
- (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
- return insn->n;
- break;
- default:
- return -EINVAL;
+ if (devpriv) {
+ /* Shut down any board ops by resetting it */
+#ifdef USE_DMA
+ if (devpriv->lcfg) {
+ RtdDma0Control(dev, 0); /* disable DMA */
+ RtdDma1Control(dev, 0); /* disable DMA */
+ RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE);
+ }
+#endif /* USE_DMA */
+ if (devpriv->las0) {
+ RtdResetBoard(dev);
+ RtdInterruptMask(dev, 0);
+ RtdInterruptClearMask(dev, ~0);
+ RtdInterruptClear(dev); /* clears bits set by mask */
+ }
+#ifdef USE_DMA
+ /* release DMA */
+ for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+ if (NULL != devpriv->dma0Buff[index]) {
+ pci_free_consistent(devpriv->pci_dev,
+ sizeof(u16) *
+ devpriv->fifoLen / 2,
+ devpriv->dma0Buff[index],
+ devpriv->
+ dma0BuffPhysAddr[index]);
+ devpriv->dma0Buff[index] = NULL;
+ }
+ }
+ if (NULL != devpriv->dma0Chain) {
+ pci_free_consistent(devpriv->pci_dev,
+ sizeof(struct plx_dma_desc) *
+ DMA_CHAIN_COUNT, devpriv->dma0Chain,
+ devpriv->dma0ChainPhysAddr);
+ devpriv->dma0Chain = NULL;
+ }
+#endif /* USE_DMA */
+ if (dev->irq) {
+ RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev)
+ & ~(ICS_PLIE | ICS_DMA0_E |
+ ICS_DMA1_E));
+ free_irq(dev->irq, dev);
+ }
+ if (devpriv->las0)
+ iounmap(devpriv->las0);
+ if (devpriv->las1)
+ iounmap(devpriv->las1);
+ if (devpriv->lcfg)
+ iounmap(devpriv->lcfg);
+ if (devpriv->pci_dev) {
+ if (devpriv->got_regions)
+ comedi_pci_disable(devpriv->pci_dev);
+ pci_dev_put(devpriv->pci_dev);
+ }
}
-
- DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits);
- /* TODO support digital match interrupts and strobes */
- RtdDioStatusWrite(dev, 0x01); /* make Dio0Ctrl point to direction */
- RtdDio0CtrlWrite(dev, s->io_bits); /* set direction 1 means Out */
- RtdDioStatusWrite(dev, 0); /* make Dio0Ctrl clear interrupts */
-
- /* port1 can only be all input or all output */
-
- /* there are also 2 user input lines and 2 user output lines */
-
- return 1;
}
static struct comedi_driver rtd520_driver = {