[PATCH 27/29] staging: comedi: addi_apci_1032: hook-up the interrupt subdevice

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The board supported by this driver can generate an interrupt based
on the state of input channels 0-15.

The apci1032_intr_insn_config() function is used to configure which
inputs are used to generate the interrupt. Currently this function
is broken since it does not follow the comedi API for insn_config
functions. Fix this function by implementing the, currently unused,
config instruction INSN_CONFIG_DIGITAL_TRIG.

Add the remaining subdevice operations necessary for the interrupt
subdevice to support async commands.

Fix the subdevice initialization so that if the interrupt is not
available the subdevice is set as COMEDI_SUBD_UNUSED.

Signed-off-by: H Hartley Sweeten <hsweeten@xxxxxxxxxxxxxxxxxxx>
Cc: Ian Abbott <abbotti@xxxxxxxxx>
Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
 drivers/staging/comedi/drivers/addi_apci_1032.c | 250 +++++++++++++++++-------
 1 file changed, 184 insertions(+), 66 deletions(-)

diff --git a/drivers/staging/comedi/drivers/addi_apci_1032.c b/drivers/staging/comedi/drivers/addi_apci_1032.c
index 5a300d9..b273635 100644
--- a/drivers/staging/comedi/drivers/addi_apci_1032.c
+++ b/drivers/staging/comedi/drivers/addi_apci_1032.c
@@ -33,8 +33,6 @@
 #include "comedi_fc.h"
 #include "amcc_s5933.h"
 
-#include "addi-data/addi_common.h"
-
 /*
  * I/O Register Map
  */
@@ -47,72 +45,191 @@
 #define APCI1032_CTRL_INT_AND		(1 << 1)
 #define APCI1032_CTRL_INT_ENA		(1 << 2)
 
-/* Digital Input IRQ Function Selection */
-#define ADDIDATA_OR				0
-#define ADDIDATA_AND				1
+struct apci1032_private {
+	unsigned int mode1;	/* rising-edge/high level channels */
+	unsigned int mode2;	/* falling-edge/low level channels */
+	unsigned int ctrl;	/* interrupt mode OR (edge) . AND (level) */
+};
 
-static unsigned int ui_InterruptStatus;
+static int apci1032_reset(struct comedi_device *dev)
+{
+	/* disable the interrupts */
+	outl(0x0, dev->iobase + APCI1032_CTRL_REG);
+	/* Reset the interrupt status register */
+	inl(dev->iobase + APCI1032_STATUS_REG);
+	/* Disable the and/or interrupt */
+	outl(0x0, dev->iobase + APCI1032_MODE1_REG);
+	outl(0x0, dev->iobase + APCI1032_MODE2_REG);
+
+	return 0;
+}
 
 /*
- * data[0] : 1 Enable  Digital Input Interrupt
- *           0 Disable Digital Input Interrupt
- * data[1] : 0 ADDIDATA Interrupt OR LOGIC
- *         : 1 ADDIDATA Interrupt AND LOGIC
- * data[2] : Interrupt mask for the mode 1
- * data[3] : Interrupt mask for the mode 2
+ * Change-Of-State (COS) interrupt configuration
+ *
+ * Channels 0 to 15 are interruptible. These channels can be configured
+ * to generate interrupts based on AND/OR logic for the desired channels.
+ *
+ *	OR logic
+ *		- reacts to rising or falling edges
+ *		- interrupt is generated when any enabled channel
+ *		  meet the desired interrupt condition
+ *
+ *	AND logic
+ *		- reacts to changes in level of the selected inputs
+ *		- interrupt is generated when all enabled channels
+ *		  meet the desired interrupt condition
+ *		- after an interrupt, a change in level must occur on
+ *		  the selected inputs to release the IRQ logic
+ *
+ * The COS interrupt must be configured before it can be enabled.
+ *
+ *	data[0] : INSN_CONFIG_DIGITAL_TRIG
+ *	data[1] : 0 = OR (edge) interrupts
+ *		  1 = AND (level) interrupts
+ *	data[2] : rising-edge/high level channels
+ *	data[3] : falling-edge/low level channels
  */
-static int apci1032_intr_insn_config(struct comedi_device *dev,
-				     struct comedi_subdevice *s,
-				     struct comedi_insn *insn,
-				     unsigned int *data)
+static int apci1032_cos_insn_config(struct comedi_device *dev,
+				    struct comedi_subdevice *s,
+				    struct comedi_insn *insn,
+				    unsigned int *data)
 {
-	unsigned int ui_TmpValue;
-	unsigned int ul_Command1 = 0;
-	unsigned int ul_Command2 = 0;
-
-  /*******************************/
-	/* Set the digital input logic */
-  /*******************************/
-	if (data[0] == ADDIDATA_ENABLE) {
-		ul_Command1 = ul_Command1 | data[2];
-		ul_Command2 = ul_Command2 | data[3];
-		outl(ul_Command1, dev->iobase + APCI1032_MODE1_REG);
-		outl(ul_Command2, dev->iobase + APCI1032_MODE2_REG);
-		if (data[1] == ADDIDATA_OR) {
-			outl(APCI1032_CTRL_INT_ENA |
-			     APCI1032_CTRL_INT_OR,
-			     dev->iobase + APCI1032_CTRL_REG);
-			ui_TmpValue =
-				inl(dev->iobase + APCI1032_CTRL_REG);
-		}		/* if (data[1] == ADDIDATA_OR) */
-		else
-			outl(APCI1032_CTRL_INT_ENA |
-			     APCI1032_CTRL_INT_AND,
-			     dev->iobase + APCI1032_CTRL_REG);
-				/* else if(data[1] == ADDIDATA_OR) */
-	}			/*  if( data[0] == ADDIDATA_ENABLE) */
-	else {
-		ul_Command1 = ul_Command1 & 0xFFFF0000;
-		ul_Command2 = ul_Command2 & 0xFFFF0000;
-		outl(ul_Command1, dev->iobase + APCI1032_MODE1_REG);
-		outl(ul_Command2, dev->iobase + APCI1032_MODE2_REG);
-		outl(0x0, dev->iobase + APCI1032_CTRL_REG);
-	}			/* else if  ( data[0] == ADDIDATA_ENABLE) */
+	struct apci1032_private *devpriv = dev->private;
+
+	switch (data[0]) {
+	case INSN_CONFIG_DIGITAL_TRIG:
+		devpriv->mode1 = data[2];
+		devpriv->mode2 = data[3];
+
+		if (devpriv->mode1 || devpriv->mode2) {
+			devpriv->ctrl = APCI1032_CTRL_INT_ENA;
+			if (data[1] == 1)
+				devpriv->ctrl = APCI1032_CTRL_INT_AND;
+			else
+				devpriv->ctrl = APCI1032_CTRL_INT_OR;
+		} else {
+			devpriv->ctrl = 0;
+			apci1032_reset(dev);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	return insn->n;
 }
 
+static int apci1032_cos_insn_bits(struct comedi_device *dev,
+				  struct comedi_subdevice *s,
+				  struct comedi_insn *insn,
+				  unsigned int *data)
+{
+	data[1] = s->state;
+
+	return 0;
+}
+
+static int apci1032_cos_cmdtest(struct comedi_device *dev,
+				struct comedi_subdevice *s,
+				struct comedi_cmd *cmd)
+{
+	int err = 0;
+
+	/* Step 1 : check if triggers are trivially valid */
+
+	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+	if (err)
+		return 1;
+
+	/* Step 2a : make sure trigger sources are unique */
+	/* Step 2b : and mutually compatible */
+
+	if (err)
+		return 2;
+
+	/* step 3: */
+
+	if (cmd->start_arg != 0) {
+		cmd->start_arg = 0;
+		err++;
+	}
+	if (cmd->scan_begin_arg != 0) {
+		cmd->scan_begin_arg = 0;
+		err++;
+	}
+	if (cmd->convert_arg != 0) {
+		cmd->convert_arg = 0;
+		err++;
+	}
+	if (cmd->scan_end_arg != 1) {
+		cmd->scan_end_arg = 1;
+		err++;
+	}
+	if (cmd->stop_arg != 0) {
+		cmd->stop_arg = 0;
+		err++;
+	}
+
+	if (err)
+		return 3;
+
+	/* step 4: ignored */
+
+	if (err)
+		return 4;
+
+	return 0;
+}
+
+/*
+ * Change-Of-State (COS) 'do_cmd' operation
+ *
+ * Enable the COS interrupt as configured by apci1032_cos_insn_config().
+ */
+static int apci1032_cos_cmd(struct comedi_device *dev,
+			    struct comedi_subdevice *s)
+{
+	struct apci1032_private *devpriv = dev->private;
+
+	if (!devpriv->ctrl) {
+		dev_warn(dev->class_dev,
+			"Interrupts disabled due to mode configuration!\n");
+		return -EINVAL;
+	}
+
+	outl(devpriv->mode1, dev->iobase + APCI1032_MODE1_REG);
+	outl(devpriv->mode2, dev->iobase + APCI1032_MODE2_REG);
+	outl(devpriv->ctrl, dev->iobase + APCI1032_CTRL_REG);
+
+	return 0;
+}
+
+static int apci1032_cos_cancel(struct comedi_device *dev,
+			       struct comedi_subdevice *s)
+{
+	return apci1032_reset(dev);
+}
+
 static irqreturn_t apci1032_interrupt(int irq, void *d)
 {
 	struct comedi_device *dev = d;
+	struct comedi_subdevice *s = dev->read_subdev;
 	unsigned int ctrl;
 
 	/* disable the interrupt */
 	ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
 	outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
 
-	ui_InterruptStatus = inl(dev->iobase + APCI1032_STATUS_REG);
-	ui_InterruptStatus = ui_InterruptStatus & 0X0000FFFF;
+	s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
+	comedi_buf_put(s->async, s->state);
+	s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+	comedi_event(dev, s);
 
 	/* enable the interrupt */
 	outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
@@ -130,27 +247,20 @@ static int apci1032_di_insn_bits(struct comedi_device *dev,
 	return insn->n;
 }
 
-static int apci1032_reset(struct comedi_device *dev)
-{
-	/* disable the interrupts */
-	outl(0x0, dev->iobase + APCI1032_CTRL_REG);
-	/* Reset the interrupt status register */
-	inl(dev->iobase + APCI1032_STATUS_REG);
-	/* Disable the and/or interrupt */
-	outl(0x0, dev->iobase + APCI1032_MODE1_REG);
-	outl(0x0, dev->iobase + APCI1032_MODE2_REG);
-
-	return 0;
-}
-
 static int apci1032_attach_pci(struct comedi_device *dev,
 			       struct pci_dev *pcidev)
 {
+	struct apci1032_private *devpriv;
 	struct comedi_subdevice *s;
 	int ret;
 
 	dev->board_name = dev->driver->driver_name;
 
+	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
+	if (!devpriv)
+		return -ENOMEM;
+	dev->private = devpriv;
+
 	ret = comedi_pci_enable(pcidev, dev->board_name);
 	if (ret)
 		return ret;
@@ -178,14 +288,22 @@ static int apci1032_attach_pci(struct comedi_device *dev,
 	s->range_table	= &range_digital;
 	s->insn_bits	= apci1032_di_insn_bits;
 
+	/* Change-Of-State (COS) interrupt subdevice */
+	s = &dev->subdevices[1];
 	if (dev->irq) {
-		s = &dev->subdevices[1];
-		s->type		= COMEDI_SUBD_DI;
+		dev->read_subdev = s;
+		s->type		= COMEDI_SUBD_DI | SDF_CMD_READ;
 		s->subdev_flags	= SDF_READABLE;
 		s->n_chan	= 1;
 		s->maxdata	= 1;
 		s->range_table	= &range_digital;
-		s->insn_config	= apci1032_intr_insn_config;
+		s->insn_config	= apci1032_cos_insn_config;
+		s->insn_bits	= apci1032_cos_insn_bits;
+		s->do_cmdtest	= apci1032_cos_cmdtest;
+		s->do_cmd	= apci1032_cos_cmd;
+		s->cancel	= apci1032_cos_cancel;
+	} else {
+		s->type		= COMEDI_SUBD_UNUSED;
 	}
 
 	apci1032_reset(dev);
-- 
1.7.11

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


[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux