Module: xenomai-abe
Branch: comedi
Commit: 0379dba5f930a372c1764f3d6834a25acb3533b4
URL:    
http://git.xenomai.org/?p=xenomai-abe.git;a=commit;h=0379dba5f930a372c1764f3d6834a25acb3533b4

Author: Alexis Berlemont <alexis.berlem...@gmail.com>
Date:   Fri Sep 11 01:26:04 2009 +0200

Add missing RTSI / clock configuration routines

---

 .../comedi/national_instruments/mio_common.c       |  227 ++++++++++++++++++++
 1 files changed, 227 insertions(+), 0 deletions(-)

diff --git a/ksrc/drivers/comedi/national_instruments/mio_common.c 
b/ksrc/drivers/comedi/national_instruments/mio_common.c
index fe1f97d..3f70c3e 100644
--- a/ksrc/drivers/comedi/national_instruments/mio_common.c
+++ b/ksrc/drivers/comedi/national_instruments/mio_common.c
@@ -4494,6 +4494,231 @@ static int ni_pfi_insn_config(comedi_subd_t *subd, 
comedi_kinsn_t *insn)
        return 0;
 }
 
+/*
+ *
+ *  RTSI Bus Functions
+ *
+ */
+
+/* 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 int reference_period_ns,
+                                        unsigned int *freq_divider, 
+                                        unsigned int *freq_multiplier,
+                                        unsigned int *actual_period_ns)
+{
+       unsigned div;
+       unsigned best_div = 1;
+       static const unsigned max_div = 0x10;
+       unsigned mult;
+       unsigned best_mult = 1;
+       static const unsigned max_mult = 0x100;
+       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;
+       static const unsigned fudge_factor_80_to_20Mhz = 4;
+       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 (abs(new_period_ps - target_picosec) <
+                               abs(best_period_picosec - target_picosec)) {
+                               best_period_picosec = new_period_ps;
+                               best_div = div;
+                               best_mult = mult;
+                       }
+               }
+       }
+       if (best_period_picosec == 0) {
+               __comedi_err("%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 * fudge_factor_80_to_20Mhz +
+               (pico_per_nano / 2)) / pico_per_nano;
+       return 0;
+}
+
+static int ni_mseries_set_pll_master_clock(comedi_dev_t * dev, 
+                                          unsigned int source, 
+                                          unsigned int period_ns)
+{
+       static const unsigned min_period_ns = 50;
+       static const unsigned max_period_ns = 1000;
+       static const unsigned timeout = 1000;
+       unsigned pll_control_bits;
+       unsigned freq_divider;
+       unsigned freq_multiplier;
+       unsigned i;
+       int retval;
+       if (source == NI_MIO_PLL_PXI10_CLOCK)
+               period_ns = 100;
+       /* These limits are somewhat arbitrary, but NI advertises 1 to
+          20MHz range so we'll use that */
+       if (period_ns < min_period_ns || period_ns > max_period_ns) {
+               comedi_err(dev,
+                          "%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);
+       pll_control_bits =
+               MSeries_PLL_Enable_Bit | MSeries_PLL_VCO_Mode_75_150MHz_Bits;
+       devpriv->clock_and_fout2 |=
+               MSeries_Timebase1_Select_Bit | MSeries_Timebase3_Select_Bit;
+       devpriv->clock_and_fout2 &= ~MSeries_PLL_In_Source_Select_Mask;
+       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(period_ns, &freq_divider,
+                       &freq_multiplier, &devpriv->clock_ns);
+               if (retval < 0)
+                       return retval;
+               break;
+       default:
+               {
+                       unsigned rtsi_channel;
+                       static const unsigned max_rtsi_channel = 7;
+                       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);
+       ni_writew(pll_control_bits, M_Offset_PLL_Control);
+       devpriv->clock_source = source;
+       /* 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) {
+               comedi_err(dev, 
+                          "%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;
+}
+
+static int ni_set_master_clock(comedi_dev_t *dev, 
+                              unsigned int source, unsigned int period_ns)
+{
+       if (source == 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 = TIMEBASE_1_NS;
+               if (boardtype.reg_type & ni_reg_m_series_mask) {
+                       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);
+               }
+               devpriv->clock_source = source;
+       } else {
+               if (boardtype.reg_type & ni_reg_m_series_mask) {
+                       return ni_mseries_set_pll_master_clock(dev, source,
+                               period_ns);
+               } else {
+                       if (source == 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);
+                               if (devpriv->clock_ns == 0) {
+                                       comedi_err(dev,
+                                                  "%s: we don't handle an "
+                                                  "unspecified clock period "
+                                                  "correctly yet, returning 
error.\n",
+                                                  __FUNCTION__);
+                                       return -EINVAL;
+                               } else {
+                                       devpriv->clock_ns = period_ns;
+                               }
+                               devpriv->clock_source = source;
+                       } else
+                               return -EINVAL;
+               }
+       }
+       return 3;
+}
+
+static void ni_rtsi_init(comedi_dev_t * dev)
+{
+       /* Initialise the RTSI bus signal switch to a default state */
+
+       /* Set clock mode to internal */
+       devpriv->clock_and_fout2 = MSeries_RTSI_10MHz_Bit;
+       if (ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0) {
+               comedi_err(dev, "ni_set_master_clock failed, bug?");
+       }
+       /* Default internal lines routing to RTSI bus lines */
+       devpriv->rtsi_trig_a_output_reg =
+               RTSI_Trig_Output_Bits(0,
+               NI_RTSI_OUTPUT_ADR_START1) | RTSI_Trig_Output_Bits(1,
+               NI_RTSI_OUTPUT_ADR_START2) | RTSI_Trig_Output_Bits(2,
+               NI_RTSI_OUTPUT_SCLKG) | RTSI_Trig_Output_Bits(3,
+               NI_RTSI_OUTPUT_DACUPDN);
+       devpriv->stc_writew(dev, devpriv->rtsi_trig_a_output_reg,
+               RTSI_Trig_A_Output_Register);
+       devpriv->rtsi_trig_b_output_reg =
+               RTSI_Trig_Output_Bits(4,
+               NI_RTSI_OUTPUT_DA_START1) | RTSI_Trig_Output_Bits(5,
+               NI_RTSI_OUTPUT_G_SRC0) | RTSI_Trig_Output_Bits(6,
+               NI_RTSI_OUTPUT_G_GATE0);
+       if (boardtype.reg_type & ni_reg_m_series_mask)
+               devpriv->rtsi_trig_b_output_reg |=
+                       RTSI_Trig_Output_Bits(7, NI_RTSI_OUTPUT_RTSI_OSC);
+       devpriv->stc_writew(dev, devpriv->rtsi_trig_b_output_reg,
+               RTSI_Trig_B_Output_Register);
+}
+
 int ni_E_init(comedi_dev_t *dev)
 {
        int ret;
@@ -4803,6 +5028,8 @@ int ni_E_init(comedi_dev_t *dev)
                return -ENOMEM;
 #if 1 /* TODO: add RTSI subdevice */
        subd->flags = COMEDI_SUBD_UNUSED;
+       ni_rtsi_init(dev);
+
 #else /* TODO: add RTSI subdevice */
        subd->flags = COMEDI_SUBD_DIO;
 


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to