These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / rtd520.c
index 4c13f5e..68ac02b 100644 (file)
@@ -72,8 +72,6 @@
  * As far as I can tell, the About interrupt doesn't work if Sample is
  * also enabled. It turns out that About really isn't needed, since
  * we always count down samples read.
- *
- * There was some timer/counter code, but it didn't follow the right API.
  */
 
 /*
@@ -99,6 +97,7 @@
 
 #include "../comedi_pci.h"
 
+#include "comedi_8254.h"
 #include "plx9080.h"
 
 /*
  */
 #define LAS0_USER_IO           0x0008  /* User I/O */
 #define LAS0_ADC               0x0010  /* FIFO Status/Software A/D Start */
-#define FS_DAC1_NOT_EMPTY      (1 << 0)        /* DAC1 FIFO not empty */
-#define FS_DAC1_HEMPTY         (1 << 1)        /* DAC1 FIFO half empty */
-#define FS_DAC1_NOT_FULL       (1 << 2)        /* DAC1 FIFO not full */
-#define FS_DAC2_NOT_EMPTY      (1 << 4)        /* DAC2 FIFO not empty */
-#define FS_DAC2_HEMPTY         (1 << 5)        /* DAC2 FIFO half empty */
-#define FS_DAC2_NOT_FULL       (1 << 6)        /* DAC2 FIFO not full */
-#define FS_ADC_NOT_EMPTY       (1 << 8)        /* ADC FIFO not empty */
-#define FS_ADC_HEMPTY          (1 << 9)        /* ADC FIFO half empty */
-#define FS_ADC_NOT_FULL                (1 << 10)       /* ADC FIFO not full */
-#define FS_DIN_NOT_EMPTY       (1 << 12)       /* DIN FIFO not empty */
-#define FS_DIN_HEMPTY          (1 << 13)       /* DIN FIFO half empty */
-#define FS_DIN_NOT_FULL                (1 << 14)       /* DIN FIFO not full */
-#define LAS0_DAC1              0x0014  /* Software D/A1 Update (w) */
-#define LAS0_DAC2              0x0018  /* Software D/A2 Update (w) */
+#define FS_DAC1_NOT_EMPTY      BIT(0)  /* DAC1 FIFO not empty */
+#define FS_DAC1_HEMPTY         BIT(1)  /* DAC1 FIFO half empty */
+#define FS_DAC1_NOT_FULL       BIT(2)  /* DAC1 FIFO not full */
+#define FS_DAC2_NOT_EMPTY      BIT(4)  /* DAC2 FIFO not empty */
+#define FS_DAC2_HEMPTY         BIT(5)  /* DAC2 FIFO half empty */
+#define FS_DAC2_NOT_FULL       BIT(6)  /* DAC2 FIFO not full */
+#define FS_ADC_NOT_EMPTY       BIT(8)  /* ADC FIFO not empty */
+#define FS_ADC_HEMPTY          BIT(9)  /* ADC FIFO half empty */
+#define FS_ADC_NOT_FULL                BIT(10) /* ADC FIFO not full */
+#define FS_DIN_NOT_EMPTY       BIT(12) /* DIN FIFO not empty */
+#define FS_DIN_HEMPTY          BIT(13) /* DIN FIFO half empty */
+#define FS_DIN_NOT_FULL                BIT(14) /* DIN FIFO not full */
+#define LAS0_UPDATE_DAC(x)     (0x0014 + ((x) * 0x4))  /* D/Ax Update (w) */
 #define LAS0_DAC               0x0024  /* Software Simultaneous Update (w) */
 #define LAS0_PACER             0x0028  /* Software Pacer Start/Stop */
 #define LAS0_TIMER             0x002c  /* Timer Status/HDIN Software Trig. */
 #define LAS0_IT                        0x0030  /* Interrupt Status/Enable */
-#define IRQM_ADC_FIFO_WRITE    (1 << 0)        /* ADC FIFO Write */
-#define IRQM_CGT_RESET         (1 << 1)        /* Reset CGT */
-#define IRQM_CGT_PAUSE         (1 << 3)        /* Pause CGT */
-#define IRQM_ADC_ABOUT_CNT     (1 << 4)        /* About Counter out */
-#define IRQM_ADC_DELAY_CNT     (1 << 5)        /* Delay Counter out */
-#define IRQM_ADC_SAMPLE_CNT    (1 << 6)        /* ADC Sample Counter */
-#define IRQM_DAC1_UCNT         (1 << 7)        /* DAC1 Update Counter */
-#define IRQM_DAC2_UCNT         (1 << 8)        /* DAC2 Update Counter */
-#define IRQM_UTC1              (1 << 9)        /* User TC1 out */
-#define IRQM_UTC1_INV          (1 << 10)       /* User TC1 out, inverted */
-#define IRQM_UTC2              (1 << 11)       /* User TC2 out */
-#define IRQM_DIGITAL_IT                (1 << 12)       /* Digital Interrupt */
-#define IRQM_EXTERNAL_IT       (1 << 13)       /* External Interrupt */
-#define IRQM_ETRIG_RISING      (1 << 14)       /* Ext Trigger rising-edge */
-#define IRQM_ETRIG_FALLING     (1 << 15)       /* Ext Trigger falling-edge */
+#define IRQM_ADC_FIFO_WRITE    BIT(0)  /* ADC FIFO Write */
+#define IRQM_CGT_RESET         BIT(1)  /* Reset CGT */
+#define IRQM_CGT_PAUSE         BIT(3)  /* Pause CGT */
+#define IRQM_ADC_ABOUT_CNT     BIT(4)  /* About Counter out */
+#define IRQM_ADC_DELAY_CNT     BIT(5)  /* Delay Counter out */
+#define IRQM_ADC_SAMPLE_CNT    BIT(6)  /* ADC Sample Counter */
+#define IRQM_DAC1_UCNT         BIT(7)  /* DAC1 Update Counter */
+#define IRQM_DAC2_UCNT         BIT(8)  /* DAC2 Update Counter */
+#define IRQM_UTC1              BIT(9)  /* User TC1 out */
+#define IRQM_UTC1_INV          BIT(10) /* User TC1 out, inverted */
+#define IRQM_UTC2              BIT(11) /* User TC2 out */
+#define IRQM_DIGITAL_IT                BIT(12) /* Digital Interrupt */
+#define IRQM_EXTERNAL_IT       BIT(13) /* External Interrupt */
+#define IRQM_ETRIG_RISING      BIT(14) /* Ext Trigger rising-edge */
+#define IRQM_ETRIG_FALLING     BIT(15) /* Ext Trigger falling-edge */
 #define LAS0_CLEAR             0x0034  /* Clear/Set Interrupt Clear Mask */
 #define LAS0_OVERRUN           0x0038  /* Pending interrupts/Clear Overrun */
 #define LAS0_PCLK              0x0040  /* Pacer Clock (24bit) */
 #define LAS0_DCNT              0x0054  /* Delay counter (16 bit) */
 #define LAS0_ACNT              0x0058  /* About counter (16 bit) */
 #define LAS0_DAC_CLK           0x005c  /* DAC clock (16bit) */
-#define LAS0_UTC0              0x0060  /* 8254 TC Counter 0 */
-#define LAS0_UTC1              0x0064  /* 8254 TC Counter 1 */
-#define LAS0_UTC2              0x0068  /* 8254 TC Counter 2 */
-#define LAS0_UTC_CTRL          0x006c  /* 8254 TC Control */
+#define LAS0_8254_TIMER_BASE   0x0060  /* 8254 timer/counter base */
 #define LAS0_DIO0              0x0070  /* Digital I/O Port 0 */
 #define LAS0_DIO1              0x0074  /* Digital I/O Port 1 */
 #define LAS0_DIO0_CTRL         0x0078  /* Digital I/O Control */
 #define LAS0_CGT_PAUSE         0x0144  /* Table Pause Enable */
 #define LAS0_CGT_RESET         0x0148  /* Reset Channel Gain Table */
 #define LAS0_CGT_CLEAR         0x014c  /* Clear Channel Gain Table */
-#define LAS0_DAC1_CTRL         0x0150  /* D/A1 output type/range */
-#define LAS0_DAC1_SRC          0x0154  /* D/A1 update source */
-#define LAS0_DAC1_CYCLE                0x0158  /* D/A1 cycle mode */
-#define LAS0_DAC1_RESET                0x015c  /* D/A1 FIFO reset */
-#define LAS0_DAC1_FIFO_CLEAR   0x0160  /* D/A1 FIFO clear */
-#define LAS0_DAC2_CTRL         0x0164  /* D/A2 output type/range */
-#define LAS0_DAC2_SRC          0x0168  /* D/A2 update source */
-#define LAS0_DAC2_CYCLE                0x016c  /* D/A2 cycle mode */
-#define LAS0_DAC2_RESET                0x0170  /* D/A2 FIFO reset */
-#define LAS0_DAC2_FIFO_CLEAR   0x0174  /* D/A2 FIFO clear */
+#define LAS0_DAC_CTRL(x)       (0x0150 + ((x) * 0x14)) /* D/Ax type/range */
+#define LAS0_DAC_SRC(x)                (0x0154 + ((x) * 0x14)) /* D/Ax update source */
+#define LAS0_DAC_CYCLE(x)      (0x0158 + ((x) * 0x14)) /* D/Ax cycle mode */
+#define LAS0_DAC_RESET(x)      (0x015c + ((x) * 0x14)) /* D/Ax FIFO reset */
+#define LAS0_DAC_FIFO_CLEAR(x) (0x0160 + ((x) * 0x14)) /* D/Ax FIFO clear */
 #define LAS0_ADC_SCNT_SRC      0x0178  /* A/D Sample Counter Source select */
 #define LAS0_PACER_SELECT      0x0180  /* Pacer Clock select */
 #define LAS0_SBUS0_SRC         0x0184  /* SyncBus 0 Source select */
 #define LAS0_SBUS2_ENABLE      0x019c  /* SyncBus 2 enable */
 #define LAS0_ETRG_POLARITY     0x01a4  /* Ext. Trigger polarity select */
 #define LAS0_EINT_POLARITY     0x01a8  /* Ext. Interrupt polarity select */
-#define LAS0_UTC0_CLOCK                0x01ac  /* UTC0 Clock select */
-#define LAS0_UTC0_GATE         0x01b0  /* UTC0 Gate select */
-#define LAS0_UTC1_CLOCK                0x01b4  /* UTC1 Clock select */
-#define LAS0_UTC1_GATE         0x01b8  /* UTC1 Gate select */
-#define LAS0_UTC2_CLOCK                0x01bc  /* UTC2 Clock select */
-#define LAS0_UTC2_GATE         0x01c0  /* UTC2 Gate select */
+#define LAS0_8254_CLK_SEL(x)   (0x01ac + ((x) * 0x8))  /* 8254 clock select */
+#define LAS0_8254_GATE_SEL(x)  (0x01b0 + ((x) * 0x8))  /* 8254 gate select */
 #define LAS0_UOUT0_SELECT      0x01c4  /* User Output 0 source select */
 #define LAS0_UOUT1_SELECT      0x01c8  /* User Output 1 source select */
 #define LAS0_DMA0_RESET                0x01cc  /* DMA0 Request state machine reset */
  */
 #define LAS1_ADC_FIFO          0x0000  /* A/D FIFO (16bit) */
 #define LAS1_HDIO_FIFO         0x0004  /* HiSpd DI FIFO (16bit) */
-#define LAS1_DAC1_FIFO         0x0008  /* D/A1 FIFO (16bit) */
-#define LAS1_DAC2_FIFO         0x000c  /* D/A2 FIFO (16bit) */
+#define LAS1_DAC_FIFO(x)       (0x0008 + ((x) * 0x4))  /* D/Ax FIFO (16bit) */
 
-/*======================================================================
-  Driver specific stuff (tunable)
-======================================================================*/
+/*
* Driver specific stuff (tunable)
+ */
 
-/* We really only need 2 buffers.  More than that means being much
-   smarter about knowing which ones are full. */
+/*
+ * We really only need 2 buffers.  More than that means being much
+ * smarter about knowing which ones are full.
+ */
 #define DMA_CHAIN_COUNT 2      /* max DMA segments/buffers in a ring (min 2) */
 
 /* Target period for periodic transfers.  This sets the user read latency. */
 /* The board support a channel list up to the FIFO length (1K or 8K) */
 #define RTD_MAX_CHANLIST       128     /* max channel list that we allow */
 
-/*======================================================================
-  Board specific stuff
-======================================================================*/
+/*
* Board specific stuff
+ */
 
 #define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */
 #define RTD_CLOCK_BASE 125     /* clock period in ns */
 /* interrupt at end of block */ | PLX_INTR_TERM_COUNT \
 /* from board to PCI */                | PLX_XFER_LOCAL_TO_PCI)
 
-/*======================================================================
-  Comedi specific stuff
-======================================================================*/
+/*
* Comedi specific stuff
+ */
 
 /*
  * The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)
@@ -352,7 +339,7 @@ struct rtd_boardinfo {
        const struct comedi_lrange *ai_range;
 };
 
-static const struct rtd_boardinfo rtd520Boards[] = {
+static const struct rtd_boardinfo rtd520_boards[] = {
        [BOARD_DM7520] = {
                .name           = "DM7520",
                .range_bip10    = 6,
@@ -376,6 +363,10 @@ struct rtd_private {
        int xfer_count;         /* # to transfer data. 0->1/2FIFO */
        int flags;              /* flag event modes */
        unsigned fifosz;
+
+       /* 8254 Timer/Counter gate and clock sources */
+       unsigned char timer_gate_src[3];
+       unsigned char timer_clk_src[3];
 };
 
 /* bit defines for "flags" */
@@ -384,11 +375,11 @@ struct rtd_private {
 #define DMA1_ACTIVE    0x04    /* DMA1 is active */
 
 /*
-  Given a desired period and the clock period (both in ns),
-  return the proper counter value (divider-1).
 Sets the original period to be the true value.
-  Note: you have to check if the value is larger than the counter range!
-*/
+ * Given a desired period and the clock period (both in ns), return the
+ * proper counter value (divider-1). Sets the original period to be the
* true value.
* Note: you have to check if the value is larger than the counter range!
+ */
 static int rtd_ns_to_timer_base(unsigned int *nanosec,
                                unsigned int flags, int base)
 {
@@ -397,38 +388,38 @@ static int rtd_ns_to_timer_base(unsigned int *nanosec,
        switch (flags & CMDF_ROUND_MASK) {
        case CMDF_ROUND_NEAREST:
        default:
-               divider = (*nanosec + base / 2) / base;
+               divider = DIV_ROUND_CLOSEST(*nanosec, base);
                break;
        case CMDF_ROUND_DOWN:
                divider = (*nanosec) / base;
                break;
        case CMDF_ROUND_UP:
-               divider = (*nanosec + base - 1) / base;
+               divider = DIV_ROUND_UP(*nanosec, base);
                break;
        }
        if (divider < 2)
                divider = 2;    /* min is divide by 2 */
 
-       /* Note: we don't check for max, because different timers
-          have different ranges */
+       /*
+        * Note: we don't check for max, because different timers
+        * have different ranges
+        */
 
        *nanosec = base * divider;
        return divider - 1;     /* countdown is divisor+1 */
 }
 
 /*
-  Given a desired period (in ns),
-  return the proper counter value (divider-1) for the internal clock.
 Sets the original period to be the true value.
-*/
+ * Given a desired period (in ns), return the proper counter value
+ * (divider-1) for the internal clock. Sets the original period to
* be the true value.
+ */
 static int rtd_ns_to_timer(unsigned int *ns, unsigned int flags)
 {
        return rtd_ns_to_timer_base(ns, flags, RTD_CLOCK_BASE);
 }
 
-/*
-  Convert a single comedi channel-gain entry to a RTD520 table entry
-*/
+/* Convert a single comedi channel-gain entry to a RTD520 table entry */
 static unsigned short rtd_convert_chan_gain(struct comedi_device *dev,
                                            unsigned int chanspec, int index)
 {
@@ -473,9 +464,7 @@ static unsigned short rtd_convert_chan_gain(struct comedi_device *dev,
        return r;
 }
 
-/*
-  Setup the channel-gain table from a comedi list
-*/
+/* Setup the channel-gain table from a comedi list */
 static void rtd_load_channelgain_list(struct comedi_device *dev,
                                      unsigned int n_chan, unsigned int *list)
 {
@@ -495,8 +484,10 @@ static void rtd_load_channelgain_list(struct comedi_device *dev,
        }
 }
 
-/* determine fifo size by doing adc conversions until the fifo half
-empty status flag clears */
+/*
+ * Determine fifo size by doing adc conversions until the fifo half
+ * empty status flag clears.
+ */
 static int rtd520_probe_fifo_depth(struct comedi_device *dev)
 {
        unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
@@ -513,7 +504,7 @@ static int rtd520_probe_fifo_depth(struct comedi_device *dev)
                unsigned fifo_status;
                /* trigger conversion */
                writew(0, dev->mmio + LAS0_ADC);
-               udelay(1);
+               usleep_range(1, 1000);
                fifo_status = readl(dev->mmio + LAS0_ADC);
                if ((fifo_status & FS_ADC_HEMPTY) == 0) {
                        fifo_size = 2 * i;
@@ -590,12 +581,6 @@ static int rtd_ai_rinsn(struct comedi_device *dev,
        return n;
 }
 
-/*
-  Get what we know is there.... Fast!
-  This uses 1/2 the bus cycles of read_dregs (below).
-
-  The manual claims that we can do a lword read, but it doesn't work here.
-*/
 static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
                     int count)
 {
@@ -608,7 +593,7 @@ static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
                unsigned int range = CR_RANGE(cmd->chanlist[async->cur_chan]);
                unsigned short d;
 
-               if (0 == devpriv->ai_count) {   /* done */
+               if (devpriv->ai_count == 0) {   /* done */
                        d = readw(devpriv->las1 + LAS1_ADC_FIFO);
                        continue;
                }
@@ -630,12 +615,6 @@ static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
        return 0;
 }
 
-/*
-  Handle all rtd520 interrupts.
-  Runs atomically and is never re-entered.
-  This is a "slow handler";  other interrupts may be active.
-  The data conversion may someday happen in a "bottom half".
-*/
 static irqreturn_t rtd_interrupt(int irq, void *d)
 {
        struct comedi_device *dev = d;
@@ -655,7 +634,7 @@ static irqreturn_t rtd_interrupt(int irq, void *d)
 
        status = readw(dev->mmio + LAS0_IT);
        /* if interrupt was not caused by our board, or handled above */
-       if (0 == status)
+       if (status == 0)
                return IRQ_HANDLED;
 
        if (status & IRQM_ADC_ABOUT_CNT) {      /* sample count -> read FIFO */
@@ -670,7 +649,7 @@ static irqreturn_t rtd_interrupt(int irq, void *d)
                        if (ai_read_n(dev, s, devpriv->fifosz / 2) < 0)
                                goto xfer_abort;
 
-                       if (0 == devpriv->ai_count)
+                       if (devpriv->ai_count == 0)
                                goto xfer_done;
                } else if (devpriv->xfer_count > 0) {
                        if (fifo_status & FS_ADC_NOT_EMPTY) {
@@ -678,7 +657,7 @@ static irqreturn_t rtd_interrupt(int irq, void *d)
                                if (ai_read_n(dev, s, devpriv->xfer_count) < 0)
                                        goto xfer_abort;
 
-                               if (0 == devpriv->ai_count)
+                               if (devpriv->ai_count == 0)
                                        goto xfer_done;
                        }
                }
@@ -715,15 +694,6 @@ xfer_done:
        return IRQ_HANDLED;
 }
 
-/*
-  cmdtest tests a particular command to see if it is valid.
-  Using the cmdtest ioctl, a user can create a valid cmd
-  and then have it executed by the cmd ioctl (asynchronously).
-
-  cmdtest returns 1,2,3,4 or 0, depending on which tests
-  the command passes.
-*/
-
 static int rtd_ai_cmdtest(struct comedi_device *dev,
                          struct comedi_subdevice *s, struct comedi_cmd *cmd)
 {
@@ -760,7 +730,7 @@ static int rtd_ai_cmdtest(struct comedi_device *dev,
 
        if (cmd->scan_begin_src == TRIG_TIMER) {
                /* Note: these are time periods, not actual rates */
-               if (1 == cmd->chanlist_len) {   /* no scanning */
+               if (cmd->chanlist_len == 1) {   /* no scanning */
                        if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
                                                         RTD_MAX_SPEED_1)) {
                                rtd_ns_to_timer(&cmd->scan_begin_arg,
@@ -795,7 +765,7 @@ static int rtd_ai_cmdtest(struct comedi_device *dev,
        }
 
        if (cmd->convert_src == TRIG_TIMER) {
-               if (1 == cmd->chanlist_len) {   /* no scanning */
+               if (cmd->chanlist_len == 1) {   /* no scanning */
                        if (comedi_check_trigger_arg_min(&cmd->convert_arg,
                                                         RTD_MAX_SPEED_1)) {
                                rtd_ns_to_timer(&cmd->convert_arg,
@@ -866,12 +836,6 @@ static int rtd_ai_cmdtest(struct comedi_device *dev,
        return 0;
 }
 
-/*
-  Execute a analog in command with many possible triggering options.
-  The data get stored in the async structure of the subdevice.
-  This is usually done by an interrupt handler.
-  Userland gets to the data using read calls.
-*/
 static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        struct rtd_private *devpriv = dev->private;
@@ -907,7 +871,7 @@ static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
        }
        writel((devpriv->fifosz / 2 - 1) & 0xffff, dev->mmio + LAS0_ACNT);
 
-       if (TRIG_TIMER == cmd->scan_begin_src) {
+       if (cmd->scan_begin_src == TRIG_TIMER) {
                /* scan_begin_arg is in nanoseconds */
                /* find out how many samples to wait before transferring */
                if (cmd->flags & CMDF_WAKE_EOS) {
@@ -959,8 +923,8 @@ static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
        switch (cmd->stop_src) {
        case TRIG_COUNT:        /* stop after N scans */
                devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len;
-               if ((devpriv->xfer_count > 0)
-                   && (devpriv->xfer_count > devpriv->ai_count)) {
+               if ((devpriv->xfer_count > 0) &&
+                   (devpriv->xfer_count > devpriv->ai_count)) {
                        devpriv->xfer_count = devpriv->ai_count;
                }
                break;
@@ -1006,8 +970,10 @@ static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
        }
        /* end configuration */
 
-       /* This doesn't seem to work.  There is no way to clear an interrupt
-          that the priority controller has queued! */
+       /*
+        * This doesn't seem to work.  There is no way to clear an interrupt
+        * that the priority controller has queued!
+        */
        writew(~0, dev->mmio + LAS0_CLEAR);
        readw(dev->mmio + LAS0_CLEAR);
 
@@ -1021,9 +987,6 @@ static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
        return 0;
 }
 
-/*
-  Stop a running data acquisition.
-*/
 static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 {
        struct rtd_private *devpriv = dev->private;
@@ -1053,49 +1016,43 @@ static int rtd_ao_eoc(struct comedi_device *dev,
        return -EBUSY;
 }
 
-static int rtd_ao_winsn(struct comedi_device *dev,
-                       struct comedi_subdevice *s, struct comedi_insn *insn,
-                       unsigned int *data)
+static int rtd_ao_insn_write(struct comedi_device *dev,
+                            struct comedi_subdevice *s,
+                            struct comedi_insn *insn,
+                            unsigned int *data)
 {
        struct rtd_private *devpriv = dev->private;
-       int i;
-       int chan = CR_CHAN(insn->chanspec);
-       int range = CR_RANGE(insn->chanspec);
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int range = CR_RANGE(insn->chanspec);
        int ret;
+       int i;
 
        /* Configure the output range (table index matches the range values) */
-       writew(range & 7,
-              dev->mmio + ((chan == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL));
+       writew(range & 7, dev->mmio + LAS0_DAC_CTRL(chan));
 
-       /* Writing a list of values to an AO channel is probably not
-        * very useful, but that's how the interface is defined. */
        for (i = 0; i < insn->n; ++i) {
-               int val = data[i] << 3;
+               unsigned int val = data[i];
 
-               /* VERIFY: comedi range and offset conversions */
-
-               if ((range > 1) /* bipolar */
-                   && (data[i] < 2048)) {
-                       /* offset and sign extend */
-                       val = (((int)data[i]) - 2048) << 3;
-               } else {        /* unipolor */
-                       val = data[i] << 3;
+               /* bipolar uses 2's complement values with an extended sign */
+               if (comedi_range_is_bipolar(s, range)) {
+                       val = comedi_offset_munge(s, val);
+                       val |= (val & ((s->maxdata + 1) >> 1)) << 1;
                }
 
-               /* a typical programming sequence */
-               writew(val, devpriv->las1 +
-                       ((chan == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO));
-               writew(0, dev->mmio + ((chan == 0) ? LAS0_DAC1 : LAS0_DAC2));
+               /* shift the 12-bit data (+ sign) to match the register */
+               val <<= 3;
 
-               s->readback[chan] = data[i];
+               writew(val, devpriv->las1 + LAS1_DAC_FIFO(chan));
+               writew(0, dev->mmio + LAS0_UPDATE_DAC(chan));
 
                ret = comedi_timeout(dev, s, insn, rtd_ao_eoc, 0);
                if (ret)
                        return ret;
+
+               s->readback[chan] = data[i];
        }
 
-       /* return the number of samples read/written */
-       return i;
+       return insn->n;
 }
 
 static int rtd_dio_insn_bits(struct comedi_device *dev,
@@ -1138,12 +1095,87 @@ static int rtd_dio_insn_config(struct comedi_device *dev,
        return insn->n;
 }
 
+static int rtd_counter_insn_config(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  struct comedi_insn *insn,
+                                  unsigned int *data)
+{
+       struct rtd_private *devpriv = dev->private;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int max_src;
+       unsigned int src;
+
+       switch (data[0]) {
+       case INSN_CONFIG_SET_GATE_SRC:
+               /*
+                * 8254 Timer/Counter gate sources:
+                *
+                * 0 = Not gated, free running (reset state)
+                * 1 = Gated, off
+                * 2 = Ext. TC Gate 1
+                * 3 = Ext. TC Gate 2
+                * 4 = Previous TC out (chan 1 and 2 only)
+                */
+               src = data[2];
+               max_src = (chan == 0) ? 3 : 4;
+               if (src > max_src)
+                       return -EINVAL;
+
+               devpriv->timer_gate_src[chan] = src;
+               writeb(src, dev->mmio + LAS0_8254_GATE_SEL(chan));
+               break;
+       case INSN_CONFIG_GET_GATE_SRC:
+               data[2] = devpriv->timer_gate_src[chan];
+               break;
+       case INSN_CONFIG_SET_CLOCK_SRC:
+               /*
+                * 8254 Timer/Counter clock sources:
+                *
+                * 0 = 8 MHz (reset state)
+                * 1 = Ext. TC Clock 1
+                * 2 = Ext. TX Clock 2
+                * 3 = Ext. Pacer Clock
+                * 4 = Previous TC out (chan 1 and 2 only)
+                * 5 = High-Speed Digital Input Sampling signal (chan 1 only)
+                */
+               src = data[1];
+               switch (chan) {
+               case 0:
+                       max_src = 3;
+                       break;
+               case 1:
+                       max_src = 5;
+                       break;
+               case 2:
+                       max_src = 4;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if (src > max_src)
+                       return -EINVAL;
+
+               devpriv->timer_clk_src[chan] = src;
+               writeb(src, dev->mmio + LAS0_8254_CLK_SEL(chan));
+               break;
+       case INSN_CONFIG_GET_CLOCK_SRC:
+               src = devpriv->timer_clk_src[chan];
+               data[1] = devpriv->timer_clk_src[chan];
+               data[2] = (src == 0) ? RTD_CLOCK_BASE : 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return insn->n;
+}
+
 static void rtd_reset(struct comedi_device *dev)
 {
        struct rtd_private *devpriv = dev->private;
 
        writel(0, dev->mmio + LAS0_BOARD_RESET);
-       udelay(100);            /* needed? */
+       usleep_range(100, 1000);        /* needed? */
        writel(0, devpriv->lcfg + PLX_INTRCS_REG);
        writew(0, dev->mmio + LAS0_IT);
        writew(~0, dev->mmio + LAS0_CLEAR);
@@ -1161,14 +1193,10 @@ static void rtd_init_board(struct comedi_device *dev)
        writel(0, dev->mmio + LAS0_OVERRUN);
        writel(0, dev->mmio + LAS0_CGT_CLEAR);
        writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
-       writel(0, dev->mmio + LAS0_DAC1_RESET);
-       writel(0, dev->mmio + LAS0_DAC2_RESET);
+       writel(0, dev->mmio + LAS0_DAC_RESET(0));
+       writel(0, dev->mmio + LAS0_DAC_RESET(1));
        /* clear digital IO fifo */
        writew(0, dev->mmio + LAS0_DIO_STATUS);
-       writeb((0 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
-       writeb((1 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
-       writeb((2 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
-       writeb((3 << 6) | 0x00, dev->mmio + LAS0_UTC_CTRL);
        /* TODO: set user out source ??? */
 }
 
@@ -1196,8 +1224,8 @@ static int rtd_auto_attach(struct comedi_device *dev,
        struct comedi_subdevice *s;
        int ret;
 
-       if (context < ARRAY_SIZE(rtd520Boards))
-               board = &rtd520Boards[context];
+       if (context < ARRAY_SIZE(rtd520_boards))
+               board = &rtd520_boards[context];
        if (!board)
                return -ENODEV;
        dev->board_ptr = board;
@@ -1254,7 +1282,7 @@ static int rtd_auto_attach(struct comedi_device *dev,
        s->n_chan       = 2;
        s->maxdata      = 0x0fff;
        s->range_table  = &rtd_ao_range;
-       s->insn_write   = rtd_ao_winsn;
+       s->insn_write   = rtd_ao_insn_write;
 
        ret = comedi_alloc_subdev_readback(s);
        if (ret)
@@ -1271,12 +1299,15 @@ static int rtd_auto_attach(struct comedi_device *dev,
        s->insn_bits    = rtd_dio_insn_bits;
        s->insn_config  = rtd_dio_insn_config;
 
-       /* timer/counter subdevices (not currently supported) */
+       /* 8254 Timer/Counter subdevice */
        s = &dev->subdevices[3];
-       s->type         = COMEDI_SUBD_COUNTER;
-       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-       s->n_chan       = 3;
-       s->maxdata      = 0xffff;
+       dev->pacer = comedi_8254_mm_init(dev->mmio + LAS0_8254_TIMER_BASE,
+                                        RTD_CLOCK_BASE, I8254_IO8, 2);
+       if (!dev->pacer)
+               return -ENOMEM;
+
+       comedi_8254_subdevice_init(s, dev->pacer);
+       dev->pacer->insn_config = rtd_counter_insn_config;
 
        rtd_init_board(dev);