mirror of https://gitee.com/openkylin/linux.git
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:
parent
48d43be475
commit
546bf3382e
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue