From a1a4fd108a0038b9cfb98e364b7899d3d3b91190 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Mon, 26 Feb 2007 17:23:49 +0000 Subject: [PATCH] Added support for configuring dio direction on NI 660x boards through subdevice 1. Also added support for selecting digital input filter with INSN_CONFIG_ALT_FILTER. --- Documentation/comedi/insn_config | 4 + comedi/comedi_fops.c | 1 + comedi/drivers/ni_65xx.c | 1 - comedi/drivers/ni_660x.c | 233 +++++++++++++++---------------- include/linux/comedi.h | 13 ++ 5 files changed, 133 insertions(+), 119 deletions(-) diff --git a/Documentation/comedi/insn_config b/Documentation/comedi/insn_config index 86e779fd..0ec1ad84 100644 --- a/Documentation/comedi/insn_config +++ b/Documentation/comedi/insn_config @@ -266,3 +266,7 @@ ID=INSN_CONFIG_SET_COUNTER_MODE: Set a counter's mode. The specific meaning of the mode field is hardware-dependent. [0] - ID [1] - mode + +ID=INSN_CONFIG_FILTER: Select an input filter. + [0] - ID + [1] - filter diff --git a/comedi/comedi_fops.c b/comedi/comedi_fops.c index 23cbdaa9..84d59137 100644 --- a/comedi/comedi_fops.c +++ b/comedi/comedi_fops.c @@ -632,6 +632,7 @@ static int check_insn_config_length(comedi_insn *insn, lsampl_t *data) case INSN_CONFIG_ARM: case INSN_CONFIG_DIO_QUERY: case INSN_CONFIG_BLOCK_SIZE: + case INSN_CONFIG_FILTER: case INSN_CONFIG_SERIAL_CLOCK: case INSN_CONFIG_BIDIRECTIONAL_DATA: case INSN_CONFIG_ALT_SOURCE: diff --git a/comedi/drivers/ni_65xx.c b/comedi/drivers/ni_65xx.c index 0e5a97b2..2be63474 100644 --- a/comedi/drivers/ni_65xx.c +++ b/comedi/drivers/ni_65xx.c @@ -333,7 +333,6 @@ static int ni_65xx_config_filter(comedi_device *dev, comedi_subdevice *s, const unsigned chan = CR_CHAN(insn->chanspec); const unsigned port = sprivate(s)->base_port + ni_65xx_port_by_channel(chan); - if(insn->n != 2)return -EINVAL; if(data[0] != INSN_CONFIG_FILTER) return -EINVAL; if(data[1]) { diff --git a/comedi/drivers/ni_660x.c b/comedi/drivers/ni_660x.c index 2dd4de93..aa3c895b 100644 --- a/comedi/drivers/ni_660x.c +++ b/comedi/drivers/ni_660x.c @@ -50,6 +50,7 @@ Things to do: #include "ni_tio.h" #define CTRS_PER_CHIP 4 // The number of counters per ni-tio chip +#define NUM_PFI_CHANNELS 40 /* See Register-Level Programmer Manual page 3.1 */ typedef enum @@ -123,16 +124,26 @@ typedef enum G3DMAConfigRegister, G3DMAStatusRegister, ClockConfigRegister, - IOConfigReg0_3, - IOConfigReg4_7, - IOConfigReg8_11, - IOConfigReg12_15, - IOConfigReg16_19, - IOConfigReg20_23, - IOConfigReg24_27, - IOConfigReg28_31, - IOConfigReg32_35, - IOConfigReg36_39, + IOConfigReg0_1, + IOConfigReg2_3, + IOConfigReg4_5, + IOConfigReg6_7, + IOConfigReg8_9, + IOConfigReg10_11, + IOConfigReg12_13, + IOConfigReg14_15, + IOConfigReg16_17, + IOConfigReg18_19, + IOConfigReg20_21, + IOConfigReg22_23, + IOConfigReg24_25, + IOConfigReg26_27, + IOConfigReg28_29, + IOConfigReg30_31, + IOConfigReg32_33, + IOConfigReg34_35, + IOConfigReg36_37, + IOConfigReg38_39, STCDIOParallelInput, STCDIOOutput, STCDIOControl, @@ -140,50 +151,11 @@ typedef enum NumRegisters, } NI_660x_Register; -static inline int IOConfigReg(int chipset, int counter_channel) +static inline unsigned IOConfigReg(unsigned pfi_channel) { - if(chipset == 0) - { - switch(counter_channel) - { - case 0: - return IOConfigReg36_39; - break; - case 1: - return IOConfigReg32_35; - break; - case 2: - return IOConfigReg28_31; - break; - case 3: - return IOConfigReg24_27; - break; - default: - DPRINTK("ni_660x: bug!, line %i\n", __LINE__); - break; - } - }else - { - switch(counter_channel) - { - case 0: - return IOConfigReg20_23; - break; - case 1: - return IOConfigReg16_19; - break; - case 2: - return IOConfigReg12_15; - break; - case 3: - return IOConfigReg8_11; - break; - default: - DPRINTK("ni_660x: bug!, line %i\n", __LINE__); - break; - } - } - return 0; + unsigned reg = IOConfigReg0_1 + pfi_channel / 2; + BUG_ON(reg > IOConfigReg38_39); + return reg; } enum ni_660x_register_width @@ -280,16 +252,26 @@ static const NI_660xRegisterData registerData[NumRegisters] = {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B}, {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B}, {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B}, - {"IO Config Register 0-3", 0x77C, NI_660x_READ_WRITE, DATA_4B}, // READWRITE - {"IO Config Register 4-7", 0x780, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 8-11", 0x784, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 12-15", 0x788, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 16-19", 0x78C, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 20-23", 0x790, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 24-27", 0x794, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 28-31", 0x798, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 32-35", 0x79C, NI_660x_READ_WRITE, DATA_4B}, - {"IO Config Register 36-39", 0x7A0, NI_660x_READ_WRITE, DATA_4B}, + {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B}, // READWRITE + {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B}, + {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B}, {"STD DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B}, {"STD DIO Output", 0x014, NI_660x_WRITE, DATA_2B}, {"STD DIO Control", 0x016, NI_660x_WRITE, DATA_2B}, @@ -302,25 +284,22 @@ static const NI_660xRegisterData registerData[NumRegisters] = #define CounterSwap 0x1<<21 // ioconfigreg -/*pin index 0 corresponds to pin A in manual, index 1 is pin B, etc*/ -static inline int pin_is_output(int pin_index) +static inline unsigned pfi_output_select_mask(unsigned pfi_channel) { - return 0x1 << (24 - 8 * pin_index); + return 0x3 << (8 * (pfi_channel % 2)); } -static inline int pin_input_select(int pin_index, int input_selection) +static inline unsigned pfi_output_select_bits(unsigned pfi_channel, unsigned output_select) { - input_selection &= 0x7; - return input_selection << (28 - 8 * pin_index); + return (output_select & 0x3) << (8 * (pfi_channel % 2)); +} +static inline unsigned pfi_input_select_mask(unsigned pfi_channel) +{ + return 0x7 << (4 + 8 * (pfi_channel % 2)); +} +static inline unsigned pfi_input_select_bits(unsigned pfi_channel, unsigned input_select) +{ + return (input_select & 0x7) << (4 + 8 * (pfi_channel % 2)); } - -// For configuring input pins -#define Digital_Filter_A_Is_Off 0x000<<28 -#define Digital_Filter_A_Is_Timebase3 0x001<<28 -#define Digital_Filter_A_Is_100_Timebase1 0x010<<28 -#define Digital_Filter_A_Is_20_Timebase1 0x011<<28 -#define Digital_Filter_A_Is_10_Timebase1 0x100<<28 -#define Digital_Filter_A_Is_2_Timebase1 0x101<<28 -#define Digital_Filter_A_Is_2_Timebase3 0x110<<28 // Offset of the GPCT chips from the base-adress of the card static const unsigned GPCT_OFFSET[2] = {0x0, 0x800}; /* First chip is at base-adress + @@ -367,6 +346,7 @@ typedef struct struct mite_struct *mite; int boardtype; struct ni_gpct counters[NI_660X_MAX_NUM_COUNTERS]; + uint64_t pfi_direction_bits; }ni_660x_private; #define devpriv ((ni_660x_private *)dev->private) @@ -667,19 +647,14 @@ static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it) /* DIGITAL I/O SUBDEVICE */ s->type = COMEDI_SUBD_DIO; s->subdev_flags = SDF_READABLE|SDF_WRITABLE; - s->n_chan = 8; // Only using 8 bits for now, instead of 32!! + s->n_chan = NUM_PFI_CHANNELS; s->maxdata = 1; s->range_table = &range_digital; - /* (Copied from skel.c) 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 */ - // Not implemented yet s->insn_bits = ni_660x_dio_insn_bits; s->insn_config = ni_660x_dio_insn_config; s->io_bits = 0; /* all bits default to input */ + // we use the ioconfig registers to control dio direction, so zero output enables in stc dio control reg + writew(0, devpriv->mite->daq_io_addr + registerData[STCDIOControl].offset); for(i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) { @@ -804,34 +779,56 @@ ni_660x_find_device(comedi_device *dev, int bus, int slot) static int ni_660x_dio_insn_bits(comedi_device *dev, - comedi_subdevice *s, - comedi_insn *insn, - lsampl_t *data) + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) { - if(insn->n!=2)return -EINVAL; - /* The insn data is a write_mask in data[0] and the new data - * in data[1], each channel corresponding to a bit. */ - - // Check if we have to write some bits - if(data[0]) - { - // Copied from skel.c, unverified - s->state &= ~data[0]; - s->state |= data[0]&data[1]; - /* Write out the new digital output lines */ - /* Check if data < n_chan ?? */ - writew(s->state,devpriv->mite->daq_io_addr + registerData[STCDIOOutput].offset); - } - /* on return, data[1] contains the value of the digital - * input and output lines. */ - data[1]=readw(devpriv->mite->daq_io_addr + registerData[STCDIOParallelInput].offset); - return 2; + unsigned base_bitfield_channel = CR_CHAN(insn->chanspec); + + // Check if we have to write some bits + if(data[0]) + { + /* Only the first 8 lines can be read/written. The rest can + only have their input/output configuration changed (although + the user manual implies the first 32 channels can be used as + general purpose dio, the register manual doesn't tell you how + this can be accomplished. */ + if((data[0] << base_bitfield_channel) > 0xff) + { + return -EINVAL; + } + s->state &= ~(data[0] << base_bitfield_channel); + s->state |= (data[0] & data[1]) << base_bitfield_channel; + /* Write out the new digital output lines */ + writew(s->state,devpriv->mite->daq_io_addr + registerData[STCDIOOutput].offset); + } + /* on return, data[1] contains the value of the digital + * input and output lines. */ + data[1] = (readw(devpriv->mite->daq_io_addr + registerData[STCDIOParallelInput].offset) >> base_bitfield_channel) & + (0xff >> base_bitfield_channel); + return 2; +} + +static void ni_660x_select_pfi_output(comedi_device *dev, unsigned pfi_channel, unsigned output_select) +{ + unsigned bits = readw(devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset); + bits &= ~pfi_output_select_mask(pfi_channel); + bits |= pfi_output_select_bits(pfi_channel, output_select); + writew(bits, devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset); +} + +static void ni660x_config_filter(comedi_device *dev, unsigned pfi_channel, enum ni_gpct_filter_select filter) +{ + unsigned bits = readw(devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset); + bits &= ~pfi_input_select_mask(pfi_channel); + bits |= pfi_input_select_bits(pfi_channel, filter); + writew(bits, devpriv->mite->daq_io_addr + registerData[IOConfigReg(pfi_channel)].offset); } static int ni_660x_dio_insn_config(comedi_device *dev, - comedi_subdevice *s, - comedi_insn *insn, - lsampl_t *data) + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) { int chan=CR_CHAN(insn->chanspec); @@ -843,23 +840,23 @@ static int ni_660x_dio_insn_config(comedi_device *dev, switch(data[0]) { case INSN_CONFIG_DIO_OUTPUT: - s->io_bits |= 1<pfi_direction_bits |= ((uint64_t)1) << chan; + ni_660x_select_pfi_output(dev, chan, 1); break; case INSN_CONFIG_DIO_INPUT: - s->io_bits &= ~(1<pfi_direction_bits &= ~(((uint64_t)1) << chan); + ni_660x_select_pfi_output(dev, chan, 0); break; case INSN_CONFIG_DIO_QUERY: - data[1] = (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; + data[1] = (devpriv->pfi_direction_bits & (((uint64_t)1) << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; return insn->n; + case INSN_CONFIG_FILTER: + ni660x_config_filter(dev, chan, data[1]); + return 0; default: return -EINVAL; break; }; - // No GPCT_OFFSET[chipset] offset here?? - writew(s->io_bits,devpriv->mite->daq_io_addr + registerData[STCDIOControl].offset); - /* We should also do something (INSN_CONFIG_ALT_FILTER) with the IO configuration registers, - see p 3-38 of register level prog. manual - */ return insn->n; } diff --git a/include/linux/comedi.h b/include/linux/comedi.h index db0ca899..c4b31c18 100644 --- a/include/linux/comedi.h +++ b/include/linux/comedi.h @@ -656,6 +656,19 @@ enum ni_gpct_arm_source NI_GPCT_ARM_UNKNOWN = 0x1000, }; +/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER +(support for m-series should also be added). */ +enum ni_gpct_filter_select +{ + NI_GPCT_FILTER_OFF = 0x0, + NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1, + NI_GPCT_FILTER_100x_TIMEBASE_1= 0x2, + NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3, + NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4, + NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5, + NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6 +}; + /* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */ enum ni_mio_clock_source { -- 2.26.2