Added support for configuring external clock sources on ni mio boards,
authorFrank Mori Hess <fmhess@speakeasy.net>
Thu, 19 Oct 2006 21:06:18 +0000 (21:06 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Thu, 19 Oct 2006 21:06:18 +0000 (21:06 +0000)
including using the PLL on m-series boards.

Documentation/comedi/insn_config
comedi/drivers/ni_mio_common.c
comedi/drivers/ni_pcimio.c
comedi/drivers/ni_stc.h
include/linux/comedi.h

index 697051431b8fc22f4b0be7d1491fd4a0e4e0dffe..aa71851c993a84a62f3b90302bad69393aa4f1c5 100644 (file)
@@ -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)
index 73db889e59994fccb11426ed39d3eeb537836837..37a0244f7686d33e85f3ae2a044636831399f03b 100644 (file)
@@ -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_arg<cmd->convert_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<<bit)) ? INSN_CONFIG_DIO_OUTPUT : INSN_CONFIG_DIO_INPUT;
-                       return 2;
-                       break;
-               default:
-                       return -EINVAL;
+static int ni_rtsi_insn_config(comedi_device *dev,comedi_subdevice *s,
+       comedi_insn *insn,lsampl_t *data)
+{
+       unsigned int chan = CR_CHAN(insn->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;
 }
 
index 32d53b6202b705f8a1b565ac393c40812ce565c3..b4f11fbe6c75c65948ed678dd7f05c780aa1106a 100644 (file)
@@ -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;
index f04db160c6b2d8290ec7594ff3534fa0ace234c1..291e58f47c8bbdaffe759427695d253fc69edfd8 100644 (file)
@@ -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;                               \
index 63707b5c4229e131c578c9a64b04c109037712ba..9a2e1cd8ab5f37ba2fda91ba75c057d14fc481cc 100644 (file)
@@ -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