[PATCH 001/108] staging: comedi: addi_apci_3120: introduce apci3120_ns_to_timer()
H Hartley Sweeten
hsweeten at visionengravers.com
Tue Nov 4 17:53:45 UTC 2014
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 at visionengravers.com>
Cc: Ian Abbott <abbotti at mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
---
.../comedi/drivers/addi-data/hwdrv_apci3120.c | 137 +++++----------------
drivers/staging/comedi/drivers/addi_apci_3120.c | 71 +++++++++++
2 files changed, 100 insertions(+), 108 deletions(-)
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
index c64799e..960002d 100644
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
@@ -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;
diff --git a/drivers/staging/comedi/drivers/addi_apci_3120.c b/drivers/staging/comedi/drivers/addi_apci_3120.c
index 056b3bf..c770e46 100644
--- a/drivers/staging/comedi/drivers/addi_apci_3120.c
+++ b/drivers/staging/comedi/drivers/addi_apci_3120.c
@@ -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)
@@ -23,6 +24,14 @@
* 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;
--
2.0.3
More information about the devel
mailing list