[PATCH 12/14] staging: comedi: ii_pci20kc: cleanup the ai subdevice

H Hartley Sweeten hsweeten at visionengravers.com
Wed Jul 24 19:13:51 UTC 2013


The PCI-200341M-1 module can both provide an analog input subdevice
in this driver. The module provides four differential input channels
with four programmable gains.

Currently the gain is configured as an option passed when the board
is attached. Rewrite the ai subdevice support functions to properly
handle the programmable gain.

For aesthetics, redefine the memory map for the module.

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/ii_pci20kc.c | 281 +++++++++++++---------------
 1 file changed, 128 insertions(+), 153 deletions(-)

diff --git a/drivers/staging/comedi/drivers/ii_pci20kc.c b/drivers/staging/comedi/drivers/ii_pci20kc.c
index 1119a1a..069cd95 100644
--- a/drivers/staging/comedi/drivers/ii_pci20kc.c
+++ b/drivers/staging/comedi/drivers/ii_pci20kc.c
@@ -55,21 +55,6 @@ are:
 Options:
   0   Board base address
   1   IRQ
-  2   first option for module 1
-  3   second option for module 1
-  4   first option for module 2
-  5   second option for module 2
-  6   first option for module 3
-  7   second option for module 3
-
-options for PCI-20006M:
-
-options for PCI-20341M:
-  first:   Analog input gain configuration
-	     0  1
-	     1  10
-	     2  100
-	     3  200
 */
 
 #include <linux/module.h>
@@ -119,26 +104,44 @@ options for PCI-20341M:
 #define II20K_AO_MSB_REG(x)		(0x0e + ((x) * 0x08))
 #define II20K_AO_STRB_BOTH_REG		0x1b
 
-#define PCI20341_ID			0x77
-#define PCI20xxx_EMPTY_ID		0xff
-
-#define PCI20341_INIT			0x04
-#define PCI20341_REPMODE		0x00	/* single shot mode */
-#define PCI20341_PACER			0x00	/* Hardware Pacer disabled */
-#define PCI20341_CONFIG_REG		0x10
-#define PCI20341_MOD_STATUS		0x01
-#define PCI20341_OPT_REG		0x11
-#define PCI20341_SET_TIME_REG		0x15
-#define PCI20341_LCHAN_ADDR_REG		0x13
-#define PCI20341_CHAN_LIST		0x80
-#define PCI20341_CC_RESET		0x1b
-#define PCI20341_CHAN_RESET		0x19
-#define PCI20341_SOFT_PACER		0x04
-#define PCI20341_STATUS_REG		0x12
-#define PCI20341_LDATA			0x02
-#define PCI20341_DAISY_CHAIN		0x20	/* On-board inputs only */
-#define PCI20341_MUX			0x04	/* Enable on-board MUX */
-#define PCI20341_SCANLIST		0x80	/* Channel/Gain Scan List */
+#define II20K_ID_PCI200341M_1		0x77	/* 4 AI channels */
+#define II20K_AI_STATUS_CMD_REG		0x01
+#define II20K_AI_STATUS_CMD_BUSY	(1 << 7)
+#define II20K_AI_STATUS_CMD_HW_ENA	(1 << 1)
+#define II20K_AI_STATUS_CMD_EXT_START	(1 << 0)
+#define II20K_AI_LSB_REG		0x02
+#define II20K_AI_MSB_REG		0x03
+#define II20K_AI_PACER_RESET_REG	0x04
+#define II20K_AI_16BIT_DATA_REG		0x06
+#define II20K_AI_CONF_REG		0x10
+#define II20K_AI_CONF_ENA		(1 << 2)
+#define II20K_AI_OPT_REG		0x11
+#define II20K_AI_OPT_TRIG_ENA		(1 << 5)
+#define II20K_AI_OPT_TRIG_INV		(1 << 4)
+#define II20K_AI_OPT_TIMEBASE(x)	(((x) & 0x3) << 1)
+#define II20K_AI_OPT_BURST_MODE		(1 << 0)
+#define II20K_AI_STATUS_REG		0x12
+#define II20K_AI_STATUS_INT		(1 << 7)
+#define II20K_AI_STATUS_TRIG		(1 << 6)
+#define II20K_AI_STATUS_TRIG_ENA	(1 << 5)
+#define II20K_AI_STATUS_PACER_ERR	(1 << 2)
+#define II20K_AI_STATUS_DATA_ERR	(1 << 1)
+#define II20K_AI_STATUS_SET_TIME_ERR	(1 << 0)
+#define II20K_AI_LAST_CHAN_ADDR_REG	0x13
+#define II20K_AI_CUR_ADDR_REG		0x14
+#define II20K_AI_SET_TIME_REG		0x15
+#define II20K_AI_DELAY_LSB_REG		0x16
+#define II20K_AI_DELAY_MSB_REG		0x17
+#define II20K_AI_CHAN_ADV_REG		0x18
+#define II20K_AI_CHAN_RESET_REG		0x19
+#define II20K_AI_START_TRIG_REG		0x1a
+#define II20K_AI_COUNT_RESET_REG	0x1b
+#define II20K_AI_CHANLIST_REG		0x80
+#define II20K_AI_CHANLIST_ONBOARD_ONLY	(1 << 5)
+#define II20K_AI_CHANLIST_GAIN(x)	(((x) & 0x3) << 3)
+#define II20K_AI_CHANLIST_MUX_ENA	(1 << 2)
+#define II20K_AI_CHANLIST_CHAN(x)	(((x) & 0x3) << 0)
+#define II20K_AI_CHANLIST_LEN		0x80
 
 /* the AO range is set by jumpers on the 20006M module */
 static const struct comedi_lrange ii20k_ao_ranges = {
@@ -148,44 +151,20 @@ static const struct comedi_lrange ii20k_ao_ranges = {
 		BIP_RANGE(10)	/* Chan 0 - W1/W3 in   Chan 1 - W2/W4 out */
 	}
 };
-static const struct comedi_lrange range_bipolar0_5 = {
-	1, {
-		BIP_RANGE(0.5)
-	}
-};
-
-static const struct comedi_lrange range_bipolar0_05 = {
-	1, {
-		BIP_RANGE(0.05)
-	}
-};
-
-static const struct comedi_lrange range_bipolar0_025 = {
-	1, {
-		BIP_RANGE(0.025)
-	}
-};
 
-static const struct comedi_lrange *const ii20k_ai_ranges[] = {
-	&range_bipolar5,
-	&range_bipolar0_5,
-	&range_bipolar0_05,
-	&range_bipolar0_025,
+static const struct comedi_lrange ii20k_ai_ranges = {
+	4, {
+		BIP_RANGE(5),		/* gain 1 */
+		BIP_RANGE(0.5),		/* gain 10 */
+		BIP_RANGE(0.05),	/* gain 100 */
+		BIP_RANGE(0.025)	/* gain 200 */
+	},
 };
 
-static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 };
-static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 };
-
 struct ii20k_ao_private {
 	unsigned int last_data[2];
 };
 
-struct ii20k_ai_private {
-	int timebase;
-	int settling_time;
-	int ai_gain;
-};
-
 struct pci20xxx_private {
 	void __iomem *ioaddr;
 };
@@ -241,82 +220,94 @@ static int ii20k_ao_insn_write(struct comedi_device *dev,
 	return insn->n;
 }
 
+static int ii20k_ai_wait_eoc(struct comedi_device *dev,
+			     struct comedi_subdevice *s,
+			     int timeout)
+{
+	void __iomem *iobase = ii20k_module_iobase(dev, s);
+	unsigned char status;
+
+	do {
+		status = readb(iobase + II20K_AI_STATUS_REG);
+		if ((status & II20K_AI_STATUS_INT) == 0)
+			return 0;
+	} while (timeout--);
+
+	return -ETIME;
+}
+
+static void ii20k_ai_setup(struct comedi_device *dev,
+			   struct comedi_subdevice *s,
+			   unsigned int chanspec)
+{
+	void __iomem *iobase = ii20k_module_iobase(dev, s);
+	unsigned int chan = CR_CHAN(chanspec);
+	unsigned int range = CR_RANGE(chanspec);
+	unsigned char val;
+
+	/* initialize module */
+	writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
+
+	/* software conversion */
+	writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
+
+	/* set the time base for the settling time counter based on the gain */
+	val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
+	writeb(val, iobase + II20K_AI_OPT_REG);
+
+	/* set the settling time counter based on the gain */
+	val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
+	writeb(val, iobase + II20K_AI_SET_TIME_REG);
+
+	/* set number of input channels */
+	writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
+
+	/* set the channel list byte */
+	val = II20K_AI_CHANLIST_ONBOARD_ONLY |
+	      II20K_AI_CHANLIST_MUX_ENA |
+	      II20K_AI_CHANLIST_GAIN(range) |
+	      II20K_AI_CHANLIST_CHAN(chan);
+	writeb(val, iobase + II20K_AI_CHANLIST_REG);
+
+	/* reset settling time counter and trigger delay counter */
+	writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
+
+	/* reset channel scanner */
+	writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
+}
+
 static int ii20k_ai_insn_read(struct comedi_device *dev,
 			      struct comedi_subdevice *s,
 			      struct comedi_insn *insn,
 			      unsigned int *data)
 {
-	struct ii20k_ai_private *ai_spriv = s->private;
 	void __iomem *iobase = ii20k_module_iobase(dev, s);
-	unsigned int i = 0, j = 0;
-	int lo, hi;
-	unsigned char eoc;	/* end of conversion */
-	unsigned int clb;	/* channel list byte */
-	unsigned int boarddata;
-
-	/* write number of input channels */
-	writeb(1, iobase + PCI20341_LCHAN_ADDR_REG);
-	clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (ai_spriv->ai_gain << 3)
-	    | CR_CHAN(insn->chanspec);
-	writeb(clb, iobase + PCI20341_CHAN_LIST);
+	int ret;
+	int i;
 
-	/* reset settling time counter and trigger delay counter */
-	writeb(0x00, iobase + PCI20341_CC_RESET);
+	ii20k_ai_setup(dev, s, insn->chanspec);
 
-	writeb(0x00, iobase + PCI20341_CHAN_RESET);
+	for (i = 0; i < insn->n; i++) {
+		unsigned int val;
 
-	/* generate Pacer */
+		/* generate a software start convert signal */
+		readb(iobase + II20K_AI_PACER_RESET_REG);
 
-	for (i = 0; i < insn->n; i++) {
-		/* data polling isn't the niciest way to get the data, I know,
-		 * but there are only 6 cycles (mean) and it is easier than
-		 * the whole interrupt stuff
-		 */
-		j = 0;
-		/* generate Pacer */
-		readb(iobase + PCI20341_SOFT_PACER);
-
-		eoc = readb(iobase + PCI20341_STATUS_REG);
-		/* poll Interrupt Flag */
-		while ((eoc < 0x80) && j < 100) {
-			j++;
-			eoc = readb(iobase + PCI20341_STATUS_REG);
-		}
-		if (j >= 100) {
-			dev_warn(dev->class_dev,
-				 "AI interrupt channel %i polling exit !\n", i);
-			return -EINVAL;
-		}
-		lo = readb(iobase + PCI20341_LDATA);
-		hi = readb(iobase + PCI20341_LDATA + 1);
-		boarddata = lo + 0x100 * hi;
-
-		/* board-data -> comedi-data */
-		data[i] = (short)((boarddata + 0x8000) & 0xffff);
-	}
+		ret = ii20k_ai_wait_eoc(dev, s, 100);
+		if (ret)
+			return ret;
 
-	return i;
-}
+		val = readb(iobase + II20K_AI_LSB_REG);
+		val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
 
-static void ii20k_ai_init(struct comedi_device *dev,
-			  struct comedi_subdevice *s)
-{
-	struct ii20k_ai_private *ai_spriv = s->private;
-	void __iomem *iobase = ii20k_module_iobase(dev, s);
-	unsigned char option;
-
-	/* depends on gain, trigger, repetition mode */
-	option = ai_spriv->timebase | PCI20341_REPMODE;
-
-	/* initialize Module */
-	writeb(PCI20341_INIT, iobase + PCI20341_CONFIG_REG);
-	/* set Pacer */
-	writeb(PCI20341_PACER, iobase + PCI20341_MOD_STATUS);
-	/* option register */
-	writeb(option, iobase + PCI20341_OPT_REG);
-	/* settling time counter */
-	writeb(ai_spriv->settling_time, iobase + PCI20341_SET_TIME_REG);
-	/* trigger not implemented */
+		/* munge two's complement data */
+		val += ((s->maxdata + 1) >> 1);
+		val &= s->maxdata;
+
+		data[i] = val;
+	}
+
+	return insn->n;
 }
 
 static void ii20k_dio_config(struct comedi_device *dev,
@@ -456,13 +447,10 @@ static int ii20k_dio_insn_bits(struct comedi_device *dev,
 }
 
 static int ii20k_init_module(struct comedi_device *dev,
-			     struct comedi_subdevice *s,
-			     struct comedi_devconfig *it)
+			     struct comedi_subdevice *s)
 {
 	struct ii20k_ao_private *ao_spriv;
-	struct ii20k_ai_private *ai_spriv;
 	void __iomem *iobase = ii20k_module_iobase(dev, s);
-	unsigned int opt0 = it->options[(2 * s->index) + 2];
 	unsigned char id;
 
 	id = readb(iobase + II20K_ID_REG);
@@ -482,28 +470,15 @@ static int ii20k_init_module(struct comedi_device *dev,
 		s->insn_read	= ii20k_ao_insn_read;
 		s->insn_write	= ii20k_ao_insn_write;
 		break;
-	case PCI20341_ID:
-		if (opt0 < 0 || opt0 > 3)
-			opt0 = 0;
-
-		ai_spriv = comedi_alloc_spriv(s, sizeof(*ai_spriv));
-		if (!ai_spriv)
-			return -ENOMEM;
-
-		ai_spriv->timebase = pci20341_timebase[opt0];
-		ai_spriv->settling_time = pci20341_settling_time[opt0];
-
+	case II20K_ID_PCI200341M_1:
 		/* Analog Input subdevice */
 		s->type		= COMEDI_SUBD_AI;
-		s->subdev_flags	= SDF_READABLE;
+		s->subdev_flags	= SDF_READABLE | SDF_DIFF;
 		s->n_chan	= 4;
 		s->maxdata	= 0xffff;
-		s->range_table	= ii20k_ai_ranges[opt0];
+		s->range_table	= &ii20k_ai_ranges;
 		s->insn_read	= ii20k_ai_insn_read;
-
-		ii20k_ai_init(dev, s);
 		break;
-	case PCI20xxx_EMPTY_ID:
 	default:
 		s->type = COMEDI_SUBD_UNUSED;
 		break;
@@ -546,7 +521,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
 	if (id & II20K_ID_MOD1_EMPTY) {
 		s->type = COMEDI_SUBD_UNUSED;
 	} else {
-		ret = ii20k_init_module(dev, s, it);
+		ret = ii20k_init_module(dev, s);
 		if (ret)
 			return ret;
 	}
@@ -555,7 +530,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
 	if (id & II20K_ID_MOD2_EMPTY) {
 		s->type = COMEDI_SUBD_UNUSED;
 	} else {
-		ret = ii20k_init_module(dev, s, it);
+		ret = ii20k_init_module(dev, s);
 		if (ret)
 			return ret;
 	}
@@ -564,7 +539,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
 	if (id & II20K_ID_MOD3_EMPTY) {
 		s->type = COMEDI_SUBD_UNUSED;
 	} else {
-		ret = ii20k_init_module(dev, s, it);
+		ret = ii20k_init_module(dev, s);
 		if (ret)
 			return ret;
 	}
-- 
1.8.3.2



More information about the devel mailing list