📄 cb_pcidas64.c
字号:
ai_se_chans: 64, ai_bits: 14, ai_speed: 1000, ao_nchan: 2, ao_scan_speed: 10000, layout: LAYOUT_64XX, ai_range_table: &ai_ranges_64xx, ai_fifo: ai_fifo_64xx, has_8255 : 1, }, { name: "pci-das64/m2/14", device_id: 0, // XXX ai_se_chans: 64, ai_bits: 14, ai_speed: 500, ao_nchan: 2, ao_scan_speed: 10000, layout: LAYOUT_64XX, ai_range_table: &ai_ranges_64xx, ai_fifo: ai_fifo_64xx, has_8255 : 1, }, { name: "pci-das64/m3/14", device_id: 0, // XXX ai_se_chans: 64, ai_bits: 14, ai_speed: 333, ao_nchan: 2, ao_scan_speed: 10000, layout: LAYOUT_64XX, ai_range_table: &ai_ranges_64xx, ai_fifo: ai_fifo_64xx, has_8255 : 1, },#endif};// Number of boards in cb_pcidas_boardsstatic inline unsigned int num_boards( void ){ return sizeof( pcidas64_boards ) / sizeof( pcidas64_board );}static struct pci_device_id pcidas64_pci_table[] __devinitdata = { { PCI_VENDOR_ID_COMPUTERBOARDS, 0x001d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x001e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0035, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0036, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0037, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x005d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x005e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0063, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0064, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_COMPUTERBOARDS, 0x0052, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0 }};MODULE_DEVICE_TABLE(pci, pcidas64_pci_table);/* * Useful for shorthand access to the particular board structure */static inline pcidas64_board* board( const comedi_device *dev ){ return (pcidas64_board *)dev->board_ptr;}struct ext_clock_info{ unsigned int convert_divisor; // master clock divisor to use for conversions with external master clock unsigned int scan_divisor; // master clock divisor to use for scans with external master clock unsigned int chanspec; // chanspec for master clock input};/* this structure is for data unique to this hardware driver. */typedef struct{ struct pci_dev *hw_dev; // pointer to board's pci_dev struct // base addresses (physical) unsigned long plx9080_phys_iobase; unsigned long main_phys_iobase; unsigned long dio_counter_phys_iobase; // base addresses (ioremapped) unsigned long plx9080_iobase; unsigned long main_iobase; unsigned long dio_counter_iobase; // local address (used by dma controller) uint32_t local0_iobase; uint32_t local1_iobase; volatile unsigned int ai_count; // number of analog input samples remaining uint16_t *ai_buffer[DMA_RING_COUNT]; // dma buffers for analog input dma_addr_t ai_buffer_phys_addr[DMA_RING_COUNT]; // physical addresses of ai dma buffers struct plx_dma_desc *dma_desc; // array of dma descriptors read by plx9080, allocated to get proper alignment dma_addr_t dma_desc_phys_addr; // physical address of dma descriptor array volatile unsigned int dma_index; // index of the dma descriptor/buffer that is currently being used volatile unsigned int ao_count; // number of analog output samples remaining volatile unsigned int ao_value[2]; // remember what the analog outputs are set to, to allow readback unsigned int hw_revision; // stc chip hardware revision number volatile unsigned int intr_enable_bits; // last bits sent to INTR_ENABLE_REG register volatile uint16_t adc_control1_bits; // last bits sent to ADC_CONTROL1_REG register volatile uint16_t fifo_size_bits; // last bits sent to FIFO_SIZE_REG register volatile uint16_t hw_config_bits; // last bits sent to HW_CONFIG_REG register volatile uint32_t plx_control_bits; // last bits written to plx9080 control register volatile uint32_t plx_intcsr_bits; // last bits written to plx interrupt control and status register volatile int calibration_source; // index of calibration source readable through ai ch0 volatile uint8_t i2c_cal_range_bits; // bits written to i2c calibration/range register volatile unsigned int ext_trig_falling; // configure digital triggers to trigger on falling edge // states of various devices stored to enable read-back unsigned int ad8402_state[2]; unsigned int caldac_state[8]; volatile unsigned ai_cmd_running : 1; unsigned int ai_fifo_segment_length; struct ext_clock_info ext_clock;} pcidas64_private;/* inline function that makes it easier to * access the private structure. */static inline pcidas64_private* priv(comedi_device *dev){ return dev->private;}/* * The comedi_driver structure tells the Comedi core module * which functions to call to configure/deconfigure (attach/detach) * the board, and also about the kernel module that contains * the device code. */static int attach(comedi_device *dev,comedi_devconfig *it);static int detach(comedi_device *dev);static comedi_driver driver_cb_pcidas={ driver_name: "cb_pcidas64", module: THIS_MODULE, attach: attach, detach: detach,};static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int ai_config_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);static int ai_cmd(comedi_device *dev,comedi_subdevice *s);static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);//static int ao_cmd(comedi_device *dev,comedi_subdevice *s);//static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);//static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);static void handle_interrupt(int irq, void *d, struct pt_regs *regs);static int ai_cancel(comedi_device *dev, comedi_subdevice *s);//static int ao_cancel(comedi_device *dev, comedi_subdevice *s);static int dio_callback(int dir, int port, int data, unsigned long arg);static int dio_callback_4020(int dir, int port, int data, unsigned long arg);static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int dio_60xx_config_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int dio_60xx_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int calib_read_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int calib_write_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int ad8402_read_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static void ad8402_write( comedi_device *dev, unsigned int channel, unsigned int value );static int ad8402_write_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static int eeprom_read_insn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);static void check_adc_timing(comedi_cmd *cmd);static unsigned int get_divisor(unsigned int ns, unsigned int flags);static void i2c_write(comedi_device *dev, unsigned int address, const uint8_t *data, unsigned int length);static void caldac_write( comedi_device *dev, unsigned int channel, unsigned int value );static int caldac_8800_write(comedi_device *dev, unsigned int address, uint8_t value);//static int dac_1590_write(comedi_device *dev, unsigned int dac_a, unsigned int dac_b);static int caldac_i2c_write(comedi_device *dev, unsigned int caldac_channel, unsigned int value);static void abort_dma(comedi_device *dev, unsigned int channel);static void disable_plx_interrupts( comedi_device *dev );static int set_ai_fifo_size( comedi_device *dev, unsigned int num_samples );static unsigned int ai_fifo_size( comedi_device *dev );static int set_ai_fifo_segment_length( comedi_device *dev, unsigned int num_entries );static void disable_ai_pacing( comedi_device *dev );static void disable_ai_interrupts( comedi_device *dev );static void enable_ai_interrupts( comedi_device *dev, const comedi_cmd *cmd );COMEDI_INITCLEANUP(driver_cb_pcidas);static unsigned int ai_range_bits_6xxx( const comedi_device *dev, unsigned int range_index ){ comedi_krange *range = &board( dev )->ai_range_table->range[ range_index ]; unsigned int bits = 0; switch( range->max ) { case 10000000: bits = 0x000; break; case 5000000: bits = 0x100; break; case 2000000: case 2500000: bits = 0x200; break; case 1000000: case 1250000: bits = 0x300; break; case 500000: bits = 0x400; break; case 200000: case 250000: bits = 0x500; break; case 100000: bits = 0x600; break; case 50000: bits = 0x700; break; default: comedi_error( dev, "bug! in ai_range_bits_6xxx" ); break; } if( range->min == 0 ) bits += 0x900; return bits;}static inline uint16_t dac_range_bits( const comedi_device *dev, unsigned int channel, unsigned int range ){ unsigned int code = board(dev)->ao_range_code[ range ]; return ( ( code ) & 0x3 ) << ( 2 * ( ( channel ) & 0x1 ) );};static inline unsigned int hw_revision( const comedi_device *dev, uint16_t hw_status_bits ){ if( board(dev)->layout == LAYOUT_4020) return ( hw_status_bits >> 13 ) & 0x7; return ( hw_status_bits >> 12) & 0xf;}// initialize plx9080 chipstatic void init_plx9080(comedi_device *dev){ uint32_t bits; unsigned long plx_iobase = priv(dev)->plx9080_iobase; priv(dev)->plx_control_bits = readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG); // plx9080 dump DEBUG_PRINT(" plx interrupt status 0x%x\n", readl(plx_iobase + PLX_INTRCS_REG)); DEBUG_PRINT(" plx id bits 0x%x\n", readl(plx_iobase + PLX_ID_REG)); DEBUG_PRINT(" plx control reg 0x%x\n", priv(dev)->plx_control_bits); DEBUG_PRINT(" plx revision 0x%x\n", readl(plx_iobase + PLX_REVISION_REG)); DEBUG_PRINT(" plx dma channel 0 mode 0x%x\n", readl(plx_iobase + PLX_DMA0_MODE_REG)); DEBUG_PRINT(" plx dma channel 1 mode 0x%x\n", readl(plx_iobase + PLX_DMA1_MODE_REG)); DEBUG_PRINT(" plx dma channel 0 pci address 0x%x\n", readl(plx_iobase + PLX_DMA0_PCI_ADDRESS_REG)); DEBUG_PRINT(" plx dma channel 0 local address 0x%x\n", readl(plx_iobase + PLX_DMA0_LOCAL_ADDRESS_REG)); DEBUG_PRINT(" plx dma channel 0 transfer size 0x%x\n", readl(plx_iobase + PLX_DMA0_TRANSFER_SIZE_REG)); DEBUG_PRINT(" plx dma channel 0 descriptor 0x%x\n", readl(plx_iobase + PLX_DMA0_DESCRIPTOR_REG)); DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n", readb(plx_iobase + PLX_DMA0_CS_REG)); DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n", readl(plx_iobase + PLX_DMA0_THRESHOLD_REG)); disable_plx_interrupts( dev ); abort_dma(dev, 0); abort_dma(dev, 1); // configure dma0 mode bits = 0; // enable ready input, not sure if this is necessary bits |= PLX_DMA_EN_READYIN_BIT; // enable dma chaining bits |= PLX_EN_CHAIN_BIT; // enable interrupt on dma done (probably don't need this, since chain never finishes) bits |= PLX_EN_DMA_DONE_INTR_BIT; // don't increment local address during transfers (we are transferring from a fixed fifo register) bits |= PLX_LOCAL_ADDR_CONST_BIT; // route dma interrupt to pci bus bits |= PLX_DMA_INTR_PCI_BIT; // enable demand mode bits |= PLX_DEMAND_MODE_BIT; // enable local burst mode bits |= PLX_DMA_LOCAL_BURST_EN_BIT; // 4020 uses 32 bit dma if(board(dev)->layout == LAYOUT_4020) { bits |= PLX_LOCAL_BUS_32_WIDE_BITS; }else { // localspace0 bus is 16 bits wide bits |= PLX_LOCAL_BUS_16_WIDE_BITS; } writel(bits, plx_iobase + PLX_DMA1_MODE_REG);}/* Allocate and initialize the subdevice structures. */static int setup_subdevices(comedi_device *dev){ comedi_subdevice *s; unsigned long dio_8255_iobase; int i; if( alloc_subdevices( dev, 10 ) < 0 ) return -ENOMEM; s = dev->subdevices + 0; /* analog input subdevice */ dev->read_subdev = s; s->type = COMEDI_SUBD_AI; s->subdev_flags = SDF_READABLE | SDF_GROUND; if(board(dev)->layout == LAYOUT_60XX) s->subdev_flags |= SDF_COMMON | SDF_DIFF; else if(board(dev)->layout == LAYOUT_64XX) s->subdev_flags |= SDF_DIFF; /* XXX Number of inputs in differential mode is ignored */ s->n_chan = board(dev)->ai_se_chans; s->len_chanlist = 0x2000; s->maxdata = (1 << board(dev)->ai_bits) - 1; s->range_table = board(dev)->ai_range_table; s->insn_read = ai_rinsn; s->insn_config = ai_config_insn; s->do_cmd = ai_cmd; s->do_cmdtest = ai_cmdtest; s->cancel = ai_cancel; if(board(dev)->layout == LAYOUT_4020) { unsigned int i; uint8_t data; // set adc to read from inputs (not internal calibration sources) priv(dev)->i2c_cal_range_bits = adc_src_4020_bits( 4 ); // set channels to +-5 volt input ranges for( i = 0; i < s->n_chan; i++) priv(dev)->i2c_cal_range_bits |= attenuate_bit( i ); data = priv(dev)->i2c_cal_range_bits; i2c_write(dev, RANGE_CAL_I2C_ADDR, &data, sizeof(data)); } /* analog output subdevice */ s = dev->subdevices + 1; if(board(dev)->ao_nchan) { // dev->write_subdev = s; s->type = COMEDI_SUBD_AO; s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND; s->n_chan = board(dev)->ao_nchan; // analog out resolution is the same as analog input resolution, so use ai_bits s->maxdata = (1 << board(dev)->ai_bits) - 1; s->range_table = board(dev)->ao_range_table; s->insn_read = ao_readback_insn; s->insn_write = ao_winsn;//XXX 4020 can't do paced analog output // s->do_cmdtest = ao_cmdtest; // s->do_cmd = ao_cmd; // s->len_chanlist = board(dev)->ao_nchan; // s->cancel = ao_cancel; } else { s->type = COMEDI_SUBD_UNUSED; } // digital input s = dev->subdevices + 2; if(board(dev)->layout == LAYOUT_64XX) { s->type = COMEDI_SUBD_DI; s->subdev_flags = SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = di_rbits; } else s->type = COMEDI_SUBD_UNUSED; // digital output if(board(dev)->layout == LAYOUT_64XX) { s = dev->subdevices + 3; s->type = COMEDI_SUBD_DO; s->subdev_flags = SDF_WRITABLE | SDF_READABLE; s->n_chan = 4; s->maxdata = 1; s->range_table = &range_digital; s->insn_bits = do_wbits; } else s->type = COMEDI_SUBD_UNUSED; /* 8255 */ s = dev->subdevices + 4; if( board(dev)->has_8255 ) { if(board(dev)->layout == LAYOUT_4020) { dio_8255_iobase = priv(dev)->main_iobase + I8255_4020_REG; subdev_8255_init(dev, s, dio_callback_4020, dio_8255_iobase); } else { dio_8255_iobase = priv(dev)->dio_counter_iobase + DIO_8255_OFFSET; subdev_8255_init(dev, s, dio_callback, dio_8255_iobase); } }else s->type = COMEDI_SUBD_UNUSED; // 8 channel dio for 60xx s = dev->subdevices + 5; if(board(dev)->layout == LAYOUT_60XX) { s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_WRITABLE | SDF_READABLE; s->n_chan = 8; s->maxdata = 1; s->range_table = &range_digital; s->insn_config = dio_60xx_config_insn; s->insn_bits = dio_60xx_wbits; } else s->type = COMEDI_SUBD_UNUSED; // caldac s = dev->subdevices + 6; s->type=COMEDI_SUBD_CALIB; s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; s->n_chan = 8; if(board(dev)->layout == LAYOUT_4020) s->maxdata = 0xfff; else s->maxdata = 0xff; s->insn_read = calib_read_insn; s->insn_write = calib_write_insn; for( i = 0; i < s->n_chan; i++ ) caldac_write( dev, i, s->maxdata / 2 ); // 2 channel ad8402 potentiometer s = dev->subdevices + 7; if(board(dev)->layout == LAYOUT_64XX) { s->type = COMEDI_SUBD_CALIB; s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; s->n_chan = 2; s->insn_read = ad8402_read_insn; s->insn_write = ad8402_write_insn; s->maxdata = 0xff; for( i = 0; i < s->n_chan; i++ ) ad8402_write( dev, i, s->maxdata / 2 ); } else s->type = COMEDI_SUBD_UNUSED; //serial EEPROM, if present s = dev->subdevices + 8; if(priv(dev)->plx_control_bits & CTL_EECHK) { s->type = COMEDI_SUBD_MEMORY; s->subdev_flags = SDF_READABLE | SDF_INTERNAL; s->n_chan = 128; s->maxdata = 0xffff; s->insn_read = eeprom_read_insn; } else s->type = COMEDI_SUBD_UNUSED; // user counter subd XXX s = dev->subdevices + 9; s->type = COMEDI_SUBD_UNUSED; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -