From: H Hartley Sweeten Date: Tue, 4 Nov 2014 17:53:45 +0000 (-0700) Subject: staging: comedi: addi_apci_3120: introduce apci3120_ns_to_timer() X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=546bf3382e41b8a5a4c216ea7bdc0f517ce04e22;p=deliverable%2Flinux.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 Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c index c64799e3513c..960002dbaeca 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 056b3bf82094..c770e4643ce0 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;