[PATCH v2 13/14] staging: comedi: multiq3: add 8254 counter/timer subdevice support
H Hartley Sweeten
hsweeten at visionengravers.com
Mon Oct 5 22:33:19 UTC 2015
The board has an 8254 timer/counter. Add support for this subdevice.
Signed-off-by: H Hartley Sweeten <hsweeten at visionengravers.com>
Cc: Ian Abbott <abbotti at mev.co.uk>
Cc: Greg Kroah-Hartman <gregkh at 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
More information about the devel
mailing list