The board has an 8254 timer/counter. Add support for this subdevice.

Signed-off-by: H Hartley Sweeten <hswee...@visionengravers.com>
Cc: Ian Abbott <abbo...@mev.co.uk>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 drivers/staging/comedi/drivers/multiq3.c | 147 ++++++++++++++++++++++++++++++-
 1 file changed, 146 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/comedi/drivers/multiq3.c 
b/drivers/staging/comedi/drivers/multiq3.c
index fc743df..50efc7c 100644
--- a/drivers/staging/comedi/drivers/multiq3.c
+++ b/drivers/staging/comedi/drivers/multiq3.c
@@ -38,6 +38,8 @@
 
 #include "../comedidev.h"
 
+#include "comedi_8254.h"       /* just for the register map */
+
 /*
  * Register map
  */
@@ -47,6 +49,8 @@
 #define MULTIQ3_AI_REG                 0x04
 #define MULTIQ3_AI_CONV_REG            0x04
 #define MULTIQ3_STATUS_REG             0x06
+#define MULTIQ3_STATUS_CT(x)           (((x) == 0) ? BIT(0) :  \
+                                        ((x) == 1) ? BIT(2) : BIT(1))
 #define MULTIQ3_STATUS_EOC             BIT(3)
 #define MULTIQ3_STATUS_EOC_I           BIT(4)
 #define MULTIQ3_CTRL_REG               0x06
@@ -257,6 +261,126 @@ static int multiq3_encoder_insn_config(struct 
comedi_device *dev,
        return insn->n;
 }
 
+static unsigned int multiq3_i8254_read(struct comedi_device *dev,
+                                      unsigned int reg)
+{
+       /* select the 8254 register then read the value */
+       multiq3_set_ctrl(dev, MULTIQ3_CTRL_RC(reg));
+       return inb(dev->iobase + MULTIQ3_CLK_REG);
+}
+
+static void multiq3_i8254_write(struct comedi_device *dev,
+                               unsigned int val, unsigned int reg)
+{
+       /* select the 8254 register then write the value */
+       multiq3_set_ctrl(dev, MULTIQ3_CTRL_RC(reg));
+       outb(val, dev->iobase + MULTIQ3_CLK_REG);
+}
+
+static int multiq3_8254_set_mode(struct comedi_device *dev,
+                                unsigned int chan, unsigned int mode)
+{
+       unsigned int byte;
+
+       if (mode > (I8254_MODE5 | I8254_BCD))
+               return -EINVAL;
+
+       byte = I8254_CTRL_SEL_CTR(chan) |       /* select counter */
+              I8254_CTRL_LSB_MSB |             /* load LSB then MSB */
+              mode;                            /* mode and BCD|binary */
+       multiq3_i8254_write(dev, byte, I8254_CTRL_REG);
+
+       return 0;
+}
+
+static int multiq3_8254_insn_read(struct comedi_device *dev,
+                                 struct comedi_subdevice *s,
+                                 struct comedi_insn *insn,
+                                 unsigned int *data)
+{
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int ctrl = I8254_CTRL_LATCH | I8254_CTRL_SEL_CTR(chan);
+       unsigned int val;
+       int i;
+
+       for (i = 0; i < insn->n; i++) {
+               /* latch counter */
+               multiq3_i8254_write(dev, ctrl, I8254_CTRL_REG);
+
+               /* read LSB then MSB */
+               val = multiq3_i8254_read(dev, chan);
+               val |= (multiq3_i8254_read(dev, chan) << 8);
+
+               data[i] = val;
+       }
+
+       return insn->n;
+}
+
+static int multiq3_8254_insn_write(struct comedi_device *dev,
+                                  struct comedi_subdevice *s,
+                                  struct comedi_insn *insn,
+                                  unsigned int *data)
+{
+       unsigned int chan = CR_CHAN(insn->chanspec);
+
+       if (insn->n) {
+               unsigned int val = data[insn->n - 1];
+
+               /* load LSB then MSB */
+               multiq3_i8254_write(dev, val & 0xff, chan);
+               multiq3_i8254_write(dev, (val >> 8) & 0xff, chan);
+       }
+
+       return insn->n;
+}
+
+static int multiq3_8254_insn_config(struct comedi_device *dev,
+                                   struct comedi_subdevice *s,
+                                   struct comedi_insn *insn,
+                                   unsigned int *data)
+{
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int status;
+       int ret;
+
+       switch (data[0]) {
+       case INSN_CONFIG_RESET:
+               ret = multiq3_8254_set_mode(dev, chan,
+                                           I8254_MODE0 | I8254_BINARY);
+               if (ret)
+                       return ret;
+               break;
+       case INSN_CONFIG_SET_COUNTER_MODE:
+               ret = multiq3_8254_set_mode(dev, chan, data[1]);
+               if (ret)
+                       return ret;
+               break;
+       case INSN_CONFIG_GET_COUNTER_STATUS:
+               data[1] = 0;
+               status = inw(dev->iobase + MULTIQ3_STATUS_REG);
+               if (status & MULTIQ3_STATUS_CT(chan))
+                       data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
+               data[2] = COMEDI_COUNTER_TERMINAL_COUNT;
+               break;
+       case INSN_CONFIG_8254_READ_STATUS:
+               multiq3_i8254_write(dev, I8254_CTRL_READBACK_STATUS |
+                                        I8254_CTRL_READBACK_SEL_CTR(chan),
+                                   I8254_CTRL_REG);
+
+               data[1] = multiq3_i8254_read(dev, chan);
+               break;
+       case INSN_CONFIG_GET_CLOCK_SRC:
+               data[1] = 0;
+               data[2] = I8254_OSC_BASE_2MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return insn->n;
+}
+
 static int multiq3_attach(struct comedi_device *dev,
                          struct comedi_devconfig *it)
 {
@@ -268,7 +392,7 @@ static int multiq3_attach(struct comedi_device *dev,
        if (ret)
                return ret;
 
-       ret = comedi_alloc_subdevices(dev, 5);
+       ret = comedi_alloc_subdevices(dev, 6);
        if (ret)
                return ret;
 
@@ -325,6 +449,27 @@ static int multiq3_attach(struct comedi_device *dev,
        for (i = 0; i < s->n_chan; i++)
                multiq3_encoder_reset(dev, i);
 
+       /*
+        * 8254 Counter/Timer subdevice
+        *
+        * This board uses the control register to address the four
+        * registers in the 8254 timer. Because of this, the comedi_8254
+        * driver cannot be used to provide support for the timer.
+        */
+       s = &dev->subdevices[5];
+       s->type         = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       s->n_chan       = 3;
+       s->maxdata      = 0xffff;
+       s->range_table  = &range_unknown;
+       s->insn_read    = multiq3_8254_insn_read;
+       s->insn_write   = multiq3_8254_insn_write;
+       s->insn_config  = multiq3_8254_insn_config;
+
+       /* reset all the counters by setting them to I8254_MODE0 */
+       for (i = 0; i < 3; i++)
+               multiq3_8254_set_mode(dev, i, I8254_MODE0 | I8254_BINARY);
+
        return 0;
 }
 
-- 
2.5.1

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to