#define EN_DAC_UNDERRUN_BIT 0x4000 // enable dac underrun status bit
#define EN_ADC_OVERRUN_BIT 0x8000 // enable adc overrun status bit
#define HW_CONFIG_REG 0x2 // hardware config register
-#define HW_CONFIG_DUMMY_BITS 0x2000 // bit with unknown function yet given as default value in pci-das64 manual
-#define SLOW_DAC_BIT 0x400 // use 225 nanosec strobe when loading dac instead of 50 nanosec
+#define MASTER_CLOCK_4020_MASK 0x3 // bits that specify master clock source for 4020
+#define INTERNAL_CLOCK_4020_BITS 0x1 // user 40 MHz internal master clock for 4020
#define EXT_QUEUE 0x200 // use external channel/gain queue (more versatile than internal queue)
+#define SLOW_DAC_BIT 0x400 // use 225 nanosec strobe when loading dac instead of 50 nanosec
+#define HW_CONFIG_DUMMY_BITS 0x2000 // bit with unknown function yet given as default value in pci-das64 manual
#define FIFO_SIZE_REG 0x4 // allows adjustment of fifo sizes, we will always use maximum
#define ADC_FIFO_SIZE_MASK 0x7f // bits that set adc fifo size
-#define ADC_FIFO_4K_BITS 0x7c // 4 kilosample adc fifo
-#define ADC_FIFO_8K_BITS 0x78 // 8 kilosample adc fifo (pci-das64 manual says 0x38!)
+#define ADC_FIFO_60XX_BITS 0x78 // 8 kilosample adc fifo for 60xx boards
+#define ADC_FIFO_64XX_BITS 0x38 // 8 kilosample adc fifo for 64xx boards
+#define ADC_FIFO_4020_BITS 0x0 // dual 64 kilosample adc fifo for 4020
#define DAC_FIFO_SIZE_MASK 0xff00 // bits that set dac fifo size
-#define DAC_FIFO_16K_BITS 0xf000
+#define DAC_FIFO_BITS 0xf000
#define DAQ_SYNC_REG 0xc
#define ADC_CONTROL0_REG 0x10 // adc control register 0
#define ADC_GATE_SRC_MASK 0x3 // bits that select gate
#define CONVERT_POLARITY_BIT 0x10
#define EOC_POLARITY_BIT 0x20
#define SW_GATE_BIT 0x40 // disables software gate of adc
-#define RETRIGGER_BIT 0x800
+#define RETRIGGER_BIT 0x800
+#define LO_CHANNEL_4020_BITS(x) (((x) & 0x3) << 8) // low channel for 4020 two channel mode
+#define HI_CHANNEL_4020_BITS(x) (((x) & 0x3) << 10) // high channel for 4020 two channel mode
+#define TWO_CHANNEL_4020_BITS 0x1000 // two channel mode for 4020
+#define FOUR_CHANNEL_4020_BITS 0x2000 // four channel mode for 4020
#define ADC_MODE_BITS(x) (((x) & 0xf) << 12)
#define CALIBRATION_REG 0x14
#define CAL_EN_BIT 0x200 // calibration enable XXX 0x40 for 6402?
#define ADC_COUNT_UPPER_REG 0x20 // upper 8 bits of hardware conversion/scan counter
#define ADC_START_REG 0x22 // software trigger to start aquisition
#define ADC_CONVERT_REG 0x24 // initiates single conversion
+#define ADC_CONVERT_CHANNEL_4020_BITS(x) (((x) & 0x3) << 8)
#define ADC_QUEUE_CLEAR_REG 0x26 // clears adc queue
#define ADC_QUEUE_LOAD_REG 0x28 // loads adc queue
#define CHAN_BITS(x) ((x) & 0x3f)
-#define GAIN_BITS(x) (((x) & 0x3) << 8) // translates range index to gain bits
-#define UNIP_BIT(x) (((x) & 0x4) << 11) // translates range index to unipolar/bipolar bit
+#define UNIP_BIT 0x800 // unipolar/bipolar bit
#define ADC_DIFFERENTIAL_BIT 0x1000 // single-ended/ differential bit
#define ADC_COMMON_BIT 0x2000 // non-referenced single-ended (common-mode input)
#define QUEUE_EOSEQ_BIT 0x4000 // queue end of sequence
#define HW_REVISION(x) (((x) >> 12) & 0xf)
#define PIPE1_READ_REG 0x4
#define ADC_READ_PNTR_REG 0x8
+#define LOWER_XFER_REG 0x10
#define ADC_WRITE_PNTR_REG 0xc
#define PREPOST_REG 0x14
#define ADC_UPP_READ_PNTR_CODE(x) (((x) >> 12) & 0x3)
#define ADC_UPP_WRITE_PNTR_CODE(x) (((x) >> 14) & 0x3)
// read-write
+#define I8255_4020_REG 0x48 // 8255 offset, for 4020 only
#define ADC_QUEUE_FIFO_REG 0x100 // external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG
#define ADC_FIFO_REG 0x200 // adc data fifo
#define DO_REG 0x20
#define DI_REG 0x28
-// analog input ranges for most boards
-static comedi_lrange ai_ranges =
+// I2C addresses for 4020
+#define RANGE_CAL_I2C_ADDR 0x40
+#define CALDAC0_I2C_ADDR 0x18
+#define CALDAC1_I2C_ADDR 0x1a
+// XXX fix ranges for 60xx
+
+// analog input ranges for 64xx boards
+static comedi_lrange ai_ranges_64xx =
{
8,
{
UNI_RANGE(1.25)
}
};
+static int ai_range_bits_64xx[] = {
+ 0x000,
+ 0x100,
+ 0x200,
+ 0x300,
+ 0x800,
+ 0x900,
+ 0xa00,
+ 0xb00,
+};
+
+// analog input ranges for 60xx boards
+static comedi_lrange ai_ranges_60xx =
+{
+ 4,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ }
+};
+static int ai_range_bits_60xx[] = {
+ 0x000,
+ 0x100,
+ 0x600,
+ 0x700,
+};
+
+// analog input ranges for 4020 board
+static comedi_lrange ai_ranges_4020 =
+{
+ 2,
+ {
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ }
+};
// analog output ranges
static comedi_lrange ao_ranges =
}
};
+enum register_layout
+{
+ LAYOUT_60XX,
+ LAYOUT_64XX,
+ LAYOUT_4020,
+};
+
typedef struct pcidas64_board_struct
{
char *name;
int ai_speed; // fastest conversion period in ns
int ao_nchan; // number of analog out channels
int ao_scan_speed; // analog output speed (for a scan, not conversion)
+ int fifo_depth; // number of entries in fifo (may be 32 bit or 16 bit entries)
+ enum register_layout layout; // different board families have slightly different registers
+ comedi_lrange *ai_range_table;
+ int *ai_range_bits;
} pcidas64_board;
static pcidas64_board pcidas64_boards[] =
ai_speed: 5000,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das6402/12", // XXX check
ai_speed: 5000,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m1/16",
ai_speed: 1000,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m2/16",
ai_speed: 500,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m3/16",
ai_speed: 333,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das6025e",
ai_speed: 5000,
ao_nchan: 2,
ao_scan_speed: 100000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_60XX,
+ ai_range_table: &ai_ranges_60xx,
+ ai_range_bits: ai_range_bits_60xx,
},
{
name: "pci-das6034e",
ai_speed: 5000,
ao_nchan: 0,
ao_scan_speed: 0,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_60XX,
+ ai_range_table: &ai_ranges_60xx,
+ ai_range_bits: ai_range_bits_60xx,
},
{
name: "pci-das6035e",
ai_speed: 5000,
ao_nchan: 2,
ao_scan_speed: 100000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_60XX,
+ ai_range_table: &ai_ranges_60xx,
+ ai_range_bits: ai_range_bits_60xx,
},
{
name: "pci-das4020/12",
ai_speed: 50,
ao_nchan: 2,
ao_scan_speed: 0, // no hardware pacing on ao
+ fifo_depth: 0x8000, // 32K 32bit entries = 64K samples
+ layout: LAYOUT_4020,
+ ai_range_table: &ai_ranges_4020,
+ ai_range_bits: NULL,
},
#if 0
{
ai_speed: 5000,
ao_nchan: 0,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m1/16/jr",
ai_speed: 1000,
ao_nchan: 0,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m2/16/jr",
ai_speed: 500,
ao_nchan: 0,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m3/16/jr",
ai_speed: 333,
ao_nchan: 0,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m1/14",
ai_speed: 1000,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m2/14",
ai_speed: 500,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
{
name: "pci-das64/m3/14",
ai_speed: 333,
ao_nchan: 2,
ao_scan_speed: 10000,
+ fifo_depth: 0x2000,
+ layout: LAYOUT_64XX,
+ ai_range_table: &ai_ranges_64xx,
+ ai_range_bits: ai_range_bits_64xx,
},
#endif
unsigned long main_iobase;
unsigned long dio_counter_iobase;
// local address (used by dma controller)
- u32 local_main_iobase;
+ u32 local0_iobase;
+ u32 local1_iobase;
volatile unsigned int ai_count; // number of analog input samples remaining
u16 *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
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 void check_adc_timing(comedi_cmd *cmd);
static unsigned int get_divisor(unsigned int ns, unsigned int flags);
-static int grey2int(int code);
/*
* A convenient macro that defines init_module() and cleanup_module(),
unsigned long dio_counter_iobase;
u32 local_range, local_decode;
unsigned int bits;
+ unsigned long dio_8255_iobase;
printk("comedi%d: cb_pcidas64\n",dev->minor);
devpriv->main_iobase = (unsigned long)ioremap(main_iobase, pci_resource_len(pcidev, PLX9080_BADRINDEX));
devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, pci_resource_len(pcidev, PLX9080_BADRINDEX));
- // figure out what local address is for main_iobase
+ // figure out what local addresses are
local_range = readl(devpriv->plx9080_iobase + PLX_LAS0RNG_REG) & LRNG_MEM_MASK;
local_decode = readl(devpriv->plx9080_iobase + PLX_LAS0MAP_REG) & local_range & LMAP_MEM_MASK ;
- devpriv->local_main_iobase = (devpriv->main_phys_iobase & ~local_range) | local_decode;
+ devpriv->local0_iobase = (devpriv->main_phys_iobase & ~local_range) | local_decode;
+ local_range = readl(devpriv->plx9080_iobase + PLX_LAS1RNG_REG) & LRNG_MEM_MASK;
+ local_decode = readl(devpriv->plx9080_iobase + PLX_LAS1MAP_REG) & local_range & LMAP_MEM_MASK ;
+ devpriv->local1_iobase = (devpriv->dio_counter_phys_iobase & ~local_range) | local_decode;
devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));
+ if( thisboard->layout == LAYOUT_4020)
+ devpriv->hw_revision >>= 1;
// disable interrupts on plx 9080
writel(0, devpriv->plx9080_iobase + PLX_INTRCS_REG);
printk(" main remapped to 0x%lx\n", devpriv->main_iobase);
printk(" diocounter remapped to 0x%lx\n", devpriv->dio_counter_iobase);
- DEBUG_PRINT(" local main io addr 0x%x\n", devpriv->local_main_iobase);
+ DEBUG_PRINT(" local 0 io addr 0x%x\n", devpriv->local0_iobase);
+ DEBUG_PRINT(" local 1 io addr 0x%x\n", devpriv->local1_iobase);
printk(" stc hardware revision %i\n", devpriv->hw_revision);
DEBUG_PRINT(" plx dma channel 0 command status 0x%x\n", readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG));
DEBUG_PRINT(" plx dma channel 0 threshold 0x%x\n", readl(devpriv->plx9080_iobase + PLX_DMA0_THRESHOLD_REG));
+ DEBUG_PRINT(" plx queue control/status 0x%x\n", readl(devpriv->plx9080_iobase + PLX_QUEUE_SC_REG));
+ DEBUG_PRINT(" queue configuration 0x%x\n", readl(devpriv->plx9080_iobase + PLX_QUEUE_CONFIG_REG));
+
// alocate pci dma buffers
for(index = 0; index < DMA_RING_COUNT; index++)
{
for(index = 0; index < DMA_RING_COUNT; index++)
{
devpriv->dma_desc[index].pci_start_addr = devpriv->ai_buffer_phys_addr[index];
- devpriv->dma_desc[index].local_start_addr = devpriv->local_main_iobase + ADC_FIFO_REG;
+ if(thisboard->layout == LAYOUT_4020)
+ devpriv->dma_desc[index].local_start_addr = devpriv->local1_iobase;
+ else
+ devpriv->dma_desc[index].local_start_addr = devpriv->local0_iobase + ADC_FIFO_REG;
devpriv->dma_desc[index].transfer_size = DMA_TRANSFER_SIZE;
devpriv->dma_desc[index].next = (devpriv->dma_desc_phys_addr + ((index + 1) % (DMA_RING_COUNT)) * sizeof(devpriv->dma_desc[0])) |
PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;
s->n_chan = thisboard->ai_se_chans;
s->len_chanlist = 0x2000;
s->maxdata = (1 << thisboard->ai_bits) - 1;
- s->range_table = &ai_ranges;
+ s->range_table = thisboard->ai_range_table;
s->insn_read = ai_rinsn;
s->do_cmd = ai_cmd;
s->do_cmdtest = ai_cmdtest;
s->type = COMEDI_SUBD_UNUSED;
}
+ // XXX 60xxx has dio
// digital input
s = dev->subdevices + 2;
s->type=COMEDI_SUBD_DI;
/* 8255 */
s = dev->subdevices + 4;
- subdev_8255_init(dev, s, dio_callback,
- (unsigned long) (devpriv->dio_counter_iobase + DIO_8255_OFFSET));
+ if(thisboard->layout == LAYOUT_4020)
+ {
+ dio_8255_iobase = devpriv->main_iobase + I8255_4020_REG;
+ subdev_8255_init(dev, s, dio_callback_4020, dio_8255_iobase);
+ } else
+ {
+ dio_8255_iobase = devpriv->dio_counter_iobase + DIO_8255_OFFSET;
+ subdev_8255_init(dev, s, dio_callback, dio_8255_iobase);
+ }
// user counter subd XXX
s = dev->subdevices + 5;
writew(0, devpriv->main_iobase + CALIBRATION_REG);
// do some initialization of plx9080 dma registers
- /* XXX there are some other bits that can be set here that might
- * improve performance, but I just want to get it working */
bits = 0;
- // localspace0 bus is 16 bits wide XXX 4020 uses 32 bit dma
- bits |= PLX_LOCAL_BUS_16_WIDE_BITS;
// enable ready input, not sure if this is necessary
bits |= PLX_DMA_EN_READYIN_BIT;
// enable BTERM# input, not sure if this is necessary
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(thisboard->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, devpriv->plx9080_iobase + PLX_DMA0_MODE_REG);
+ // set fifos to maximum size
+ devpriv->fifo_size_bits = DAC_FIFO_BITS;
+ switch(thisboard->layout)
+ {
+ case LAYOUT_64XX:
+ devpriv->fifo_size_bits |= ADC_FIFO_64XX_BITS;
+ break;
+ case LAYOUT_60XX:
+ devpriv->fifo_size_bits |= ADC_FIFO_60XX_BITS;
+ break;
+ case LAYOUT_4020:
+ devpriv->fifo_size_bits |= ADC_FIFO_4020_BITS;
+ break;
+ default:
+ printk(" bug! unknown register layout\n");
+ return -1;
+ break;
+ }
+ writew(devpriv->fifo_size_bits, devpriv->main_iobase + FIFO_SIZE_REG);
+
return 0;
}
devpriv->adc_control1_bits &= ADC_QUEUE_CONFIG_BIT;
writew(devpriv->adc_control1_bits, devpriv->main_iobase + ADC_CONTROL1_REG);
- // use internal queue
- writew(SLOW_DAC_BIT, devpriv->main_iobase + HW_CONFIG_REG);
+ if(thisboard->layout != LAYOUT_4020)
+ {
+ // use internal queue
+ writew(SLOW_DAC_BIT, devpriv->main_iobase + HW_CONFIG_REG);
- // load internal queue
- bits = 0;
- // set channel
- bits |= CHAN_BITS(CR_CHAN(insn->chanspec));
- // set gain
- bits |= GAIN_BITS(CR_RANGE(insn->chanspec));
- // set unipolar / bipolar
- bits |= UNIP_BIT(CR_RANGE(insn->chanspec));
- // set single-ended / differential
- if(CR_AREF(insn->chanspec) == AREF_DIFF)
- bits |= ADC_DIFFERENTIAL_BIT;
- if(CR_AREF(insn->chanspec) == AREF_COMMON)
- bits |= ADC_COMMON_BIT;
- // set stop channel
- writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
- // set start channel, and rest of settings
- writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ // load internal queue
+ bits = 0;
+ // set channel
+ bits |= CHAN_BITS(CR_CHAN(insn->chanspec));
+ // set gain
+ bits |= thisboard->ai_range_bits[CR_RANGE(insn->chanspec)];
+ // set single-ended / differential
+ if(CR_AREF(insn->chanspec) == AREF_DIFF)
+ bits |= ADC_DIFFERENTIAL_BIT;
+ if(CR_AREF(insn->chanspec) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ // set stop channel
+ writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
+ // set start channel, and rest of settings
+ writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ }
// calibration testing
//writew(CAL_EN_BIT | CAL_SRC_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + CALIBRATION_REG);
for(n = 0; n < insn->n; n++)
{
- /* trigger conversion */
- writew(~0, devpriv->main_iobase + ADC_CONVERT_REG);
+ /* trigger conversion, bits sent only matter for 4020 */
+ writew(ADC_CONVERT_CHANNEL_4020_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_CONVERT_REG);
// wait for data
for(i = 0; i < timeout; i++)
if(i == timeout)
{
comedi_error(dev, " analog input read insn timed out");
-printk("status 0x%x\n", bits);
+ printk(" status 0x%x\n", bits);
return -ETIME;
}
- data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
+ if(thisboard->layout == LAYOUT_4020)
+ data[n] = readl(devpriv->dio_counter_iobase) & 0xffff;
+ else
+ data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
}
return n;
unsigned int tmp_arg, tmp_arg2;
int i;
int aref;
+ unsigned int triggers;
/* step 1: make sure trigger sources are trivially valid */
if(!cmd->start_src || tmp != cmd->start_src) err++;
tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
+ triggers = TRIG_FOLLOW;
+ if(thisboard->layout != LAYOUT_4020)
+ triggers |= TRIG_TIMER;
+ cmd->scan_begin_src &= triggers;
if(!cmd->scan_begin_src || tmp != cmd->scan_begin_src) err++;
tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ triggers = TRIG_TIMER;
+ if(thisboard->layout != LAYOUT_4020)
+ triggers |= TRIG_EXT;
+ cmd->convert_src &= triggers;
if(!cmd->convert_src || tmp != cmd->convert_src) err++;
tmp = cmd->scan_end_src;
unsigned int scan_counter_value;
unsigned int i;
-writew(0, devpriv->main_iobase + CALIBRATION_REG);
-
-// disable card's interrupt sources
// disable card's analog input interrupt sources
devpriv->intr_enable_bits &= ~EN_ADC_INTR_SRC_BIT & ~EN_ADC_DONE_INTR_BIT &
~EN_ADC_ACTIVE_INTR_BIT & ~EN_ADC_STOP_INTR_BIT & ~EN_ADC_OVERRUN_BIT &
devpriv->adc_control1_bits &= ADC_QUEUE_CONFIG_BIT;
writew(devpriv->adc_control1_bits, devpriv->main_iobase + ADC_CONTROL1_REG);
+ // set hardware configuration register
+ bits = 0;
+ if(thisboard->layout == LAYOUT_4020)
+ bits |= INTERNAL_CLOCK_4020_BITS;
// use external queue
- writew(EXT_QUEUE | SLOW_DAC_BIT, devpriv->main_iobase + HW_CONFIG_REG);
-
- // set fifo size
- devpriv->fifo_size_bits &= ADC_FIFO_SIZE_MASK;
- devpriv->fifo_size_bits |= ADC_FIFO_8K_BITS;
- writew(devpriv->fifo_size_bits, devpriv->main_iobase + FIFO_SIZE_REG);
+ else bits |= EXT_QUEUE | SLOW_DAC_BIT;
+ writew(bits, devpriv->main_iobase + HW_CONFIG_REG);
// set conversion pacing
if(cmd->convert_src == TRIG_TIMER)
{
check_adc_timing(cmd);
- // supposed to load counter with desired divisor minus 3
- convert_counter_value = cmd->convert_arg / TIMER_BASE - 3;
+
+ // supposed to load counter with desired divisor minus 2 or 3 depending on board
+ if(thisboard->layout == LAYOUT_4020)
+ convert_counter_value = cmd->convert_arg / TIMER_BASE - 2;
+ else
+ convert_counter_value = cmd->convert_arg / TIMER_BASE - 3;
// load lower 16 bits
writew(convert_counter_value & 0xffff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
DEBUG_PRINT("convert counter 0x%x\n", convert_counter_value);
// load upper 8 bits
writew((convert_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+
// set scan pacing
- scan_counter_value = 0;
- if(cmd->scan_begin_src == TRIG_TIMER)
- {
- // figure out how long we need to delay at end of scan
- scan_counter_value = (cmd->scan_begin_arg - (cmd->convert_arg * (cmd->chanlist_len - 1)))
- / TIMER_BASE;
- }else if(cmd->scan_begin_src == TRIG_FOLLOW)
+ if(thisboard->layout != LAYOUT_4020)
{
- scan_counter_value = cmd->convert_arg / TIMER_BASE;
+ scan_counter_value = 0;
+ if(cmd->scan_begin_src == TRIG_TIMER)
+ {
+ // figure out how long we need to delay at end of scan
+ scan_counter_value = (cmd->scan_begin_arg - (cmd->convert_arg * (cmd->chanlist_len - 1)))
+ / TIMER_BASE;
+ }else if(cmd->scan_begin_src == TRIG_FOLLOW)
+ {
+ scan_counter_value = cmd->convert_arg / TIMER_BASE;
+ }
+ // load lower 16 bits
+ writew(scan_counter_value & 0xffff, devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);
+ // load upper 8 bits
+ writew((scan_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);
+ DEBUG_PRINT("scan counter 0x%x\n", scan_counter_value);
}
- // load lower 16 bits
- writew(scan_counter_value & 0xffff, devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);
- // load upper 8 bits
- writew((scan_counter_value >> 16) & 0xff, devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);
- DEBUG_PRINT("scan counter 0x%x\n", scan_counter_value);
}
// load hardware conversion counter with non-zero value so it doesn't mess with us
if(cmd->stop_src == TRIG_COUNT)
devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len;
- /* XXX cannot write to queue fifo while dac fifo is being written to
- * ( need spinlock, or try to use internal queue instead */
- // clear queue pointer
- writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
- // load external queue
- for(i = 0; i < cmd->chanlist_len; i++)
+ if(thisboard->layout != LAYOUT_4020)
{
- bits = 0;
- // set channel
- bits |= CHAN_BITS(CR_CHAN(cmd->chanlist[i]));
- // set gain
- bits |= GAIN_BITS(CR_RANGE(cmd->chanlist[i]));
- // set unipolar / bipolar
- bits |= UNIP_BIT(CR_RANGE(cmd->chanlist[i]));
- // set single-ended / differential
- if(CR_AREF(cmd->chanlist[i]) == AREF_DIFF)
- bits |= ADC_DIFFERENTIAL_BIT;
- if(CR_AREF(cmd->chanlist[i]) == AREF_COMMON)
- bits |= ADC_COMMON_BIT;
- // mark end of queue
- if(i == cmd->chanlist_len - 1)
- bits |= QUEUE_EOSCAN_BIT | QUEUE_EOSEQ_BIT;
- writew(bits, devpriv->main_iobase + ADC_QUEUE_FIFO_REG);
- }
+ /* XXX cannot write to queue fifo while dac fifo is being written to
+ * ( need spinlock, or try to use internal queue instead */
+ // clear queue pointer
+ writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
+ // load external queue
+ for(i = 0; i < cmd->chanlist_len; i++)
+ {
+ bits = 0;
+ // set channel
+ bits |= CHAN_BITS(CR_CHAN(cmd->chanlist[i]));
+ // set gain
+ bits |= thisboard->ai_range_bits[CR_RANGE(cmd->chanlist[i])];
+ // set single-ended / differential
+ if(CR_AREF(cmd->chanlist[i]) == AREF_DIFF)
+ bits |= ADC_DIFFERENTIAL_BIT;
+ if(CR_AREF(cmd->chanlist[i]) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ // mark end of queue
+ if(i == cmd->chanlist_len - 1)
+ bits |= QUEUE_EOSCAN_BIT | QUEUE_EOSEQ_BIT;
+ writew(bits, devpriv->main_iobase + ADC_QUEUE_FIFO_REG);
+ }
- // prime queue holding register
- writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ // prime queue holding register
+ writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ }
// clear adc buffer
writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
EN_ADC_DONE_INTR_BIT | EN_ADC_ACTIVE_INTR_BIT;
if(cmd->stop_src == TRIG_EXT)
devpriv->intr_enable_bits |= EN_ADC_STOP_INTR_BIT;
+ // Use pio transfer and interrupt on end of conversion if TRIG_WAKE_EOS flag is set.
if(cmd->flags & TRIG_WAKE_EOS)
- devpriv->intr_enable_bits |= ADC_INTR_EOSCAN_BITS;
- else
- devpriv->intr_enable_bits |= ADC_INTR_QFULL_BITS; // for clairity only, since quarter-full bits are zero
-// devpriv->intr_enable_bits |= EN_ADC_INTR_SRC_BIT;
+ devpriv->intr_enable_bits |= ADC_INTR_EOSCAN_BITS | EN_ADC_INTR_SRC_BIT;
writew(devpriv->intr_enable_bits, devpriv->main_iobase + INTR_ENABLE_REG);
DEBUG_PRINT("intr enable bits 0x%x\n", devpriv->intr_enable_bits);
- // make sure interrupt is clear
- bits = readw(devpriv->main_iobase + HW_STATUS_REG);
- DEBUG_PRINT("hw status bits 0x%x\n", bits);
-
/* set mode, disable software conversion gate */
devpriv->adc_control1_bits |= SW_GATE_BIT;
- if(cmd->convert_src == TRIG_EXT)
- devpriv->adc_control1_bits |= ADC_MODE_BITS(13); // good old mode 13
- else
- devpriv->adc_control1_bits |= ADC_MODE_BITS(8); // mode 8. What else could you need?
-
+ if(thisboard->layout != LAYOUT_4020)
+ {
+ if(cmd->convert_src == TRIG_EXT)
+ devpriv->adc_control1_bits |= ADC_MODE_BITS(13); // good old mode 13
+ else
+ devpriv->adc_control1_bits |= ADC_MODE_BITS(8); // mode 8. What else could you need?
+ } else
+ {
+ if(cmd->chanlist_len == 4)
+ devpriv->adc_control1_bits |= FOUR_CHANNEL_4020_BITS;
+ else
+ devpriv->adc_control1_bits |= TWO_CHANNEL_4020_BITS;
+ devpriv->adc_control1_bits |= LO_CHANNEL_4020_BITS(CR_CHAN(cmd->chanlist[0]));
+ devpriv->adc_control1_bits |= HI_CHANNEL_4020_BITS(CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]));
+ }
+ // use pio transfer and interrupt on end of scan if TRIG_WAKE_EOS flag is set
+#if 0
+// this causes interrupt on end of scan to be disabled on 60xx?
+ if(cmd->flags & TRIG_WAKE_EOS)
+ devpriv->adc_control1_bits |= ADC_DMA_DISABLE_BIT;
+#endif
writew(devpriv->adc_control1_bits, devpriv->main_iobase + ADC_CONTROL1_REG);
DEBUG_PRINT("control1 bits 0x%x\n", devpriv->adc_control1_bits);
- // give location of first dma descriptor
- bits = devpriv->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;;
- writel(bits, devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
- // enable dma transfer
- writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ if(cmd->flags & TRIG_WAKE_EOS)
+ writeb(0, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ else
+ {
+ // give location of first dma descriptor
+ bits = devpriv->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;;
+ writel(bits, devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ // enable dma transfer
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ }
/* enable pacing, triggering, etc */
bits = ADC_ENABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT;
return 0;
}
+// read num_samples from 16 bit wide ai fifo
+static void pio_drain_ai_fifo_16(comedi_device *dev, unsigned int num_samples)
+{
+ comedi_subdevice *s = dev->read_subdev;
+ comedi_async *async = s->async;
+ unsigned int i;
+
+ DEBUG_PRINT(" read %i samples from fifo\n", num_samples);
+
+ devpriv->ai_count -= num_samples;
+
+ for(i = 0; i < num_samples; i++)
+ {
+ comedi_buf_put(async, readw(devpriv->main_iobase + ADC_FIFO_REG));
+ }
+}
+
+// read num_samples from 32 bit wide ai fifo of 4020
+static void pio_drain_ai_fifo_32(comedi_device *dev, unsigned int num_samples)
+{
+ comedi_subdevice *s = dev->read_subdev;
+ comedi_async *async = s->async;
+ unsigned int i;
+ u32 fifo_data;
+
+ DEBUG_PRINT(" read %i samples from fifo\n", num_samples);
+
+ devpriv->ai_count -= num_samples;
+
+ for(i = 0; i < num_samples / 2; i++)
+ {
+ fifo_data = readl(devpriv->dio_counter_iobase);
+ comedi_buf_put(async, fifo_data & 0xffff);
+ comedi_buf_put(async, (fifo_data >> 16) & 0xffff);
+ }
+}
+
+// figure out how many samples are in fifo and read them
+static void pio_drain_ai_fifo(comedi_device *dev)
+{
+ comedi_subdevice *s = dev->read_subdev;
+ comedi_async *async = s->async;
+ comedi_cmd *cmd = &async->cmd;
+ int read_segment, read_index, write_segment, write_index;
+ const int num_fifo_segments = 4;
+ unsigned int num_samples;
+
+ // figure out how many samples we should read from board's fifo
+
+ do
+ {
+ /* Get most significant bits. Different boards encode the meaning of these bits
+ * differently, so use a scheme that doesn't depend on encoding */
+ read_segment = ADC_UPP_READ_PNTR_CODE(readw(devpriv->main_iobase + PREPOST_REG));
+ write_segment = ADC_UPP_WRITE_PNTR_CODE(readw(devpriv->main_iobase + PREPOST_REG));
+ // get least significant 15 bits
+ read_index = readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & 0x7fff;
+ write_index = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff;
+
+ /* if read and write pointers are not on the same fifo segment, read to the
+ * end of the read segment */
+ if(read_segment != write_segment)
+ num_samples = (thisboard->fifo_depth / num_fifo_segments) - read_index;
+ else
+ num_samples = write_index - read_index;
+
+ // 4020 stores two samples per 32 bit fifo entry
+ if(thisboard->layout == LAYOUT_4020)
+ num_samples *= 2;
+
+ if(cmd->stop_src == TRIG_COUNT)
+ {
+ if(num_samples > devpriv->ai_count)
+ {
+ num_samples = devpriv->ai_count;
+ }
+ }
+
+ if(thisboard->layout == LAYOUT_4020)
+ pio_drain_ai_fifo_32(dev, num_samples);
+ else
+ pio_drain_ai_fifo_16(dev, num_samples);
+
+ if(cmd->stop_src == TRIG_COUNT && devpriv->ai_count <= 0)
+ {
+ break;
+ }
+
+ } while (read_segment != write_segment);
+
+ async->events |= COMEDI_CB_BLOCK;
+}
+
static void handle_interrupt(int irq, void *d, struct pt_regs *regs)
{
comedi_device *dev = d;
unsigned int status;
u32 plx_status;
u32 plx_bits;
- unsigned int dma_status;
-
-static int interrupt_count = 0;
-interrupt_count++;
+ unsigned int dma0_status;
plx_status = readl(devpriv->plx9080_iobase + PLX_INTRCS_REG);
status = readw(devpriv->main_iobase + HW_STATUS_REG);
+ dma0_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
-if(interrupt_count < 10000)
-{
- DEBUG_PRINT(" cb_pcidas64 interrupt status 0x%x\n", status);
+ DEBUG_PRINT(" isr hw status 0x%x\n", status);
DEBUG_PRINT(" plx status 0x%x\n", plx_status);
-}
+ DEBUG_PRINT(" user counter 0x%x\n", readw(devpriv->main_iobase + LOWER_XFER_REG));
if((status &
(ADC_INTR_PENDING_BIT | ADC_DONE_BIT | ADC_STOP_BIT |
if(plx_status & ICS_DMA0_A)
{ // dma chan 0 interrupt
- dma_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
- DEBUG_PRINT("dma status 0x%x\n", dma_status);
+ DEBUG_PRINT("dma0 status 0x%x\n", dma0_status);
// XXX possible race
- writeb((dma_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT, devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
- if(dma_status & PLX_DMA_EN_BIT)
+ if(dma0_status & PLX_DMA_EN_BIT)
{
// transfer data from dma buffer to comedi buffer
num_samples = DMA_TRANSFER_SIZE / sizeof(devpriv->ai_buffer[0][0]);
DEBUG_PRINT(" cleared dma ch0 interrupt\n");
}
- // if interrupt was due to analog input data being available
- if(status & ADC_INTR_PENDING_BIT)
+ // pio transfer
+ if((status & ADC_INTR_PENDING_BIT) && (dma0_status & PLX_DMA_EN_BIT) == 0)
{
- // figure out how many samples we should read from board's fifo
- int read_index, write_index;
- // get most significant bits
- read_index = grey2int(ADC_UPP_READ_PNTR_CODE(readw(devpriv->main_iobase + PREPOST_REG)));
- write_index = grey2int(ADC_UPP_WRITE_PNTR_CODE(readw(devpriv->main_iobase + PREPOST_REG)));
- read_index *= QUARTER_AI_FIFO_SIZE;
- write_index *= QUARTER_AI_FIFO_SIZE;
- // get least significant 15 bits
- read_index += readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & 0x7fff;
- write_index += readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff;
- num_samples = write_index - read_index;
- if(num_samples < 0)
- num_samples += 4 * QUARTER_AI_FIFO_SIZE;
- DEBUG_PRINT(" fifo has %i samples\n", num_samples);
+ pio_drain_ai_fifo(dev);
}
// clear possible plx9080 interrupt sources
}
// if we are have all the data, then quit
- if(cmd->stop_src == TRIG_COUNT ||
+ if((cmd->stop_src == TRIG_COUNT && devpriv->ai_count <= 0) ||
(cmd->stop_src == TRIG_EXT && (status & ADC_STOP_BIT)))
{
- if(devpriv->ai_count <= 0)
- {
- ai_cancel(dev, s);
- async->events |= COMEDI_CB_EOA;
- }
+ ai_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
}
comedi_event(dev, s, async->events);
}
}
+static int dio_callback_4020(int dir, int port, int data, unsigned long iobase)
+{
+ if(dir)
+ {
+ writew(data, iobase + 2 * port);
+ return 0;
+ }else
+ {
+ return readw(iobase + 2 * port);
+ }
+}
+
static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
{
lsampl_t bits;
return divisor;
}
-
-// decodes encoding of 2 most significant bits of read/write pointer addresses coming from board
-static int grey2int(int code)
-{
- switch(code)
- {
- case 0:
- return 0;
- break;
- case 1:
- return 3;
- break;
- case 2:
- return 1;
- break;
- case 3:
- return 2;
- break;
- default:
- break;
- }
-
- return -1;
-}