staging: comedi: addi_apci_3120: introduce apci3120_ns_to_timer()

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>
This commit is contained in:
H Hartley Sweeten 2014-11-04 10:53:45 -07:00 committed by Greg Kroah-Hartman
parent 48d43be475
commit 546bf3382e
2 changed files with 100 additions and 108 deletions

View 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;

View 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)
@ -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;