staging: comedi: addi_apci_3120: introduce apci3120_ns_to_timer()
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Tue, 4 Nov 2014 17:53:45 +0000 (10:53 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Nov 2014 17:33:56 +0000 (09:33 -0800)
The timer divisor calculations in this driver are over complicated.

There are three timers on the board. They all use the same base clock
with a fixed prescaler for each timer. The base clock used depends on
the board version and type:

  APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns)
  APCI-3120 Rev B boards OSC = 20MHz base clock (50ns)
  APCI-3001 boards OSC = 20MHz base clock (50ns)

The prescalers for each timer are:

  Timer 0 CLK = OSC/10
  Timer 1 CLK = OSC/1000
  Timer 2 CLK = OSC/1000

Add a new member to the private data, 'osc_base', to hold the base clock
time. Set this member during the board attach.

Introduce a helper function to calculate the divisor needed to generate
a nanosecond time with a given timer.

Use the new helper function in the driver to clarify the code.

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
drivers/staging/comedi/drivers/addi_apci_3120.c

index c64799e3513ce2f1b5ddd72113c465a31fde03cd..960002dbaeca5d1bb05f3fe5547859fca93a2ecb 100644 (file)
@@ -309,14 +309,16 @@ static int apci3120_ai_insn_read(struct comedi_device *dev,
                                 unsigned int *data)
 {
        struct apci3120_private *devpriv = dev->private;
-       unsigned short us_ConvertTiming, us_TmpValue, i;
+       unsigned int divisor;
+       unsigned int ns;
+       unsigned short us_TmpValue, i;
        unsigned char b_Tmp;
 
        /*  fix conversion time to 10 us */
        if (!devpriv->ui_EocEosConversionTime)
-               us_ConvertTiming = 10;
+               ns = 10000;
        else
-               us_ConvertTiming = (unsigned short) (devpriv->ui_EocEosConversionTime / 1000);  /*  nano to useconds */
+               ns = devpriv->ui_EocEosConversionTime;
 
        /*  Clear software registers */
        devpriv->b_TimerSelectMode = 0;
@@ -329,20 +331,7 @@ static int apci3120_ai_insn_read(struct comedi_device *dev,
        } else {
                devpriv->tsk_Current = current; /*  Save the current process task structure */
 
-               /*
-                * Testing if board have the new Quartz and calculate the time value
-                * to set in the timer
-                */
-               us_TmpValue = inw(dev->iobase + APCI3120_RD_STATUS);
-
-               /* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
-               if ((us_TmpValue & 0x00B0) == 0x00B0
-                       || !strcmp(dev->board_name, "apci3001")) {
-                       us_ConvertTiming = (us_ConvertTiming * 2) - 2;
-               } else {
-                       us_ConvertTiming =
-                               ((us_ConvertTiming * 12926) / 10000) - 1;
-               }
+               divisor = apci3120_ns_to_timer(dev, 0, ns, CMDF_ROUND_NEAREST);
 
                us_TmpValue = (unsigned short) devpriv->b_InterruptMode;
 
@@ -408,8 +397,7 @@ static int apci3120_ai_insn_read(struct comedi_device *dev,
                        outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
                        /* Set the conversion time */
-                       outw(us_ConvertTiming,
-                            dev->iobase + APCI3120_TIMER_VALUE);
+                       outw(divisor, dev->iobase + APCI3120_TIMER_VALUE);
 
                        us_TmpValue =
                                (unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
@@ -467,8 +455,7 @@ static int apci3120_ai_insn_read(struct comedi_device *dev,
                        outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
                        /* Set the conversion time */
-                       outw(us_ConvertTiming,
-                            dev->iobase + APCI3120_TIMER_VALUE);
+                       outw(divisor, dev->iobase + APCI3120_TIMER_VALUE);
 
                        /* Set the scan bit */
                        devpriv->b_ModeSelectRegister =
@@ -719,14 +706,11 @@ static int apci3120_cyclic_ai(int mode,
        struct apci3120_private *devpriv = dev->private;
        struct comedi_cmd *cmd = &s->async->cmd;
        unsigned char b_Tmp;
-       unsigned int ui_DelayTiming = 0;
-       unsigned int ui_TimerValue1 = 0;
+       unsigned int divisor1 = 0;
        unsigned int dmalen0 = 0;
        unsigned int dmalen1 = 0;
        unsigned int ui_TimerValue2 = 0;
-       unsigned int ui_TimerValue0;
-       unsigned int ui_ConvertTiming;
-       unsigned short us_TmpValue;
+       unsigned int divisor0;
 
        /* Resets the FIFO */
        inb(dev->iobase + APCI3120_RESET_FIFO);
@@ -759,42 +743,17 @@ static int apci3120_cyclic_ai(int mode,
 
        /* value for timer2  minus -2 has to be done */
        ui_TimerValue2 = cmd->stop_arg - 2;
-       ui_ConvertTiming = cmd->convert_arg;
-
-       if (mode == 2)
-               ui_DelayTiming = cmd->scan_begin_arg;
 
        /* Initializes the sequence array */
        if (!apci3120_setup_chan_list(dev, s, devpriv->ui_AiNbrofChannels,
                        cmd->chanlist, 0))
                return -EINVAL;
 
-       us_TmpValue = (unsigned short) inw(dev->iobase + APCI3120_RD_STATUS);
-
-       /* EL241003 Begin: add this section to replace floats calculation by integer calculations */
-       /* EL250804: Testing if board APCI3120 have the new Quartz or if it is an APCI3001 */
-       if ((us_TmpValue & 0x00B0) == 0x00B0
-               || !strcmp(dev->board_name, "apci3001")) {
-               ui_TimerValue0 = ui_ConvertTiming * 2 - 2000;
-               ui_TimerValue0 = ui_TimerValue0 / 1000;
-
-               if (mode == 2) {
-                       ui_DelayTiming = ui_DelayTiming / 1000;
-                       ui_TimerValue1 = ui_DelayTiming * 2 - 200;
-                       ui_TimerValue1 = ui_TimerValue1 / 100;
-               }
-       } else {
-               ui_ConvertTiming = ui_ConvertTiming / 1000;
-               ui_TimerValue0 = ui_ConvertTiming * 12926 - 10000;
-               ui_TimerValue0 = ui_TimerValue0 / 10000;
-
-               if (mode == 2) {
-                       ui_DelayTiming = ui_DelayTiming / 1000;
-                       ui_TimerValue1 = ui_DelayTiming * 12926 - 1;
-                       ui_TimerValue1 = ui_TimerValue1 / 1000000;
-               }
+       divisor0 = apci3120_ns_to_timer(dev, 0, cmd->convert_arg, cmd->flags);
+       if (mode == 2) {
+               divisor1 = apci3120_ns_to_timer(dev, 1, cmd->scan_begin_arg,
+                                               cmd->flags);
        }
-       /* EL241003 End */
 
        if (devpriv->b_ExttrigEnable == APCI3120_ENABLE)
                apci3120_exttrig_enable(dev);   /*  activate EXT trigger */
@@ -813,8 +772,7 @@ static int apci3120_cyclic_ai(int mode,
                        APCI3120_SELECT_TIMER_0_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
                /* Set the conversion time */
-               outw(((unsigned short) ui_TimerValue0),
-                       dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor0, dev->iobase + APCI3120_TIMER_VALUE);
                break;
 
        case 2:
@@ -831,8 +789,7 @@ static int apci3120_cyclic_ai(int mode,
                        APCI3120_SELECT_TIMER_1_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
                /* Set the conversion time */
-               outw(((unsigned short) ui_TimerValue1),
-                       dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor1, dev->iobase + APCI3120_TIMER_VALUE);
 
                /*  init timer0 in mode 2 */
                devpriv->b_TimerSelectMode =
@@ -848,8 +805,7 @@ static int apci3120_cyclic_ai(int mode,
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
                /* Set the conversion time */
-               outw(((unsigned short) ui_TimerValue0),
-                       dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor0, dev->iobase + APCI3120_TIMER_VALUE);
                break;
 
        }
@@ -1487,8 +1443,7 @@ static int apci3120_config_insn_timer(struct comedi_device *dev,
                                      unsigned int *data)
 {
        struct apci3120_private *devpriv = dev->private;
-       unsigned int ui_Timervalue2;
-       unsigned short us_TmpValue;
+       unsigned int divisor;
        unsigned char b_Tmp;
 
        if (!data[1])
@@ -1496,22 +1451,7 @@ static int apci3120_config_insn_timer(struct comedi_device *dev,
 
        devpriv->b_Timer2Interrupt = (unsigned char) data[2];   /*  save info whether to enable or disable interrupt */
 
-       ui_Timervalue2 = data[1] / 1000;        /*  convert nano seconds  to u seconds */
-
-       us_TmpValue = inw(dev->iobase + APCI3120_RD_STATUS);
-
-       /*
-        * EL250804: Testing if board APCI3120 have the new Quartz or if it
-        * is an APCI3001 and calculate the time value to set in the timer
-        */
-       if ((us_TmpValue & 0x00B0) == 0x00B0
-               || !strcmp(dev->board_name, "apci3001")) {
-               /* Calculate the time value to set in the timer */
-               ui_Timervalue2 = ui_Timervalue2 / 50;
-       } else {
-               /* Calculate the time value to set in the timer */
-               ui_Timervalue2 = ui_Timervalue2 / 70;
-       }
+       divisor = apci3120_ns_to_timer(dev, 2, data[1], CMDF_ROUND_DOWN);
 
        /* Reset gate 2 of Timer 2 to disable it (Set Bit D14 to 0) */
        devpriv->us_OutputRegister =
@@ -1551,15 +1491,14 @@ static int apci3120_config_insn_timer(struct comedi_device *dev,
                                b_DigitalOutputRegister) & 0xF0) |
                        APCI3120_SELECT_TIMER_2_LOW_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
-               outw(ui_Timervalue2 & 0xffff,
-                    dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor & 0xffff, dev->iobase + APCI3120_TIMER_VALUE);
 
                /* Writing HIGH unsigned short */
                b_Tmp = ((devpriv->
                                b_DigitalOutputRegister) & 0xF0) |
                        APCI3120_SELECT_TIMER_2_HIGH_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
-               outw((ui_Timervalue2 >> 16) & 0xffff,
+               outw((divisor >> 16) & 0xffff,
                     dev->iobase + APCI3120_TIMER_VALUE);
                /*  timer2 in Timer mode enabled */
                devpriv->b_Timer2Mode = APCI3120_TIMER;
@@ -1586,8 +1525,7 @@ static int apci3120_config_insn_timer(struct comedi_device *dev,
                                b_DigitalOutputRegister) & 0xF0) |
                        APCI3120_SELECT_TIMER_2_LOW_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
-               outw(ui_Timervalue2 & 0xffff,
-                    dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor & 0xffff, dev->iobase + APCI3120_TIMER_VALUE);
 
                /* Writing HIGH unsigned short */
                b_Tmp = ((devpriv->
@@ -1595,7 +1533,7 @@ static int apci3120_config_insn_timer(struct comedi_device *dev,
                        APCI3120_SELECT_TIMER_2_HIGH_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
-               outw((ui_Timervalue2 >> 16) & 0xffff,
+               outw((divisor >> 16) & 0xffff,
                     dev->iobase + APCI3120_TIMER_VALUE);
                /* watchdog enabled */
                devpriv->b_Timer2Mode = APCI3120_WATCHDOG;
@@ -1624,8 +1562,7 @@ static int apci3120_write_insn_timer(struct comedi_device *dev,
                                     unsigned int *data)
 {
        struct apci3120_private *devpriv = dev->private;
-       unsigned int ui_Timervalue2 = 0;
-       unsigned short us_TmpValue;
+       unsigned int divisor;
        unsigned char b_Tmp;
 
        if ((devpriv->b_Timer2Mode != APCI3120_WATCHDOG)
@@ -1640,11 +1577,6 @@ static int apci3120_write_insn_timer(struct comedi_device *dev,
                                "timer2 not configured in TIMER MODE\n");
                        return -EINVAL;
                }
-
-               if (data[1])
-                       ui_Timervalue2 = data[1];
-               else
-                       ui_Timervalue2 = 0;
        }
 
        switch (data[0]) {
@@ -1734,28 +1666,17 @@ static int apci3120_write_insn_timer(struct comedi_device *dev,
                                "timer2 not configured in TIMER MODE\n");
                        return -EINVAL;
                }
-               us_TmpValue = inw(dev->iobase + APCI3120_RD_STATUS);
 
-               /*
-                * EL250804: Testing if board APCI3120 have the new Quartz or if it
-                * is an APCI3001 and calculate the time value to set in the timer
-                */
-               if ((us_TmpValue & 0x00B0) == 0x00B0
-                       || !strcmp(dev->board_name, "apci3001")) {
-                       /* Calculate the time value to set in the timer */
-                       ui_Timervalue2 = ui_Timervalue2 / 50;
-               } else {
-                       /* Calculate the time value to set in the timer */
-                       ui_Timervalue2 = ui_Timervalue2 / 70;
-               }
+               divisor = apci3120_ns_to_timer(dev, 2, data[1],
+                                              CMDF_ROUND_DOWN);
+
                /* Writing LOW unsigned short */
                b_Tmp = ((devpriv->
                                b_DigitalOutputRegister) & 0xF0) |
                        APCI3120_SELECT_TIMER_2_LOW_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
-               outw(ui_Timervalue2 & 0xffff,
-                    dev->iobase + APCI3120_TIMER_VALUE);
+               outw(divisor & 0xffff, dev->iobase + APCI3120_TIMER_VALUE);
 
                /* Writing HIGH unsigned short */
                b_Tmp = ((devpriv->
@@ -1763,7 +1684,7 @@ static int apci3120_write_insn_timer(struct comedi_device *dev,
                        APCI3120_SELECT_TIMER_2_HIGH_WORD;
                outb(b_Tmp, dev->iobase + APCI3120_TIMER_CRT0);
 
-               outw((ui_Timervalue2 >> 16) & 0xffff,
+               outw((divisor >> 16) & 0xffff,
                     dev->iobase + APCI3120_TIMER_VALUE);
 
                break;
index 056b3bf820946c86afdca939b97974ecb4240c9b..c770e4643ce0ce6bd87ee76d2f7da0aa54f3d43a 100644 (file)
@@ -15,6 +15,7 @@
 /*
  * PCI BAR 1 register map (dev->iobase)
  */
+#define APCI3120_STATUS_TO_VERSION(x)          (((x) >> 4) & 0xf)
 #define APCI3120_AO_REG(x)                     (0x08 + (((x) / 4) * 2))
 #define APCI3120_AO_MUX(x)                     (((x) & 0x3) << 14)
 #define APCI3120_AO_DATA(x)                    ((x) << 0)
  * PCI BAR 2 register map (devpriv->addon)
  */
 
+/*
+ * Board revisions
+ */
+#define APCI3120_REVA                          0xa
+#define APCI3120_REVB                          0xb
+#define APCI3120_REVA_OSC_BASE                 70      /* 70ns = 14.29MHz */
+#define APCI3120_REVB_OSC_BASE                 50      /* 50ns = 20MHz */
+
 enum apci3120_boardid {
        BOARD_APCI3120,
        BOARD_APCI3001,
@@ -55,6 +64,7 @@ struct apci3120_dmabuf {
 struct apci3120_private {
        unsigned long amcc;
        unsigned long addon;
+       unsigned int osc_base;
        unsigned int ui_AiNbrofChannels;
        unsigned int ui_AiChannelList[32];
        unsigned int ui_AiReadData[32];
@@ -76,6 +86,59 @@ struct apci3120_private {
        struct task_struct *tsk_Current;
 };
 
+/*
+ * There are three timers on the board. They all use the same base
+ * clock with a fixed prescaler for each timer. The base clock used
+ * depends on the board version and type.
+ *
+ * APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns)
+ * APCI-3120 Rev B boards OSC = 20MHz base clock (50ns)
+ * APCI-3001 boards OSC = 20MHz base clock (50ns)
+ *
+ * The prescalers for each timer are:
+ * Timer 0 CLK = OSC/10
+ * Timer 1 CLK = OSC/1000
+ * Timer 2 CLK = OSC/1000
+ */
+static unsigned int apci3120_ns_to_timer(struct comedi_device *dev,
+                                        unsigned int timer,
+                                        unsigned int ns,
+                                        unsigned int flags)
+{
+       struct apci3120_private *devpriv = dev->private;
+       unsigned int prescale = (timer == 0) ? 10 : 1000;
+       unsigned int timer_base = devpriv->osc_base * prescale;
+       unsigned int divisor;
+
+       switch (flags & CMDF_ROUND_MASK) {
+       case CMDF_ROUND_UP:
+               divisor = DIV_ROUND_UP(ns, timer_base);
+               break;
+       case CMDF_ROUND_DOWN:
+               divisor = ns / timer_base;
+               break;
+       case CMDF_ROUND_NEAREST:
+       default:
+               divisor = DIV_ROUND_CLOSEST(ns, timer_base);
+               break;
+       }
+
+       if (timer == 2) {
+               /* timer 2 is 24-bits */
+               if (divisor > 0x00ffffff)
+                       divisor = 0x00ffffff;
+       } else {
+               /* timers 0 and 1 are 16-bits */
+               if (divisor > 0xffff)
+                       divisor = 0xffff;
+       }
+       /* the timers require a minimum divisor of 2 */
+       if (divisor < 2)
+               divisor = 2;
+
+       return divisor;
+}
+
 #include "addi-data/hwdrv_apci3120.c"
 
 static void apci3120_dma_alloc(struct comedi_device *dev)
@@ -131,6 +194,7 @@ static int apci3120_auto_attach(struct comedi_device *dev,
        const struct apci3120_board *this_board = NULL;
        struct apci3120_private *devpriv;
        struct comedi_subdevice *s;
+       unsigned int status;
        int ret;
 
        if (context < ARRAY_SIZE(apci3120_boardtypes))
@@ -165,6 +229,13 @@ static int apci3120_auto_attach(struct comedi_device *dev,
                }
        }
 
+       status = inw(dev->iobase + APCI3120_RD_STATUS);
+       if (APCI3120_STATUS_TO_VERSION(status) == APCI3120_REVB ||
+           context == BOARD_APCI3001)
+               devpriv->osc_base = APCI3120_REVB_OSC_BASE;
+       else
+               devpriv->osc_base = APCI3120_REVA_OSC_BASE;
+
        ret = comedi_alloc_subdevices(dev, 5);
        if (ret)
                return ret;