From c6417d0e39e6921974f52b259a36f203679afce3 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Thu, 19 Oct 2006 21:06:18 +0000 Subject: [PATCH] Added support for configuring external clock sources on ni mio boards, including using the PLL on m-series boards. --- Documentation/comedi/insn_config | 27 ++- comedi/drivers/ni_mio_common.c | 326 +++++++++++++++++++++++-------- comedi/drivers/ni_pcimio.c | 1 - comedi/drivers/ni_stc.h | 26 ++- include/linux/comedi.h | 39 ++-- 5 files changed, 306 insertions(+), 113 deletions(-) diff --git a/Documentation/comedi/insn_config b/Documentation/comedi/insn_config index 69705143..aa71851c 100644 --- a/Documentation/comedi/insn_config +++ b/Documentation/comedi/insn_config @@ -171,8 +171,10 @@ ID=INSN_CONFIG_ALT_SOURCE: select alternate input source (internal calibration r [0] - ID [1] - source - -ID=INSN_CONFIG_TIMER_1: Configure an external master clock and divisor + +ID=INSN_CONFIG_TIMER_1: Deprecated. Use INSN_CONFIG_SET_CLOCK_SRC and + TRIG_COUNT or TRIG_TIMER. + Configure an external master clock and divisor to divide clock by. Used with commands by setting scan_begin_src or convert_src set to TRIG_OTHER. @@ -182,7 +184,7 @@ ID=INSN_CONFIG_TIMER_1: Configure an external master clock and divisor [3] - primary combining machine configuration (should always be 0x04) [4] - divisor - + ID=INSN_CONFIG_PWM_OUTPUT: Configure a pulse-width-modulation output. Returns EAGAIN error with modified values if exact timing is not achievable. @@ -197,3 +199,22 @@ ID=INSN_CONFIG_GET_PWM_OUTPUT: Query a pulse-width-modulation output. [0] - ID [1] - up time (nanoseconds) [2] - down time (nanoseconds) + +ID=INSN_CONFIG_SET_CLOCK_SRC: Choose a source for the master clock. + The frequency of the clock is specified in data[2], or left + unspecified by using a value of 0. The driver will ignore the clock + period setting if it already knows what the clock period should be + for the specified source (i.e. for an internal master clock). Certain + boards which use a phase-locked loop to synchronize to an external + clock source must be told the frequency of the external clock. + Specifying a clock period for an external clock may also allow + the driver to support TRIG_TIMER sources in commands while + using the external clock. + [0] - ID + [1] - clock source + [2] - clock period (nanoseconds) + +ID=INSN_CONFIG_GET_CLOCK_SRC: Ask which master clock is being used + [0] - ID + [1] - clock source + [2] - clock period (nanoseconds) diff --git a/comedi/drivers/ni_mio_common.c b/comedi/drivers/ni_mio_common.c index 73db889e..37a0244f 100644 --- a/comedi/drivers/ni_mio_common.c +++ b/comedi/drivers/ni_mio_common.c @@ -259,7 +259,7 @@ static int ni_ao_reset(comedi_device *dev,comedi_subdevice *s); static int ni_8255_callback(int dir,int port,int data,unsigned long arg); -static int ni_ns_to_timer(int *nanosec,int round_mode); +static int ni_ns_to_timer(comedi_device *dev, int *nanosec, int round_mode); /*GPCT function def's*/ @@ -293,6 +293,8 @@ static int ni_m_series_pwm_config(comedi_device *dev, comedi_subdevice *s, static int ni_6143_pwm_config(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int ni_set_master_clock(comedi_device *dev, unsigned source, unsigned period_ns); + enum aimodes { AIMODE_NONE = 0, @@ -1611,29 +1613,25 @@ static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan, } } -#define TIMER_BASE 50 /* 20 Mhz base */ - -static int ni_ns_to_timer(int *nanosec,int round_mode) +static int ni_ns_to_timer(comedi_device *dev, int *nanosec, int round_mode) { - int divider,base; - - base=TIMER_BASE; - - switch(round_mode){ + int divider; + switch(round_mode) + { case TRIG_ROUND_NEAREST: default: - divider=(*nanosec+base/2)/base; + divider = (*nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns; break; case TRIG_ROUND_DOWN: - divider=(*nanosec)/base; + divider = (*nanosec) / devpriv->clock_ns; break; case TRIG_ROUND_UP: - divider=(*nanosec+base-1)/base; + divider=(*nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns; break; } - *nanosec=base*divider; - return divider-1; + *nanosec = devpriv->clock_ns * divider; + return divider - 1; } static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) @@ -1709,8 +1707,8 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) cmd->scan_begin_arg=boardtype.ai_speed; err++; } - if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){ - cmd->scan_begin_arg=TIMER_BASE*0xffffff; + if(cmd->scan_begin_arg > devpriv->clock_ns * 0xffffff){ + cmd->scan_begin_arg = devpriv->clock_ns * 0xffffff; err++; } }else if(cmd->scan_begin_src==TRIG_EXT){ @@ -1740,8 +1738,8 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) cmd->convert_arg=boardtype.ai_speed; err++; } - if(cmd->convert_arg>TIMER_BASE*0xffff){ - cmd->convert_arg=TIMER_BASE*0xffff; + if(cmd->convert_arg>devpriv->clock_ns*0xffff){ + cmd->convert_arg=devpriv->clock_ns*0xffff; err++; } } @@ -1793,13 +1791,13 @@ static int ni_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) if(cmd->scan_begin_src==TRIG_TIMER){ tmp=cmd->scan_begin_arg; - ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK); + ni_ns_to_timer(dev, &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->scan_begin_arg)err++; } if(cmd->convert_src==TRIG_TIMER){ if((boardtype.reg_type != ni_reg_611x) && (boardtype.reg_type != ni_reg_6143)){ tmp=cmd->convert_arg; - ni_ns_to_timer(&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK); + ni_ns_to_timer(dev, &cmd->convert_arg, cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->convert_arg)err++; if(cmd->scan_begin_src==TRIG_TIMER && cmd->scan_begin_argconvert_arg*cmd->scan_end_arg){ @@ -1947,7 +1945,7 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s) devpriv->stc_writew(dev, mode2, AI_Mode_2_Register); /* load SI */ - timer=ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST); + timer = ni_ns_to_timer(dev, &cmd->scan_begin_arg, TRIG_ROUND_NEAREST); devpriv->stc_writel(dev, timer,AI_SI_Load_A_Registers); devpriv->stc_writew(dev, AI_SI_Load,AI_Command_1_Register); break; @@ -1971,7 +1969,7 @@ static int ni_ai_cmd(comedi_device *dev,comedi_subdevice *s) if( cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW ) timer = 1; else - timer=ni_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_NEAREST); + timer = ni_ns_to_timer(dev, &cmd->convert_arg, TRIG_ROUND_NEAREST); devpriv->stc_writew(dev, 1,AI_SI2_Load_A_Register); /* 0,0 does not work. */ devpriv->stc_writew(dev, timer,AI_SI2_Load_B_Register); @@ -2479,7 +2477,7 @@ static int ni_ao_cmd(comedi_device *dev,comedi_subdevice *s) comedi_error(dev, "cannot run command without an irq"); return -EIO; } - trigvar = ni_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST); + trigvar = ni_ns_to_timer(dev, &cmd->scan_begin_arg, TRIG_ROUND_NEAREST); devpriv->stc_writew(dev, AO_Configuration_Start,Joint_Reset_Register); @@ -2656,8 +2654,8 @@ static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) err++; } #endif - if(cmd->scan_begin_arg>TIMER_BASE*0xffffff){ /* XXX check */ - cmd->scan_begin_arg=TIMER_BASE*0xffffff; + if(cmd->scan_begin_arg>devpriv->clock_ns*0xffffff){ /* XXX check */ + cmd->scan_begin_arg=devpriv->clock_ns*0xffffff; err++; } if(cmd->convert_arg!=0){ @@ -2686,7 +2684,7 @@ static int ni_ao_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) /* step 4: fix up any arguments */ tmp = cmd->scan_begin_arg; - ni_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK); + ni_ns_to_timer(dev, &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->scan_begin_arg)err++; if(err)return 4; @@ -3263,7 +3261,7 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) s=dev->subdevices+10; s->type=COMEDI_SUBD_DIO; s->subdev_flags=SDF_READABLE|SDF_WRITABLE|SDF_INTERNAL; - s->n_chan=7; + s->n_chan=8; s->maxdata=1; s->insn_bits = ni_rtsi_insn_bits; s->insn_config = ni_rtsi_insn_config; @@ -3329,7 +3327,6 @@ static int ni_E_init(comedi_device *dev,comedi_devconfig *it) } printk("\n"); - return 0; } @@ -3395,8 +3392,8 @@ static int ni_m_series_eeprom_insn_read(comedi_device *dev,comedi_subdevice *s, static int ni_get_pwm_config(comedi_device *dev, lsampl_t *data) { - data[1] = devpriv->pwm_up_count * TIMER_BASE; - data[2] = devpriv->pwm_down_count * TIMER_BASE; + data[1] = devpriv->pwm_up_count * devpriv->clock_ns; + data[2] = devpriv->pwm_down_count * devpriv->clock_ns; return 3; } @@ -3410,13 +3407,13 @@ static int ni_m_series_pwm_config(comedi_device *dev, comedi_subdevice *s, switch(data[1]) { case TRIG_ROUND_NEAREST: - up_count = (data[2] + TIMER_BASE / 2) / TIMER_BASE; + up_count = (data[2] + devpriv->clock_ns / 2) / devpriv->clock_ns; break; case TRIG_ROUND_DOWN: - up_count = data[2] / TIMER_BASE; + up_count = data[2] / devpriv->clock_ns; break; case TRIG_ROUND_UP: - up_count = (data[2] + TIMER_BASE - 1) / TIMER_BASE; + up_count = (data[2] + devpriv->clock_ns - 1) / devpriv->clock_ns; break; default: return -EINVAL; @@ -3425,23 +3422,23 @@ static int ni_m_series_pwm_config(comedi_device *dev, comedi_subdevice *s, switch(data[3]) { case TRIG_ROUND_NEAREST: - down_count = (data[4] + TIMER_BASE / 2) / TIMER_BASE; + down_count = (data[4] + devpriv->clock_ns / 2) / devpriv->clock_ns; break; case TRIG_ROUND_DOWN: - down_count = data[4] / TIMER_BASE; + down_count = data[4] / devpriv->clock_ns; break; case TRIG_ROUND_UP: - down_count = (data[4] + TIMER_BASE - 1) / TIMER_BASE; + down_count = (data[4] + devpriv->clock_ns - 1) / devpriv->clock_ns; break; default: return -EINVAL; break; } - if(up_count * TIMER_BASE != data[2] || - down_count * TIMER_BASE != data[4]) + if(up_count * devpriv->clock_ns != data[2] || + down_count * devpriv->clock_ns != data[4]) { - data[2] = up_count * TIMER_BASE; - data[4] = down_count * TIMER_BASE; + data[2] = up_count * devpriv->clock_ns; + data[4] = down_count * devpriv->clock_ns; return -EAGAIN; } ni_writel(MSeries_Cal_PWM_High_Time_Bits(up_count) | MSeries_Cal_PWM_Low_Time_Bits(down_count), M_Offset_Cal_PWM); @@ -3469,13 +3466,13 @@ static int ni_6143_pwm_config(comedi_device *dev, comedi_subdevice *s, switch(data[1]) { case TRIG_ROUND_NEAREST: - up_count = (data[2] + TIMER_BASE / 2) / TIMER_BASE; + up_count = (data[2] + devpriv->clock_ns / 2) / devpriv->clock_ns; break; case TRIG_ROUND_DOWN: - up_count = data[2] / TIMER_BASE; + up_count = data[2] / devpriv->clock_ns; break; case TRIG_ROUND_UP: - up_count = (data[2] + TIMER_BASE - 1) / TIMER_BASE; + up_count = (data[2] + devpriv->clock_ns - 1) / devpriv->clock_ns; break; default: return -EINVAL; @@ -3484,23 +3481,23 @@ static int ni_6143_pwm_config(comedi_device *dev, comedi_subdevice *s, switch(data[3]) { case TRIG_ROUND_NEAREST: - down_count = (data[4] + TIMER_BASE / 2) / TIMER_BASE; + down_count = (data[4] + devpriv->clock_ns / 2) / devpriv->clock_ns; break; case TRIG_ROUND_DOWN: - down_count = data[4] / TIMER_BASE; + down_count = data[4] / devpriv->clock_ns; break; case TRIG_ROUND_UP: - down_count = (data[4] + TIMER_BASE - 1) / TIMER_BASE; + down_count = (data[4] + devpriv->clock_ns - 1) / devpriv->clock_ns; break; default: return -EINVAL; break; } - if(up_count * TIMER_BASE != data[2] || - down_count * TIMER_BASE != data[4]) + if(up_count * devpriv->clock_ns != data[2] || + down_count * devpriv->clock_ns != data[4]) { - data[2] = up_count * TIMER_BASE; - data[4] = down_count * TIMER_BASE; + data[2] = up_count * devpriv->clock_ns; + data[4] = down_count * devpriv->clock_ns; return -EAGAIN; } ni_writel(up_count, Calibration_HighTime_6143); @@ -4308,8 +4305,11 @@ static void ni_rtsi_init(comedi_device *dev) // Initialises the RTSI bus signal switch to a default state // Set clock mode to internal - devpriv->stc_writew(dev, COMEDI_RTSI_CLOCK_MODE_INTERNAL, RTSI_Trig_Direction_Register); - + devpriv->clock_and_fout2 = MSeries_RTSI_10MHz_Bit; + if(ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0) + { + rt_printk("ni_set_master_clock failed, bug?"); + } // Standard internal lines are routed to standard RTSI bus lines devpriv->stc_writew(dev, 0x3210, RTSI_Trig_A_Output_Register); devpriv->stc_writew(dev, 0x0654, RTSI_Trig_B_Output_Register); @@ -4328,46 +4328,204 @@ static int ni_rtsi_insn_bits(comedi_device *dev,comedi_subdevice *s, return 2; } -static int ni_rtsi_insn_config(comedi_device *dev,comedi_subdevice *s, - comedi_insn *insn,lsampl_t *data) +/* Find best multiplier/divider to try and get the PLL running at 80 MHz + * given an arbitrary frequency input clock */ +static int ni_mseries_get_pll_parameters(unsigned reference_period_ns, + unsigned *freq_divider, unsigned *freq_multiplier, unsigned *actual_period_ns) { - unsigned int chan; - unsigned int bit; - - if(insn->n < 1) return -EINVAL; + unsigned div; + unsigned best_div = 1; + static const unsigned max_div = 16; + unsigned mult; + unsigned best_mult = 1; + static const unsigned max_mult = 256; + static const unsigned pico_per_nano = 1000; + + const unsigned reference_picosec = reference_period_ns * pico_per_nano; + /* m-series wants the phased-locked loop to output 80MHz, which is divided by 4 to + * 20 MHz for most timing clocks */ + static const unsigned target_picosec = 12500; + int best_period_picosec = 0; + for(div = 1; div <= max_div; ++div) + { + for(mult = 1; mult <= max_mult; ++mult) + { + unsigned new_period_ps = (reference_picosec * div) / mult; + if((new_period_ps < best_period_picosec && new_period_ps >= target_picosec) || + (new_period_ps > best_period_picosec && new_period_ps <= target_picosec)) + { + best_period_picosec = new_period_ps; + best_div = div; + best_mult = mult; + } + } + } + if(best_period_picosec == 0) + { + rt_printk("%s: bug, failed to find pll parameters\n", __FUNCTION__); + return -EIO; + } + *freq_divider = best_div; + *freq_multiplier = best_mult; + *actual_period_ns = best_period_picosec + (pico_per_nano / 2) / pico_per_nano; + return 0; +} - if(data[0] == INSN_CONFIG_SET_RTSI_CLOCK_MODE){ - if(data[1] > 3) - return -EINVAL; +static int ni_mseries_set_pll_master_clock(comedi_device *dev, unsigned source, unsigned period_ns) +{ + // these limits are somewhat arbitrary, but NI advertises 1 to 20MHz range so we'll use that + static const unsigned min_period_ns = 50; + static const unsigned max_period_ns = 1000; + if(period_ns < min_period_ns || period_ns > max_period_ns) + { + rt_printk("%s: you must specify an input clock frequency between %i and %i nanosec " + "for the phased-lock loop.\n", __FUNCTION__, min_period_ns, max_period_ns); + return -EINVAL; + } + devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit; + devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); + unsigned pll_control_bits = MSeries_PLL_Enable_Bit | MSeries_PLL_VCO_Mode_75_150MHz_Bits; + devpriv->clock_and_fout2 |= MSeries_Timebase1_Select_Bit; + devpriv->clock_and_fout2 &= ~MSeries_PLL_In_Source_Select_Mask; + int retval; + unsigned freq_divider; + unsigned freq_multiplier; + switch(source) + { + case NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK: + devpriv->clock_and_fout2 |= MSeries_PLL_In_Source_Select_Star_Trigger_Bits; + retval = ni_mseries_get_pll_parameters(period_ns, &freq_divider, + &freq_multiplier, &devpriv->clock_ns); + if(retval < 0) return retval; + break; + case NI_MIO_PLL_PXI10_CLOCK: + /* pxi clock is 10MHz */ + devpriv->clock_and_fout2 |= MSeries_PLL_In_Source_Select_PXI_Clock10; + retval = ni_mseries_get_pll_parameters(100, &freq_divider, + &freq_multiplier, &devpriv->clock_ns); + if(retval < 0) return retval; + default: + { + unsigned rtsi_channel; + static const unsigned max_rtsi_channel = 7; /* channel 7 should be rtsi clock */ + for(rtsi_channel = 0; rtsi_channel <= max_rtsi_channel; ++rtsi_channel) + { + if(source == NI_MIO_PLL_RTSI_CLOCK(rtsi_channel)) + { + devpriv->clock_and_fout2 |= MSeries_PLL_In_Source_Select_RTSI_Bits(rtsi_channel); + break; + } + } + if(rtsi_channel > max_rtsi_channel) return -EINVAL; + retval = ni_mseries_get_pll_parameters(period_ns, &freq_divider, + &freq_multiplier, &devpriv->clock_ns); + if(retval < 0) return retval; + } + break; + } + ni_writew(devpriv->clock_and_fout2, M_Offset_Clock_and_Fout2); + pll_control_bits |= MSeries_PLL_Divisor_Bits(freq_divider) | MSeries_PLL_Multiplier_Bits(freq_multiplier); + // rt_printk("using divider=%i, multiplier=%i for PLL.\n", freq_divider, freq_multiplier); + ni_writew(pll_control_bits, M_Offset_PLL_Control); + unsigned i; + static const unsigned timeout = 1000; + /* it seems to typically take a few hundred microseconds for PLL to lock */ + for(i = 0; i < timeout; ++i) + { + if(ni_readw(M_Offset_PLL_Status) & MSeries_PLL_Locked_Bit) + { + break; + } + udelay(1); + } + if(i == timeout) + { + rt_printk("%s: timed out waiting for PLL to lock to reference clock source %i with period %i ns.\n", + __FUNCTION__, source, period_ns); + return -ETIMEDOUT; + } + return 3; +} - devpriv->rtsi_trig_direction_reg &= ~0x03; - devpriv->rtsi_trig_direction_reg |= data[1]; +static int ni_set_master_clock(comedi_device *dev, unsigned source, unsigned period_ns) +{ + switch(source) + { + case NI_MIO_INTERNAL_CLOCK: + devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit; + devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); + devpriv->clock_ns = 50; + if(boardtype.reg_type == ni_reg_m_series) + { + devpriv->clock_and_fout2 &= ~(MSeries_Timebase1_Select_Bit | MSeries_Timebase3_Select_Bit); + ni_writew(devpriv->clock_and_fout2, M_Offset_Clock_and_Fout2); + ni_writew(0, M_Offset_PLL_Control); + } + break; + case NI_MIO_RTSI_CLOCK: + devpriv->rtsi_trig_direction_reg |= Use_RTSI_Clock_Bit; devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); + devpriv->clock_ns = period_ns; + if(boardtype.reg_type == ni_reg_m_series) + { + devpriv->clock_and_fout2 &= ~(MSeries_Timebase1_Select_Bit | MSeries_Timebase3_Select_Bit); + devpriv->clock_and_fout2 |= MSeries_RTSI_10MHz_Bit; + ni_writew(devpriv->clock_and_fout2, M_Offset_Clock_and_Fout2); + ni_writew(0, M_Offset_PLL_Control); + } + break; + default: + if(boardtype.reg_type == ni_reg_m_series) + { + return ni_mseries_set_pll_master_clock(dev, source, period_ns); + } + return -EINVAL; } - else { - chan = CR_CHAN(insn->chanspec); - if(chan > 6) return -EINVAL; - - bit = 9 + chan; + return 3; +} - switch(data[0]){ - case INSN_CONFIG_DIO_OUTPUT: - devpriv->rtsi_trig_direction_reg |= (1 << bit); - devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); - break; - case INSN_CONFIG_DIO_INPUT: - devpriv->rtsi_trig_direction_reg &= ~(1 << bit); - devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); - break; - case INSN_CONFIG_DIO_QUERY: - data[1] = (devpriv->rtsi_trig_direction_reg & (1<chanspec); + static const unsigned RTSI_clock_channel = 7; + switch(data[0]){ + case INSN_CONFIG_DIO_OUTPUT: + if(chan == RTSI_clock_channel) + { + devpriv->rtsi_trig_direction_reg |= Drive_RTSI_Clock_Bit; + }else + { + devpriv->rtsi_trig_direction_reg |= RTSI_Output_Bit(chan); + } + devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); + break; + case INSN_CONFIG_DIO_INPUT: + if(chan == RTSI_clock_channel) + { + devpriv->rtsi_trig_direction_reg &= ~Drive_RTSI_Clock_Bit; + }else + { + devpriv->rtsi_trig_direction_reg &= ~RTSI_Output_Bit(chan); } + devpriv->stc_writew(dev, devpriv->rtsi_trig_direction_reg, RTSI_Trig_Direction_Register); + break; + case INSN_CONFIG_DIO_QUERY: + if(chan == RTSI_clock_channel) + { + data[1] = (devpriv->rtsi_trig_direction_reg & Drive_RTSI_Clock_Bit) ? INSN_CONFIG_DIO_OUTPUT : INSN_CONFIG_DIO_INPUT; + }else + { + data[1] = (devpriv->rtsi_trig_direction_reg & RTSI_Output_Bit(chan)) ? INSN_CONFIG_DIO_OUTPUT : INSN_CONFIG_DIO_INPUT; + } + return 2; + break; + case INSN_CONFIG_SET_CLOCK_SRC: + return ni_set_master_clock(dev, data[1], data[2]); + break; + default: + return -EINVAL; } - return 1; } diff --git a/comedi/drivers/ni_pcimio.c b/comedi/drivers/ni_pcimio.c index 32d53b62..b4f11fbe 100644 --- a/comedi/drivers/ni_pcimio.c +++ b/comedi/drivers/ni_pcimio.c @@ -1245,7 +1245,6 @@ static uint16_t m_series_stc_readw(comedi_device *dev, int reg) case Joint_Status_2_Register: offset = M_Offset_Joint_Status_2; break; - /* FIXME: DIO_Parallel_Input_Register (16 bit reg) is replaced by M_Offset_Static_Digital_Input (32 bit) */ default: rt_printk("%s: bug! unhandled register=0x%x in switch.\n", __FUNCTION__, reg); return 0; diff --git a/comedi/drivers/ni_stc.h b/comedi/drivers/ni_stc.h index f04db160..291e58f4 100644 --- a/comedi/drivers/ni_stc.h +++ b/comedi/drivers/ni_stc.h @@ -305,6 +305,20 @@ enum AO_FIFO_Mode_Bits #define IO_Bidirection_Pin_Register 57 #define RTSI_Trig_Direction_Register 58 +enum RTSI_Trig_Direction_Bits +{ + Drive_RTSI_Clock_Bit = 0x1, + Use_RTSI_Clock_Bit = 0x2, +}; +static inline unsigned RTSI_Output_Bit(unsigned channel) +{ + if(channel > 6) + { + rt_printk("%s: bug, invalid RTSI_channel=%i\n", __FUNCTION__, channel); + return 0; + } + return 1 << (9 + channel); +} #define Interrupt_Control_Register 59 #define Interrupt_B_Enable _bit15 @@ -1029,6 +1043,7 @@ enum MSeries_Clock_and_Fout2_Bits MSeries_PLL_In_Source_Select_Star_Trigger_Bits = 0x14, MSeries_PLL_In_Source_Select_RTSI7_Bits = 0x1b, MSeries_PLL_In_Source_Select_PXI_Clock10 = 0x1d, + MSeries_PLL_In_Source_Select_Mask = 0x1f, MSeries_Timebase1_Select_Bit = 0x20, // use PLL for timebase 1 MSeries_Timebase3_Select_Bit = 0x40, // use PLL for timebase 3 MSeries_RTSI_10MHz_Bit = 0x80 // use 10MHz instead of 20MHz for RTSI clock frequency @@ -1054,23 +1069,23 @@ enum MSeries_PLL_Control_Bits }; static inline unsigned MSeries_PLL_Divisor_Bits(unsigned divisor) { - static const unsigned max_divisor = 0xf; + static const unsigned max_divisor = 0x10; if(divisor < 1 || divisor > max_divisor) { rt_printk("%s: bug, invalid divisor=%i\n", __FUNCTION__, divisor); return 0; } - return divisor << 8; + return (divisor & 0xf) << 8; } static inline unsigned MSeries_PLL_Multiplier_Bits(unsigned multiplier) { - static const unsigned max_multiplier = 0xff; + static const unsigned max_multiplier = 0x100; if(multiplier < 1 || multiplier > max_multiplier) { rt_printk("%s: bug, invalid multiplier=%i\n", __FUNCTION__, multiplier); return 0; } - return multiplier; + return multiplier & 0xff; } enum MSeries_PLL_Status @@ -1218,12 +1233,15 @@ static ni_board ni_boards[]; unsigned long serial_interval_ns; \ unsigned char serial_hw_mode; \ unsigned short clock_and_fout; \ + unsigned short clock_and_fout2; \ \ volatile unsigned short int_a_enable_reg; \ volatile unsigned short int_b_enable_reg; \ unsigned short io_bidirection_pin_reg; \ unsigned short rtsi_trig_direction_reg; \ \ + unsigned clock_ns; \ + \ unsigned short atrig_mode; \ unsigned short atrig_high; \ unsigned short atrig_low; \ diff --git a/include/linux/comedi.h b/include/linux/comedi.h index 63707b5c..9a2e1cd8 100644 --- a/include/linux/comedi.h +++ b/include/linux/comedi.h @@ -243,13 +243,12 @@ enum configuration_ids INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, // Use CTR as single pulsegenerator INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, // Use CTR as pulsetraingenerator INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, // Use the counter as encoder - INSN_CONFIG_SET_GATE_SRC = 2001, // Set CTR gate source - INSN_CONFIG_GET_GATE_SRC = 2002, // Get CTR gate source - INSN_CONFIG_SET_CLOCK_SRC = 2003, // Set CTR clock source - INSN_CONFIG_GET_CLOCK_SRC = 2004, // Get CTR clock source + INSN_CONFIG_SET_GATE_SRC = 2001, // Set gate source + INSN_CONFIG_GET_GATE_SRC = 2002, // Get gate source + INSN_CONFIG_SET_CLOCK_SRC = 2003, // Set master clock source + INSN_CONFIG_GET_CLOCK_SRC = 2004, // Get master clock source INSN_CONFIG_8254_SET_MODE = 4097, - INSN_CONFIG_8254_READ_STATUS = 4098, - INSN_CONFIG_SET_RTSI_CLOCK_MODE = 5000 // Set RTSI bus clock mode + INSN_CONFIG_8254_READ_STATUS = 4098 }; @@ -521,21 +520,19 @@ enum i8254_mode I8254_BINARY = 0 }; -/* RTSI Clock mode */ -#define COMEDI_RTSI_CLOCK_MODE_INTERNAL 0x00 // Internal clock mode -#define COMEDI_RTSI_CLOCK_MODE_OUTPUT 0x01 // Outputs clock to RTSI -#define COMEDI_RTSI_CLOCK_MODE_SLAVE 0x02 // Runs from RTSI clock -#define COMEDI_RTSI_CLOCK_MODE_MASTER 0x03 // Outputs clock to RTSI and runs from this external clock - -/* RTSI BUS pins */ -#define NI_RTSI_0 0 -#define NI_RTSI_1 1 -#define NI_RTSI_2 2 -#define NI_RTSI_3 3 -#define NI_RTSI_4 4 -#define NI_RTSI_5 5 -#define NI_RTSI_6 6 -#define NI_RTSI_7 7 +/* clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */ +enum ni_mio_clock_source +{ + NI_MIO_INTERNAL_CLOCK = 0, + NI_MIO_RTSI_CLOCK = 1, + NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2, + NI_MIO_PLL_PXI10_CLOCK = 3, + NI_MIO_PLL_RTSI0_CLOCK = 4 +}; +static inline unsigned NI_MIO_PLL_RTSI_CLOCK(unsigned rtsi_channel) +{ + return NI_MIO_PLL_RTSI0_CLOCK + rtsi_channel; +} /* RTSI BUS pin usage in standard configuration */ #define NI_RTSI_STD_AI_START1 0 -- 2.26.2