[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