📄 cb_pcidas64.c
字号:
static void disable_plx_interrupts( comedi_device *dev ){ priv(dev)->plx_intcsr_bits = 0; writel(priv(dev)->plx_intcsr_bits, priv(dev)->plx9080_iobase + PLX_INTRCS_REG);}static void init_stc_registers( comedi_device *dev ){ uint16_t bits; unsigned long flags; comedi_spin_lock_irqsave( &dev->spinlock, flags ); // bit should be set for 6025, although docs say boards with <= 16 chans should be cleared XXX if( 1 ) priv(dev)->adc_control1_bits |= ADC_QUEUE_CONFIG_BIT; writew( priv(dev)->adc_control1_bits, priv(dev)->main_iobase + ADC_CONTROL1_REG ); bits = SLOW_DAC_BIT | DMA_CH_SELECT_BIT; if(board(dev)->layout == LAYOUT_4020) bits |= INTERNAL_CLOCK_4020_BITS; priv(dev)->hw_config_bits |= bits; writew(priv(dev)->hw_config_bits, priv(dev)->main_iobase + HW_CONFIG_REG); writew(0, priv(dev)->main_iobase + DAQ_SYNC_REG); writew(0, priv(dev)->main_iobase + CALIBRATION_REG); comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); // set fifos to maximum size priv(dev)->fifo_size_bits |= DAC_FIFO_BITS; set_ai_fifo_segment_length( dev, board(dev)->ai_fifo->max_segment_length );};/* * Attach is called by the Comedi core to configure the driver * for a particular board. */static int attach(comedi_device *dev, comedi_devconfig *it){ struct pci_dev* pcidev; int index; uint32_t local_range, local_decode; int retval; printk("comedi%d: cb_pcidas64\n",dev->minor);/* * Allocate the private structure area. */ if(alloc_private(dev,sizeof(pcidas64_private)) < 0) return -ENOMEM;/* * Probe the device to determine what device in the series it is. */ pci_for_each_dev( pcidev ) { // is it not a computer boards card? if( pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS ) continue; // loop through cards supported by this driver for(index = 0; index < num_boards(); index++) { if( pcidas64_boards[index].device_id != pcidev->device ) continue; // was a particular bus/slot requested? if( it->options[0] || it->options[1] ) { // are we on the wrong bus/slot? if( pcidev->bus->number != it->options[0] || PCI_SLOT( pcidev->devfn ) != it->options[1] ) { continue; } } dev->board_ptr = pcidas64_boards + index; break; } if( dev->board_ptr ) break; } if( dev->board_ptr == NULL ) { printk("No supported ComputerBoards/MeasurementComputing card found\n"); return -EIO; } printk("Found %s on bus %i, slot %i\n", board(dev)->name, pcidev->bus->number, PCI_SLOT(pcidev->devfn)); if( pci_enable_device( pcidev ) ) return -EIO; pci_set_master( pcidev ); priv(dev)->hw_dev = pcidev; //Initialize dev->board_name dev->board_name = board(dev)->name; if( pci_request_regions( pcidev, driver_cb_pcidas.driver_name ) ) { /* Couldn't allocate io space */ printk(KERN_WARNING " failed to allocate io memory\n"); return -EIO; } priv(dev)->plx9080_phys_iobase = pci_resource_start(pcidev, PLX9080_BADDRINDEX); priv(dev)->main_phys_iobase = pci_resource_start(pcidev, MAIN_BADDRINDEX); priv(dev)->dio_counter_phys_iobase = pci_resource_start(pcidev, DIO_COUNTER_BADDRINDEX); // remap, won't work with 2.0 kernels but who cares priv(dev)->plx9080_iobase = (unsigned long)ioremap(priv(dev)->plx9080_phys_iobase, pci_resource_len(pcidev, PLX9080_BADDRINDEX)); priv(dev)->main_iobase = (unsigned long)ioremap(priv(dev)->main_phys_iobase, pci_resource_len(pcidev, MAIN_BADDRINDEX)); priv(dev)->dio_counter_iobase = (unsigned long)ioremap(priv(dev)->dio_counter_phys_iobase, pci_resource_len(pcidev, DIO_COUNTER_BADDRINDEX)); DEBUG_PRINT(" plx9080 remapped to 0x%lx\n", priv(dev)->plx9080_iobase); DEBUG_PRINT(" main remapped to 0x%lx\n", priv(dev)->main_iobase); DEBUG_PRINT(" diocounter remapped to 0x%lx\n", priv(dev)->dio_counter_iobase); // get irq if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev )) { printk(" unable to allocate irq %d\n", pcidev->irq); return -EINVAL; } dev->irq = pcidev->irq; printk(" irq %i\n", dev->irq); // figure out what local addresses are local_range = readl(priv(dev)->plx9080_iobase + PLX_LAS0RNG_REG) & LRNG_MEM_MASK; local_decode = readl(priv(dev)->plx9080_iobase + PLX_LAS0MAP_REG) & local_range & LMAP_MEM_MASK ; priv(dev)->local0_iobase = (priv(dev)->main_phys_iobase & ~local_range) | local_decode; local_range = readl(priv(dev)->plx9080_iobase + PLX_LAS1RNG_REG) & LRNG_MEM_MASK; local_decode = readl(priv(dev)->plx9080_iobase + PLX_LAS1MAP_REG) & local_range & LMAP_MEM_MASK ; priv(dev)->local1_iobase = (priv(dev)->dio_counter_phys_iobase & ~local_range) | local_decode; DEBUG_PRINT(" local 0 io addr 0x%x\n", priv(dev)->local0_iobase); DEBUG_PRINT(" local 1 io addr 0x%x\n", priv(dev)->local1_iobase); priv(dev)->hw_revision = hw_revision( dev, readw(priv(dev)->main_iobase + HW_STATUS_REG ) ); printk(" stc hardware revision %i\n", priv(dev)->hw_revision); init_plx9080(dev); // alocate pci dma buffers for(index = 0; index < DMA_RING_COUNT; index++) { priv(dev)->ai_buffer[index] = pci_alloc_consistent(priv(dev)->hw_dev, DMA_BUFFER_SIZE, &priv(dev)->ai_buffer_phys_addr[index]); } // allocate dma descriptors priv(dev)->dma_desc = pci_alloc_consistent(priv(dev)->hw_dev, sizeof(struct plx_dma_desc) * DMA_RING_COUNT, &priv(dev)->dma_desc_phys_addr); // initialize dma descriptors for(index = 0; index < DMA_RING_COUNT; index++) { priv(dev)->dma_desc[index].pci_start_addr = priv(dev)->ai_buffer_phys_addr[index]; if(board(dev)->layout == LAYOUT_4020) priv(dev)->dma_desc[index].local_start_addr = priv(dev)->local1_iobase + ADC_FIFO_REG; else priv(dev)->dma_desc[index].local_start_addr = priv(dev)->local0_iobase + ADC_FIFO_REG; priv(dev)->dma_desc[index].transfer_size = 0; priv(dev)->dma_desc[index].next = (priv(dev)->dma_desc_phys_addr + ((index + 1) % (DMA_RING_COUNT)) * sizeof(priv(dev)->dma_desc[0])) | PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI; } retval = setup_subdevices(dev); if(retval < 0) { return retval; } init_stc_registers( dev ); return 0;}/* * _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 detach(comedi_device *dev){ unsigned int i; printk("comedi%d: cb_pcidas: remove\n",dev->minor); if(dev->irq) comedi_free_irq(dev->irq, dev); if(priv(dev)) { if( priv(dev)->hw_dev ) { if(priv(dev)->plx9080_iobase) { disable_plx_interrupts( dev ); iounmap((void*)priv(dev)->plx9080_iobase); } if(priv(dev)->main_iobase) iounmap((void*)priv(dev)->main_iobase); if(priv(dev)->dio_counter_iobase) iounmap((void*)priv(dev)->dio_counter_iobase); if(priv(dev)->plx9080_phys_iobase || priv(dev)->main_phys_iobase || priv(dev)->dio_counter_phys_iobase) pci_release_regions( priv(dev)->hw_dev ); // free pci dma buffers for(i = 0; i < DMA_RING_COUNT; i++) { if( priv(dev)->ai_buffer[i] ) pci_free_consistent( priv(dev)->hw_dev, DMA_BUFFER_SIZE, priv(dev)->ai_buffer[i], priv(dev)->ai_buffer_phys_addr[i] ); } // free dma descriptors if(priv(dev)->dma_desc) pci_free_consistent( priv(dev)->hw_dev, sizeof( struct plx_dma_desc ) * DMA_RING_COUNT, priv(dev)->dma_desc, priv(dev)->dma_desc_phys_addr ); pci_disable_device( priv(dev)->hw_dev ); } } if(dev->subdevices) subdev_8255_cleanup(dev,dev->subdevices + 4); return 0;}static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data){ unsigned int bits = 0, n, i; const int timeout = 100; unsigned int channel, range, aref; unsigned long flags; DEBUG_PRINT("chanspec 0x%x\n", insn->chanspec); channel = CR_CHAN(insn->chanspec); range = CR_RANGE(insn->chanspec); aref = CR_AREF(insn->chanspec); // disable card's analog input interrupt sources and pacing // 4020 generates dac done interrupts even though they are disabled disable_ai_pacing( dev ); comedi_spin_lock_irqsave( &dev->spinlock, flags ); if( insn->chanspec & CR_DITHER ) priv(dev)->adc_control1_bits |= ADC_DITHER_BIT; else priv(dev)->adc_control1_bits &= ~ADC_DITHER_BIT; writew( priv(dev)->adc_control1_bits, priv(dev)->main_iobase + ADC_CONTROL1_REG ); comedi_spin_unlock_irqrestore( &dev->spinlock, flags ); if(board(dev)->layout != LAYOUT_4020) { // use internal queue priv(dev)->hw_config_bits &= ~EXT_QUEUE_BIT; writew(priv(dev)->hw_config_bits, priv(dev)->main_iobase + HW_CONFIG_REG); // load internal queue bits = 0; // set gain bits |= ai_range_bits_6xxx( dev, CR_RANGE(insn->chanspec) ); // set single-ended / differential if( ( board(dev)->layout == LAYOUT_64XX && aref != AREF_DIFF ) || ( board(dev)->layout == LAYOUT_60XX && aref == AREF_DIFF ) ) bits |= ADC_SE_DIFF_BIT; if( aref == AREF_COMMON) bits |= ADC_COMMON_BIT; // ALT_SOURCE is internal calibration reference if(insn->chanspec & CR_ALT_SOURCE) { unsigned int cal_en_bit; DEBUG_PRINT("reading calibration source\n"); if( board(dev)->layout == LAYOUT_60XX) cal_en_bit = CAL_EN_60XX_BIT; else cal_en_bit = CAL_EN_64XX_BIT; // select internal reference source to connect to channel 0 writew(cal_en_bit | adc_src_bits( priv(dev)->calibration_source ), priv(dev)->main_iobase + CALIBRATION_REG); } else { // make sure internal calibration source is turned off writew(0, priv(dev)->main_iobase + CALIBRATION_REG); } bits |= adc_chan_bits( channel ); // set start channel, and rest of settings writew(bits, priv(dev)->main_iobase + ADC_QUEUE_LOAD_REG); // set stop channel writew( adc_chan_bits( channel ), priv(dev)->main_iobase + ADC_QUEUE_HIGH_REG ); }else { uint8_t old_cal_range_bits = priv(dev)->i2c_cal_range_bits; priv(dev)->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK; if(insn->chanspec & CR_ALT_SOURCE) { DEBUG_PRINT("reading calibration source\n"); priv(dev)->i2c_cal_range_bits |= adc_src_4020_bits( priv(dev)->calibration_source ); } else { //select BNC inputs priv(dev)->i2c_cal_range_bits |= adc_src_4020_bits( 4 ); } // select range if( ai_range_bits_6xxx( dev, range ) ) priv(dev)->i2c_cal_range_bits |= attenuate_bit( channel ); else priv(dev)->i2c_cal_range_bits &= ~attenuate_bit( channel ); // update calibration/range i2c register only if necessary, as it is very slow if(old_cal_range_bits != priv(dev)->i2c_cal_range_bits) { uint8_t i2c_data = priv(dev)->i2c_cal_range_bits; i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data, sizeof(i2c_data)); } /* 4020 manual asks that sample interval register to be set before writing to convert register. * Using somewhat arbitrary setting of 4 master clock ticks = 0.1 usec */ writew(0, priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG); writew(2, priv(dev)->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG); } for(n = 0; n < insn->n; n++) { // clear adc buffer (inside loop for 4020 sake) writew(0, priv(dev)->main_iobase + ADC_BUFFER_CLEAR_REG); /* trigger conversion, bits sent only matter for 4020 */ writew( adc_convert_chan_4020_bits( CR_CHAN( insn->chanspec ) ), priv(dev)->main_iobase + ADC_CONVERT_REG ); // wait for data for(i = 0; i < timeout; i++) { bits = readw(priv(dev)->main_iobase + HW_STATUS_REG); DEBUG_PRINT(" pipe bits 0x%x\n", pipe_full_bits( bits ) ); if(board(dev)->layout == LAYOUT_4020) { if( readw( priv(dev)->main_iobase + ADC_WRITE_PNTR_REG ) ) break; }else { if( pipe_full_bits( bits ) ) break; } comedi_udelay(1); } DEBUG_PRINT(" looped %i times waiting for data\n", i); if(i == timeout) { comedi_error(dev, " analog input read insn timed out"); rt_printk(" status 0x%x\n", bits); return -ETIME; } if(board(dev)->layout == LAYOUT_4020) data[n] = readl(priv(dev)->dio_counter_iobase + ADC_FIFO_REG) & 0xffff; else data[n] = readw(priv(dev)->main_iobase + PIPE1_READ_REG); } return n;}static int ai_config_calibration_source( comedi_device *dev, lsampl_t *data ){ static const int num_calibration_sources = 8; lsampl_t source = data[1]; if(source >= num_calibration_sources) { printk( "invalid calibration source: %i\n", source ); return -EINVAL; } DEBUG_PRINT("setting calibration source to %i\n", source); priv(dev)->calibration_source = source; return 2;}static int ai_config_block_size( comedi_device *dev, lsampl_t *data ){ int fifo_size; const hw_fifo_info_t *const fifo = board(dev)->ai_fifo; unsigned int block_size, requested_block_size; int retval; static const int bytes_per_sample = 2; requested_block_size = data[ 1 ]; if( requested_block_size ) { fifo_size = requested_block_size * fifo->num_segments / bytes_per_sample; retval = set_ai_fifo_size( dev, fifo_size ); if( retval < 0 ) return retval; } block_size = ai_fifo_size( dev ) / fifo->num_segments * bytes_per_sample; data[ 1 ] = block_size; return 2;}static int ai_config_master_clock_4020( comedi_device *dev, lsampl_t *data ){ unsigned int divisor = data[4]; int retval = 0; if( divisor < 2 ) { divisor = 2; retval = -EAGAIN; } switch( data[1] ) { case COMEDI_EV_SCAN_BEGIN: priv(dev)->ext_clock.scan_divisor = divisor; priv(dev)->ext_clock.chanspec = data[2]; break; default: return -EINVAL; break; } data[4] = divisor; return retval ? retval : 5;}// XXX could add support for 60xx seriesstatic int ai_config_master_clock( comedi_device *dev, lsampl_t *data ){ switch( board(dev)->layout ) { case LAYOUT_4020: return ai_config_master_clock_4020( dev, data ); break; default: return -EINVAL; break; } return -EINVAL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -