📄 rtd520.c
字号:
*/ if (alloc_subdevices(dev, 4)<0) { return -ENOMEM; } 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; 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; } s->len_chanlist = RTD_MAX_CHANLIST; /* thisboard->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 */ 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; 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; /* 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; /* check if our interrupt is available and get it */ dev->irq = devpriv->pci_dev->irq; if (dev->irq > 0) { if((ret=comedi_request_irq (dev->irq, rtd_interrupt, SA_SHIRQ, "rtd520", dev)) < 0) { printk("Could not get interrupt! (%d)\n", dev->irq); return ret; } printk("( irq=%d )", dev->irq); } else { printk("( NO IRQ )"); }#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(index = 0; index < DMA_CHAIN_COUNT; index++) { devpriv->dma0Buff[index] = pci_alloc_consistent(devpriv->pci_dev, sizeof (u16) * thisboard->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]);*/ } /* 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) * thisboard->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);*/ } if (devpriv->dma0Chain == NULL) { ret = -ENOMEM; goto rtd_attach_die_error; } RtdDma0Mode (dev, DMA_MODE_BITS); RtdDma0Source (dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source*/ } else { printk("( no IRQ->no DMA )\n"); }#else /* USE_DMA */ printk("\n"); /* end configuration line */#endif /* USE_DMA */ /* initialize board, per RTD spec */ /* also, initialize shadow registers */ RtdResetBoard (dev); comedi_udelay (100); /* needed? */ 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 (dev->irq) { /* enable plx9080 interrupts */ RtdPlxInterruptWrite (dev, ICS_PIE | ICS_PLIE); } printk("comedi%d: rtd520 driver attached.\n", dev->minor); return 1; /* 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) * thisboard->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)); comedi_free_irq (dev->irq, dev); } /* release all regions that were allocated */ if (devpriv->las0) { iounmap (devpriv->las0); } if (devpriv->las1) { iounmap (devpriv->las1); } if (devpriv->lcfg) { iounmap (devpriv->lcfg); } return ret;}/* * _detach is called to deconfigure a device. It should deallocate * resources. * This function is also called when _attach() fails, so it should be * careful not to release resources that were not necessarily * allocated by _attach(). dev->private and dev->subdevices are * deallocated automatically by the core. */static int rtd_detach ( comedi_device *dev){#ifdef USE_DMA int index;#endif DPRINTK("comedi%d: rtd520: removing (%ld ints)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n", dev->minor, devpriv->intCount, 0xffff & RtdInterruptStatus (dev), 0xffff & RtdInterruptOverrunStatus (dev), (0xffff & RtdFifoStatus (dev)) ^ 0x6666); if (devpriv) { /* Shut down any board ops by resetting it */#ifdef USE_DMA RtdDma0Control (dev, 0); /* disable DMA */ RtdDma1Control (dev, 0); /* disable DMA */ RtdPlxInterruptWrite (dev, ICS_PIE | ICS_PLIE);#endif /* USE_DMA */ 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) * thisboard->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 */ /* release IRQ */ if (dev->irq) { /* disable interrupt controller */ RtdPlxInterruptWrite ( dev, RtdPlxInterruptRead (dev) & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E)); comedi_free_irq (dev->irq, dev); } /* release all regions that were allocated */ if (devpriv->las0) { iounmap (devpriv->las0); } if (devpriv->las1) { iounmap (devpriv->las1); } if (devpriv->lcfg) { iounmap (devpriv->lcfg); } } printk("comedi%d: rtd520: removed.\n",dev->minor); return 0;}/* Convert a single comedi channel-gain entry to a RTD520 table entry*/static unsigned short rtdConvertChanGain ( comedi_device *dev, unsigned int comediChan, int chanIndex) /* index in channel list */{ unsigned int chan, range, aref; unsigned short r=0; chan = CR_CHAN (comediChan); range = CR_RANGE (comediChan); aref = CR_AREF (comediChan); r |= chan & 0xf; /* 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 */ r |= ((range - thisboard->range10Start) & 0x7) << 4; /* gain */ CHAN_ARRAY_SET (devpriv->chanBipolar, chanIndex); } else { /* last batch is +10 */ r |= 0x200; /* +10 range */ r |= ((range-thisboard->rangeUniStart) & 0x7) << 4; /* gain */ CHAN_ARRAY_CLEAR (devpriv->chanBipolar, chanIndex); } 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 ( 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)); } } else { /* just use the channel gain latch */ RtdEnableCGT(dev, 0); /* disable table, enable latch */ RtdWriteCGLatch (dev, rtdConvertChanGain (dev, list[0], 0)); }}/* "instructions" read/write data in "one-shot" or "software-triggered" mode (simplest case). This doesnt use interrupts. Note, we don't do any settling delays. Use a instruction list to select, delay, then read. */static int rtd_ai_rinsn ( comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data){ int n, ii; int stat; /* clear any old fifo data */ RtdAdcClearFifo (dev); /* write channel to multiplexer and clear channel gain table */ rtd_load_channelgain_list (dev, 1, &insn->chanspec); /* set conversion source */ RtdAdcConversionSource (dev, 0); /* software */ /* 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_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)) { data[n] = d + 2048; /* convert to comedi unsigned data */ } else { data[n] = d; } } /* return the number of samples read/written */ return n;}/* 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 ( comedi_device *dev, comedi_subdevice *s, int count){ int ii; for (ii = 0; ii < count; ii++) { sampl_t sample; s16 d; if (0 == devpriv->aiCount) { /* done */ d = RtdAdcFifoGet (dev); /* Read N and discard */ continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -