[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