summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/addi_apci_3120.c
diff options
context:
space:
mode:
authorH Hartley Sweeten <hsweeten@visionengravers.com>2014-11-04 10:53:45 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-07 09:33:56 -0800
commit546bf3382e41b8a5a4c216ea7bdc0f517ce04e22 (patch)
treefe77f423d91e79a2e174164679060e185633aee2 /drivers/staging/comedi/drivers/addi_apci_3120.c
parent48d43be475e48f5673b89c1faa6f7855d25f9a85 (diff)
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>
Diffstat (limited to 'drivers/staging/comedi/drivers/addi_apci_3120.c')
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3120.c71
1 files changed, 71 insertions, 0 deletions
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;